From 743b427168caa926170995908bb33d0243eb2737 Mon Sep 17 00:00:00 2001 From: mabam Date: Wed, 18 Mar 2015 22:05:00 +0100 Subject: [PATCH] CAP 6.0 pl198 + asip1 --- CAP.faq | 223 + CAP60.README | 559 ++ Configure | 1975 +++++++ LICENSE | 1 + MANIFEST | 535 ++ MODIFICATIONS | 73 + Makefile | 94 + NOTES | 231 + NOTICE | 38 + PORTING | 154 + README | 338 ++ applications/Makefile.m4 | 29 + applications/README | 12 + applications/aufs/INSTALLATION | 234 + applications/aufs/Makefile.m4 | 193 + applications/aufs/NOTES | 172 + applications/aufs/README | 59 + applications/aufs/abmisc2.c | 83 + applications/aufs/afpavl.c | 316 ++ applications/aufs/afpavl.h | 38 + applications/aufs/afpdid.c | 1190 +++++ applications/aufs/afpdid.h | 21 + applications/aufs/afpdir.c | 1070 ++++ applications/aufs/afpdsi.c | 2029 +++++++ applications/aufs/afpdsi.h | 108 + applications/aufs/afpdt.c | 2159 ++++++++ applications/aufs/afpdt.h | 148 + applications/aufs/afpfid.c | 99 + applications/aufs/afpfile.c | 726 +++ applications/aufs/afpfork.c | 705 +++ applications/aufs/afpgc.c | 251 + applications/aufs/afpgc.h | 48 + applications/aufs/afpid.notes | 160 + applications/aufs/afpidlist.c | 102 + applications/aufs/afpidsrvr.c | 830 +++ applications/aufs/afpidtool.c | 216 + applications/aufs/afpmisc.c | 106 + applications/aufs/afpntoh.h | 140 + applications/aufs/afpos.c | 5258 +++++++++++++++++++ applications/aufs/afposenum.c | 990 ++++ applications/aufs/afposfi.c | 907 ++++ applications/aufs/afposncs.c | 817 +++ applications/aufs/afposncs.h | 40 + applications/aufs/afppasswd.c | 167 + applications/aufs/afppasswd.h | 24 + applications/aufs/afps.h | 259 + applications/aufs/afpserver.c | 1677 ++++++ applications/aufs/afpspd.c | 410 ++ applications/aufs/afpudb.c | 391 ++ applications/aufs/afpudb.h | 45 + applications/aufs/afpvols | 12 + applications/aufs/afpvols.c | 1043 ++++ applications/aufs/afpvols.h | 48 + applications/aufs/aufs.c | 1977 +++++++ applications/aufs/aufs_vers | 1 + applications/aufs/aufs_vers.sh | 17 + applications/aufs/aufscicon.c | 2389 +++++++++ applications/aufs/aufsicon.c | 2179 ++++++++ applications/aufs/aufsv.c | 2 + applications/aufs/design.notes | 537 ++ applications/aufs/makefile | 168 + applications/aufs/sizeserver.c | 89 + applications/aufs/sizeserver.h | 4 + applications/aufs/todo | 76 + applications/aufs/user.doc | 163 + applications/aufs/whatiswhat | 33 + applications/lwsrv/DBfile | 552 ++ applications/lwsrv/LWFonts | 18 + applications/lwsrv/LWIIfgFonts | 37 + applications/lwsrv/LWPlusFonts | 38 + applications/lwsrv/Makefile.m4 | 144 + applications/lwsrv/README | 638 +++ applications/lwsrv/fix.serial.ps | 29 + applications/lwsrv/fontlist.c | 150 + applications/lwsrv/fontlist.h | 30 + applications/lwsrv/list.c | 373 ++ applications/lwsrv/list.h | 49 + applications/lwsrv/lwsrv.c | 2765 ++++++++++ applications/lwsrv/lwsrv.conf | 13 + applications/lwsrv/lwsrvconfig.c | 710 +++ applications/lwsrv/makefile | 76 + applications/lwsrv/packed.c | 118 + applications/lwsrv/packed.h | 46 + applications/lwsrv/papstream.c | 182 + applications/lwsrv/papstream.h | 55 + applications/lwsrv/parse.c | 402 ++ applications/lwsrv/parse.h | 45 + applications/lwsrv/parsel.l | 226 + applications/lwsrv/parsey.y | 478 ++ applications/lwsrv/procset.c | 396 ++ applications/lwsrv/procset.h | 38 + applications/lwsrv/query.c | 201 + applications/lwsrv/query.h | 64 + applications/lwsrv/query.ps | 207 + applications/lwsrv/simple.c | 1739 ++++++ applications/lwsrv/spmisc.c | 117 + applications/lwsrv/spmisc.h | 27 + applications/lwsrv/whatiswhat | 12 + applications/makefile | 34 + applications/papif/Makefile.m4 | 80 + applications/papif/README | 222 + applications/papif/add_at_printer | 195 + applications/papif/cap.printers | 15 + applications/papif/makefile | 65 + applications/papif/papif.c | 1851 +++++++ applications/papif/papif.interface.template | 1046 ++++ applications/papif/papof.c | 275 + applications/papif/printcap.samp1 | 24 + applications/papif/printcap.samp2 | 22 + cap60.patches/asip1.patch | 3801 ++++++++++++++ cap60.patches/cicon.patch | 2605 +++++++++ cap60.patches/desktop.patch | 657 +++ cap60.patches/extnd.patch | 280 + cap60.pl198+asip.README | 22 + conf.func.lst | 37 + conf.func.sh | 345 ++ conf.sysv.lst | 41 + conf.sysv.sh | 191 + contrib/AppManager/Makefile | 13 + contrib/AppManager/README | 93 + contrib/AppManager/aufslock.c | 95 + contrib/AppManager/aufsmon.c | 212 + contrib/AsyncATalk/INSTALLATION | 50 + contrib/AsyncATalk/Makefile | 56 + contrib/AsyncATalk/README.rfc | 123 + contrib/AsyncATalk/async.1.4.hqx | 475 ++ contrib/AsyncATalk/async.c | 1345 +++++ contrib/AsyncATalk/async.h | 172 + contrib/AsyncATalk/asyncad.c | 184 + contrib/AsyncATalk/atalkdbm.c | 228 + contrib/AsyncATalk/macros.h | 51 + contrib/AufsTools/MANIFEST | 60 + contrib/AufsTools/Makefile | 30 + contrib/AufsTools/README | 24 + contrib/AufsTools/binhex/8to6.c | 64 + contrib/AufsTools/binhex/Makefile | 35 + contrib/AufsTools/binhex/aufs.h | 95 + contrib/AufsTools/binhex/binhex.c | 192 + contrib/AufsTools/binhex/crc.c | 81 + contrib/AufsTools/binhex/gethead.c | 172 + contrib/AufsTools/binhex/run.c | 59 + contrib/AufsTools/capit/Makefile | 11 + contrib/AufsTools/capit/capit.c | 284 + contrib/AufsTools/man/binhex.1 | 23 + contrib/AufsTools/man/capit.1 | 22 + contrib/AufsTools/man/cleanup.1 | 12 + contrib/AufsTools/man/drag.1 | 18 + contrib/AufsTools/man/dup.1 | 20 + contrib/AufsTools/man/m2u.1 | 17 + contrib/AufsTools/man/macp.1 | 16 + contrib/AufsTools/man/makeman | 5 + contrib/AufsTools/man/mcmp.1 | 11 + contrib/AufsTools/man/mcvert.1 | 149 + contrib/AufsTools/man/newfolder.1 | 15 + contrib/AufsTools/man/sit.1 | 59 + contrib/AufsTools/man/toaufs.1 | 23 + contrib/AufsTools/man/trash.1 | 15 + contrib/AufsTools/man/unsit.1 | 131 + contrib/AufsTools/man/unstuffit.1 | 90 + contrib/AufsTools/mcvert/Makefile | 27 + contrib/AufsTools/mcvert/hqxify.c | 588 +++ contrib/AufsTools/mcvert/mactypes.h | 177 + contrib/AufsTools/mcvert/mcvert.c | 379 ++ contrib/AufsTools/mcvert/unpack.c | 192 + contrib/AufsTools/shell/Makefile | 6 + contrib/AufsTools/shell/cleanup | 7 + contrib/AufsTools/shell/drag | 21 + contrib/AufsTools/shell/dup | 21 + contrib/AufsTools/shell/m2u | 7 + contrib/AufsTools/shell/macp | 4 + contrib/AufsTools/shell/mcmp | 9 + contrib/AufsTools/shell/newfolder | 7 + contrib/AufsTools/shell/toaufs | 6 + contrib/AufsTools/shell/trash | 4 + contrib/AufsTools/shell/u2m | 7 + contrib/AufsTools/stuffit/Makefile | 19 + contrib/AufsTools/stuffit/sit.c | 476 ++ contrib/AufsTools/stuffit/sit.h | 72 + contrib/AufsTools/stuffit/updcrc.c | 191 + contrib/AufsTools/unstuffit/Makefile | 24 + contrib/AufsTools/unstuffit/getopt.c | 64 + contrib/AufsTools/unstuffit/stuffit.h | 64 + contrib/AufsTools/unstuffit/unsit.c | 1158 ++++ contrib/AufsTools/unstuffit/updcrc.c | 191 + contrib/DeskTop/Makefile | 24 + contrib/DeskTop/README | 50 + contrib/DeskTop/builddt.c | 632 +++ contrib/DeskTop/dt.h | 184 + contrib/DeskTop/dtmisc.c | 82 + contrib/DeskTop/dumpdt.c | 139 + contrib/MacPS/Installation | 75 + contrib/MacPS/Makefile | 35 + contrib/MacPS/ReadMe | 101 + contrib/MacPS/macaux.c | 177 + contrib/MacPS/macps-22.hdr | 44 + contrib/MacPS/macps-22.shar | 1339 +++++ contrib/MacPS/macps.1 | 87 + contrib/MacPS/macps.c | 354 ++ contrib/MacPS/macps.config | 13 + contrib/MacPS/prepfix.1 | 70 + contrib/MacPS/prepfix.c | 251 + contrib/MacPS/str.h | 39 + contrib/MacPS/ucbwhich.c | 125 + contrib/MacPS/ucbwhich.h | 23 + contrib/Makefile.m4 | 82 + contrib/Messages/Makefile | 47 + contrib/Messages/README | 62 + contrib/Messages/dot.forward | 1 + contrib/Messages/macto.1l | 71 + contrib/Messages/macuser.1l | 53 + contrib/Messages/macuser.c | 431 ++ contrib/Messages/macwho.1l | 40 + contrib/Messages/macwho.c | 77 + contrib/Messages/messages.c | 204 + contrib/Messages/messages.hqx | 545 ++ contrib/Messages/notify.c | 275 + contrib/Messages/notify.h | 51 + contrib/README | 32 + contrib/Timelord/Makefile | 17 + contrib/Timelord/README | 34 + contrib/Timelord/tardis.1.3.sit.hqx | 337 ++ contrib/Timelord/timelord.1l | 32 + contrib/Timelord/timelord.c | 353 ++ contrib/Timelord/timelord.h | 25 + contrib/atprint | 31 + contrib/aufsicon.hqx | 340 ++ contrib/aufsmkkey.c | 162 + contrib/aufsmkusr.c | 475 ++ contrib/cvt2apple.c | 396 ++ contrib/cvt2cap.c | 303 ++ contrib/lwrename.c | 666 +++ contrib/lwsrv-relay | 35 + contrib/makefile | 67 + contrib/printQDA.hqx | 596 +++ contrib/printqueue.c | 691 +++ contrib/snitch.c | 503 ++ doc/Makefile | 11 + doc/README | 13 + doc/abmisc.doc | 120 + doc/asp.notes | 139 + doc/atp.notes | 57 + doc/cap.auth.doc | 28 + doc/cap.notes | 106 + doc/glossary | 75 + doc/install.ms | 567 ++ doc/iptalk.cookbook | 76 + doc/nbp.ext | 232 + doc/pap.notes | 60 + doc/print.cookbook | 294 ++ doc/sched.notes | 210 + doc/uab.desc.ms | 174 + doc/uar.cookbook | 86 + etc/Makefile.m4 | 47 + etc/README | 8 + etc/S99appletalk | 59 + etc/atalk.local | 21 + etc/atis.c | 1251 +++++ etc/aufsIPFilter | 26 + etc/etalk.local | 20 + etc/kill-cap-servers | 18 + etc/list-cap-servers | 2 + etc/makefile | 51 + etc/nisaux.c | 338 ++ etc/start-cap-servers | 45 + extras/Makefile.m4 | 33 + extras/README | 11 + extras/afpfile | 31 + extras/att_getopt.c | 120 + extras/des.c | 524 ++ extras/dummy.des.c | 59 + extras/iwif.c | 101 + extras/lib.cap.macusers | 14 + extras/lib.cap.refused | 25 + extras/lnof.c | 39 + extras/makefile | 40 + extras/printcap.iw | 7 + gen.makes | 38 + lib/Makefile.m4 | 37 + lib/afp/Makefile.m4 | 59 + lib/afp/README | 5 + lib/afp/afpcmd.c | 656 +++ lib/afp/afperr.c | 109 + lib/afp/afpidaufs.c | 637 +++ lib/afp/afpidaufs.h | 182 + lib/afp/afpidclnt.c | 307 ++ lib/afp/afpidgnrl.c | 817 +++ lib/afp/afpidnames.h | 25 + lib/afp/afposlock.c | 621 +++ lib/afp/afppacks.c | 754 +++ lib/afp/afppass.c | 771 +++ lib/afp/makefile | 58 + lib/afpc/Makefile.m4 | 33 + lib/afpc/README | 22 + lib/afpc/afpc.c | 970 ++++ lib/afpc/afpc.mss | 965 ++++ lib/afpc/afpcc.c | 879 ++++ lib/afpc/makefile | 39 + lib/afpc/probs | 33 + lib/cap/Makefile.m4 | 90 + lib/cap/abasp.c | 2490 +++++++++ lib/cap/abasp.h | 146 + lib/cap/abatp.c | 1525 ++++++ lib/cap/abatp.h | 121 + lib/cap/abauxddp.c | 552 ++ lib/cap/abauxnbp.c | 582 ++ lib/cap/abddp.c | 459 ++ lib/cap/abkas.c | 1287 +++++ lib/cap/abkip.c | 695 +++ lib/cap/ablap.c | 179 + lib/cap/ablog.c | 182 + lib/cap/abmisc.c | 626 +++ lib/cap/abnbp.c | 824 +++ lib/cap/abnet.c | 455 ++ lib/cap/abpap.c | 1023 ++++ lib/cap/abpap.h | 177 + lib/cap/abpapc.c | 279 + lib/cap/abpaps.c | 631 +++ lib/cap/abpp.c | 119 + lib/cap/abqueue.c | 307 ++ lib/cap/absched.c | 922 ++++ lib/cap/abversion.c | 57 + lib/cap/abzip.c | 211 + lib/cap/atalkdbm.c | 619 +++ lib/cap/atalkdbm.h | 24 + lib/cap/authenticate.c | 447 ++ lib/cap/cap_conf.h | 44 + lib/cap/makefile | 94 + lib/cap/scandir.c | 139 + lib/cap/todist | 23 + lib/cap/whatiswhat | 18 + lib/makefile | 41 + lib/xenix/Makefile | 41 + lib/xenix/fsync.c | 19 + lib/xenix/ftruncate.c | 50 + lib/xenix/groups.c | 36 + lib/xenix/rename.c | 82 + lib/xenix/scandir.c | 108 + m4.setup | 383 ++ man/AUFS.1 | 481 ++ man/AUFS.8 | 381 ++ man/CAP.3 | 74 + man/CAP.8 | 136 + man/UAB.8 | 161 + man/aarpd.8 | 69 + man/ash.1 | 108 + man/atalk.local.5 | 141 + man/atis.8 | 156 + man/atlook.1 | 164 + man/atprint.1 | 105 + man/aufsmkkey.8 | 65 + man/aufsmkusr.8 | 107 + man/cvt2apple.1 | 58 + man/etalk.local.5 | 47 + man/getzones.1 | 51 + man/instappl.1 | 97 + man/lwrename.8 | 168 + man/lwsrv.8 | 276 + man/makefile | 49 + man/papif.8 | 429 ++ man/snitch.1 | 117 + netat/Makefile | 11 + netat/Makefile.m4 | 15 + netat/aberrors.h | 83 + netat/abnbp.h | 131 + netat/abqueue.h | 33 + netat/afp.h | 287 + netat/afpc.h | 24 + netat/afpcmd.h | 723 +++ netat/afppass.h | 101 + netat/appletalk.h | 544 ++ netat/compat.h | 71 + netat/fcntldomv.h | 35 + netat/macfile.h | 122 + netat/sysvcompat.h | 237 + samples/Makefile.m4 | 117 + samples/README | 36 + samples/ash.c | 1102 ++++ samples/atistest.c | 138 + samples/atlook.c | 678 +++ samples/getzones.c | 127 + samples/instappl.c | 181 + samples/isrv.c | 245 + samples/look.c | 455 ++ samples/lwpr.c | 363 ++ samples/makefile | 116 + samples/papstatus.c | 331 ++ samples/ruiwpr.c | 357 ++ samples/tlw.c | 329 ++ support/capd/Makefile.m4 | 39 + support/capd/README | 11 + support/capd/capd.c | 441 ++ support/capd/capd.kas.c | 29 + support/enet/README | 167 + support/enet/enet.4 | 752 +++ support/enet/enet.c | 1824 +++++++ support/enet/enet.h | 174 + support/enet/enetdefs.h | 237 + support/enet/enetfilter.h | 1 + support/enet/etherstat.8 | 30 + support/enet/etherstat.c | 350 ++ support/ethertalk/Makefile.m4 | 76 + support/ethertalk/README | 88 + support/ethertalk/STILL_TO_DO | 15 + support/ethertalk/aarpd.c | 651 +++ support/ethertalk/aarpd.h | 12 + support/ethertalk/aarpd.x | 12 + support/ethertalk/aarpd_clnt.c | 96 + support/ethertalk/aarpd_svc.c | 90 + support/ethertalk/aarpd_xdr.c | 30 + support/ethertalk/aarptest.c | 87 + support/ethertalk/abelap.c | 973 ++++ support/ethertalk/bpfiltp.c | 958 ++++ support/ethertalk/dlip.c | 315 ++ support/ethertalk/ethertalk.c | 519 ++ support/ethertalk/makefile | 75 + support/ethertalk/rangetest.c | 85 + support/ethertalk/rtmptest.c | 83 + support/ethertalk/sdlpi.c | 986 ++++ support/ethertalk/senetp.c | 740 +++ support/ethertalk/snitp.c | 580 ++ support/ethertalk/spfiltp.c | 660 +++ support/uab/Makefile.m4 | 61 + support/uab/README | 65 + support/uab/STILL_TO_DO | 11 + support/uab/aarp.c | 1116 ++++ support/uab/aarp.h | 72 + support/uab/aarp_defs.h | 128 + support/uab/asyncatalk.c | 483 ++ support/uab/bpfiltp.c | 450 ++ support/uab/bridge_desc | 89 + support/uab/ddpport.c | 227 + support/uab/ddpport.h | 119 + support/uab/ddprouter.c | 492 ++ support/uab/ddpsvcs.c | 303 ++ support/uab/dlip.c | 276 + support/uab/ethertalk.c | 536 ++ support/uab/ethertalk.h | 39 + support/uab/gw.h | 96 + support/uab/hash.3 | 669 +++ support/uab/hash.c | 793 +++ support/uab/hash.h | 127 + support/uab/if_desc.h | 64 + support/uab/kip_mpx.c | 535 ++ support/uab/log.c | 177 + support/uab/log.h | 34 + support/uab/makefile | 68 + support/uab/mpxddp.h | 87 + support/uab/node.h | 52 + support/uab/patches/bug.1 | 171 + support/uab/patches/bug.2 | 47 + support/uab/patches/bug.3 | 25 + support/uab/proto_intf.h | 66 + support/uab/rtmp.c | 1389 +++++ support/uab/senetp.c | 400 ++ support/uab/sllap.c | 378 ++ support/uab/snitp.c | 409 ++ support/uab/snooppf.c | 360 ++ support/uab/spfiltp.c | 390 ++ support/uab/srawetherp.c | 282 + support/uab/uab.c | 923 ++++ support/uab/whatiswhat | 42 + 461 files changed, 139331 insertions(+) create mode 100644 CAP.faq create mode 100644 CAP60.README create mode 100755 Configure create mode 100644 LICENSE create mode 100644 MANIFEST create mode 100644 MODIFICATIONS create mode 100644 Makefile create mode 100644 NOTES create mode 100644 NOTICE create mode 100644 PORTING create mode 100644 README create mode 100644 applications/Makefile.m4 create mode 100644 applications/README create mode 100644 applications/aufs/INSTALLATION create mode 100644 applications/aufs/Makefile.m4 create mode 100644 applications/aufs/NOTES create mode 100644 applications/aufs/README create mode 100644 applications/aufs/abmisc2.c create mode 100644 applications/aufs/afpavl.c create mode 100644 applications/aufs/afpavl.h create mode 100644 applications/aufs/afpdid.c create mode 100644 applications/aufs/afpdid.h create mode 100644 applications/aufs/afpdir.c create mode 100644 applications/aufs/afpdsi.c create mode 100644 applications/aufs/afpdsi.h create mode 100644 applications/aufs/afpdt.c create mode 100644 applications/aufs/afpdt.h create mode 100644 applications/aufs/afpfid.c create mode 100644 applications/aufs/afpfile.c create mode 100644 applications/aufs/afpfork.c create mode 100644 applications/aufs/afpgc.c create mode 100644 applications/aufs/afpgc.h create mode 100644 applications/aufs/afpid.notes create mode 100644 applications/aufs/afpidlist.c create mode 100644 applications/aufs/afpidsrvr.c create mode 100644 applications/aufs/afpidtool.c create mode 100644 applications/aufs/afpmisc.c create mode 100644 applications/aufs/afpntoh.h create mode 100644 applications/aufs/afpos.c create mode 100644 applications/aufs/afposenum.c create mode 100644 applications/aufs/afposfi.c create mode 100644 applications/aufs/afposncs.c create mode 100644 applications/aufs/afposncs.h create mode 100644 applications/aufs/afppasswd.c create mode 100644 applications/aufs/afppasswd.h create mode 100644 applications/aufs/afps.h create mode 100644 applications/aufs/afpserver.c create mode 100644 applications/aufs/afpspd.c create mode 100644 applications/aufs/afpudb.c create mode 100644 applications/aufs/afpudb.h create mode 100644 applications/aufs/afpvols create mode 100644 applications/aufs/afpvols.c create mode 100644 applications/aufs/afpvols.h create mode 100644 applications/aufs/aufs.c create mode 100755 applications/aufs/aufs_vers create mode 100644 applications/aufs/aufs_vers.sh create mode 100644 applications/aufs/aufscicon.c create mode 100644 applications/aufs/aufsicon.c create mode 100644 applications/aufs/aufsv.c create mode 100644 applications/aufs/design.notes create mode 100644 applications/aufs/makefile create mode 100644 applications/aufs/sizeserver.c create mode 100644 applications/aufs/sizeserver.h create mode 100644 applications/aufs/todo create mode 100644 applications/aufs/user.doc create mode 100644 applications/aufs/whatiswhat create mode 100644 applications/lwsrv/DBfile create mode 100644 applications/lwsrv/LWFonts create mode 100644 applications/lwsrv/LWIIfgFonts create mode 100644 applications/lwsrv/LWPlusFonts create mode 100644 applications/lwsrv/Makefile.m4 create mode 100644 applications/lwsrv/README create mode 100644 applications/lwsrv/fix.serial.ps create mode 100644 applications/lwsrv/fontlist.c create mode 100644 applications/lwsrv/fontlist.h create mode 100644 applications/lwsrv/list.c create mode 100644 applications/lwsrv/list.h create mode 100644 applications/lwsrv/lwsrv.c create mode 100644 applications/lwsrv/lwsrv.conf create mode 100644 applications/lwsrv/lwsrvconfig.c create mode 100644 applications/lwsrv/makefile create mode 100644 applications/lwsrv/packed.c create mode 100644 applications/lwsrv/packed.h create mode 100644 applications/lwsrv/papstream.c create mode 100644 applications/lwsrv/papstream.h create mode 100644 applications/lwsrv/parse.c create mode 100644 applications/lwsrv/parse.h create mode 100644 applications/lwsrv/parsel.l create mode 100644 applications/lwsrv/parsey.y create mode 100644 applications/lwsrv/procset.c create mode 100644 applications/lwsrv/procset.h create mode 100644 applications/lwsrv/query.c create mode 100644 applications/lwsrv/query.h create mode 100644 applications/lwsrv/query.ps create mode 100644 applications/lwsrv/simple.c create mode 100644 applications/lwsrv/spmisc.c create mode 100644 applications/lwsrv/spmisc.h create mode 100644 applications/lwsrv/whatiswhat create mode 100644 applications/makefile create mode 100644 applications/papif/Makefile.m4 create mode 100644 applications/papif/README create mode 100644 applications/papif/add_at_printer create mode 100644 applications/papif/cap.printers create mode 100644 applications/papif/makefile create mode 100644 applications/papif/papif.c create mode 100644 applications/papif/papif.interface.template create mode 100644 applications/papif/papof.c create mode 100644 applications/papif/printcap.samp1 create mode 100644 applications/papif/printcap.samp2 create mode 100644 cap60.patches/asip1.patch create mode 100644 cap60.patches/cicon.patch create mode 100644 cap60.patches/desktop.patch create mode 100644 cap60.patches/extnd.patch create mode 100644 cap60.pl198+asip.README create mode 100644 conf.func.lst create mode 100755 conf.func.sh create mode 100644 conf.sysv.lst create mode 100755 conf.sysv.sh create mode 100644 contrib/AppManager/Makefile create mode 100644 contrib/AppManager/README create mode 100644 contrib/AppManager/aufslock.c create mode 100644 contrib/AppManager/aufsmon.c create mode 100644 contrib/AsyncATalk/INSTALLATION create mode 100644 contrib/AsyncATalk/Makefile create mode 100644 contrib/AsyncATalk/README.rfc create mode 100644 contrib/AsyncATalk/async.1.4.hqx create mode 100644 contrib/AsyncATalk/async.c create mode 100644 contrib/AsyncATalk/async.h create mode 100644 contrib/AsyncATalk/asyncad.c create mode 100644 contrib/AsyncATalk/atalkdbm.c create mode 100644 contrib/AsyncATalk/macros.h create mode 100644 contrib/AufsTools/MANIFEST create mode 100644 contrib/AufsTools/Makefile create mode 100644 contrib/AufsTools/README create mode 100644 contrib/AufsTools/binhex/8to6.c create mode 100644 contrib/AufsTools/binhex/Makefile create mode 100644 contrib/AufsTools/binhex/aufs.h create mode 100644 contrib/AufsTools/binhex/binhex.c create mode 100644 contrib/AufsTools/binhex/crc.c create mode 100644 contrib/AufsTools/binhex/gethead.c create mode 100644 contrib/AufsTools/binhex/run.c create mode 100644 contrib/AufsTools/capit/Makefile create mode 100644 contrib/AufsTools/capit/capit.c create mode 100644 contrib/AufsTools/man/binhex.1 create mode 100644 contrib/AufsTools/man/capit.1 create mode 100644 contrib/AufsTools/man/cleanup.1 create mode 100644 contrib/AufsTools/man/drag.1 create mode 100644 contrib/AufsTools/man/dup.1 create mode 100644 contrib/AufsTools/man/m2u.1 create mode 100644 contrib/AufsTools/man/macp.1 create mode 100755 contrib/AufsTools/man/makeman create mode 100644 contrib/AufsTools/man/mcmp.1 create mode 100644 contrib/AufsTools/man/mcvert.1 create mode 100644 contrib/AufsTools/man/newfolder.1 create mode 100644 contrib/AufsTools/man/sit.1 create mode 100644 contrib/AufsTools/man/toaufs.1 create mode 100644 contrib/AufsTools/man/trash.1 create mode 100644 contrib/AufsTools/man/unsit.1 create mode 100644 contrib/AufsTools/man/unstuffit.1 create mode 100644 contrib/AufsTools/mcvert/Makefile create mode 100644 contrib/AufsTools/mcvert/hqxify.c create mode 100644 contrib/AufsTools/mcvert/mactypes.h create mode 100644 contrib/AufsTools/mcvert/mcvert.c create mode 100644 contrib/AufsTools/mcvert/unpack.c create mode 100644 contrib/AufsTools/shell/Makefile create mode 100755 contrib/AufsTools/shell/cleanup create mode 100755 contrib/AufsTools/shell/drag create mode 100755 contrib/AufsTools/shell/dup create mode 100755 contrib/AufsTools/shell/m2u create mode 100755 contrib/AufsTools/shell/macp create mode 100755 contrib/AufsTools/shell/mcmp create mode 100755 contrib/AufsTools/shell/newfolder create mode 100755 contrib/AufsTools/shell/toaufs create mode 100755 contrib/AufsTools/shell/trash create mode 100755 contrib/AufsTools/shell/u2m create mode 100644 contrib/AufsTools/stuffit/Makefile create mode 100644 contrib/AufsTools/stuffit/sit.c create mode 100644 contrib/AufsTools/stuffit/sit.h create mode 100644 contrib/AufsTools/stuffit/updcrc.c create mode 100644 contrib/AufsTools/unstuffit/Makefile create mode 100644 contrib/AufsTools/unstuffit/getopt.c create mode 100644 contrib/AufsTools/unstuffit/stuffit.h create mode 100644 contrib/AufsTools/unstuffit/unsit.c create mode 100644 contrib/AufsTools/unstuffit/updcrc.c create mode 100644 contrib/DeskTop/Makefile create mode 100644 contrib/DeskTop/README create mode 100644 contrib/DeskTop/builddt.c create mode 100644 contrib/DeskTop/dt.h create mode 100644 contrib/DeskTop/dtmisc.c create mode 100644 contrib/DeskTop/dumpdt.c create mode 100644 contrib/MacPS/Installation create mode 100644 contrib/MacPS/Makefile create mode 100644 contrib/MacPS/ReadMe create mode 100644 contrib/MacPS/macaux.c create mode 100644 contrib/MacPS/macps-22.hdr create mode 100644 contrib/MacPS/macps-22.shar create mode 100644 contrib/MacPS/macps.1 create mode 100644 contrib/MacPS/macps.c create mode 100644 contrib/MacPS/macps.config create mode 100644 contrib/MacPS/prepfix.1 create mode 100644 contrib/MacPS/prepfix.c create mode 100644 contrib/MacPS/str.h create mode 100644 contrib/MacPS/ucbwhich.c create mode 100644 contrib/MacPS/ucbwhich.h create mode 100644 contrib/Makefile.m4 create mode 100644 contrib/Messages/Makefile create mode 100644 contrib/Messages/README create mode 100644 contrib/Messages/dot.forward create mode 100644 contrib/Messages/macto.1l create mode 100644 contrib/Messages/macuser.1l create mode 100644 contrib/Messages/macuser.c create mode 100644 contrib/Messages/macwho.1l create mode 100644 contrib/Messages/macwho.c create mode 100644 contrib/Messages/messages.c create mode 100644 contrib/Messages/messages.hqx create mode 100644 contrib/Messages/notify.c create mode 100644 contrib/Messages/notify.h create mode 100644 contrib/README create mode 100644 contrib/Timelord/Makefile create mode 100644 contrib/Timelord/README create mode 100644 contrib/Timelord/tardis.1.3.sit.hqx create mode 100644 contrib/Timelord/timelord.1l create mode 100644 contrib/Timelord/timelord.c create mode 100644 contrib/Timelord/timelord.h create mode 100755 contrib/atprint create mode 100644 contrib/aufsicon.hqx create mode 100644 contrib/aufsmkkey.c create mode 100644 contrib/aufsmkusr.c create mode 100644 contrib/cvt2apple.c create mode 100644 contrib/cvt2cap.c create mode 100644 contrib/lwrename.c create mode 100644 contrib/lwsrv-relay create mode 100644 contrib/makefile create mode 100644 contrib/printQDA.hqx create mode 100644 contrib/printqueue.c create mode 100644 contrib/snitch.c create mode 100644 doc/Makefile create mode 100644 doc/README create mode 100644 doc/abmisc.doc create mode 100644 doc/asp.notes create mode 100644 doc/atp.notes create mode 100644 doc/cap.auth.doc create mode 100644 doc/cap.notes create mode 100644 doc/glossary create mode 100644 doc/install.ms create mode 100644 doc/iptalk.cookbook create mode 100644 doc/nbp.ext create mode 100644 doc/pap.notes create mode 100644 doc/print.cookbook create mode 100644 doc/sched.notes create mode 100644 doc/uab.desc.ms create mode 100644 doc/uar.cookbook create mode 100644 etc/Makefile.m4 create mode 100644 etc/README create mode 100644 etc/S99appletalk create mode 100644 etc/atalk.local create mode 100644 etc/atis.c create mode 100644 etc/aufsIPFilter create mode 100644 etc/etalk.local create mode 100644 etc/kill-cap-servers create mode 100644 etc/list-cap-servers create mode 100644 etc/makefile create mode 100644 etc/nisaux.c create mode 100644 etc/start-cap-servers create mode 100644 extras/Makefile.m4 create mode 100644 extras/README create mode 100644 extras/afpfile create mode 100644 extras/att_getopt.c create mode 100644 extras/des.c create mode 100644 extras/dummy.des.c create mode 100644 extras/iwif.c create mode 100644 extras/lib.cap.macusers create mode 100644 extras/lib.cap.refused create mode 100644 extras/lnof.c create mode 100644 extras/makefile create mode 100644 extras/printcap.iw create mode 100755 gen.makes create mode 100644 lib/Makefile.m4 create mode 100644 lib/afp/Makefile.m4 create mode 100644 lib/afp/README create mode 100644 lib/afp/afpcmd.c create mode 100644 lib/afp/afperr.c create mode 100644 lib/afp/afpidaufs.c create mode 100644 lib/afp/afpidaufs.h create mode 100644 lib/afp/afpidclnt.c create mode 100644 lib/afp/afpidgnrl.c create mode 100644 lib/afp/afpidnames.h create mode 100644 lib/afp/afposlock.c create mode 100644 lib/afp/afppacks.c create mode 100644 lib/afp/afppass.c create mode 100644 lib/afp/makefile create mode 100644 lib/afpc/Makefile.m4 create mode 100644 lib/afpc/README create mode 100644 lib/afpc/afpc.c create mode 100644 lib/afpc/afpc.mss create mode 100644 lib/afpc/afpcc.c create mode 100644 lib/afpc/makefile create mode 100644 lib/afpc/probs create mode 100644 lib/cap/Makefile.m4 create mode 100644 lib/cap/abasp.c create mode 100644 lib/cap/abasp.h create mode 100644 lib/cap/abatp.c create mode 100644 lib/cap/abatp.h create mode 100644 lib/cap/abauxddp.c create mode 100644 lib/cap/abauxnbp.c create mode 100644 lib/cap/abddp.c create mode 100644 lib/cap/abkas.c create mode 100644 lib/cap/abkip.c create mode 100644 lib/cap/ablap.c create mode 100644 lib/cap/ablog.c create mode 100644 lib/cap/abmisc.c create mode 100644 lib/cap/abnbp.c create mode 100644 lib/cap/abnet.c create mode 100644 lib/cap/abpap.c create mode 100644 lib/cap/abpap.h create mode 100644 lib/cap/abpapc.c create mode 100644 lib/cap/abpaps.c create mode 100644 lib/cap/abpp.c create mode 100644 lib/cap/abqueue.c create mode 100644 lib/cap/absched.c create mode 100644 lib/cap/abversion.c create mode 100644 lib/cap/abzip.c create mode 100644 lib/cap/atalkdbm.c create mode 100644 lib/cap/atalkdbm.h create mode 100644 lib/cap/authenticate.c create mode 100644 lib/cap/cap_conf.h create mode 100644 lib/cap/makefile create mode 100644 lib/cap/scandir.c create mode 100644 lib/cap/todist create mode 100644 lib/cap/whatiswhat create mode 100644 lib/makefile create mode 100644 lib/xenix/Makefile create mode 100644 lib/xenix/fsync.c create mode 100644 lib/xenix/ftruncate.c create mode 100644 lib/xenix/groups.c create mode 100644 lib/xenix/rename.c create mode 100644 lib/xenix/scandir.c create mode 100644 m4.setup create mode 100644 man/AUFS.1 create mode 100644 man/AUFS.8 create mode 100644 man/CAP.3 create mode 100644 man/CAP.8 create mode 100644 man/UAB.8 create mode 100644 man/aarpd.8 create mode 100644 man/ash.1 create mode 100644 man/atalk.local.5 create mode 100644 man/atis.8 create mode 100644 man/atlook.1 create mode 100644 man/atprint.1 create mode 100644 man/aufsmkkey.8 create mode 100644 man/aufsmkusr.8 create mode 100644 man/cvt2apple.1 create mode 100644 man/etalk.local.5 create mode 100644 man/getzones.1 create mode 100644 man/instappl.1 create mode 100644 man/lwrename.8 create mode 100644 man/lwsrv.8 create mode 100644 man/makefile create mode 100644 man/papif.8 create mode 100644 man/snitch.1 create mode 100644 netat/Makefile create mode 100644 netat/Makefile.m4 create mode 100644 netat/aberrors.h create mode 100644 netat/abnbp.h create mode 100644 netat/abqueue.h create mode 100644 netat/afp.h create mode 100644 netat/afpc.h create mode 100644 netat/afpcmd.h create mode 100644 netat/afppass.h create mode 100644 netat/appletalk.h create mode 100644 netat/compat.h create mode 100644 netat/fcntldomv.h create mode 100644 netat/macfile.h create mode 100644 netat/sysvcompat.h create mode 100644 samples/Makefile.m4 create mode 100644 samples/README create mode 100644 samples/ash.c create mode 100644 samples/atistest.c create mode 100644 samples/atlook.c create mode 100644 samples/getzones.c create mode 100644 samples/instappl.c create mode 100644 samples/isrv.c create mode 100644 samples/look.c create mode 100644 samples/lwpr.c create mode 100644 samples/makefile create mode 100644 samples/papstatus.c create mode 100644 samples/ruiwpr.c create mode 100644 samples/tlw.c create mode 100644 support/capd/Makefile.m4 create mode 100644 support/capd/README create mode 100644 support/capd/capd.c create mode 100644 support/capd/capd.kas.c create mode 100644 support/enet/README create mode 100644 support/enet/enet.4 create mode 100644 support/enet/enet.c create mode 100644 support/enet/enet.h create mode 100644 support/enet/enetdefs.h create mode 100644 support/enet/enetfilter.h create mode 100644 support/enet/etherstat.8 create mode 100644 support/enet/etherstat.c create mode 100644 support/ethertalk/Makefile.m4 create mode 100644 support/ethertalk/README create mode 100644 support/ethertalk/STILL_TO_DO create mode 100644 support/ethertalk/aarpd.c create mode 100644 support/ethertalk/aarpd.h create mode 100644 support/ethertalk/aarpd.x create mode 100644 support/ethertalk/aarpd_clnt.c create mode 100644 support/ethertalk/aarpd_svc.c create mode 100644 support/ethertalk/aarpd_xdr.c create mode 100644 support/ethertalk/aarptest.c create mode 100644 support/ethertalk/abelap.c create mode 100644 support/ethertalk/bpfiltp.c create mode 100644 support/ethertalk/dlip.c create mode 100644 support/ethertalk/ethertalk.c create mode 100644 support/ethertalk/makefile create mode 100644 support/ethertalk/rangetest.c create mode 100644 support/ethertalk/rtmptest.c create mode 100644 support/ethertalk/sdlpi.c create mode 100644 support/ethertalk/senetp.c create mode 100644 support/ethertalk/snitp.c create mode 100644 support/ethertalk/spfiltp.c create mode 100644 support/uab/Makefile.m4 create mode 100644 support/uab/README create mode 100644 support/uab/STILL_TO_DO create mode 100644 support/uab/aarp.c create mode 100644 support/uab/aarp.h create mode 100644 support/uab/aarp_defs.h create mode 100644 support/uab/asyncatalk.c create mode 100644 support/uab/bpfiltp.c create mode 100644 support/uab/bridge_desc create mode 100644 support/uab/ddpport.c create mode 100644 support/uab/ddpport.h create mode 100644 support/uab/ddprouter.c create mode 100644 support/uab/ddpsvcs.c create mode 100644 support/uab/dlip.c create mode 100644 support/uab/ethertalk.c create mode 100644 support/uab/ethertalk.h create mode 100644 support/uab/gw.h create mode 100644 support/uab/hash.3 create mode 100644 support/uab/hash.c create mode 100644 support/uab/hash.h create mode 100644 support/uab/if_desc.h create mode 100644 support/uab/kip_mpx.c create mode 100644 support/uab/log.c create mode 100644 support/uab/log.h create mode 100644 support/uab/makefile create mode 100644 support/uab/mpxddp.h create mode 100644 support/uab/node.h create mode 100644 support/uab/patches/bug.1 create mode 100644 support/uab/patches/bug.2 create mode 100644 support/uab/patches/bug.3 create mode 100644 support/uab/proto_intf.h create mode 100644 support/uab/rtmp.c create mode 100644 support/uab/senetp.c create mode 100644 support/uab/sllap.c create mode 100644 support/uab/snitp.c create mode 100644 support/uab/snooppf.c create mode 100644 support/uab/spfiltp.c create mode 100644 support/uab/srawetherp.c create mode 100644 support/uab/uab.c create mode 100644 support/uab/whatiswhat diff --git a/CAP.faq b/CAP.faq new file mode 100644 index 0000000..c0ed864 --- /dev/null +++ b/CAP.faq @@ -0,0 +1,223 @@ +CAP FAQ +------- +Last update: +Mon Aug 3 02:10:07 EST 1992 + + +> What is CAP ? + +The Columbia AppleTalk Package (CAP) implements the AppleTalk protocol +stack on a variety of UNIX machines. The main applications provide an +AppleShare 2.0 compatible server (aufs), a LaserWriter Spooler (lwsrv) +and a program to print to LaserWriters (papif). The latter can be used +to talk to EtherTalk LaserWriters, LocalTalk LaserWriters used on Dayna +EtherPrint cards or on a LocalTalk/EtherTalk gateway. There are also a +number of contributed packages bundled with CAP (see cap60/contrib). + + +> What extra hardware is required for CAP ? + +On a SUN or ULTRIX machine, CAP can speak Phase 1 or Phase 2 EtherTalk +packets using packet filters based on the Stanford ENET model. Where +available, CAP can also use Kernel based AppleTalk or run the UNIX +AppleTalk Bridge (UAB) to act as a gateway. On other UNIX boxes you +need to have a gateway that can translate IPTalk packets. Suitable +candidates are Shiva FastPath, Cayman GatorBox or Webster MultiPort Gateway. + + +> What machines will CAP run on ? + +The CAP Configure script has specific support for the following +Operating Systems: + + sunos - SUN SunOS + ultrixnn - DEC Ultrix 1.2, 2.0, 4.N + irix - Silicon Graphics IRIS/IRIX + aix - IBM AIX System V + hpux - HP HP-UX 8.0 + uts - Amdahl UTS + aux - Apple A/UX 2.0 + encore - Encore MultiMax BSD + pyr - Pyramid (BSD Universe) + next - NeXT/MACH + dynix - Sequent Balance + newsos - Sony NEWS + bsd - BSD 4.2, 4.3 + + +> What is the current CAP version ? + +CAP is at version 6.0 with (currently) 125 patches to be applied, most of +which add extra functionality to the original. To determine the current +patch level of your sources, check the cap60/README file for a line of +the form + + o CAP Distribution 6.0, Patch Level 125, July 1992 + + +> Where can I get CAP ? + +CAP can be obtained by anonymous FTP from + +rutgers.EDU src/{cap60.tar.Z,cap60.patches/*} +munnari.OZ.AU mac/{cap60.tar.Z,cap.patches/*} +gatekeeper.DEC.COM pub/net/appletalk/cap/{cap60.tar.Z,cap.patches/*} +ftp.kuis.kyoto-u.AC.JP net/cap/{cap60.tar.Z,cap60.patches/*.Z} +src.doc.ic.AC.UK mac/multigate/{cap60.tar.Z,cap.patches/*} + +Please choose an appropriate site and an off-peak time for the transfer. + +The patches are available individually or as the files "patches.1-75.tar.Z", +"patches.76-100.tar.Z" and "patches.101-125.tar.Z". Additionally, for new +users, a partially patched source file is available as "cap60.pl100.tar.Z" +(the file cap60.tar.Z is unpatched). + + +> How do I apply the patches ? + +To make the process of patching easier, you should get the 'patch' utility +written by Larry Wall, it is normally archived at sites that archive +comp.sources.unix in volume7/patch2. If you can't find anywhere closer +it is on munnari.OZ.AU. CAP Version 6.1 will be 6.0 with all the patches +applied and updated cap60/contrib packages. It will appear when the number +of patches stabilizes. + +For each of the patches, run 'patch -p < cap60.patch0NN' from the top level +cap60 directory, for example, in csh + + foreach i (cap60.patches/cap60.patch*) + patch -p < $i >>& /tmp/patches + end + +and check the /tmp/patches file for patching errors (should be none). +To remove the *.orig files that patch leaves behind (containing the original +version of the file), run 'make spotless' from the top level directory (note +that spotless also removes all makefiles so gen.makes needs to be run to +regenerate them). + + +> I picked up CAP 6.0 from rutgers.edu, but our Unix system administrator +> refuses to install it because there are 74 patches (can't say I blame him)! + +Sigh. Using the patch program to apply patches requires a minimal amount of +time and energy. Taking time to install the patch program will pay off for +CAP and other program updates. + +% date +Sat Mar 7 23:51:05 EST 1992 +% touch /tmp/patches +% foreach i (cap60.patches/cap60.patch*) +? patch -p < $i >>& /tmp/patches +? end +% grep rej /tmp/patches +% date +Sat Mar 7 23:54:26 EST 1992 + +It took just 3:11 to apply 75 patches to the raw CAP tar source. The extra +overhead is the time to FTP 3 files, cap60.tar.Z, patches.1-74.tar.Z and +cap60.patch075 and uncompress them. The total time spent on this ... 8:02. + + +> Is CAP Public Domain ? + +No, CAP is distributed with the following condition on its use: + + Copyright (c) 1986, 1987, 1988, The Trustees of Columbia University in + the City of New York. Charlie C. Kim, User Services Group, Academic + Information Services Division, Libraries and Center for Computing + Activities and Bill Schilit, formerly of Computer Research Facilities, + Computer Science Department. + + Permission is granted to any individual or institution to use, copy, + or redistribute this software so long as it is not sold for profit, + provided that this notice and the original copyright notices are + retained. Columbia University makes no representations about the + suitability of this software for any purpose. It is provided "as is" + without express or implied warranty. + +Some portions of CAP have other copyrights and are suitably marked. + + +> Is there a version of CAP available that supports Ethertalk Phase 2 ? + +Phase 2 support was added in CAP 6.0 patches 25, 28 & 35 for SUN NIT, ULTRIX +4.n and SUN enet driver respectively. + + +> Can a workstation with 2 ethernet cards running Cap s/w act as a phaseII +> to (phaseI or phaseII) "Gateway"? If so, what patch level of Cap do I need +> to do this? + +The UNIX AppleTalk Bridge (UAB) is the EtherTalk gateway. It can handle +multiple ethernet interfaces and gateway between them. This is its primary +function but it can also be set up to provide CAP services. But ... + + +> Does anyone have a patch that will support the phase 2 appletalk protocol +> on UAB (CAP6.0). + +UAB is *currently* Phase 1 only. + + +> Is there any standard way yet of implementing a execute only, copy +> protected CAP AUFS server? + +The original method of achieving this was to removing read permission from +the data fork of the Application (make it non-zero length if the file is +empty). The current method involves using the Application Manager added in +patch number 61. Either of these methods can be circumvented by users who +are either knowledgeable or determined. + + +> Recently I introduced a new color postscript printer, Tektronix Phaser III +> 4698PXi, under one of the LocalTalks. It works fine from Macintosh's so I +> tried to use it from papif but failed. + +CAP uses DDP checksums by default, the checksum code in the Phaser III PXi +and the Apple Personal LaserWriter NTR incorrectly calculate the checksum +value and the packet is dropped. The programs aufs, lwsrv, papif, atis, +atlooklws and tlw now support the -k option to disable CAP checksums on a +per-program basis. To disable CAP checksums completely, add the following +lines to the m4.features file, rerun gen.makes and remake CAP. + +# + DONT_DOCHKSUM no DDP checksums (Tektronix Phaser III PXi & Apple LW NTR) +define(`specialcflags',concat(specialcflags,` -DDONT_DOCHKSUM')) +# + +No other code changes are necessary. + + +> We've just upgraded our DECstations to version 4.2 of ULTRIX, and our CAP +> distribution very neatly broke. + +You need to obtain the net_common.o and pfilt.o patches from DEC. Do not +turn on the ifconfig copyall (stop-gap measure previously suggested on the +net). Do not define ULT42PFBUG in m4.features. + + +> When we run aarpd with a command like: +> aarpd pf0 our_zone +> the daemon doesn't start & we get the message 'failed to start ZIS listener'. + +This means that something has been allocated the UPD port 774 (768+6) +by 'portmap'. You can check this with the command 'rpcinfo -p'. + +There are two solutions, break 'start-cap-servers' into two so that aarpd +and atis is started before portmap. Alternatively, and preferably, install +the NIC assigned port numbers into /etc/services. See the doc in the file +cap60/doc/install.ms for more details. + + +> I have installed the enet driver. But aarpd won't start successfully. +> Instead the following error message is being printed. +> unix!: # aarpd le0 EtherTalk +> open: /dev/enetXX: No such file or directory + +The ENET drivers use devices of the form enet0, enet1 etc. Replace the +string "le0" with "enet0". + + + +For more information, see the CAP60.README file in the CAP distribution. + +Send CAP FAQ contributions to cap@munnari.OZ.AU diff --git a/CAP60.README b/CAP60.README new file mode 100644 index 0000000..7291cdd --- /dev/null +++ b/CAP60.README @@ -0,0 +1,559 @@ +CAP 6.0 +------- + +This distribution of CAP is an attempt to collect together all of the patches +available for cap50 and provides an opportunity to perform some very necessary +maintenance. The code contains the original "official" patches plus bug fixes +and extra features contributed by *many* people. Local configuration of the +extra features is controlled by the file 'm4.features'. For more details, +see Features below. + +Other changes +------------- + + * AUFS support for AFP 2.0 is (practically) complete (see below). + * 'Configure' will automatically recognize the host byte ordering. + * 'Configure' has support for more machines and custom local features. + * UAB is now bundled and configured with this CAP distribution. + * UAB now supports Asynchronous AppleTalk on a UNIX host. + * The format of 'atalk.local' has been extended for async appletalk. + * Zone names in 'atalk.local' MUST now be quoted to include spaces. + * A "free format" 'etalk.local' is used by EtherTalk LAPs (& UAB/UAR). + * atis is now Phase 2 NBP compatible (partial obj/type matches). + * There are more (& updated) manual entries and documentation. + * There are more contributed packages bundled with CAP. + + +Features +-------- + +Additional features have been incorporated into CAP. Most of these were +supplied as patch files from a variety of sources. In addition, some changes +were made at The University of Melbourne, these changes concentrated on making +AUFS comply with AFP2.0 specifications and adding extra facilities and host +support to Configure. Currently, AUFS supports ProDOS* (Apple IIgs) +workstations running AppleTalk, the extended directory and volume attributes +and the additional error result codes. AUFS supports user editable encrypted +passwords using the DISTRIB_PASSWDS feature. + + * ProDOS requires the NOCASEMATCH feature, or use of upper-case + application filenames only. + +Specific host support that was new with CAP 6.0 includes ... + + A/UX 2.0 Native AppleTalk William Roberts + IBM Risc 6000 AIX System V David Hornsby + Silicon Graphics IRIS-4D/IRIX David Hornsby + SCO Xenix System V Chip Salzenberg + Sequent Balance William Roberts + ICL DRS6000 Michael Brown + DEC OSF/1 Alpha Scooter Morris + Amdahl UTS Mark Haynie + Sun Solaris 2.N Andy Polyakov + Sony NEWS TAYA Shin'ichiro + Control Data CD4000-EP/IX John Huntley + 386/BSD, FreeBSD 2.0 Dave Matthews + NetBSD 1.0 Paul Nash + BSDI BSD/386 1.1 David Hornsby + HP/Apollo Domain BSD 4.3 Darrell Skinner + + +The features file ("m4.features") can be edited from within Configure by +answering yes to the question "Do you wish to customise the feature list ?". +The contents of the file are not processed by Configure so you can edit the +feature list at any time then (re)generate the makefiles with "./gen.makes". +To include a particular feature, uncomment (remove '#') the definition line. + +The available flags, their meanings and original authors are: + + SHORT_NAMES + Include AUFS code to support short file names as required by + AppleShare client implementations on PCs. + Bridget Rogers + NOCASEMATCH + Make AUFS filenames case insensitive as in other AppleShare + servers and Mac applications. + Edward Moy + SIZESERVER + Compile 'sizeserver' daemon for AUFS to obtain volume size + information. Useful with operating systems that have no + support for getmnt() or statfs() to determine the amount of + free space on a filesystem. + Edward Moy + FIXED_DIRIDS + Implement server and AUFS code to provide support for + fixed directory and file IDs. The 'afpidsrvr' daemon must + be started before any AUFS processes. Refer to the file + "applications/aufs/afpid.notes" for more information. + John Forrest + Scooter Morris + LWSRV_AUFS_SECURITY + Require LWSRV LaserWriter spooler users to have an AUFS + volume connected (and therefore be password validated). + Phil Budne + LWSRV_LPR_LOG + Include stdout/stderr messages from lpr in the lwsrv log. + Rakesh Patel + AUTHENTICATE + AUFS or LWSRV connections must comply with the rules + specified in "/etc/cap.auth". This may specify permit + or deny permission by network number and/or server type. + Refer to the file cap60/doc/cap.auth.doc for details. + Edward Moy + STAT_CACHE + Provide a speed enhancement by caching UNIX stat(2) calls + within AUFS. + Dan Oscarsson + TREL_TIMEOUT + ATP transaction release packets (TREL) can sometimes be + lost. AUFS will timeout after 30 seconds and continue with + the next request. This option adds a second request + listener to avoid the timeout delays. + Dan Oscarsson + ATPREQCACHE + Cache ATP TREQ packets to avoid 2 second wait if request + control block (RqCB) not set up in time by AUFS. + Rudy Nedved + RUTGERS + Include Rutgers specific code. You probably don't want + either this or MELBOURNE unless you check the sources. + + MELBOURNE + Include Melbourne specific code. + + USE_HOST_ICON + If available, the AUFS volume ICON will represent the + underlying machine hardware (instead of the BSD daemon). + + PERMISSIVE_USER_NAME + Allow the AUFS login name (from the Chooser User Name) to be + partially matched to the gecos field in the password entry. + IE: you can login with full user name, first name or surname. + Jean-Luc Mounier + ULTRIX_SECURITY + Activate AUFS login security based on an authorisation + file. Check this file if normal password field is "*". + Rusty Wright + DIGITAL_UNIX_SECURITY + Use Digital UNIX enhanced security for AUFS logins. + Andrew Greer + Richard Rogers + USE_MAC_DATES + Keep the modification date of the Mac file intact when + copied to an AUFS volume (otherwise it is the latter of + the UNIX creation and modify times). + David Hornsby + DEV_NIT + Allow an alternate name for the NIT interface. The default + name is "/dev/nit". + Austin Shelton + APPLICATION_MANAGER + Control the maximum number of times an Application may be + run and optionally prevent Finder copying for AUFS mounted + volumes. More details in contrib/AppManager/README + David Hornsby + DENYREADWRITE + Implement AFP Access/Deny modes on AUFS file accesses. + David Hornsby + AUFS_README + Add '-r ' option to AUFS. The is expected to + be a full path name to an explanatory README file and is + linked into the top level volume of a new AUFS user. + Edward Moy + AUFS_IDLE_TIMEOUT + Add -i and -I options to AUFS to disconnect users who + exceed the specified idle time. -I includes all users, + -i is for guest sessions only. + David Hornsby + REREAD_AFPVOLS + A SIGUSR1 signal sent to AUFS causes the global AFP volumes + file to be re-read. + Eugene Bogaart + CLOSE_LOG_SIG + A SIGUSR2 signal sent to AUFS causes the log file to be + closed and then re-opened (allows the file to be truncated, + for example, by a script which regularly does something like + 'cat > logfile; kill -USR2 '). See also PID_FILE. + Scooter Morris + PID_FILE + Cause AUFS to write a (named) process-ID file. + Scooter Morris + XDEV_RENAME + Allows AUFS to copy/move files across file systems. + Mark Abbott + NOCHGRPEXEC + Make AUFS use the third argument to chown(2) instead of + exec'ing chgrp(1) to change group membership of file. + NB: Unavailable under System V, recommended for others. + Edward Moy + USEDIRSETGID + Set the group ID bit on directories whose group is not the + primary group of the AUFS user. Under SunOS and System V, + new files and directories are by default created with the + primary group unless the group ID bit of the parent is set. + David Hornsby + USR_FILE_TYPES + Map UNIX filename suffix to Mac Type/Creator. The map file + may be global (specified with -F option to AUFS) or per + user (~/.afpfile or ~/afpfile). The mapping also controls + the file translation method used (ascii, text or raw). See + the sample file in cap60/extras/afpfile. + David Hornsby et. al. + CREATE_AFPVOL + Create the .afpvols file, .finderinfo and .resource + directories in a subdirectory (default 'mac') of the home + directory of a new AUFS user (if they don't already exist). + An alternate subdirectory/volume name is specified with + the string -DCREATE_AFPVOL=\"other\" in m4.features + Heather Ebey + CREATE_AFPVOL_NAME (now CREAT_AFPVOL_NAM) + Modifies the CREATE_AFPVOL option to use the user/account + name for the volume label instead of the value of the + CREATE_AFPVOL variable. Assumes CREATE_AFPVOL. + Craig Zook + NETWORKTRASH + Under System 7.0, the "Network Trash Folder" mode is set + to 0x707 (world writeable). Defining NETWORKTRASH sets the + mode to that of the parent (top level) directory. + Edward Moy + ISO_TRANSLATE + Translate ISO 8859 characters in command line arguments + and configuration files into their Macintosh equivalents. + Likewise translate Macintosh characters to ISO 8859 for + etalk.local zone name, log file entries. + Austin Shelton + ISO_FILENAMES + Extend ISO/Mac character translation to AUFS file names + (assumes ISO_TRANSLATE is in use) + Dan Oscarsson + ISO_FILE_TRANS + Translate ISO 8859 characters in Creator "unix", Type + "TEXT" AUFS files into Mac characters on read and vice + versa on write (assumes ISO_TRANSLATE is in use). + Dan Oscarsson + NCS_ALL_TEXT + Apply ASCII or ISO 8859 file translation to all files of + type "TEXT", regardless of Creator. + Dan Oscarsson + LOGIN_AUTH_PROG + Allows AUFS -L command line argument to specify an external + authorization program for AUFS logins. See man/AUFS.8 + Irwin S. Tillman + PSJOBHEADER + If defined, PAPIF will check for an environmental variable of + the same name. The variable specifies a file which contains + a postcript header (for example to change paper trays or + print double sided) to be sent before the print job. + Jay Plett + DUPLEXMODE + If defined, PAPIF accepts a -2 option (also if the printer + name contains -dup*) to set double sided printing. Duplex + printing must be supported by the printer hardware. + Dan Mosedale + TRANS3 + Setup PAPIF for 'psdman' from the TranScript 3.0 package. + Dan Mosedale + PASS_THRU + If defined for LWSRV, print jobs will be passed through + with no Adobe pre-processing. This is useful for spoolers + providing service for PCs. + Gavin Longmuir + DONT_PARSE + If defined for LWSRV, print jobs will be passed through + with no Adobe pre-processing at all. This is useful for + spoolers which require an unaltered file, as the PASS_THRU + option does not disable some Adobe handling. The crtolf + option will continue to work if it is enabled with this + option. + Rakesh Patel + LPRARGS + Allows LWSRV -L command line arguments to be passed directly + to lpr. For example, printing via TransScript without + -T quote8bit set causes option-key characters to be printed + incorrectly. Setting -T quote8bit causes binary PostScipt + (usually gray scale images) to fail. In this situation use + LWSRV argument "-L-l" to pass "-l" to lpr. + Edward Moy + RUN_AS_USER & USER_REQUIRED + Attempt to map Mac Chooser/Owner name to a real UNIX user + name (based on map file, eg: cap60/extras/lib.cap.macusers) + then setuid() the lwsrv session as this user. IE: lpr can be + lprm'd as normal. USER_REQUIRED must find a match otherwise + it prints a failure message eg: cap60/extras/lib.cap.refused. + NB: no password authentication is provided. + Maarten Carels + PROCSET_PATCH + If defined for LWSRV, procset "patches" are not passed + through to the printer. These patches are code changes + for Apple LaserWriters and can have strange effects if + used on non-Apple printers. + Alexander Dupuy + ULT42PFBUG + Workaround for ULTRIX 4.2 packet filter problem (see Gotchas) + David Hornsby + STEAL_PORTS + Extend DDP Dynamic Socket range down into the experimental + AppleTalk socket range 64 - 128. Adds another 64 possible + sockets for server use. + Rakesh Patel + David Hornsby + USING_FDDI_NET + Includes code to determine if interface is running on + FDDI network and adjust packets accordingly. Digital UNIX + and Ultrix only at this stage. + Conrad Huang + NOCAPDOTPRINTERS + If defined, PAPIF will not use /etc/cap.printers, instead + the first line of the file /etc/lp/printers/lw/comment (for + printer "lw") is expected to contain a fully qualified + AppleTalk NBP name for the printer, ie: lw:LaserWriter@Zone + This option is intended for use on Solaris 2.N hosts only. + Andy Polyakov + USESYSLOG + If defined, PAPIF will send Information, Warning and Debug + messages to syslog(). + Andy Polyakov + DISTRIB_PASSWDS + If defined, CAP will use AUFS authentication methods that + involve exchanging encrypted random numbers instead of + sending passwords in clear text over the network. Refer to + manual entries aufsmkkey.8 and aufsmkusr.8 in cap60/man. + David Hornsby + AFP_DISTPW_PATH + Specifies that the distributed password files used in + DISTRIB_PASSWDS be kept in a directory other than the + user's home directory. For example, you could use the + following -DAFP_DISTPW_PATH=\"/usr/local/lib/cap/upw\" + David Hornsby + AFP_DISTPW_MODE + Modifies the file mode being enforced with DISTRIB_PASSWDS. + For use when user home directories are mounted via NFS. Uses + mode 0600 by default, use 0644 with NFS. + David Hornsby + DEBUG_AFP_CMD + If defined, AUFS will dump detailed logs of AFP command I/O + parameters in the file specified with the AUFS -Z option. + Use in conjunction with Inside AppleTalk 2nd edition for + debugging the AUFS AFP implementation. + David Hornsby + LOG_WTMP + If defined, AUFS will write an entry into the wtmp log file + on each successful AUFS connection. Allows usage tracking. + Gavin Longmuir + Heather Ebey + LOG_WTMP_FILE + Specifies alternate name for the wtmp file used in LOG_WTMP. + Heather Ebey + ADMIN_GRP + If defined, AUFS will check if the user is a member of a + special admin group and connect them with superuser privs. + Tim Leamy + TEMPFILE + Allow the LWSRV temporary file path to be specified. + Serge + USELPRSYM + Option to allow optional lpr '-s' (broken under OSF/1). + Serge + + + The following were originally to be defined in m4.setup, they have + been moved to m4.features for convenience. + + NONLXLATE - don't translate newlines on "line-at-a-time" reads + FULL_NCS_SUPPORT - National Character Support + GGTYPE="gid_t" - Host type definition for groups + DOCNAME - include document name in lpr job name + PAGECOUNT - include page count in lpr job name + ADOBE_DSC2_CONFORMANT - Conform to Adobe DSC2 specs + + +CAP Patches +----------- + +The primary source of CAP information is, as always, the network newsgroup +comp.protocols.appletalk. This forum will continue to be used to disseminate +information about CAP 6.0 updates, but we would very much like to encourage a +slightly more formal approach to the process of propogating CAP patches. + +Therefore, please send patches for CAP bug-fixes and new features to + + cap@munnari.OZ.AU + +Context diffs (from 'diff -c') are preferred, but not essential. Patches +received will be assigned a patch number, a priority and will be included in +regular (but not too frequent) CAP releases. In addition to then being sent +to comp.protocols.appletalk, the patches will be available via FTP from + + munnari.OZ.AU (in mac/cap.patches) + ftp-ns.rutgers.edu (in pub/cap/cap.patches) + gatekeeper.DEC.COM (in pub/net/appletalk/cap/cap.patches) + ftp.kuis.kyoto-u.AC.JP (in net/cap/cap60.patches) + src.doc.ic.AC.UK (in mac/multigate/cap.patches) + +The intent is to minimise the nightmare of trying to apply useful patches +that have come from divergent versions of the source code. Another goal is +to reduce the maintenance needed for CAP code, if the incremental patches are +applied in order, the result will be identical to any new release. + +To apply the patches with an absolute minimum of effort, it is recommended +that you use the 'patch' program written by Larry Wall. This can be obtained +from sites that archive postings from the newsgroup comp.sources.unix in the +directory volume7/patch2. It is important to ensure that any patches for the +patch sources are applied, some CAP patches can fail if the unmodified patch +code is used. + +The current patch level is recorded in the cap60/README file. New patches +are applied from the top level directory with the command + + % patch -p < cap60.patches/cap60.patch0NN + +where NN is the patch number. The -p option tells patch to obtain +information about which file to alter from the patch header (use -p0 on +a DEC Alpha under OSF/1). If you attempt to apply a patch more than once, +patch will enquire about "a reversed or previously applied patch", answering +yes to this will remove the patch, leaving the original file (and bug), this +is not good ... + + +CAP and AppleTalk Phase 2 +------------------------- + +CAP 6.0 supports EtherTalk Phase 2 using the SunOS NIT interface, the enet +packet filter (requiring kernel mods, see cap60/support/enet) and the ULTRIX +packet filter (ULTRIX 4.0 or later). It also is possible to use CAP in a +Phase 2 environment with IPTalk as indicated below. + +Traditionally, the CAP transport mechanism uses UDP/IP packets. This is +called IPTalk (also known as KIP) and is a "non-extended" (1 net number +and 1 zone name) network. CAP with IPTalk can run on a LARGE variety of +UNIX platforms. + +On some machines, CAP can also produce EtherTalk Phase 1 packets directly +(using Native EtherTalk, Kernel AppleTalk or UAB - the UNIX AppleTalk +Bridge). EtherTalk Phase 1 is also a "non-extended" network. + +Macintoshes on EtherNet can speak EtherTalk Phase 1 or Phase 2. EtherTalk +Phase 2 networks are always "extended", allowing multiple network numbers +and zone names on a single cable. An "extended" network can be "restricted" +to have one net number and one zone name. This permits the Phase 2 routing +information to be translated into Phase 1 routing packet format. This is +not otherwise possible. + +For completeness, LocalTalk networks can use either Phase 1 or Phase 2 +AppleTalk packets, but such networks are always "non-extended". + +In a brief summary of AppleTalk Phase 1 and Phase 2 differences on LocalTalk, +routing information (RTMP) packet formats are different. NBP packets allow a +new character (approxEquals or 0xc5) for partial NBP name and type matching. +Nodes are no longer required to send to the last RTMP sender that they heard +from, rather, they can choose to send to the "best" router. There are other +differences for Phase 1 and Phase 2 EtherTalk packets on EtherNet. These +differences relate mainly to delivery mechanisms. + +If you use CAP with IPTalk and *NO* AppleTalk routing over IP (ie: via an +IP link between IP gateways) then you can use CAP with Phase 2, "extended" +networks with no restrictions (except of course, that you must have an +AppleTalk Gateway capable of talking IPTalk and EtherTalk Phase 2). + +On the other hand, if you are using CAP and IPTalk with AppleTalk routing +over IP, or CAP with EtherTalk Phase 1, then your Phase 2 networks must all +be "restricted" and you need to have a gateway capable of operating in +"transition" mode (the problem with the first case is due to the way IPTalk +works rather than CAP. IPTalk 2 is under development and will solve this). + +The gateway translates the packets from one format to another. Suitable +IPTalk/Phase 2 gateways are the Webster MultiPort Gateway (running Megan +2.1 or later) and the Shiva FastPath 4 (running KSTAR 8.0 or later). Most +earlier versions of the gateway code will happily translate from IPTalk to +either Phase 1 or LocalTalk. + + +See Also +-------- + + Manual entries cap60/man/* + Various documentation cap60/doc/* + Native EtherTalk cap60/support/ethertalk/README + Kernel AppleTalk lib/cap/abkas.c, netatalk-1.2 distribution + + http://www.cs.mu.OZ.AU/appletalk/atalk.html + +Gotchas +------- + +There are various problems known to exist with CAP and particular UNIX +systems or programs. + + ULTRIX: + You may see the error message + open: ln0: No such file or directory + when running aarpd. The problem is due to missing packet + filter devices in /dev/pf. For details on how to make the + devices, refer to the manual entry for 'packetfilter'. + + ULTRIX 4.1: + There is a problem with packet filter code in ULTRIX 4.1 + that prevents CAP writing packets to the network. Before + running CAP with 4.1 (on any hardware platform), request + the patched version of the file "net_common.o" from your + DEC Customer Support Center. You may also need to obtain + the updated "if_ln.o" to prevent problems with LAT. + + ULTRIX 4.2/4.2A: + There are three known "problems" with packet filter code + and unicast/802.3 packet handling under ULTRIX 4.2. The + problems have been identified and fixed. Contact your + local DEC Customer Support Center and query the status + of patches for "net_common.o" and "pfilt.o". The problems + and WORKAROUNDS for UNPATCHED systems are as follows: + + "unicast packets are not delivered properly to PF clients" + define COPYALL mode using 'ifconfig ... +copyall' + + "802.3 (phase 2) AARP packets converted to EtherNet equiv." + apply CAP patch 75, define ULT42PFBUG in Configure + + "3rd-party Mac EtherNet cards that round up odd 802.3 packets" + no workaround, requires new net_common.o, pfilt.o + with less stringent 802.3 length sanity checking. + + ULTRIX 4.3 + under ULTRIX V4.3 you may need to obtain a patched version of + /etc/lockd to use DENYREADWRITE code. A possible workaround is + to 'nfssetlock off' ... this will impact NFS mounted volumes. + + HP/Apollo Domain BSD 4.3 + Avoid using the //node/path syntax in afpvols, either + explicitly or from expansion of ~. If such a definition + appears in afpvols, the volume corresponding to it may + be visible in the Chooser but be unmountable or may not + appear in the Chooser. Other volumes defined after such + a line also may not show up in the Chooser. You may use + directories mounted on other nodes using soft links or + a path such as /../node/path. It is not clear how much of + the file locking carries over directories on other nodes. + See NOTES for avoiding the //node/path construction. + + NFS + In some circumstances, CAP AUFS using NFS mounted + filesystems may complain about files being locked when + they are not obviously so. Ensure that you have the latest + NFS bugfixes for your UNIX platform. + + +Bug Fixes +--------- + +The following CAP 5.0 bug fixes were incorporated into CAP 6.0. + + abpap.c.tickletimer Jim Guyton + abpaps.c.flowquantum William Roberts + procset.c.looping William Roberts + mips.ultrix.byteswap + snitch.c.stringlength Phil Farrell + papif.c.atpresponse Phil Farrell + papif.c.variousbugs Jeff Stearns + papif.c.reverse Robert McLay + afpos.c.strcmp JQ Johnson + simple.c.comment Mark Rawling + rtmp.c.nocase John Sellens diff --git a/Configure b/Configure new file mode 100755 index 0000000..2aa8612 --- /dev/null +++ b/Configure @@ -0,0 +1,1975 @@ +#!/bin/sh +# $Author: djh $ $Date: 1996/09/10 14:11:08 $ +# $Header: /mac/src/cap60/RCS/Configure,v 2.107 1996/09/10 14:11:08 djh Rel djh $ +# $Revision: 2.107 $ +# CAP configuration shell script. This ain't perfect, but it's a start. +# Execute with /bin/sh Configure if your system won't run it (ksh is okay too) +# +# Usage: Configure [output file name] +# +fastether="define([fastether],1) # For papif and samples" +needselfdef="define([selfdefinetypes],1)" +# ccompiler="define([thecompiler],[cc])" +if [ -z "${CC}" ]; then + ccompiler=cc +else + ccompiler=${CC} +fi +export ccompiler +# lpd system "bsd" or sys v "lp" +lpd="bsd" +mydir=`pwd` +PCAT=/bin/cat +PGREP=grep +if [ -f /bin/nm ]; then + PNM=/bin/nm +else + if [ -f /usr/ccs/bin/nm ]; then + PNM=/usr/ccs/bin/nm + else + PNM=/usr/bin/nm + fi +fi +# define to sh or /bin/sh if shell scripts can't be "executed" for some reason +USESH="" +export PGREP +os="" +ossecondary="" + +echo +echo "This is the CAP configuration script. It will attempt to help" +echo "you get the CAP libraries and component programs configured." +echo +if [ -f /etc/atalk.local ]; then + ${PGREP} _ /etc/atalk.local > /dev/null + rc=$? + if [ $rc -eq 0 ]; then + echo "WARNING: Your /etc/atalk.local file MAY need to be edited." + echo "Zone names must now be quoted to include spaces. The use" + echo "of underscores to denote a space is no longer supported." + echo + fi +fi + +echo +echo "MAJOR CONFIGURATION" + +useatis=1 +# Configure OS +echo "Checking for Ultrix 4.0, OSF1, or later versions of Ultrix" +# Ultrix4.0 +if [ -f /bin/uname ]; then + case "`/bin/uname`" in + "ULTRIX") + if [ `/bin/uname -r` -ge 4 ]; then + osdefault="ultrix40" + fi + ;; + "OSF1") + osdefault="osf1" + ;; + esac +fi +# Ultrix2.0 +if [ -z "${osdefault}" ]; then + echo "Checking for Ultrix 2.0" + if [ -f /vmb.exe ]; then + if [ -d /var ]; then + ossecondary="ultrix22" + echo "Ultrix 2.2 or later version of Ultrix" + else + echo "Ultrix 2.0 or later version of Ultrix" + fi + osdefault="ultrix20" + fi +fi +# sunos +if [ -z "${osdefault}" ]; then + echo "Checking for SunOS/Solaris" + if [ -f /bin/uname ]; then + if [ `uname -s` = "SunOS" ]; then + if [ -f /bin/uname ]; then + if [ `uname -r` -ge "5.0" ]; then + echo "Solaris" + osdefault="solaris" + else + echo "SunOS" + osdefault="sunos" + fi + else + echo "SunOS" + osdefault="sunos" + fi + fi + fi +fi +# Encore Multimax +if [ -z "${osdefault}" ]; then + echo "Checking for Encore MultiMax" + if [ -f /Umax.image ]; then + osdefault="encore" + fi +fi +# IBM RT +if [ -z "${osdefault}" ]; then + echo "Checking for IBM RT running ACIS 4.2/4.3" + if [ -f /lib/ropt ]; then + if [ -d /usr/ibm ]; then + osdefault="bsd" + echo "IBM RT with ACIS 4.x" + ossecondary="acis 4.x" + if [ -f /bin/pcc ]; then +echo "I see /bin/pcc - I will use this instead of cc in case" +echo "you have the MetaWare High C compiler set as default" + ccompiler="pcc" + else +echo "WARNING: the MetaWare High C compiler and CAP are not compatible" +echo "modify the thecompiler define to point to the portable C compiler" +echo "if the default happens to be hc" + fi + fi + fi +fi +# Pyramid +if [ -z "${osdefault}" ]; then + echo "Checking for Pyramid" + if [ -f /bin/pyr ]; then + /bin/pyr + rc=$? + if [ $rc -eq 0 ]; then + echo "Pyramid" + osdefault="pyr" + fi + fi +fi +#Amdahl +if [ -z "${osdefault}" ]; then + echo "Checking for UTS" + if [ -f /bin/uts ]; then + /bin/uts + rc=$? + if [ $rc -eq 0 ]; then + echo "uts" + osdefault="uts" + lpd="lp" + fi + fi +fi +#ICL DRS6000 with DRS/NX 4.0 +if [ -z "${osdefault}" ]; then + echo "Checking for DRS/NX" + if [ -f /bin/uname ]; then + if [ `uname -m` = "DRS6000" ]; then + if [ `uname -s` = "unix" ]; then + echo "drsnx - ensure /usr/ucb comes before /usr/bin" + osdefault="drsnx" + lpd="lp" + fi + fi + fi +fi +useauxappletalk=0 +# A/UX, hpux, SCO Xenix System V +if [ -z "${osdefault}" ]; then + echo "Checking for System V based machines" + if [ -f /bin/uname ]; then + case "`uname`" in + "A/UX") + echo "A/UX" + osdefault="aux" + if [ `uname -r` -ge 2 ]; then + echo "Release 2.0 or greater" + echo "(defaulting to native AppleTalk)" + useauxappletalk=1 + useatis=0 + lpd="lpr" + else + echo "Release 1.1.1 or less" + lpd="lp" + fi + break + ;; + "HP-UX") + echo "HP-UX" + osdefault="hpux" + lpd="lp" + break + ;; + "XENIX") + echo "SCO Xenix System V" + osdefault="xenix5" + lpd="lp" + break + ;; + "AIX") + echo "IBM AIX System V" + osdefault="aix" + lpd="lp" + break + ;; + "DomainOS") + osdefault="domainossysv" + echo "HP/Apollo Domain OS System V" + echo "Sorry, I do not build under Sys 5.3 environment." + echo "Try building under BSD 4.3 environment." + exit + break + ;; + esac + fi +fi +# Sequent Balance +if [ -z "${osdefault}" ]; then + echo "Checking for Sequent Balance" + if [ -f /dynix ]; then + echo "Sequent Balance" + osdefault="dynix" + fi +fi +# Silicon Graphics +if [ -z "${osdefault}" ]; then + echo "Checking for Silicon Graphics IRIS/IRIX" + if [ -f /bin/4d ]; then + /bin/4d + rc=$? + if [ $rc -eq 0 ]; then + echo "Silicon Graphics IRIS/IRIX" + osdefault="irix" + fi + fi +fi +# Sony NEWS +if [ -z "${osdefault}" ]; then + echo "Checking for Sony NEWS" + if [ -d /usr/sony/bin ]; then + echo "Sony NEWS" + osdefault="newsos" + fi +fi +# Control Data EP/IX V1/V2 +if [ -z "${osdefault}" ]; then + echo "Checking for Control Data EP/IX V1/V2" + if [ -f /bin/uname ]; then + if [ "`/bin/uname -v`" = "UMIPS" \ + -o "`/bin/uname -v`" = "RISCos" ]; then + echo "Control Data EP/IX" + osdefault="epix" + echo "Compile using bsd43 environment" + ccompiler="cc -systype bsd43" + fi + fi +fi +# 386bsd, FreeBSD 1.n and derivatives +if [ -z "${osdefault}" ]; then + echo "Checking for 386bsd etc." + if [ -x /386bsd ]; then + echo "386BSD and derivatives" + osdefault="386bsd" + fi +fi +# NetBSD 1.0 +if [ -z "${osdefault}" ]; then + echo "Checking for NetBSD 1.0" + if [ -x /netbsd ]; then + echo "NetBSD 1.0" + osdefault="netbsd" + fi +fi +# FreeBSD 2.0 +if [ -z "${osdefault}" ]; then + echo "Checking for FreeBSD" + if [ -f /usr/bin/uname ]; then + if [ "`/usr/bin/uname -s`" = "FreeBSD" ]; then + echo "FreeBSD" + osdefault="freebsd" + fi + fi +fi +# BSDI 1.1 +if [ -z "${osdefault}" ]; then + echo "Checking for BSDI BSD/386 1.1" + if [ -f /bsd ]; then + echo "BSDI BSD/386 1.1" + osdefault="bsdi" + fi +fi +# Linux 1.3.N +if [ -z "${osdefault}" ]; then + echo "Checking for Linux" + if [ -f /vmlinuz ]; then + echo "Linux" + osdefault="linux" + fi +fi +# HP/Apollo Domain +if [ -z "${osdefault}" ]; then + echo "Checking for HP/Apollo Domain 10.4" + if [ -f /lib/clib -a -f /lib/libc ]; then + if [ "${SYSTYPE}" = "bsd4.3" ]; then + domainvers=none + domainvers=`/usr/apollo/bin/bldt | { read ln; read ln; read d1 d2 d3 ver ln; echo $ver; }` + case "$domainvers" in + 10.0, | 10.0.1, ) domainvers=10.0;; + 10.1, ) domainvers=10.1;; + 10.2, | 10.2.[0-9]*, ) domainvers=10.2;; + 10.3, | 10.3.[0-9]*, ) domainvers=10.3;; + 10.4, | 10.4.[0-9]*, ) domainvers=10.4;; + esac + echo "HP/Apollo Domain Version ${domainvers} under BSD 4.3 env." + osdefault="domainosbsd" + echo "Read NOTES about //node/path syntax in afpvols" + if [ "${domainvers}" != "10.4" ]; then + echo "WARNING: untested under Domain OS other than 10.4" + fi + fi + fi +fi +# NeXTstation +if [ -z "${osdefault}" ]; then + echo "Checking for NeXT." + if [ -x /NextApps ]; then + echo "NeXT" + osdefault="next" + fi +fi +# Default +if [ -z "${osdefault}" ]; then + echo "Establishing default as BSD" + echo "Warning: unable to guess unix variant, setting to bsd standard." + echo "bsd should be good for the following:" + echo " IBM-RT (ACIS 4.2,4.3), Ultrix 1.0, Ultrix 1.1, BSD 4.2," + echo " and BSD 4.3" + osdefault="bsd" +fi +echo +valid=0 +echo -n test | ${PGREP} n > /dev/null +rc=$? +if [ $rc -ne 0 ]; then + PROMPT="echo -n" +elif [ -x /usr/ucb/echo ]; then + PROMPT="/usr/ucb/echo -n" +else + PROMPT="echo" +fi +while [ ${valid} -eq 0 ]; +do + ${PROMPT} "Operating System type? (default ${osdefault}, ? for valid items) " + read os arg1 + if [ -z "${os}" ]; then + os=${osdefault} + fi + case ${os} in + "osf1") valid=1;; + "ultrix20") valid=1;; + "ultrix40") valid=1;; + "ultrix12") valid=1;; + "bsd") valid=1;; + "hpux") valid=1;; + "aux") valid=1;; + "xenix5") valid=1;; + "aix") valid=1;; + "uts") valid=1;; + "pyr") valid=1;; + "sunos") valid=1;; + "solaris") valid=1;; + "encore") valid=1;; + "next") valid=1;; + "dynix") valid=1;; + "irix") valid=1;; + "newsos") valid=1;; + "drsnx") valid=1;; + "epix") valid=1;; + "386bsd") valid=1;; + "netbsd") valid=1;; + "freebsd") valid=1;; + "bsdi") valid=1;; + "linux") valid=1;; + "domainosbsd") valid=1;; + "?"|*) + if [ "${os}" != "?" ]; then + echo "unknown type ${os}, valid are:" + fi + echo " bsd - try if you aren't sure"; + echo " ultrix12 - Ultrix 1.2"; + echo " ultrix20 - Ultrix 2.0 or later"; + echo " ultrix40 - Ultrix 4.0 or later"; + echo " osf1 - DEC-OSF/1 1.3 or later"; + echo " hpux - HP-UX for series 9000"; + echo " aux - A/UX"; + echo " uts - Amdahl UTS"; + echo " xenix5 - SCO Xenix System V"; + echo " aix - IBM AIX System V"; + echo " pyr - pyramid"; + echo " sunos - suns under SunOS 4.N or less"; + echo " solaris - suns under Solaris 2.0 or greater"; + echo " encore - Encore MultiMax"; + echo " next - NeXT/MACH"; + echo " dynix - Sequent Balance" + echo " irix - Silicon Graphics IRIS/IRIX" + echo " newsos - Sony NEWS" + echo " drsnx - ICL DRS/NX V4.0" + echo " epix - Control Data EP/IX" + echo " 386bsd - 386/BSD, FreeBSD 1.n and derivatives" + echo " netbsd - NetBSD 1.0" + echo " freebsd - FreeBSD 2.0" + echo " bsdi - BSDI BSD/386 1.1" + echo " linux - Linux" + echo " domainosbsd - HP/Apollo Domain BSD 4.3" + ;; + esac +done +echo "Will configure for ${os}" +echo +echo +uabprogs="define([uabprogs],[])" +uabplibs="define([uabplibs],[])" +uabpobjs="define([uabpobjs],[])" +etherprogs="define([etherprogs],[])" +etherpobjs="define([etherpobjs],[])" +capdprogs="define([capdprogs],[])" +capdpobjs="define([capdpobjs],[])" +lapobj="define([lapobj],[abkip.o abddp.o abnbp.o atalkdbm.o])" +usingphase2="# define([usephase2],1)" +usingatis="# define([useatis],1)" +singletree="# define([debug],1)" +includefile="# define([includef],1)" +result=0 +usephase2=0 +uabsupport=0 +uarsupport=0 +ethersupport=0 +kernelsupport=0 +case ${os} in + "sunos"|"ultrix40"|"ultrix20"|"ultrix12"|"osf1") + uabsupport=1 + uarsupport=1 + ethersupport=1 + ;; + "solaris") + uarsupport=1 + ethersupport=1 + ;; + "aix") + uarsupport=1 + ;; + "pyr") +# later, someone should try this. +# uabsupport=1 + ethersupport=1 + ;; + "irix") + uabsupport=1 + uarsupport=1 + ethersupport=0 + ;; + "newsos") + uabsupport=1 + uarsupport=1 + ethersupport=0 + ;; + "hpux") + uabsupport=1 + uarsupport=1 + ethersupport=0 + ;; + "386bsd") + uabsupport=1 + uarsupport=1 + ethersupport=1 + ;; + "netbsd") + uabsupport=1 + uarsupport=1 + ethersupport=1 + ;; + "freebsd") + uabsupport=1 + uarsupport=1 + ethersupport=1 + ;; + "bsdi") + uabsupport=1 + uarsupport=1 + ethersupport=1 + ;; + "linux") + uarsupport=1 + kernelsupport=1 + ;; + "next") + uarsupport=1 + ethersupport=1 + ;; + *) + uabsupport=0 + uarsupport=0 + ethersupport=0 + ;; +esac +# Tell people their list of choices... +if [ ${uabsupport} -eq 1 -o ${uarsupport} -eq 1 \ + -o ${ethersupport} -eq 1 -o ${kernelsupport} -eq 1 ]; then + echo "In addition to IPTalk (the default), this operating system" + echo "can also support CAP AppleTalk packets over EtherTalk via:" + echo + if [ ${uabsupport} -eq 1 ]; then + echo " UAB (Unix AppleTalk Bridge)" + fi + if [ ${uarsupport} -eq 1 ]; then + echo " UAR (Unix AppleTalk Router)" + fi + if [ ${ethersupport} -eq 1 ]; then + echo " Native EtherTalk" + fi + if [ ${kernelsupport} -eq 1 ]; then + echo " Kernel AppleTalk" + fi + echo + echo "See the README files and other documentation for further info." + echo +fi + +if [ ${uabsupport} -eq 1 ]; then + result=0 + while : + do + ${PROMPT} "Do you wish to use UAB (Unix AppleTalk Bridge) (default no) ? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "yes"|"y") result=1; break;; + "no" |"n") result=0; break;; + *) echo "you must answer yes or no or carriage return for no." ;; + esac + done + if [ $result -eq 0 ]; then + echo "Not using UAB." + uabprogs="define([uabprogs],[])" + uabplibs="define([uabplibs],[])" + uabpobjs="define([uabpobjs],[])" + lapobj="define([lapobj],[abkip.o abddp.o abnbp.o atalkdbm.o])" + else + uabprogs="define([uabprogs],[uab])" + uabplibs="define([uabplibs],[libuabcap.a])" + lapobj="define([lapobj],[abmkip.o abddp.o abnbp.o atalkdbm.o])" + case ${os} in + "ultrix20"|"ultrix12") + echo "OK, setting things up for UAB." + uabpobjs="define([uabpobjs],[dlip.o])";; + "ultrix40") + echo "OK, setting things up for UAB." + uabpobjs="define([uabpobjs],[spfiltp.o])";; + "osf1") + echo "OK, setting things up for UAB." + uabpobjs="define([uabpobjs],[spfiltp.o])";; + "pyr") + echo "OK, setting things up for UAB." + uabpobjs="define([uabpobjs],[senetp.o])";; + "irix") + echo "OK, setting things up for UAB." + uabpobjs="define([uabpobjs],[snooppf.o])";; + "newsos") + echo "OK, setting things up for UAB." + uabpobjs="define([uabpobjs],[srawetherp.o])";; + "hpux") + echo "OK, setting things up for UAB." + uabpobjs="define([uabpobjs],[sllap.o])";; + "386bsd"|"netbsd"|"freebsd"|"bsdi"|"next") + echo "OK, setting things up for UAB." + uabpobjs="define([uabpobjs],[bpfiltp.o])";; + "solaris") + echo "OK, setting things up for UAB." + uabpobjs="define([uabpobjs],[sdlpi.o])";; + "sunos") + ${PROMPT} "Have you installed the 'enet' driver (no) ? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "yes"|"y") aresult=1;; + *) aresult=0;; + esac + if [ $aresult -eq 0 ]; then + echo "OK, using the NIT ethernet interface." + uabpobjs="define([uabpobjs],[snitp.o])" + else + echo "OK, using the 'enet' ethernet interface." + uabpobjs="define([uabpobjs],[senetp.o])" + fi + esac + fi +fi +echo +if [ $result -eq 0 ]; then + if [ ${uarsupport} -eq 1 ]; then + result=0 + while : + do + ${PROMPT} "Do you wish to use UAR (Unix AppleTalk Router)(default no) ? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "yes"|"y") result=1; break;; + "no" |"n") result=0; break;; + *) echo "you must answer yes or no or carriage return for no." ;; + esac + done + if [ $result -eq 0 ]; then + echo "Not using UAR." + uabprogs="define([uabprogs],[])" + uabplibs="define([uabplibs],[])" + uabpobjs="define([uabpobjs],[])" + lapobj="define([lapobj],[abkip.o abddp.o abnbp.o atalkdbm.o])" + else + uabprogs="define([uabprogs],[])" + uabplibs="define([uabplibs],[])" + uabpobjs="define([uabpobjs],[])" + lapobj="define([lapobj],[abmkip.o abddp.o abnbp.o atalkdbm.o])" + ${PROMPT} "Do you want Phase 2 compatibility (yes) ? " + read ans + if [ -z "${ans}" ]; then + ans="yes" + fi + case ${ans} in + "yes"|"y") + usephase2=1 + echo "OK, setting things up for UAR, Phase 2." + ;; + *) + echo "OK, setting things up for UAR, Phase 1." + ;; + esac + fi + fi +fi +echo +if [ $result -eq 0 ]; then + if [ ${ethersupport} -eq 1 ]; then + result=0 + while : + do + ${PROMPT} "Do you wish to use Native EtherTalk (default no) ? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "yes"|"y") result=1; break;; + "no" |"n") result=0; break;; + *) echo "you must answer yes or no or carriage return for no." ;; + esac + done + if [ $result -eq 0 ]; then + echo "Not using Native EtherTalk." + etherprogs="define([etherprogs],[])" + etherpobjs="define([etherpobjs],[])" + lapobj="define([lapobj],[abkip.o abddp.o abnbp.o atalkdbm.o])" + else + etherprogs="define([etherprogs],[aarpd])" + lapobj="define([lapobj],[abetalk.o abddp.o abnbp.o atalkdbm.o])" + case ${os} in + "ultrix20"|"ultrix12") + echo "OK, setting things up for Native EtherTalk." + etherpobjs="define([etherpobjs],[dlip.o])";; + "osf1"|"ultrix40") + ${PROMPT} "Do you want Phase 2 compatibility (yes) ? " + read ans + if [ -z "${ans}" ]; then + ans="yes" + fi + case ${ans} in + "yes"|"y") + usephase2=1 + echo "OK, setting things up for Native EtherTalk, Phase 2" + ;; + *) + echo "OK, setting things up for Native EtherTalk." + ;; + esac + case ${os} in + "osf1") + etherpobjs="define([etherpobjs],[spfiltp.o])" + ;; + "ultrix40") + etherpobjs="define([etherpobjs],[spfiltp.o])" + ;; + esac + ;; + "pyr") + echo "OK, setting things up for Native EtherTalk." + etherpobjs="define([etherpobjs],[senetp.o])";; + "386bsd") + echo "OK, setting things up for Native EtherTalk." + etherpobjs="define([etherpobjs],[bpfiltp.o])";; + "netbsd"|"freebsd"|"bsdi"|"next") + ${PROMPT} "Do you want Phase 2 compatibility (yes) ? " + read ans + if [ -z "${ans}" ]; then + ans="yes" + fi + case ${ans} in + "yes"|"y") + usephase2=1 + echo "OK, setting things up for Native EtherTalk, Phase2." + ;; + *) + echo "OK, setting things up for Native EtherTalk." + ;; + esac + etherpobjs="define([etherpobjs],[bpfiltp.o])";; + "solaris") + ${PROMPT} "Do you want Phase 2 compatibility (yes) ? " + read ans + if [ -z "${ans}" ]; then + ans="yes" + fi + case ${ans} in + "yes"|"y") + usephase2=1 + ;; + *) + ;; + esac + ${PROMPT} "OK, using the Streams ethernet interface " + if [ $usephase2 -eq 1 ]; then + echo "for Phase 2 support." + else + echo "for Phase 1 support." + fi + etherpobjs="define([etherpobjs],[sdlpi.o])";; + "sunos") + ${PROMPT} "Do you want Phase 2 compatibility (yes) ? " + read ans + if [ -z "${ans}" ]; then + ans="yes" + fi + case ${ans} in + "yes"|"y") + usephase2=1 + ;; + *) + ;; + esac + ${PROMPT} "Have you installed the 'enet' driver (no) ? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "yes"|"y") aresult=1;; + *) aresult=0;; + esac + if [ $aresult -eq 0 ]; then + ${PROMPT} "OK, using the NIT ethernet interface" + etherpobjs="define([etherpobjs],[snitp.o])" + else + ${PROMPT} "OK, using the 'enet' ethernet interface" + etherpobjs="define([etherpobjs],[senetp.o])" + fi + if [ $usephase2 -eq 1 ]; then + echo " with Phase 2." + else + echo "." + fi + ;; + esac + fi + fi +fi +echo +if [ $result -eq 0 ]; then + if [ ${kernelsupport} -eq 1 ]; then + result=0 + while : + do + ${PROMPT} "Do you wish to use Kernel AppleTalk (default no) ? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "yes"|"y") result=1; break;; + "no" |"n") result=0; break;; + *) echo "you must answer yes or no or carriage return for no." ;; + esac + done + if [ $result -eq 0 ]; then + echo "Not using Kernel AppleTalk." + capdprogs="define([capdprogs],[])" + capdpobjs="define([capdpobjs],[])" + lapobj="define([lapobj],[abkip.o abddp.o abnbp.o atalkdbm.o])" + else + echo "OK, setting things up for Kernel AppleTalk." + capdprogs="define([capdprogs],[capd])" + capdpobjs="define([capdpobjs],[capd.kas.o])" + lapobj="define([lapobj],[abkas.o abddp.o abnbp.o atalkdbm.o])" + usephase2=1 + fi + fi +fi +if [ $useauxappletalk -ne 0 ]; then + lapobj="define([lapobj],[abauxddp.o abauxnbp.o])" +fi +if [ $usephase2 -ne 0 ]; then + usingphase2="define([usephase2],1)" +fi +if [ $useatis -ne 0 ]; then + usingatis="define([useatis],1)" +fi +echo +echo "CAP can be setup to occupy a single directory tree (for testing)." +result=0 +while : +do + ${PROMPT} "Do you wish to restrict CAP to this subdirectory (default no) ? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "yes"|"y") result=1; break;; + "no" |"n") result=0; break;; + *) echo "you must answer yes or no or carriage return for no." ;; + esac +done +if [ $result -ne 0 ]; then + ${PROMPT} "Which directory (" `pwd` ") ? " + read ans + if [ ! -z "${ans}" ]; then + if [ -d "${ans}" ]; then + mydir=${ans} + else + echo "Warning: the directory" ${ans} "doesn't exist." + fi + fi + echo "OK, using" ${mydir} "as the CAP root directory." + singletree="define([debug],1)" +else + echo + result=0 + while : + do + ${PROMPT} "Look for my include files in other than /usr/include (no) ? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "yes"|"y") result=1; break;; + "no" |"n") result=0; break;; + *) echo "you must answer yes or no or carriage return for no." ;; + esac + done + if [ $result -ne 0 ]; then + ${PROMPT} "Include file location (" ${mydir} ") ? " + read ans + if [ -n "${ans}" ]; then + mydir="${ans}" + fi + echo "OK, using include files in" ${mydir} + includefile="define([includef],1)" + fi +fi +echo +echo "CAP can be configured for various optional features." +echo "For more information, refer to the file 'CAP60.README'." +echo +echo "Checking for special feature customisation ..." +${PCAT} <<\EOT0 > /tmp/m4.f.$$ +# m4.features +# +# This file is used to determine the default set of additional features +# that will be incorporated into CAP. These features have been contributed +# by many people throughout the network community. +# +# Necessary preamble - please don't alter +define(`concat',$1$2$3$4$5$6$7$8$9) +define(`specialcflags',`') +define(`aufsosflags',`') +define(`simpleflags',`') +define(`lwflags',`') +# +# To change the feature set, simply uncomment the wanted defines(). +# The Configure file will then use the customised version. +# For more details, refer to the file 'CAP60.README' +# +# + SHORT_NAMES adds short name support to AUFS for AppleShare PC. +# define(`specialcflags',concat(specialcflags,` -DSHORT_NAMES')) +# +# + NOCASEMATCH removes UNIX case sensitivity in AUFS (like Macs). +# define(`specialcflags',concat(specialcflags,` -DNOCASEMATCH')) +# +# + SIZESERVER adds a daemon per AUFS server process for volume size info. +# define(`specialcflags',concat(specialcflags,` -DSIZESERVER')) +# +# + LWSRV_AUFS_SECURITY provides printer security based on AUFS connection. +# define(`specialcflags',concat(specialcflags,` -DLWSRV_AUFS_SECURITY')) +# +# + HIDE_LWSEC_FILE increases security of LWSRV_AUFS_SECURITY flag file +# define(`aufsosflags',concat(aufsosflags,` -DHIDE_LWSEC_FILE')) +# define(`lwflags',concat(lwflags,` -DHIDE_LWSEC_FILE')) +# +# + LWSRV_LPR_LOG causes stdout/stderr lpr output to be included in lwsrv log +# define(`specialcflags',concat(specialcflags,` -DLWSRV_LPR_LOG')) +# +# + AUTHENTICATE turn on alternate AUFS/LWSRV authentication method +# define(`specialcflags',concat(specialcflags,` -DAUTHENTICATE')) +# +# + STAT_CACHE causes critical AUFS stat() calls to be cached. +# define(`specialcflags',concat(specialcflags,` -DSTAT_CACHE')) +# +# + TREL_TIMEOUT causes an extra TREL timeout listener to run to avoid timeouts +# define(`specialcflags',concat(specialcflags,` -DTREL_TIMEOUT')) +# +# + RUTGERS includes local modifications for Rutgers University +# define(`specialcflags',concat(specialcflags,` -DRUTGERS')) +# +# + MELBOURNE includes local modifications for Melbourne University +# define(`specialcflags',concat(specialcflags,` -DMELBOURNE')) +# +# + USE_HOST_ICON provides automatic aufs ICON selection on supported hosts +# define(`specialcflags',concat(specialcflags,` -DUSE_HOST_ICON')) +# +# + PERMISSIVE_USER_NAME allows AUFS users to have their real name in Chooser +# define(`specialcflags',concat(specialcflags,` -DPERMISSIVE_USER_NAME')) +# +# + ULTRIX_SECURITY adds ULTRIX enhanced security to aufs +# define(`specialcflags',concat(specialcflags,` -DULTRIX_SECURITY')) +# +# + DIGITAL_UNIX_SECURITY adds OSF/1 enhanced security to aufs +# define(`specialcflags',concat(specialcflags,` -DDIGITAL_UNIX_SECURITY')) +# +# + USING_FDDI_NET adds support for FDDI networks (Digital UNIX/Ultrix only) +# define(`specialcflags',concat(specialcflags,` -DUSING_FDDI_NET')) +# +# + USE_MAC_DATES maintains Mac Create/Modify dates on file copy +# define(`specialcflags',concat(specialcflags,` -DUSE_MAC_DATES')) +# +# + DEV_NIT specifies alternate NIT device name (default "/dev/nit") +# define(`specialcflags',concat(specialcflags,` -DDEV_NIT=\"/dev/nit\"')) +# +# + APPLICATION_MANAGER control the use of designated applications +# define(`specialcflags',concat(specialcflags,` -DAPPLICATION_MANAGER')) +# +# + AUFS_README links readme file into new user's top level +# define(`specialcflags',concat(specialcflags,` -DAUFS_README')) +# +# + ULT42PFBUG unpatched ULTRIX 4.2 packet filter workaround (also need COPYALL) +# define(`specialcflags',concat(specialcflags,` -DULT42PFBUG')) +# +# + AUFS_IDLE_TIMEOUT will disconnect idle AUFS sessions (-[i|I] period) +# define(`specialcflags',concat(specialcflags,` -DAUFS_IDLE_TIMEOUT')) +# +# + REREAD_AFPVOLS kill -USR1 will make parent aufs re-read system vols file +# define(`specialcflags',concat(specialcflags,` -DREREAD_AFPVOLS')) +# +# + PASS_THRU pass through LWSRV jobs with no adobe preprocessing (for PCs) +# define(`specialcflags',concat(specialcflags,` -DPASS_THRU')) +# +# + DONT_PARSE pass through LWSRV jobs with no adobe preprocessing at all +# define(`specialcflags',concat(specialcflags,` -DDONT_PARSE')) +# +# + XDEV_RENAME allow files to be moved over cross device links +# define(`specialcflags',concat(specialcflags,` -DXDEV_RENAME')) +# +# + USR_FILE_TYPES user defined file suffix to creator/type/xlate mapping +# define(`specialcflags',concat(specialcflags,` -DUSR_FILE_TYPES')) +# +# + CREATE_AFPVOL create user .afpvols and 'mac' directories if non-existent +# define(`specialcflags',concat(specialcflags,` -DCREATE_AFPVOL=\"mac\"')) +# +# + CREAT_AFPVOL_NAM customize name for the volume (modifies CREATE_AFPVOL) +# (CREAT_AFPVOL_NAM fmt string: %U user, %H host, %V vol, %D home eg: "%U@%H") +# define(`specialcflags',concat(specialcflags,` -DCREAT_AFPVOL_NAM="\"%U\""')) +# +# + ISO_TRANSLATE use Macintosh/ISO 8859 character translation on I/O +# define(`specialcflags',concat(specialcflags,` -DISO_TRANSLATE')) +# +# + ISO_FILENAMES extend ISO translation to filenames (assumes ISO_TRANSLATE) +# define(`specialcflags',concat(specialcflags,` -DISO_FILENAMES')) +# +# + ISO_FILE_TRANS use ISO translation on unix/TEXT file contents +# define(`specialcflags',concat(specialcflags,` -DISO_FILE_TRANS')) +# +# + DENYREADWRITE implement AUFS read/write access and deny modes +# define(`specialcflags',concat(specialcflags,` -DDENYREADWRITE')) +# +# + STEAL_PORTS extends DDP dynamic skts down into experimental range 64-128 +# (WARNING!!: Requires uar.1.0.patch11 or uar.1.1.patch09 for use with UAR) +# define(`specialcflags',concat(specialcflags,` -DSTEAL_PORTS')) +# +# + USESYSLOG sends PAPIF Information, Warning & Debug messages to syslog() +# define(`specialcflags',concat(specialcflags,` -DUSESYSLOG')) +# +# + NOCAPDOTPRINTERS uses /etc/lp/printers//comment instead of cap.printers +# (Note: intended for use with Solaris 2.N only) +# define(`specialcflags',concat(specialcflags,` -DNOCAPDOTPRINTERS')) +# +# +# AUFS definable options (previously required editing m4.setup) +# +# + FIXED_DIRIDS compile server and AUFS code for fixed directory & file IDs +# define(`specialcflags',concat(specialcflags,` -DFIXED_DIRIDS')) +# +# + DISTRIB_PASSWDS use ~/.afppass for encrypted passwords (see CAP60.README) +# define(`specialcflags',concat(specialcflags,` -DDISTRIB_PASSWDS')) +# +# + AFP_DISTPW_PATH alternate directory for .afppass files (not in ~user) +# define(`specialcflags',concat(specialcflags,` -DAFP_DISTPW_PATH=\"/etc/dp\"')) +# +# + AFP_DISTPW_MODE enforced .afppass mode (must be 0644 for NFS mounted dirs) +# define(`specialcflags',concat(specialcflags,` -DAFP_DISTPW_MODE=0600')) +# +# + DEBUG_AFP_CMD write detailed AUFS AFP debugging info to -Z specified file +# define(`specialcflags',concat(specialcflags,` -DDEBUG_AFP_CMD')) +# +# + PID_FILE write an aufs process-ID file +# define(`specialcflags',concat(specialcflags,` -DPID_FILE=\"aufs.pid\"')) +# +# + CLOSE_LOG_SIG close and reopen the aufs log on signal -USR2 +# define(`specialcflags',concat(specialcflags,` -DCLOSE_LOG_SIG=SIGUSR2')) +# +# + LOG_WTMP add entry to the 'wtmp' file for each AUFS connection (not IRIX4) +# define(`aufsosflags',concat(aufsosflags,` -DLOG_WTMP')) +# +# + LOG_WTMP_FILE specify name of alternate wtmp file for LOG_WTMP feature +# define(`aufsosflags',concat(aufsosflags,` -DLOG_WTMP_FILE=\"/usr/adm/wtmp\"')) +# +# + ADMIN_GRP allows users in specified UNIX group to login as AUFS superuser +# define(`aufsosflags',concat(aufsosflags,` -DADMIN_GRP=\"macadm\"')) +# +# + NONLXLATE for aufs +# define(`aufsosflags',concat(aufsosflags,` -DNONLXLATE')) +# +# + FULL_NCS_SUPPORT (National Character Support) for aufs +# define(`aufsosflags',concat(aufsosflags,` -DFULL_NCS_SUPPORT')) +# +# + NCS_ALL_TEXT perform specified translation on any TEXT file +# define(`aufsosflags',concat(aufsosflags,` -DNCS_ALL_TEXT')) +# +# + GGTYPE="gid_t" (group ID type) for AUFS +# define(`aufsosflags',concat(aufsosflags,` -DGGTYPE="gid_t"')) +# +# + NOCHGRPEXEC don't exec chgrp, just use chown(2) 3rd arg. [Not for Sys V] +# define(`aufsosflags',concat(aufsosflags,` -DNOCHGRPEXEC')) +# +# + USEDIRSETGID set group ID bit on non-primary group dirs [SunOS/Sys V] +# define(`aufsosflags',concat(aufsosflags,` -DUSEDIRSETGID')) +# +# + NETWORKTRASH fix permissions on network trash stuff +# define(`aufsosflags',concat(aufsosflags,` -DNETWORKTRASH')) +# +# + SHADOW_PASSWD add support for shadow password files if supported +# define(`aufsosflags',concat(aufsosflags,` -DSHADOW_PASSWD')) +# +# + LOGIN_AUTH_PROG AUFS -L option specifies external authorization program +# define(`aufsosflags',concat(aufsosflags,` -DLOGIN_AUTH_PROG')) +# +# + USEQUOTA forces usage of ufs quotas [Solaris 2.x only] +# note: check if you applied ufs quota patch: +# 100833-xx for Solaris 2.1 +# 101039-xx for Solaris 2.2 +# define(`aufsosflags',concat(aufsosflags,` -DUSEQUOTA')) +# +# +# LWSRV definable options (previously required editing m4.setup) +# +# + LPRARGS passes arguments on lwsrv command line directly to lpr +# define(`lwflags',concat(lwflags,` -DLPRARGS')) +# +# + DOCNAME adds document name as a part of job name to lpr +# define(`lwflags',concat(lwflags,` -DDOCNAME')) +# +# + LPRCMD redefine BSD lpr to use alternate path +# define(`lwflags',concat(lwflags,` -DLPRCMD=\"/usr/local/bin/lpr\"')) +# +# + TEMPFILE set alternative path for lwsrv spool files (default /tmp) +# define(`lwflags',concat(lwflags,` -DTEMPFILE=\"/usr/tmp/lwsrvXXXXXX\"')) +# +# + USELPRSYM add -s (symlink) option to lwsrv lpr call (not for OSF/1) +# define(`lwflags',concat(lwflags,` -DUSELPRSYM')) +# +# + USESYSVLP required for HP-UX (and probably other System V) spooling +# define(`lwflags',concat(lwflags,` -DUSESYSVLP')) +# +# + PAGECOUNT adds page count to job name (value is internal buffer size) +# define(`lwflags',concat(lwflags,` -DPAGECOUNT=512')) +# define(`simpleflags',concat(simpleflags,` -DPAGECOUNT=512')) +# +# + ADOBE_DSC2_CONFORMANT forces Adobe DSC2 conformance +# define(`simpleflags',concat(simpleflags,` -DADOBE_DSC2_CONFORMANT')) +# +# + PROCSET_PATCH delete PatchPrep header for non-Apple LaserWriters +# define(`simpleflags',concat(simpleflags,` -DPROCSET_PATCH')) +# +# + RUN_AS_USER run lpd job as Chooser Name if valid UNIX account (no authent) +# define(`lwflags',concat(lwflags,` -DRUN_AS_USER')) +# +# + USER_REQUIRED (requires RUN_AS_USER) refuse printing if Chsr name invalid +# define(`lwflags',concat(lwflags,` -DUSER_REQUIRED')) +# +# +# PAPIF options +# +# + DUPLEXMODE tell suitably equipped printer to print on both sides of a page +# define(`specialcflags',concat(specialcflags,` -DDUPLEXMODE')) +# +# + TRANS3 add support for TranScript 3.0 'psdman' (instead of pstext) +# define(`specialcflags',concat(specialcflags,` -DTRANS3')) +# +# + PSJOBHEADER send PS header in file specified in env. variable "PSJOBHEADER" +# define(`specialcflags',concat(specialcflags,` -DPSJOBHEADER')) +# +# +EOT0 +result=0 +if [ -f m4.features ]; then + diff m4.features /tmp/m4.f.$$ > /dev/null 2>/dev/null + rc=$? + if [ $rc -ne 0 ]; then + echo + echo "You appear to have a locally customised version of m4.features." + while : + do + ${PROMPT} "I assume that you want to use it rather than the default (y) ? " + read ans + if [ -z "${ans}" ]; then + ans="yes" + fi + case ${ans} in + "y"|"ye"|"yes") result=1; break;; + "no"|"n") result=0; break;; + *) echo "you must answer yes or no or carriage return for yes." ;; + esac + done + if [ $result -eq 0 ]; then + echo "OK, copying m4.features to /tmp and replacing with the default." + ${PCAT} m4.features > /tmp/m4.features + ${PCAT} /tmp/m4.f.$$ > m4.features + else + echo "Using the locally customised version of m4.features." + fi + else + echo "Using the default feature list." + fi +else + echo "Using the default feature list." + ${PCAT} /tmp/m4.f.$$ > m4.features +fi +rm -f /tmp/m4.f.$$ +echo +echo +result=0 +while : +do + ${PROMPT} "Do you wish to customise the feature list (default no) ? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "y"|"yes") result=1; break;; + "n"|"no") result=0; break;; + *) echo "you must answer yes or no or carriage return for no." ;; + esac +done +if [ $result -ne 0 ]; then + case ${os} in + "ultrix40"|"ultrix20"|"ultrix12"|"bsd"|"encore"|"newsos"|"epix"|"osf1") + ${EDITOR-vi} m4.features + ;; + *) + ${EDITOR:-vi} m4.features + ;; + esac +fi +echo +echo +echo "Some machines send packets fast enough to overrun a Kinetics box" +echo "running KIP. You should answer yes if you know this is the case." +echo "If you aren't sure, answer no. You can go back and recompile the" +echo "relevant programs later. Generally, the programs affected are the" +echo "LaserWriter printing programs." +echo +echo "Systems affected are generally those whose network interfaces are faster" +echo "than the DEQNA (DEC Q-Bus Network Adapter). Machines at risk may" +echo "include, but are not limited to, Sun 3's and 4's and DEC VAX 8xxx class" +echo "machines with DEBNT/DEBNA's" +echo +result=0 +while : +do + ${PROMPT} "Do you want to throttle output for a Kinetics box (default no)? " + read ans + if [ -z "${ans}" ]; then + ans="no" + fi + case ${ans} in + "y"|"ye"|"yes") result=1; break;; + "no"|"n") fastether="# $fastether"; result=0; break;; + *) echo "you must answer yes or no or carriage return for no" ;; + esac +done +if [ $result -eq 0 ]; then + echo "We won't slow down printing code." +else + echo "We will slow down printing code." +fi +# check for byte swapping +echo +echo "Checking the byte ordering on your machine ..." +${PCAT} <<\EOTF > /tmp/bs$$.c +#include + +main() +{ + unsigned short aword, result; + char *pchar = (char *) &aword; + + *pchar++ = 0x42; + *pchar = 0x53; + + result = (aword != 0x4253); + printf("This appears to be a '%s-endian' machine.\n",(result)?"little":"big"); + exit(result); +} +EOTF +byteswapping="# define([usebyteswap],1)" +${ccompiler} -o /tmp/bs$$ /tmp/bs$$.c > /dev/null 2>/dev/null +rc=$? +if [ $rc -eq 0 ]; then + /tmp/bs$$ + rc=$? + if [ $rc -ne 0 ]; then + byteswapping="define([usebyteswap],1)" + echo "Defining BYTESWAPPED for correct operation." + fi +else + echo "Hmmm, can't get sensible results, assuming non-byteswapped" + echo "Uncomment the 'usebyteswap' line in m4.setup if incorrect." +fi +rm -f /tmp/bs$$ /tmp/bs$$.c +# check types.h +echo +echo "Checking to see if the sequence" +echo " #include " +echo " #include " +echo "causes a problem at your site." +echo "Assuming that your C compiler returns error codes." +echo " Temporary files: /tmp/cx$$.c, cx$$.o" +echo +${PROMPT} "[Hit carriage return to continue] " +read ans +trap " +echo Exiting... Wait... +if [ -f /tmp/cx$$.c ]; then rm -f /tmp/cx$$.c; fi; +if [ -f cx$$.o ]; then rm -f cx$$.o fi +exit 255" 2 +echo "#include " > /tmp/cx$$.c +echo "#ifndef _TYPES" >> /tmp/cx$$.c +echo "# include " >> /tmp/cx$$.c +echo "#endif" >> /tmp/cx$$.c +${ccompiler} -c /tmp/cx$$.c > /dev/null 2>/dev/null +rc=$? +if [ $rc -ne 0 ]; then + echo + echo "The sequence does cause a problem, checking to see if the" + echo "actual sequence:" + echo " #include " + echo " #ifndef _TYPES" + echo " # include " + echo " #endif" + echo "can be made to work by define _TYPES everywhere" + echo + ${PROMPT} "[Hit carriage return to continue] " + read ans + ${ccompiler} -c -D_TYPES /tmp/cx$$.c > /dev/null 2>/dev/null + rc=$? + if [ $rc -ne 0 ]; then + echo + echo "This failed, continuing configuration, but you probably won't be" + echo "able to compile because sys/param.h or sys/types are probably not" + echo "there and even if they are, CAP sources make invalid assumptions" + echo "about them" + fi +else + echo + echo "No problems here" + needselfdef="# ${needselfdef}" +fi +rm -f /tmp/cx$$.c +if [ -f cx$$.o ]; then + rm -f cx$$.o +fi +trap 2 +echo +echo "Checking for various system calls. (But not the list of system" +echo "call for system V compatibility). Temp files: m4.tmp, /tmp/cx$$" +echo +${PROMPT} "[Hit carriage return to continue] " +read ans +echo +trap " +echo Exiting... Wait... +if [ -f /tmp/cx$$ ]; then rm -f /tmp/cx$$; fi; exit 255" 2 +libs="" +case "${osdefault}" in +"xenix5") + echo "Getting name list from /lib/386/Slib{[cx],socket}.a..." + ${PNM} /lib/386/Slibc.a > /tmp/cx$$ + ${PNM} /lib/386/Slibx.a >> /tmp/cx$$ + ${PNM} /lib/386/Slibsocket.a >> /tmp/cx$$ + ;; +"solaris") + echo "Getting name list from /lib/libc.a ..." + ${PNM} /lib/libc.a > /tmp/cx$$ + echo "Getting name list from /lib/libsocket.a ..." + ${PNM} /lib/libsocket.a >> /tmp/cx$$ + echo "Getting name list from /lib/libnsl.a ..." + ${PNM} /lib/libnsl.a >> /tmp/cx$$ + libs="-lsocket -lnsl" + ;; +"drsnx") + echo "Getting name list from /usr/ccs/libc.a /usr/ucblib/libucb.a..." + ${PNM} /usr/ccs/lib/libc.a > /tmp/cx$$ + ${PNM} /usr/ucblib/libucb.a >> /tmp/cx$$ + ${PNM} /usr/lib/libsocket.a >> /tmp/cx$$ + ;; +"epix") + echo "Getting name list from /bsd43/usr/lib/libc.a" + ${PNM} /bsd43/usr/lib/libc.a > /tmp/cx$$ + ;; +"osf1") + echo "Getting name list from /usr/lib/libc.a" + ${PNM} -B /usr/lib/libc.a > /tmp/cx$$ + ;; +"domainosbsd") + echo "Getting name list from /lib/clib /lib/libc..." + ${PNM} /lib/clib > /tmp/cx$$ + ${PNM} /lib/libc >> /tmp/cx$$ + ;; +*) + if [ -f /lib/libc.a ]; then + echo "Getting name list from /lib/libc.a..." + ${PNM} /lib/libc.a > /tmp/cx$$ + else + if [ -f /usr/lib/libc.a ]; then + echo "Getting name list from /usr/lib/libc.a..." + ${PNM} /usr/lib/libc.a > /tmp/cx$$ + else + if [ -f /lib/libsys_s.a ]; then + echo "Getting NeXT 2.0 name list from /lib/libsys_s.a..." + ${PNM} /lib/libsys_s.a > /tmp/cx$$ + else + if [ -f /lib/libc.so.1 ]; then + echo "Getting IRIX 5 name list from /lib/libc.so.1" + ${PNM} /lib/libc.so.1 > /tmp/cx$$ + else + echo "Cannot get the namelist ... HELP" + fi + fi + fi + fi + ;; +esac +names=$? +# FreeBSD (1.0) gives an error in nm, ignore it. +if [ $names -ne 0 -a "${os}" != "386bsd" -a "${os}" != "freebsd" -a "${os}" != "bsdi" -a "${os}" != "netbsd" ]; then + echo "Couldn't get the name list!" +else + echo "Done, running function configuration" + confos=${os}; export confos; export ccompiler; export libs + ${USESH} ./conf.func.sh /tmp/cx$$ conf.func.lst m4 m4.tmp + rc=$? + if [ $rc -eq 1 ]; then + if [ -z "${USESH}" ]; then + sh conf.func.sh /tmp/cx$$ conf.func.lst m4 m4.tmp + fi + fi + unset confos + echo "Done." +fi +rm -f /tmp/cx$$ +trap 2 +# now setup +if [ -z "$1" ]; then + of=m4.setup +else + of=$1 +fi +echo +${PROMPT} "[Hit carriage return to continue] " +read ans +echo +if [ -f ${of} ]; then + echo "Getting ready to overwrite existing ${of}" +else + echo "Getting ready to create ${of}" +fi +echo +${PROMPT} "[Hit carriage return to continue] " +read ans +echo "Creating ${of}" +rm -f ${of} +exec > ${of} +${PCAT} <<\EOT +define(`concat',$1$2$3$4$5$6$7$8$9) +changequote([,]) + +# os - one of: +# "bsd" - bsd 4.2, bsd 4.3, ultrix 1.1, acis 4.2,4.3 other +# "standard" bsd systems without nfs +# "ultrix12" - Ultrix 1.2 +# "ultrix20" - Ultrix 2.0 or greater +# "ultrix40" - Ultrix 4.0 or greater +# "osf1" - OSF/1 1.3 or greater +# "hpux" - HP/UX +# "aux" - A/UX +# "uts" - Amdahl UTS +# "xenix5" - SCO Xenix System V +# "aix" - IBM AIX System V +# "pyr" - pyramid (in BSD universe) +# "sunos" - SunOS 4.N or less +# "solaris" - Solaris 2.N (SunOS 5.0 and greater) +# "encore" - Encore MultiMax +# "next" - NeXT/MACH +# "dynix" - Sequent Balance +# "irix" - Silicon Graphics IRIS-4D/IRIX +# "newsos" - Sony NEWS +# "drsnx" - ICL DRS/NX V4.0 +# "epix" - Control Data EP/IX +# "386bsd" - 386/BSD and derivatives +# "netbsd" - NetBSD 1.0 +# "freebsd" - FreeBSD 2.0 +# "bsdi" - BSDI BSD/386 1.1 +# "domainosbsd" - HP/Apollo Domain BSD 4.3 +# Warning: hpux, pyr are hardcoded in some of the makefiles (sorry) + +# MAJOR CONFIGURATION +# set to one of the above (or configure your own below) +EOT +echo "define([os],[[$os]])" +echo +echo "# System call configuration (not for system v compatibilty)" +echo "# known: X_GETOPT, X_VPRINTF, X_GETMNT, X_STATFS, X_QUOTA," +echo "# X_SUNQUOTA, X_FLOCK, X_LOCKF, X_FCNTLLOCKF" +${PCAT} m4.tmp +echo +echo "# GETOPT support" +if [ "${os}" = "dynix" ]; then + echo "# Dynix provides getopt in -lseq" +else + echo "ifdef([X_GETOPT],[],[define([needgetopt],[att_getopt.o])])" +fi +echo "# VPRINTF support" +echo "ifdef([X_VPRINTF],[define([usevprintf], 1)],[])" +echo +echo "# Path to cap sources: useful for testing" +echo "define([cwd],[${mydir}])" +echo "# turn on if your system sends packets very quickly" +echo "# (see applications/aufs/INSTALLATION)" +echo "${fastether}" +echo +echo "# The following selects the correct lap delivery objects" +echo "${lapobj}" +echo +echo "# This determines what happens to UAB and UAR" +echo "${uabprogs}" +echo "${uabplibs}" +echo "${uabpobjs}" +echo +echo "# This sets up Native EtherTalk support" +echo "${etherprogs}" +echo "${etherpobjs}" +echo +echo "# This sets up capd" +echo "${capdprogs}" +echo "${capdpobjs}" +echo +echo "# And this determines if Phase 2 packets are used" +echo "${usingphase2}" +echo +echo "# The following selects byteswapping or otherwise" +echo "${byteswapping}" +echo +echo "# We use build_here to restrict CAP to a single directory tree" +echo "# but still using etcdest (see below) for the location of atalk.local" +echo "# and other configuration files" +echo "# define([build_here],1)" +echo +echo "# We use the debug flag to restrict CAP to a single directory tree" +echo "# for testing purposes, e.g. testing new versions" +echo "${singletree}" +echo +echo "# The includef flag sets an alternate localtion for include files" +echo "${includefile}" +echo +echo "# Systems with kernel AppleTalk support do not need atis" +echo "${usingatis}" +echo +echo "# uncomment if your param.h includes types.h and types.h doesn't" +echo "# prevent itself from being included twice" +echo "# define _TYPES" +echo "${needselfdef}" +${PCAT} <<\EOT1 +# MINOR CONFIGURATION: configure various programs + +#define([columbia],1) # so columbia can do things quickly +#define([rutgersdef],1) # so rutgers can do things quickly + +# location of include files +define([includedir],[[/usr/include]]) +ifelse(os,[epix],[define([includedir],[[/bsd43/usr/include]])]) +# location of des subroutine source (see lib/afp/README) +define([desloc],[[../../extras]]) +# location of atalk.local, etc. +define([etcdest],[[/etc]]) +ifdef([columbia],[define([etcdest],[[/usr/local/lib/cap]])]) +# location of user cap programs +define([capdestdir],[[/usr/local/cap]]) +# location of cap "server" programs (aufs, lwsrv, papif, uab etc.) +define([capsrvrdestdir],[[/usr/local/cap]]) +# location of some cap data files +define([caplibdestdir],[[/usr/local/lib/cap]]) +ifdef([rutgersdef],[define([caplibdestdir],[[/usr/local/cap/lib]])]) +# location of cap libraries +define([libdestdir],[[/usr/local/lib]]) +# override for aux (doesn't search /usr/local/lib) +ifelse(os,[aux],[define([libdestdir],[[/usr/lib]])]) +# cap library names +define([caplib],[[libcap.a]]) +define([afplib],[[libafp.a]]) +define([afpclib],[[libafpc.a]]) +# names to load cap libraries with +define([libcap],[[-lcap]]) +define([libafp],[[-lafp]]) +define([libafpc],[[-lafpc]]) +ifelse(os,[encore], + [define([libcap],concat([-L],libdestdir,[ ],libcap))]) +ifelse(os,[encore], + [define([libafp],concat([-L],libdestdir,[ ],libafp))]) +ifelse(os,[encore], + [define([libafpc],concat([-L],libdestdir,[ ],libafpc))]) +ifelse(os,[aix], + [define([libcap],concat([-L],libdestdir,[ ],libcap))]) +ifelse(os,[aix], + [define([libafp],concat([-L],libdestdir,[ ],libafp))]) +ifelse(os,[aix], + [define([libafpc],concat([-L],libdestdir,[ ],libafpc))]) +ifelse(os,[hpux], + [define([libcap],concat([-L],libdestdir,[ ],libcap))]) +ifelse(os,[hpux], + [define([libafp],concat([-L],libdestdir,[ ],libafp))]) +ifelse(os,[hpux], + [define([libafpc],concat([-L],libdestdir,[ ],libafpc))]) +ifelse(os,[epix], + [define([libcap],concat([-L],libdestdir,[ ],libcap))]) +ifelse(os,[epix], + [define([libafp],concat([-L],libdestdir,[ ],libafp))]) +ifelse(os,[epix], + [define([libafpc],concat([-L],libdestdir,[ ],libafpc))]) +ifelse(os,[solaris], + [define([libcap],concat([-L],libdestdir,[ ],libcap))]) +ifelse(os,[solaris], + [define([libafp],concat([-L],libdestdir,[ ],libafp))]) +ifelse(os,[solaris], + [define([libafpc],concat([-L],libdestdir,[ ],libafpc))]) +ifelse(os,[netbsd], + [define([libcap],concat([-L],libdestdir,[ ],libcap))]) +ifelse(os,[netbsd], + [define([libafp],concat([-L],libdestdir,[ ],libafp))]) +ifelse(os,[netbsd], + [define([libafpc],concat([-L],libdestdir,[ ],libafpc))]) +ifelse(os,[freebsd], + [define([libcap],concat([-L],libdestdir,[ ],libcap))]) +ifelse(os,[freebsd], + [define([libafp],concat([-L],libdestdir,[ ],libafp))]) +ifelse(os,[freebsd], + [define([libafpc],concat([-L],libdestdir,[ ],libafpc))]) +# any special libraries +define([libspecial],[]) +ifelse(os,[ultrix40],[ + define([libspecial],concat(libspecial,[ -lauth]))]) +ifelse(os,[osf1],[ + define([libspecial],concat(libspecial,[ -lsecurity]))]) +ifelse(os,[xenix5],[ + define([libspecial],concat(libspecial,[ -lsocket]))]) +ifelse(os,[drsnx],[ + define([libspecial],concat(libspecial,[ -lucb]))]) +ifelse(os,[uts],[ + define([libspecial],concat(libspecial,[ -lsocket -lbsd -la]))]) +ifelse(os,[dynix],[ + define([libspecial],concat(libspecial,[ -lseq]))]) +ifelse(os,[irix],[ + define([libspecial],concat(libspecial,[ -lbsd]))]) +ifelse(os,[aix],[ + define([libspecial],concat(libspecial,[ -lbsd]))]) +ifelse(os,[aux],[ + define([libspecial],concat(libspecial,[ -lat -lbsd]))]) +ifelse(os,[hpux],[ + define([libspecial],concat(libspecial,[ -lBSD -lndbm]))]) +ifelse(os,[epix],[ + define([libspecial],concat(libspecial,[ -lc -lsec]))]) +ifelse(os,[386bsd],[ + define([libspecial],concat(libspecial,[ -lrpc]))]) +ifelse(os,[netbsd],[ + define([libspecial],concat(libspecial,[ -lcrypt]))]) +ifelse(os,[freebsd],[ + define([libspecial],concat(libspecial,[ -lcrypt]))]) +ifelse(os,[bsdi],[ + define([libspecial],concat(libspecial,[ -lrpc -lkvm]))]) +ifelse(os,[linux],[ + define([libspecial],concat(libspecial,[ -lgdbm]))]) +ifelse(os,[solaris],[ + define([libspecial],concat(libspecial,[ -lsocket -lnsl ]))]) +# rutgers specific +define([libru],[]) +ifdef([rutgersdef], [ +ifelse(os,[solaris],[ + define([libru],concat(libru,[ -L/usr/local/lib -R/usr/local/lib -lru ]))],[ + define([libru],concat(libru,[ -lru ]))])]) + +# +# special configurations for individual source files +# + +# +# Aufs: see applications/aufs/INSTALLATION +# +# WARNING: OS DEPENDENT +# define([smartunixfinderinfo],1) +# +# Set -DNONLXLATE, -DFULL_NCS_SUPPORT, -DGGTYPE="gid_t" or -DNOCHGRPEXEC +# in m4.features +# Others: USESTATFS, USEGETMNT, USECHOWN, USEQUOTA, USESUNQUOTA, USEBSDQUOTA +# are autoconfigured +define([aufsosdefs],[aufsosflags()]) + +# +# lib/cap/authenticate.c: configuration file +# +define([authconfig],concat([\"],etcdest,[/],[cap.auth],[\"])) + +# +# lwsrv: see applications/lwsrv/README +# +# lwflags and simpleflags are now defined in m4.features + +# +# lwrename: set name of file containing list of printers to be renamed +# +define([lwrenamefile],concat([\"],caplibdestdir,[/lwrename.list\"])) + +# +# papif: see applications/papif/README +# +# uncomment and set to right location to turn on printing "plain text files" +# define([pstextloc],[[\"/usr/local/lib/ps/pstext\"]]) +# +# uncomment and set to right location to turn on page reversal +# define([psrevloc],[[\"/usr/local/lib/ps/psrev\"]]) +# +# Valid are: +# -DVERBOSELOG - default (set =0 to turn off) +# -DNO_STRUCT - default is on (structured) +# -DNOACCT - default is on (accounting) +# -DIDLESTUFF - default is off +# -DSFLOWQ - default is 8 (min 1, max 8) +# -DRFLOWQ - default is 8 (min 1, max 8) +# -DATPRESPONSETIMEOUT - default is 120 (2 minutes) - in seconds +# -DWATCHTIME - default is 10 seconds (in seconds) +# -DDEBUG - default is off +# -DSTRIPCONTROLD - default is off +# -DMAPCRTOLF - default is off +# -DMACUSER - default is off (need LPD_JOB environment variable in lpd) +# -DPLP - Use with PLP rather than Berkeley lpd +# see papif README file for explanations of the above +# The following defines are recommended for System V lp printers (vs. bsd lpd) +# -DWATCHTIME=0 (no status) -DNOACCT +define([papflags],[-DMACUSER]) +EOT1 +if [ "${lpd}" = "lp" ]; then + echo "define([papflags],concat(papflags,[ -DWATCHTIME=0 -DNOACCT]))" +else + echo "define([papflags],concat(papflags,[ -DIDLESTUFF]))" +fi +echo +echo "# Set -DBANNERFIRST if you want the banner page to come out as the" +echo "# first page instead of the last page " +echo "# Set -DBANNERLAST if want it last" +echo "# Set -DPSBANNER if you want a custom PostScipt Banner (must specify" +echo "# short banners in printcap). This still defaults to regular banners" +echo "# Add -DCHARGEBANNER if you want to charge the banner page to the user" +echo "# on system V - there is no accounting, so leave blank" +if [ "${lpd}" = "lp" ]; then + echo "define([papbanner],[])" +else + echo "define([papbanner],[-DCHARGEBANNER])" +fi +${PCAT} <<\EOT3 + +# for cap.printers - uncomment and change the following line to point +# papif, et. al. to a location other than /etc/cap.printers. (Note: +# line below would set it to $etcdest/cap.printers) +# define([capprinters],concat([\"],etcdest,[/],[cap.printers],[\"])) +ifdef([columbia], + [define([capprinters],concat([\"],etcdest,[/],[cap.printers],[\"]))]) +# for atalkdbm - allows change following line(s) to modify atalk.local +# (probably shouldn't). Remember that atalk.local is expected to +# be in etcdest +define([atalklocal],concat([\"],etcdest,[/],[atalk.local],[\"])) +define([etalklocal],concat([\"],etcdest,[/],[etalk.local],[\"])) +define([configdir],concat([\"],etcdest,[\"])) +define([uabpidfile],concat([\"],caplibdestdir,[/],[uab.pid],[\"])) +define([uabbrdescr],concat([\"],caplibdestdir,[/],[bridge_desc],[\"])) + +# in case you want to globally change the c compiler +EOT3 +echo "define([thecompiler],[${ccompiler}])" +${PCAT} <<\EOT4 +define([theloader],[ld]) +define([theinstaller],[cp]) + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# END OF CONFIGABLE OPTIONS # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +# You should only edit past here if you are "porting" +# Automatics +define([osname],[Unknown]) +ifelse(os,[ultrix12],[define([osname],[Ultrix 1.2])]) +ifelse(os,[ultrix20],[define([osname],[Ultrix 2.0])]) +ifelse(os,[ultrix40],[define([osname],[Ultrix 4.0])]) +ifelse(os,[osf1],[define([osname],[OSF/1])]) +ifelse(os,[aux],[define([osname],[A/UX])]) +ifelse(os,[sunos],[define([osname],[SunOS])]) +ifelse(os,[solaris],[define([osname],[Solaris 2.x in native SVR4 mode])]) +ifelse(os,[encore],[define([osname],[Encore Umax])]) +ifelse(os,[hpux],[define([osname],[HP-UX (for 9000 series)])]) +ifelse(os,[uts],[define([osname],[Amdahl UTS])]) +ifelse(os,[bsd],[define([osname],[Standard BSD])]) +ifelse(os,[pyr],[define([osname],[Pyramid in BSD universe])]) +ifelse(os,[xenix5],[define([osname],[SCO Xenix System V])]) +ifelse(os,[aix],[define([osname],[IBM AIX System V])]) +ifelse(os,[next],[define([osname],[NeXT/MACH])]) +ifelse(os,[dynix],[define([osname],[Sequent Balance])]) +ifelse(os,[irix],[define([osname],[Silicon Graphics IRIS/IRIX])]) +ifelse(os,[newsos],[define([osname],[Sony NEWS])]) +ifelse(os,[drsnx],[define([osname],[ICL DRS])]) +ifelse(os,[epix],[define([osname],[Control Data EP/IX])]) +ifelse(os,[386bsd],[define([osname],[386/BSD etc])]) +ifelse(os,[netbsd],[define([osname],[NetBSD 1.0])]) +ifelse(os,[freebsd],[define([osname],[FreeBSD])]) +ifelse(os,[bsdi],[define([osname],[BSD/386])]) +ifelse(os,[domainosbsd],[define([osname],[HP/Apollo Domain BSD 4.3])]) +# +define([cflags],ifdef([selfdefinetypes],[-D_TYPES],[])) +define([cflags],concat(cflags,ifdef([usebyteswap],[ -DBYTESWAPPED],[]))) +define([cflags],concat(cflags,ifdef([usephase2],[ -DPHASE2],[]))) +define([bigcflags],ifelse(os,[hpux],[])) +# The encore optimiser is slightly over zealous +ifelse(os,[encore],[define([cflags],concat(cflags,[ -Dencore]))],[ + define([cflags],concat(cflags,[ -O]))]) +ifelse(os,[pyr],[ + define([cflags],concat(cflags,[ -q]))]) +ifelse(os,[next],[ + define([cflags],concat(cflags,[ -DNeXT -DADDRINPACK]))]) +ifelse(os,[xenix5],[ + define([cflags],concat(cflags,[ -Dxenix5 -I$I -DLAI_TCP -Di386]))]) +ifelse(os,[aix],[ + define([cflags],concat(cflags,[ -DAIX -DUSETIMES -DNOWAIT3 -DUSEDIRENT]))]) +ifelse(os,[solaris],[ + define([cflags],concat(cflags,[ -DSOLARIS]))]) +ifelse(os,[uts],[ + define([cflags],concat(cflags,[ -eft ]))]) +ifelse(os,[irix],[ + define([cflags],concat(cflags,[ -cckr -D_BSD_COMPAT]))]) +ifelse(os,[drsnx],[ + define([cflags],concat(cflags,[ -DNOWAIT3]))]) +ifelse(os,[epix],[ + define([cflags],concat(cflags,[ -DEPIX]))]) +ifelse(os,[osf1],[ + define([cflags],concat(cflags,[ -DUSEDIRENT]))]) +ifelse(os,[netbsd],[ + define([cflags],concat(cflags,[ -D__386BSD__]))]) +ifelse(os,[bsdi],[ + define([cflags],concat(cflags,[ -D__386BSD__]))]) +ifelse(os,[linux],[ + define([cflags],concat(cflags,[ -DUSE_GDBM]))]) + +# was used for nbp, but found we needed more... leave in case +define([nbpflags],[]) +define([lflags],[]) +define([mflags],[]) + +# aux's c compiler isn't nice - it doesn't have a preprocessor +# definition for aux. So, let's invent one. Also turn on -n +# for shared code. +ifelse(os,[aux],[ + define([cflags],concat(cflags,[ -n -Daux])) + define([lflags],concat(lflags,[ -n]))]) + +# for NetBSD 1.0 on 68k strip symbols to avoid ld error on some progs +ifelse(os,[netbsd],[define([lflags],concat(lflags,[ -s]))]) + +# use the transitional option of the C compiler - interpret as K&R C, not ANSI +ifelse(os,[drsnx],[ + define([cflags],concat(cflags,[ -Xt -Ddrsnx]))]) + +# check to see if we need sysvinstall usage +ifelse(os,[hpux6],[define([sysvinstall],[yes])], + os,[irix3],[define([sysvinstall],[yes])]) + +# or if we really want to use install +# ifelse(os,[someOS], [define([theinstaller],[install])]) + +# for cap library +# Valid are: +# NOFFS - no ffs function defined, fake it out +# LOCALTIME_GTOD - uses pd localtime, but gettimeofday always reads +# disk based time of day. Always defined for AUX for now. Probably +# needs to be changed for versions of Aux after 1.0 +# NORECVMSG - no recvmsg in system (fake it out) +# NOSENDMSG - no recvmsg in system (fake it out) +define([caposdefs], + concat( ifdef([X_NOFFS],[ -DNOFFS],[]), + ifelse(os,[aux],[ -DLOCALTIME_GTOD],[]), + ifdef([X_NORECVMSG], [ -DNORECVMSG],[]), + ifdef([X_NOSENDMSG], [ -DNOSENDMSG],[]))) +# for afp library +# two cases for X_LOCKF - if lockf isn't defined +# first: X_FCNTLLOCKF is defined, so just note that lockf uses fcntl.h +# second: " " isn't defined so define no lockf +# we "know" that flock() was hiding in libbsd.a under aix +define([afposdefs], + concat( ifdef([X_FLOCK],[], ifelse(os,[aix],[],[ -DNOFLOCK])), + ifdef([X_LOCKF],[], + [ifdef([X_FCNTLLOCKF],[ -DLOCKFUSESFCNTL],[ -DNOLOCKF])]))) +# for aufs +define([aufsosdefs], + concat(aufsosdefs, + ifdef([X_STATFS],[ -DUSESTATFS],[]), + ifdef([X_GETMNT],[ -DUSEGETMNT],[]), + ifdef([X_QUOTA],[ -DUSEQUOTA],[]), + ifdef([X_SUNQUOTA],[ -DUSESUNQUOTA],[]), + ifdef([X_BSDQUOTA],[ -DUSEBSDQUOTA],[]), + ifelse(os,[irix],[ -DNOVFORK]), + ifelse(os,[aix],[ -DNOVFORK -DUSECHOWN],[]))) + +# if no ranlib (or fakeout like hpux) and need to order libaries +ifelse(os,[dummy], [define(uselordertsort,[1])], + os,[aux], [define(uselordertsort,[1])], + os,[solaris], [define(uselordertsort,[1])], + os,[uts], [define(uselordertsort,[1])], + os,[drsnx], [define(uselordertsort,[1])], + os,[irix], [define(uselordertsort,[1])]) + +# lw config +define([lwflags], + concat(lwflags, + ifdef([fastether],[ -DSFLOWQ=1],[]), + ifelse(os,[aix],[],[]))) + +# more papif config +define([papflags], + concat(papflags, + ifelse(os,[irix],[ -DNOVFORK]), + ifelse(os,[aix],[ -DNOVFORK],[]))) + +#NBPFLAGS = nbpflags() +#SPECCFLAGS = specialcflags() +#BIGCFLAGS = bigcflags() +#CFLAGS = cflags() +#LFLAGS = lflags() +#AFPOSDEFS = afposdefs() +#AUFSOSDEFS = aufsosdefs() + +ifdef([debug],[ + define([build_here],1) +# location of atalk.local, etc. + define([etcdest],concat(cwd,[[[/etc]]])) +]) + +ifdef([includef],[ +# location of include files + define([includedir],[cwd]) + define([cflags],concat(cflags,[ -I],includedir)) +]) + +ifdef([build_here],[ +# location of include files + define([includedir],[cwd]) +# location of des subroutine source (see lib/afp/README) + define([desloc],[[../../extras]]) +# location of user cap programs + define([capdestdir],concat(cwd,[[[/bin]]])) +# location of cap "server" programs (aufs, lwsrv, papif, uab etc.) + define([capsrvrdestdir],concat(cwd,[[[/bin]]])) +# location of some cap data files + define([caplibdestdir],concat(cwd,[[[/bin]]])) +# location of cap libraries + define([libdestdir],concat(cwd,[[[/lib]]])) +# cap library names + define([caplib],[[libcap.a]]) + define([afplib],[[libafp.a]]) + define([afpclib],[[libafpc.a]]) +# names to load cap libraries with + define([libcap],concat(cwd,[[[/lib/libcap.a]]])) + define([libafp],concat(cwd,[[[/lib/libafp.a]]])) + define([libafpc],concat(cwd,[[[/lib/libafpc.a]]])) + define([capprinters],concat([\"],etcdest,[/],[cap.printers],[\"])) + define([cflags],concat(cflags,[ -I],includedir)) + define([atalklocal],concat([\"],etcdest,[/],[atalk.local],[\"])) + define([etalklocal],concat([\"],etcdest,[/],[etalk.local],[\"])) + define([configdir],concat([\"],etcdest,[\"])) +]) + +define([datestring],maketemp(/tmp/capcXXXXXX)) +syscmd(date > datestring()) +##########MARKER########## +# Makefile autoconfigured for ... +[#] osname() system on include(datestring()) +syscmd(rm -f datestring()) + +MFLAGS=mflags() +LFLAGS=lflags() +CC=thecompiler() +LD=theloader() +SHELL=/bin/sh +INSTALLER=theinstaller() +EOT4 +exec > /dev/tty +echo "${of} configured" +echo +echo "Getting ready to create the makefiles" +echo +echo "You can (re)generate the makefiles at any time by typing:" +echo "gen.makes (or sh gen.makes) from the top level directory." +echo +echo "You need to build new makefiles if you change the user" +echo "options by editing the 'm4.setup' or 'm4.features' files" +echo "or have run 'make spotless' to remove all {m,M}akefiles." +echo +${PROMPT} "[Hit carriage return to continue] " +read ans +echo +${USESH} ./gen.makes +echo +case ${os} in + "aux") + echo "WARNING: remove old 'Makefiles' by typing: 'make spotless'" + echo "then re-run 'gen.makes' to create new small-m 'makefiles'" + echo + ;; +esac +echo "CAP Configuration complete!" +echo +echo "You can now start the build process with 'make programs'" +echo "(but it would be best to read the documentation in 'doc')." +echo diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..30935a2 --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +For license information see 'NOTICE' and 'README' \ No newline at end of file diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..594120f --- /dev/null +++ b/MANIFEST @@ -0,0 +1,535 @@ +-rw-r--r-- 1 djh 9033 Mar 14 23:57 CAP60.README +-rwxr-xr-x 1 djh 31416 Mar 13 19:32 Configure +-rw-r--r-- 1 djh 25821 Mar 15 15:14 MANIFEST +-rw-r--r-- 1 djh 2759 Feb 28 23:42 MODIFICATIONS +-rw-r--r-- 1 djh 1461 Mar 14 16:15 Makefile +-rw-r--r-- 1 djh 7310 Mar 14 16:14 NOTES +-rw-r--r-- 1 djh 1460 Feb 28 23:42 NOTICE +-rw-r--r-- 1 djh 6312 Feb 28 23:42 PORTING +-rw-r--r-- 1 djh 9256 Mar 13 20:04 README +-rw-r--r-- 1 djh 1747 Mar 13 19:56 conf.func.lst +-rwxr-xr-x 1 djh 8221 Feb 28 23:42 conf.func.sh +-rw-r--r-- 1 djh 1842 Feb 28 23:45 conf.sysv.lst +-rwxr-xr-x 1 djh 4760 Feb 28 23:45 conf.sysv.sh +-rwxr-xr-x 1 djh 825 Feb 28 23:45 gen.makes +-rw-r--r-- 1 djh 12974 Feb 28 23:46 m4.setup + +applications: +-rw-r--r-- 1 djh 467 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 321 Feb 28 23:44 Makefile.m4 +-rw-r--r-- 1 djh 429 Feb 28 23:44 README +-rw-r--r-- 1 djh 460 Feb 28 23:44 makefile + +applications/aufs: +-rw-r--r-- 1 djh 10802 Feb 28 23:44 INSTALLATION +-rw-r--r-- 1 djh 4954 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 5016 Feb 28 23:44 Makefile.m4 +-rw-r--r-- 1 djh 7223 Feb 28 23:44 NOTES +-rw-r--r-- 1 djh 1882 Feb 28 23:44 README +-rw-r--r-- 1 djh 1832 Feb 28 23:44 abmisc2.c +-rw-r--r-- 1 djh 8405 Feb 28 23:44 afpavl.c +-rw-r--r-- 1 djh 843 Feb 28 23:44 afpavl.h +-rw-r--r-- 1 djh 26109 Mar 13 20:18 afpdid.c +-rw-r--r-- 1 djh 400 Feb 28 23:44 afpdid.h +-rw-r--r-- 1 djh 9761 Feb 28 23:44 afpdir.c +-rw-r--r-- 1 djh 45300 Mar 13 20:21 afpdt.c +-rw-r--r-- 1 djh 4533 Feb 28 23:44 afpdt.h +-rw-r--r-- 1 djh 7731 Feb 28 23:44 afpfile.c +-rw-r--r-- 1 djh 11102 Mar 14 14:37 afpfork.c +-rw-r--r-- 1 djh 6638 Feb 28 23:44 afpgc.c +-rw-r--r-- 1 djh 1379 Feb 28 23:44 afpgc.h +-rw-r--r-- 1 djh 2252 Feb 28 23:44 afpmisc.c +-rw-r--r-- 1 djh 4341 Feb 28 23:44 afpntoh.h +-rw-r--r-- 1 djh 84426 Mar 13 19:40 afpos.c +-rw-r--r-- 1 djh 23870 Mar 14 16:21 afposenum.c +-rw-r--r-- 1 djh 20664 Feb 28 23:44 afposfi.c +-rw-r--r-- 1 djh 21166 Mar 13 20:29 afposncs.c +-rw-r--r-- 1 djh 936 Feb 28 23:44 afposncs.h +-rw-r--r-- 1 djh 3279 Mar 13 20:29 afppasswd.c +-rw-r--r-- 1 djh 535 Feb 28 23:44 afppasswd.h +-rw-r--r-- 1 djh 7479 Feb 28 23:44 afps.h +-rw-r--r-- 1 djh 20917 Mar 13 20:29 afpserver.c +-rw-r--r-- 1 djh 5111 Feb 28 23:44 afpspd.c +-rw-r--r-- 1 djh 7484 Feb 28 23:44 afpudb.c +-rw-r--r-- 1 djh 577 Feb 28 23:44 afpudb.h +-rw-r--r-- 1 djh 212 Feb 28 23:44 afpvols +-rw-r--r-- 1 djh 13145 Mar 13 20:29 afpvols.c +-rw-r--r-- 1 djh 1206 Feb 28 23:44 afpvols.h +-rw-r--r-- 1 djh 35073 Mar 14 20:00 aufs.c +-rwxr-xr-x 1 djh 16 Mar 13 19:51 aufs_vers +-rw-r--r-- 1 djh 356 Feb 28 23:44 aufs_vers.sh +-rw-r--r-- 1 djh 13042 Mar 14 16:03 aufsicon.c +-rw-r--r-- 1 djh 92 Mar 13 19:54 aufsv.c +-rw-r--r-- 1 djh 21413 Feb 28 23:44 design.notes +-rw-r--r-- 1 djh 5001 Feb 28 23:44 makefile +-rw-r--r-- 1 djh 1672 Mar 14 14:30 sizeserver.c +-rw-r--r-- 1 djh 45 Feb 28 23:44 sizeserver.h +-rw-r--r-- 1 djh 3377 Feb 28 23:44 todo +-rw-r--r-- 1 djh 7564 Feb 28 23:44 user.doc +-rw-r--r-- 1 djh 1171 Feb 28 23:44 whatiswhat + +applications/lwsrv: +-rw-r--r-- 1 djh 280 Feb 28 23:44 LWFonts +-rw-r--r-- 1 djh 664 Feb 28 23:44 LWPlusFonts +-rw-r--r-- 1 djh 1995 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 1971 Feb 28 23:44 Makefile.m4 +-rw-r--r-- 1 djh 5471 Feb 28 23:44 README +-rw-r--r-- 1 djh 1747 Feb 28 23:44 fontlist.c +-rw-r--r-- 1 djh 717 Feb 28 23:44 fontlist.h +-rw-r--r-- 1 djh 20867 Mar 14 13:42 lwsrv.c +-rw-r--r-- 1 djh 2014 Feb 28 23:44 makefile +-rw-r--r-- 1 djh 3680 Feb 28 23:44 papstream.c +-rw-r--r-- 1 djh 1366 Feb 28 23:44 papstream.h +-rw-r--r-- 1 djh 5500 Feb 28 23:44 procset.c +-rw-r--r-- 1 djh 832 Feb 28 23:44 procset.h +-rw-r--r-- 1 djh 18717 Feb 28 23:44 simple.c +-rw-r--r-- 1 djh 2068 Feb 28 23:44 spmisc.c +-rw-r--r-- 1 djh 711 Feb 28 23:44 spmisc.h +-rw-r--r-- 1 djh 355 Feb 28 23:44 whatiswhat + +applications/papif: +-rw-r--r-- 1 djh 1337 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 2108 Feb 28 23:44 Makefile.m4 +-rw-r--r-- 1 djh 7842 Feb 28 23:44 README +-rw-r--r-- 1 djh 426 Feb 28 23:44 cap.printers +-rw-r--r-- 1 djh 1355 Feb 28 23:44 makefile +-rw-r--r-- 1 djh 36711 Mar 14 13:52 papif.c +-rw-r--r-- 1 djh 4919 Feb 28 23:44 papof.c +-rw-r--r-- 1 djh 917 Feb 28 23:44 printcap.samp1 +-rw-r--r-- 1 djh 469 Feb 28 23:44 printcap.samp2 + +contrib: +-rw-r--r-- 1 djh 1338 Mar 14 17:59 Makefile +-rw-r--r-- 1 djh 1296 Mar 13 20:42 Makefile.m4 +-rw-r--r-- 1 djh 1208 Mar 13 20:44 README +-rwxr-xr-x 1 djh 749 Mar 14 15:59 atprint +-rw-r--r-- 1 djh 7714 Feb 28 23:44 cvt2apple.c +-rw-r--r-- 1 djh 6218 Feb 28 23:45 cvt2cap.c +-rw-r--r-- 1 djh 5200 Feb 28 23:45 lwrename.c +-rw-r--r-- 1 djh 1357 Mar 14 17:59 makefile +-rw-r--r-- 1 djh 38620 Feb 28 23:45 printQDA.hqx +-rw-r--r-- 1 djh 14370 Feb 28 23:45 printqueue.c +-rw-r--r-- 1 djh 12296 Feb 28 23:45 snitch.c + +contrib/AsyncATalk: +-rw-r--r-- 1 djh 2087 Feb 28 23:45 INSTALLATION +-rw-r--r-- 1 djh 1181 Feb 28 23:45 Makefile +-rw-r--r-- 1 djh 5238 Feb 28 23:45 README.rfc +-rw-r--r-- 1 djh 30688 Feb 28 23:45 async.1.4.hqx +-rw-r--r-- 1 djh 31083 Feb 28 23:45 async.c +-rw-r--r-- 1 djh 4124 Feb 28 23:45 async.h +-rw-r--r-- 1 djh 3872 Mar 14 14:27 asyncad.c +-rw-r--r-- 1 djh 5323 Feb 28 23:45 atalkdbm.c +-rw-r--r-- 1 djh 1235 Feb 28 23:45 macros.h + +contrib/AufsTools: +-rw-r--r-- 1 djh 1912 Feb 28 23:44 MANIFEST +-rw-r--r-- 1 djh 488 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 739 Feb 28 23:44 README + +contrib/AufsTools/binhex: +-rw-r--r-- 1 djh 1248 Feb 28 23:44 8to6.c +-rw-r--r-- 1 djh 482 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 4134 Feb 28 23:44 aufs.h +-rw-r--r-- 1 djh 4441 Feb 28 23:44 binhex.c +-rw-r--r-- 1 djh 1488 Feb 28 23:44 crc.c +-rw-r--r-- 1 djh 4043 Feb 28 23:44 gethead.c +-rw-r--r-- 1 djh 1175 Feb 28 23:44 run.c + +contrib/AufsTools/capit: +-rw-r--r-- 1 djh 141 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 8924 Feb 28 23:44 capit.c + +contrib/AufsTools/man: +-rw-r--r-- 1 djh 526 Feb 28 23:44 binhex.1 +-rw-r--r-- 1 djh 499 Feb 28 23:44 capit.1 +-rw-r--r-- 1 djh 335 Feb 28 23:44 cleanup.1 +-rw-r--r-- 1 djh 400 Feb 28 23:44 drag.1 +-rw-r--r-- 1 djh 401 Feb 28 23:44 dup.1 +-rw-r--r-- 1 djh 341 Feb 28 23:44 m2u.1 +-rw-r--r-- 1 djh 362 Feb 28 23:44 macp.1 +-rwxr-xr-x 1 djh 85 Feb 28 23:44 makeman +-rw-r--r-- 1 djh 5991 Feb 28 23:44 mcvert.1 +-rw-r--r-- 1 djh 299 Feb 28 23:44 newfolder.1 +-rw-r--r-- 1 djh 1006 Feb 28 23:44 sit.1 +-rw-r--r-- 1 djh 657 Feb 28 23:44 toaufs.1 +-rw-r--r-- 1 djh 348 Feb 28 23:44 trash.1 +-rw-r--r-- 1 djh 4233 Feb 28 23:44 unsit.1 +-rw-r--r-- 1 djh 2942 Feb 28 23:44 unstuffit.1 + +contrib/AufsTools/mcvert: +-rw-r--r-- 1 djh 443 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 19992 Feb 28 23:44 hqxify.c +-rw-r--r-- 1 djh 7478 Feb 28 23:44 mactypes.h +-rw-r--r-- 1 djh 13666 Feb 28 23:44 mcvert.c +-rw-r--r-- 1 djh 6606 Feb 28 23:44 unpack.c + +contrib/AufsTools/shell: +-rw-r--r-- 1 djh 108 Feb 28 23:44 Makefile +-rwxr-xr-x 1 djh 120 Feb 28 23:44 cleanup +-rwxr-xr-x 1 djh 513 Feb 28 23:44 drag +-rwxr-xr-x 1 djh 513 Feb 28 23:44 dup +-rwxr-xr-x 1 djh 83 Feb 28 23:44 m2u +-rwxr-xr-x 1 djh 55 Feb 28 23:44 macp +-rwxr-xr-x 1 djh 99 Feb 28 23:44 newfolder +-rwxr-xr-x 1 djh 71 Feb 28 23:44 toaufs +-rwxr-xr-x 1 djh 64 Feb 28 23:44 trash +-rwxr-xr-x 1 djh 83 Feb 28 23:44 u2m + +contrib/AufsTools/stuffit: +-rw-r--r-- 1 djh 229 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 12353 Feb 28 23:44 sit.c +-rw-r--r-- 1 djh 1834 Feb 28 23:44 sit.h +-rw-r--r-- 1 djh 5848 Feb 28 23:44 updcrc.c + +contrib/AufsTools/unstuffit: +-rw-r--r-- 1 djh 364 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 1074 Feb 28 23:44 getopt.c +-rw-r--r-- 1 djh 1871 Feb 28 23:44 stuffit.h +-rw-r--r-- 1 djh 31757 Feb 28 23:44 unsit.c +-rw-r--r-- 1 djh 5848 Feb 28 23:44 updcrc.c + +contrib/MacPS: +-rw-r--r-- 1 djh 3485 Feb 28 23:45 Installation +-rw-r--r-- 1 djh 1119 Feb 28 23:45 Makefile +-rw-r--r-- 1 djh 4682 Feb 28 23:45 ReadMe +-rw-r--r-- 1 djh 3585 Feb 28 23:45 macaux.c +-rw-r--r-- 1 djh 1949 Feb 28 23:45 macps-22.hdr +-rw-r--r-- 1 djh 36921 Feb 28 23:45 macps-22.shar +-rw-r--r-- 1 djh 2592 Feb 28 23:45 macps.1 +-rw-r--r-- 1 djh 5053 Feb 28 23:45 macps.c +-rw-r--r-- 1 djh 536 Feb 28 23:45 macps.config +-rw-r--r-- 1 djh 2100 Feb 28 23:45 prepfix.1 +-rw-r--r-- 1 djh 5999 Feb 28 23:45 prepfix.c +-rw-r--r-- 1 djh 1160 Feb 28 23:45 str.h +-rw-r--r-- 1 djh 3273 Feb 28 23:45 ucbwhich.c +-rw-r--r-- 1 djh 778 Feb 28 23:45 ucbwhich.h + +contrib/Messages: +-rw-r--r-- 1 djh 918 Mar 14 17:27 Makefile +-rw-r--r-- 1 djh 3067 Mar 14 17:27 README +-rw-r--r-- 1 djh 41 Feb 28 23:45 dot.forward +-rw-r--r-- 1 djh 2056 Feb 28 23:45 macto.1l +-rw-r--r-- 1 djh 1872 Feb 28 23:45 macuser.1l +-rw-r--r-- 1 djh 8122 Mar 14 14:21 macuser.c +-rw-r--r-- 1 djh 1068 Feb 28 23:45 macwho.1l +-rw-r--r-- 1 djh 1835 Mar 14 14:23 macwho.c +-rw-r--r-- 1 djh 4676 Mar 14 14:22 messages.c +-rw-r--r-- 1 djh 35239 Mar 14 19:27 messages.hqx +-rw-r--r-- 1 djh 6556 Mar 14 14:24 notify.c +-rw-r--r-- 1 djh 1671 Mar 14 14:25 notify.h + +contrib/Timelord: +-rw-r--r-- 1 djh 214 Mar 14 17:30 Makefile +-rw-r--r-- 1 djh 18399 Feb 28 23:45 tardis.hqx +-rw-r--r-- 1 djh 951 Feb 28 23:45 timelord.1l +-rw-r--r-- 1 djh 7478 Mar 14 14:26 timelord.c +-rw-r--r-- 1 djh 471 Feb 28 23:45 timelord.h + +doc: +-rw-r--r-- 1 djh 107 Mar 14 16:10 Makefile +-rw-r--r-- 1 djh 516 Mar 14 16:09 README +-rw-r--r-- 1 djh 3633 Feb 28 23:42 abmisc.doc +-rw-r--r-- 1 djh 4824 Feb 28 23:42 asp.notes +-rw-r--r-- 1 djh 2163 Feb 28 23:42 atp.notes +-r--r--r-- 1 djh 1097 Mar 14 14:39 cap.auth.doc +-rw-r--r-- 1 djh 3742 Feb 28 23:42 cap.notes +-rw-r--r-- 1 djh 11245 Mar 14 16:13 install.ms +-rw-r--r-- 1 djh 10507 Feb 28 23:42 nbp.ext +-rw-r--r-- 1 djh 2741 Feb 28 23:42 pap.notes +-rw-r--r-- 1 djh 8214 Feb 28 23:42 sched.notes +-rw-r--r-- 1 djh 8069 Feb 28 23:42 uab.desc.ms + +etc: +-rw-r--r-- 1 djh 921 Feb 28 23:43 Makefile +-rw-r--r-- 1 djh 941 Feb 28 23:43 Makefile.m4 +-rw-r--r-- 1 djh 400 Feb 28 23:43 README +-rw-r--r-- 1 djh 902 Feb 28 23:43 atalk.local +-rw-r--r-- 1 djh 27084 Mar 13 19:48 atis.c +-rw-r--r-- 1 djh 517 Feb 28 23:43 etalk.local +-rw-r--r-- 1 djh 939 Feb 28 23:43 makefile +-rw-r--r-- 1 djh 9281 Mar 13 19:50 nisaux.c +-rw-r--r-- 1 djh 673 Feb 28 23:43 start-cap-servers + +extras: +-rw-r--r-- 1 djh 506 Feb 28 23:43 Makefile +-rw-r--r-- 1 djh 440 Feb 28 23:43 Makefile.m4 +-rw-r--r-- 1 djh 324 Mar 14 17:36 README +-rw-r--r-- 1 djh 3666 Feb 28 23:43 att_getopt.c +-rw-r--r-- 1 djh 13301 Feb 28 23:43 des.c +-rw-r--r-- 1 djh 1258 Feb 28 23:43 dummy.des.c +-rw-r--r-- 1 djh 2241 Feb 28 23:43 iwif.c +-rw-r--r-- 1 djh 926 Feb 28 23:44 lnof.c +-rw-r--r-- 1 djh 525 Feb 28 23:44 makefile +-rw-r--r-- 1 djh 249 Feb 28 23:44 printcap.iw + +lib: +-rw-r--r-- 1 djh 506 Feb 28 23:42 Makefile +-rw-r--r-- 1 djh 480 Feb 28 23:43 Makefile.m4 +-rw-r--r-- 1 djh 499 Feb 28 23:43 makefile + +lib/afp: +-rw-r--r-- 1 djh 1374 Feb 28 23:43 Makefile +-rw-r--r-- 1 djh 1420 Feb 28 23:43 Makefile.m4 +-rw-r--r-- 1 djh 188 Feb 28 23:43 README +-rw-r--r-- 1 djh 13894 Feb 28 23:43 afpcmd.c +-rw-r--r-- 1 djh 2356 Feb 28 23:43 afperr.c +-rw-r--r-- 1 djh 9202 Mar 13 19:44 afposlock.c +-rw-r--r-- 1 djh 23206 Feb 28 23:43 afppacks.c +-rw-r--r-- 1 djh 1384 Feb 28 23:43 makefile + +lib/afpc: +-rw-r--r-- 1 djh 748 Feb 28 23:43 Makefile +-rw-r--r-- 1 djh 798 Feb 28 23:43 Makefile.m4 +-rw-r--r-- 1 djh 968 Feb 28 23:43 README +-rw-r--r-- 1 djh 19432 Mar 14 14:36 afpc.c +-rw-r--r-- 1 djh 24803 Feb 28 23:43 afpc.mss +-rw-r--r-- 1 djh 17132 Feb 28 23:43 afpcc.c +-rw-r--r-- 1 djh 767 Feb 28 23:43 makefile +-rw-r--r-- 1 djh 1405 Feb 28 23:43 probs + +lib/cap: +-rw-r--r-- 1 djh 2731 Mar 14 18:32 Makefile +-rw-r--r-- 1 djh 2810 Mar 14 14:01 Makefile.m4 +-rw-r--r-- 1 djh 60621 Mar 14 13:47 abasp.c +-rw-r--r-- 1 djh 3941 Feb 28 23:42 abasp.h +-rw-r--r-- 1 djh 33717 Feb 28 23:42 abatp.c +-rw-r--r-- 1 djh 2721 Feb 28 23:42 abatp.h +-rw-r--r-- 1 djh 13024 Feb 28 23:42 abauxddp.c +-rw-r--r-- 1 djh 14591 Feb 28 23:42 abauxnbp.c +-rw-r--r-- 1 djh 11505 Mar 14 14:35 abddp.c +-rw-r--r-- 1 djh 16059 Feb 28 23:42 abkip.c +-rw-r--r-- 1 djh 3846 Feb 28 23:42 ablap.c +-rw-r--r-- 1 djh 3712 Mar 13 20:16 ablog.c +-rw-r--r-- 1 djh 7955 Mar 14 14:29 abmisc.c +-rw-r--r-- 1 djh 20150 Mar 13 20:00 abnbp.c +-rw-r--r-- 1 djh 10688 Feb 28 23:42 abnet.c +-rw-r--r-- 1 djh 23370 Feb 28 23:42 abpap.c +-rw-r--r-- 1 djh 4393 Feb 28 23:42 abpap.h +-rw-r--r-- 1 djh 6863 Feb 28 23:42 abpapc.c +-rw-r--r-- 1 djh 15404 Feb 28 23:42 abpaps.c +-rw-r--r-- 1 djh 2294 Feb 28 23:42 abpp.c +-rw-r--r-- 1 djh 5975 Feb 28 23:42 abqueue.c +-rw-r--r-- 1 djh 22880 Feb 28 23:42 absched.c +-rw-r--r-- 1 djh 1067 Feb 28 23:43 abversion.c +-rw-r--r-- 1 djh 4029 Feb 28 23:43 abzip.c +-rw-r--r-- 1 djh 13844 Mar 15 14:56 atalkdbm.c +-rw-r--r-- 1 djh 478 Feb 28 23:43 atalkdbm.h +-rw-r--r-- 1 djh 8881 Feb 28 23:43 authenticate.c +-rw-r--r-- 1 djh 1201 Feb 28 23:43 cap_conf.h +-rw-r--r-- 1 djh 2736 Mar 14 18:32 makefile +-rw-r--r-- 1 djh 392 Feb 28 23:43 todist +-rw-r--r-- 1 djh 929 Feb 28 23:43 whatiswhat + +lib/xenix: +-rw-r--r-- 1 djh 773 Feb 28 23:43 Makefile +-rw-r--r-- 1 djh 302 Feb 28 23:43 fsync.c +-rw-r--r-- 1 djh 1347 Feb 28 23:43 ftruncate.c +-rw-r--r-- 1 djh 585 Feb 28 23:43 groups.c +-rw-r--r-- 1 djh 1791 Feb 28 23:43 rename.c +-rw-r--r-- 1 djh 3196 Feb 28 23:43 scandir.c + +man: +-rw-r--r-- 1 djh 16823 Feb 28 23:42 AUFS.1 +-rw-r--r-- 1 djh 8483 Feb 28 23:42 AUFS.8 +-rw-r--r-- 1 djh 2228 Feb 28 23:42 CAP.3 +-rw-r--r-- 1 djh 5237 Mar 13 20:45 CAP.8 +-rw-r--r-- 1 djh 6129 Feb 28 23:42 UAB.8 +-rw-r--r-- 1 djh 3301 Feb 28 23:42 ash.1 +-rw-r--r-- 1 djh 5971 Mar 13 20:47 atalk.local.5 +-rw-r--r-- 1 djh 3869 Feb 28 23:42 atis.8 +-rw-r--r-- 1 djh 3059 Feb 28 23:42 atlook.1 +-rw-r--r-- 1 djh 3329 Feb 28 23:42 atprint.1 +-rw-r--r-- 1 djh 2080 Feb 28 23:42 cvt2apple.1 +-rw-r--r-- 1 djh 1516 Mar 15 00:50 etalk.local.5 +-rw-r--r-- 1 djh 557 Feb 28 23:42 getzones.1 +-rw-r--r-- 1 djh 2541 Feb 28 23:42 instappl.1 +-rw-r--r-- 1 djh 8346 Feb 28 23:42 lwsrv.8 +-rw-r--r-- 1 djh 1020 Feb 28 23:42 makefile +-rw-r--r-- 1 djh 13770 Feb 28 23:42 papif.8 +-rw-r--r-- 1 djh 3455 Feb 28 23:42 snitch.1 + +netat: +-rw-r--r-- 1 djh 216 Feb 28 23:43 Makefile +-rw-r--r-- 1 djh 2571 Feb 28 23:43 aberrors.h +-rw-r--r-- 1 djh 3024 Feb 28 23:43 abnbp.h +-rw-r--r-- 1 djh 640 Feb 28 23:43 abqueue.h +-rw-r--r-- 1 djh 10274 Feb 28 23:43 afp.h +-rw-r--r-- 1 djh 482 Feb 28 23:43 afpc.h +-rw-r--r-- 1 djh 20501 Feb 28 23:43 afpcmd.h +-rw-r--r-- 1 djh 13515 Feb 28 23:43 appletalk.h +-rw-r--r-- 1 djh 1779 Feb 28 23:43 compat.h +-rw-r--r-- 1 djh 3533 Feb 28 23:43 macfile.h +-rw-r--r-- 1 djh 3550 Feb 28 23:43 sysvcompat.h + +patches: +-rw-r--r-- 1 djh 512 Mar 14 17:50 README +-rw-r--r-- 1 djh 2345 Feb 28 23:45 abpap.c.tickletimer +-rw-r--r-- 1 djh 2010 Feb 28 23:45 abpaps.c.flowquantum +-rw-r--r-- 1 djh 1594 Feb 28 23:45 afpos.c.strcmp +-rw-r--r-- 1 djh 1787 Feb 28 23:45 mips.ultrix.byteswap +-rw-r--r-- 1 djh 2352 Feb 28 23:45 papif.c.atpresponse +-rw-r--r-- 1 djh 2565 Feb 28 23:45 papif.c.reverse +-rw-r--r-- 1 djh 4528 Feb 28 23:45 papif.c.variousbugs +-rw-r--r-- 1 djh 3045 Feb 28 23:45 procset.c.looping +-rw-r--r-- 1 djh 2699 Mar 14 23:27 rtmp.c.nocase +-rw-r--r-- 1 djh 2414 Feb 28 23:45 simple.c.comments +-rw-r--r-- 1 djh 2117 Feb 28 23:45 snitch.c.stringlength + +patches/AUX.NATIVE: +-rw-r--r-- 1 djh 14519 Feb 28 23:45 cap.for.aux.2 +-rw-r--r-- 1 djh 29478 Feb 28 23:45 cap.for.aux.2-2 + +patches/BUGS.orig: +-rw-r--r-- 1 djh 3701 Feb 28 23:45 cap5.0-patch00001 +-rw-r--r-- 1 djh 4401 Feb 28 23:45 cap5.0-patch00002 +-rw-r--r-- 1 djh 2806 Feb 28 23:45 cap5.0-patch00003 +-rw-r--r-- 1 djh 1369 Feb 28 23:45 cap5.0-patch00004 +-rw-r--r-- 1 djh 622 Feb 28 23:45 cap5.0-patch00005 +-rw-r--r-- 1 djh 803 Feb 28 23:45 cap5.0-patch00006 +-rw-r--r-- 1 djh 2959 Feb 28 23:45 cap5.0-report0001 +-rw-r--r-- 1 djh 3773 Feb 28 23:45 cap5.0-report0002 +-rw-r--r-- 1 djh 3604 Feb 28 23:45 cap5.0-report0003 +-rw-r--r-- 1 djh 1509 Feb 28 23:45 cap5.0-report0004 +-rw-r--r-- 1 djh 865 Feb 28 23:45 cap5.0-report0005 +-rw-r--r-- 1 djh 919 Feb 28 23:45 cap5.0-report0006 + +patches/Dan@lth.se: +-rw-r--r-- 1 djh 1588 Feb 28 23:45 README +-rw-r--r-- 1 djh 479 Feb 28 23:45 README.FIRST +-rw-r--r-- 1 djh 5074 Feb 28 23:45 afpspd.c +-rw-r--r-- 1 djh 14907 Feb 28 23:45 stat.cache.patches +-rw-r--r-- 1 djh 1686 Feb 28 23:45 trel.README +-rw-r--r-- 1 djh 5421 Feb 28 23:45 trel.patches + +patches/Moy@Berkeley: +-rw-r--r-- 1 djh 2258 Feb 28 23:45 Configure.diff +-rw-r--r-- 1 djh 1327 Feb 28 23:45 README +-rw-r--r-- 1 djh 2206 Feb 28 23:45 abasp.c.diff +-rw-r--r-- 1 djh 448 Feb 28 23:45 abatp.c.diff +-rw-r--r-- 1 djh 710 Feb 28 23:45 abkip.c.diff +-rw-r--r-- 1 djh 1428 Feb 28 23:45 afpc.c.diff +-rw-r--r-- 1 djh 3235 Feb 28 23:45 afpcmd.h.diff +-rw-r--r-- 1 djh 1305 Feb 28 23:45 afpdid.c.diff +-rw-r--r-- 1 djh 459 Feb 28 23:45 afpdir.c.diff +-rw-r--r-- 1 djh 391 Feb 28 23:45 afpdt.c.diff +-rw-r--r-- 1 djh 41043 Feb 28 23:45 afpos.c.diff +-rw-r--r-- 1 djh 7261 Feb 28 23:45 afposenum.c.diff +-rw-r--r-- 1 djh 6644 Feb 28 23:45 afposfi.c.diff +-rw-r--r-- 1 djh 895 Feb 28 23:45 afps.h.diff +-rw-r--r-- 1 djh 460 Feb 28 23:45 afpserver.c.diff +-rw-r--r-- 1 djh 557 Feb 28 23:45 atalkdbm.c.diff +-rw-r--r-- 1 djh 2688 Feb 28 23:45 atis.c.diff +-rw-r--r-- 1 djh 470 Feb 28 23:45 papof.c.diff +-rw-r--r-- 1 djh 7955 Feb 28 23:45 sizeserver.shar + +patches/PC.aufs: +-rw-r--r-- 1 djh 2075 Feb 28 23:45 README +-rw-r--r-- 1 djh 2174 Feb 28 23:45 README.orig +-rw-r--r-- 1 djh 1644 Feb 28 23:45 afp.h.diffs +-rw-r--r-- 1 djh 7480 Feb 28 23:45 afpdid.c.diffs +-rw-r--r-- 1 djh 1161 Feb 28 23:45 afpdir.c.diffs +-rw-r--r-- 1 djh 1675 Feb 28 23:45 afpfile.c.diffs +-rw-r--r-- 1 djh 613 Feb 28 23:45 afpfork.c.diffs +-rw-r--r-- 1 djh 2409 Feb 28 23:45 afpos.c.diffs +-rw-r--r-- 1 djh 8229 Feb 28 23:45 afposenum.c.diffs +-rw-r--r-- 1 djh 1252 Feb 28 23:45 afposfi.c.diffs +-rw-r--r-- 1 djh 2681 Feb 28 23:45 afposncs.c.diffs +-rw-r--r-- 1 djh 1389 Feb 28 23:45 afpserver.c.diffs +-rw-r--r-- 1 djh 1420 Feb 28 23:45 afpvols.c.diffs + +patches/Rutgers: +-rw-r--r-- 1 djh 1826 Feb 28 23:45 README +-rw-r--r-- 1 djh 14645 Feb 28 23:45 afpos.c.diffs +-rw-r--r-- 1 djh 14589 Feb 28 23:45 aufs-lwsrv-mods +-rw-r--r-- 1 djh 6787 Feb 28 23:45 lwsrv.c.diffs + +patches/XENIX: +-rw-r--r-- 1 djh 11503 Feb 28 23:45 file.1 +-rw-r--r-- 1 djh 12009 Feb 28 23:45 file.2 +-rw-r--r-- 1 djh 35225 Feb 28 23:45 file.3 + +samples: +-rw-r--r-- 1 djh 2465 Feb 28 23:44 Makefile +-rw-r--r-- 1 djh 2508 Feb 28 23:44 Makefile.m4 +-rw-r--r-- 1 djh 1421 Feb 28 23:44 README +-rw-r--r-- 1 djh 22919 Feb 28 23:44 ash.c +-rw-r--r-- 1 djh 2593 Feb 28 23:44 atistest.c +-rw-r--r-- 1 djh 14500 Mar 14 13:59 atlook.c +-rw-r--r-- 1 djh 1654 Feb 28 23:44 getzones.c +-rw-r--r-- 1 djh 4043 Feb 28 23:44 instappl.c +-rw-r--r-- 1 djh 5081 Mar 14 14:38 isrv.c +-rw-r--r-- 1 djh 10624 Feb 28 23:44 look.c +-rw-r--r-- 1 djh 7246 Feb 28 23:44 lwpr.c +-rw-r--r-- 1 djh 2484 Feb 28 23:44 makefile +-rw-r--r-- 1 djh 7386 Mar 15 00:53 ruiwpr.c +-rw-r--r-- 1 djh 5801 Feb 28 23:44 tlw.c + +support/enet: +-rw-r--r-- 1 djh 6454 Feb 28 23:46 README +-rw-r--r-- 1 djh 21000 Feb 28 23:46 enet.4 +-rw-r--r-- 1 djh 45628 Feb 28 23:46 enet.c +-rw-r--r-- 1 djh 6108 Feb 28 23:46 enet.h +-rw-r--r-- 1 djh 7613 Feb 28 23:46 enetdefs.h +-rw-r--r-- 1 djh 23 Feb 28 23:46 enetfilter.h +-rw-r--r-- 1 djh 1185 Feb 28 23:46 etherstat.8 +-rw-r--r-- 1 djh 9976 Feb 28 23:46 etherstat.c + +support/ethertalk: +-rw-r--r-- 1 djh 829 Feb 28 23:46 Makefile +-rw-r--r-- 1 djh 1527 Mar 13 20:06 Makefile.m4 +-rw-r--r-- 1 djh 2599 Mar 15 15:12 README +-rw-r--r-- 1 djh 422 Feb 28 23:46 STILL_TO_DO +-rw-r--r-- 1 djh 7033 Mar 13 19:34 aarpd.c +-rw-r--r-- 1 djh 267 Feb 28 23:46 aarpd.h +-rw-r--r-- 1 djh 201 Feb 28 23:46 aarpd.x +-rw-r--r-- 1 djh 1351 Mar 14 14:32 aarpd_clnt.c +-rw-r--r-- 1 djh 1565 Mar 14 14:32 aarpd_svc.c +-rw-r--r-- 1 djh 474 Mar 14 14:33 aarpd_xdr.c +-rw-r--r-- 1 djh 1474 Mar 13 20:12 aarptest.c +-rw-r--r-- 1 djh 20676 Mar 13 20:10 abelap.c +-rw-r--r-- 1 djh 6867 Feb 28 23:46 dlip.c +-rw-r--r-- 1 djh 10526 Mar 14 20:06 ethertalk.c +-rw-r--r-- 1 djh 1470 Feb 28 23:46 makefile +-rw-r--r-- 1 djh 1749 Mar 13 20:13 rtmptest.c +-rw-r--r-- 1 djh 10810 Mar 13 20:15 senetp.c +-rw-r--r-- 1 djh 10134 Feb 28 23:46 snitp.c + +support/uab: +-rw-r--r-- 1 djh 1374 Feb 28 23:45 Makefile.m4 +-rw-r--r-- 1 djh 1420 Feb 28 23:45 README +-rw-r--r-- 1 djh 241 Feb 28 23:45 STILL_TO_DO +-rw-r--r-- 1 djh 26462 Mar 13 20:08 aarp.c +-rw-r--r-- 1 djh 1880 Feb 28 23:45 aarp.h +-rw-r--r-- 1 djh 3727 Feb 28 23:45 aarp_defs.h +-rw-r--r-- 1 djh 13436 Mar 13 20:38 asyncatalk.c +-rw-r--r-- 1 djh 4171 Feb 28 23:45 bridge_desc +-rw-r--r-- 1 djh 6448 Mar 13 20:38 ddpport.c +-rw-r--r-- 1 djh 4223 Feb 28 23:45 ddpport.h +-rw-r--r-- 1 djh 14131 Feb 28 23:45 ddprouter.c +-rw-r--r-- 1 djh 6971 Mar 13 20:39 ddpsvcs.c +-rw-r--r-- 1 djh 6212 Feb 28 23:45 dlip.c +-rw-r--r-- 1 djh 12707 Mar 13 20:39 ethertalk.c +-rw-r--r-- 1 djh 1012 Feb 28 23:45 ethertalk.h +-rw-r--r-- 1 djh 2563 Feb 28 23:45 gw.h +-rw-r--r-- 1 djh 17620 Feb 28 23:45 hash.3 +-rw-r--r-- 1 djh 21202 Feb 28 23:45 hash.c +-rw-r--r-- 1 djh 4509 Feb 28 23:45 hash.h +-rw-r--r-- 1 djh 1927 Feb 28 23:45 if_desc.h +-rw-r--r-- 1 djh 12116 Mar 13 20:39 kip_mpx.c +-rw-r--r-- 1 djh 3579 Mar 13 20:38 log.c +-rw-r--r-- 1 djh 945 Feb 28 23:45 log.h +-rw-r--r-- 1 djh 1440 Feb 28 23:45 makefile +-rw-r--r-- 1 djh 2210 Feb 28 23:45 mpxddp.h +-rw-r--r-- 1 djh 1324 Feb 28 23:45 node.h +-rw-r--r-- 1 djh 2206 Feb 28 23:45 proto_intf.h +-rw-r--r-- 1 djh 35153 Mar 14 20:26 rtmp.c +-rw-r--r-- 1 djh 8127 Feb 28 23:45 senetp.c +-rw-r--r-- 1 djh 8529 Feb 28 23:45 snitp.c +-rw-r--r-- 1 djh 21040 Mar 13 20:37 uab.c +-rw-r--r-- 1 djh 969 Feb 28 23:46 whatiswhat + +support/uab/patches: +-rw-r--r-- 1 djh 4930 Feb 28 23:45 bug.1 +-rw-r--r-- 1 djh 1494 Feb 28 23:45 bug.2 +-rw-r--r-- 1 djh 718 Feb 28 23:45 bug.3 diff --git a/MODIFICATIONS b/MODIFICATIONS new file mode 100644 index 0000000..0e3e10d --- /dev/null +++ b/MODIFICATIONS @@ -0,0 +1,73 @@ +The following is an (incomplete) list of the modifications from CAP +Distribution 5 to CAP Distribution 6. + + * Many patches for extra functionality have been incorporated. + * Subdirectory 'support' has been added for alternate LAPs. + * UAB is now bundled with this CAP distribution (support/uab). + * 'Configure' will recognize the host byte ordering. + * 'Configure' handles a wider range of machine types. + * AUFS support for AFP 2.0 is (practically) complete. + * UAB now supports Asynchronous AppleTalk on a UNIX host. + * The format of 'atalk.local' has been extended for async appletalk. + * Zone names in 'atalk.local' MUST now be quoted to include spaces. + * A "free format" 'etalk.local' is used by EtherTalk LAPs. + * atis is now Phase 2 NBP compatible (obj/type partial matching). + +The following is an (incomplete) list of the modifications from CAP +Distribution 4 to CAP Distribution 5. + +The general change are: + o various directories have been reorganized. The major ones are: + doc - documentation, man - manual pages, samples - sample + programs, applications - heavily used programs (at Columbia), + lib - libraries, and etc - support programs. + + o While a generic Makefile is included, an "os specific" makefile + generation facility is included and should now be used. + +We do not list bug fixes below, only major changes in functionality. + +In samples, the various programs now use a define to find cap.printers +allowing it to be relocated from etc. In addition, look has been +revised to make its operation easier and renamed to atlook to prevent +naming conflicts (looks => atlooklws, and pinger => atpinger) + +applications/lwsrv + lwsrv rewritten. The major change is that it now stores prep + files in a directory and automatically captures unknown ones. +applications/papif + essentially rewritten + supports psrev, pstext now + pscomm compatible now + +applications/aufs + user data base of icon types added + normalizing character set for different types of ascii + clean up of kbox too slow code + new desktop format + new finderinfo format + aux support + sun quota fixed + .afpvols used if no afpvols + shutdown code from Scooter Morris added. + +lib/cap + allow modifcation of response cache timeout in atp + pap fix for cleaning up outstanding papwrites + fix problem with timeouts in abmisc + print nis node + fix up asp to match up specifiable values better + rework abnet.c,abddp.c,abmisc.c so that they "layer" better + abnet moved into abkip.c and absched.c + parts of abmisc.c moved to absched.c + abddp had KIP (udp) encapsulation dependencies removed + or hidden + absched now "schedules" protocol events + abkip now holds all code for KIP (udp) encapsulation + +lib/afp + fix afposlock up for nfs systems w/o lock daemon + +lib/afpc + clean up + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fe7a766 --- /dev/null +++ b/Makefile @@ -0,0 +1,94 @@ + +all: + @echo "You didn't read NOTES and doc/install.ms did you?" + @echo + @echo "First two steps are: Configure and gen.makes" + +include: . + (cd netat; make install) + +libsmade: + (cd lib/cap; make) + (cd lib/afp; make) + (cd lib/afpc; make) + touch libsmade + +libinstall: libsmade + (cd lib/cap; make install) + (cd lib/afp; make install) + (cd lib/afpc; make install) + touch libinstall + +programs: libinstall + -(cd etc; make) + -(cd samples; make) + -(cd contrib; make) + -(cd applications; make) + -(cd support/uab; make) + -(cd support/capd; make) + -(cd support/ethertalk; make) + touch programs + +install: libinstall programs + -(cd etc; make install) + -(cd samples; make install) + -(cd contrib; make install) + -(cd applications; make install) + -(cd support/uab; make install) + -(cd support/capd; make install) + -(cd support/ethertalk; make install) + +cap.shar: listtodist + cap.dist shar + +cap.tar: listtodist + cap.dist tar + +dist: + @cat todist + @(cd netat; make dist) + @(cd lib; make dist) + @(cd etc; make dist) + @(cd samples; make dist) + @(cd contrib; make dist) + @(cd extras; make dist) + @(cd doc; make dist) + @(cd man; make dist) + @(cd applications; make dist) + +clean: + -rm -f m4.tmp + -rm -f libsmade + -rm -f programs + -rm -f libinstall + -(cd netat; make clean) + -(cd man; make clean) + -(cd doc; make clean) + -(cd lib; make clean) + -(cd etc; make clean) + -(cd samples; make clean) + -(cd contrib; make clean) + -(cd extras; make clean) + -(cd applications; make clean) + -(cd support/uab; make clean) + -(cd support/capd; make clean) + -(cd support/ethertalk; make clean) + +spotless: + -rm -f *.orig + -rm -f m4.tmp + -rm -f libsmade + -rm -f programs + -rm -f libinstall + -(cd netat; make spotless) + -(cd man; make spotless) + -(cd doc; make spotless) + -(cd lib; make spotless) + -(cd etc; make spotless) + -(cd samples; make spotless) + -(cd contrib; make spotless) + -(cd extras; make spotless) + -(cd applications; make spotless) + -(cd support/uab; make spotless) + -(cd support/capd; make spotless) + -(cd support/ethertalk; make spotless) diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..6d8b23b --- /dev/null +++ b/NOTES @@ -0,0 +1,231 @@ +CAP Installation notes + +The Columbia University Appletalk Package is actually a rather broad +collection of libraries and programs. Following are some guidelines +in building the various programs and libraries. + +Following are notes about the various components of CAP that require +special attention during installation or porting. Hopefully, we've +caught most of the major points, but there may be things missing. + +Index: + CONFIGURATION + INSTALLATION + MACHINE SPECIFIC + LIBRARIES + SAMPLE PROGRAMS + CONTRIB + APPLICATIONS + + +************* +CONFIGURATION +************* + +Configuration is somewhat automated. The configuration parameters are +divided into features and major and minor parameters. The major parameters +must be set to compile properly. The minor parameters are for +configuration of programs, etc. + +To start off, you can run the "Configure" shell script to configure +the main parameters. One of the options in Configure allows you to edit +a file called 'm4.features'. This file is used to specify which of a set +of alternate features are included. For more details about the features, +see the README files in the patches subdirectories. Configure creates a +file called m4.setup. If necessary, you should then modify the secondary +(minor) parameters in this file. + +Makefiles are generated from templates. m4.setup and m4.features define +variables used in the template. The command gen.makes builds the Makefiles. + +Makefiles are included in all directories (Makefile) configured for +a standard BSD system. (Major configuration: bsd, no fastether, +needgetopt, no quotas). gen.makes creates "makefile" which should +be used in preference to "Makefile" on most machines. + +Make sure you read "MACHINE SPECIFIC"! It points out os/machine +dependencies that are not handled by Configure. + + +************ +DESTINATIONS +************ + +Or where do the files go? Generally, the defaults are: + /usr/local/cap - general programs + /usr/local/lib/cap - programs not for general use, data files + /usr/local/lib - libraries + /etc - some configuration files + +Certainly machines do not search /usr/local/lib by default. +In these cases, the libraries are place in /usr/lib. + +You should be modifying these in m4.setup. + +************ +INSTALLATION +************ + +A detailed procedure with testing mechanisms is defined in +doc/install.ms (a formatted copy is doc/install.doc). +However, you should peruse this document first. + + +**************** +MACHINE SPECIFIC +**************** + +The current CAP distribution should work on: + BSD 4.2 + BSD 4.3 + Ultrix 1.0, 1.1, 1.2, 2.0 and hopefully 2.2 + Sun OS 3.2 or higher + Pyramid's Unix under the BSD universe + ACIS 4.2 or 4.3 for the IBM RT PC + A/UX (using native AppleTalk with 2.0) + HP-UX for the series 9000 (release 6.0) + Encore (Multimax) (version unknown) + IRIS/IRIX for Silicon Graphics + AIX for the IBM 6000 +and the majority should work under: + Convex Unix V6.1 +and most should work with some manual work under: + HP-UX - release before 6.0 + +However, it should be noted that the baseline development system is +Ultrix 2.0 and things are tuned for that environment. + +If your machine isn't listed, take a close look at the document +"PORTING" for things to watch out for. + +On the pyramid, everything is compiled with the "-q" option to be +safe. "-q" tells it to pack the structures, possibly at the expense +of speed. SEE LOCKF below. + +On the Encore Multimax, there are reports that the optimizer may be +overzealous. It may be wise to compile without optimization (or try +recompiling without optimization if there are problems). + +On HP-UX, if you have an old release, you will have to define +"NEEDMSGHDR" in caposdefs in m4.setup as output by Configure. (You +can do it lib/cap/makefile, but that is not recommended). + +On MORE/BSD, getgroups may return an array of gid_t instead of type +int. If this happens to be the case, you should edit aufsosdefs in +m4.setup and add: + -DGGTYPE="gid_t" +ALSO SEE LOCKF BELOW. + +LOCKF - You should be careful here. Lockf is known to work properly +under Ultrix and A/UX. It should work okay under SunOS. There have +been reports of problems with lockf under Pyramid OSx and MORE/BSD. +If it is truly broken then you should comment out the "lockf in +system" in m4.setup. In other words, find the line: + define([X_LOCKF],1) +and put a "#" in front of it. If you do this and have already +compiled, then you should regenerate your makefiles with gen.makes, +regenerate libafp (lib/afp) making sure you recompile afposlock and +relink any programs that use libafp (currently samples/ash and +applications/aufs). Note that some systems may require a special +daemon for lockf to function (e.g. locking down outside the kernel). +Another problem may simply be the number of allowable locks in the +system. + +Paul Campbell reports that AUX 1.0 goes to the disk for every call to +gettimeofday to validate the TZ information. In absched, a larger +number of gettimeofday calls are done that do not require TZ +information (even if they did require this information there are +better ways of doing things than going to disk on every call). To get +around this, the cap library LOCALTIME_GTOD says to call the function +_gettimeofday to grab the time only. This is noted here because +future versions of aux will probably require slightly different +handling. + +On HP/Apollo Domain OS, CAP builds under the BSD 4.3 environment, +for Domain OS version 10.4. The main applications (aufs and +papif tested, lwsrv not tested) work on both 68K and 88K/PRISM +machines. The Domain OS syntax //node/path must be avoided in the +afpvol file. Note that this may result from the expansion of ~ if +home directories are defined using the full network syntax. The +problem is purely one of syntax, directories on other nodes may be +mounted using soft links or alternative syntax such as /../node/path. +You may want to modify the aufs man page to communicate this. +[Note: it is better for aufs (and other Unix software) if home +directories are defined without reference to //node and soft links +used between different nodes, so that ~ will never expand to the +unusual Apollo syntax.] Both flock, lockf and (if either the +APPLICATION_MANAGER or DENYREADWRITE options are chosen, see +netat/fcntldomv.h) undocumented fcntl range locking is used. Since +the underlying Domain file system handles locking, one hopes this +may all work -- but, from restrictions in the Domain file sytem, +perhaps not as well on files on remote nodes. If you primarily use +the System V environment but installed the BSD 4.3 environment, you +could probably build aufs, at least, under the BSD environment and +use it under System V. + +********* +LIBRARIES +********* + +There are three sets of libraries. The first is the core Appletalk +libraries. The second is the generic AFP libraries. The third is the +AFP client libraries. In the following we discuss some of the points +you should be aware of when building these. + +CAP LIBRARIES +------------- + +The core of CAP is the Appletalk libraries. + +The major configuration parameter is for atalkdbm.c and tells it where +to find the atalk.local file. By default, it assumes +/etc/atalk.local, but can be reconfigured in m4.setup. + +cap_conf.h contains a set of configuration parameters that defined +standard timeouts, etc, for protocols. At present, it only contains +parameters for PAP. You shouldn't need to touch this file. + +AFP Libraries +------------- + +o locking + +The two defines for locking are "NOLOCKF" and "NOFLOCK" that should be +set appropriately for your machine. Most BSD systems should have +flock available. The only program that uses these calls is Aufs. For +the implications of what happens when you do or don't have these, see +the documentation on the various client and server (Aufs) programs. +In particular, see applications/aufs/INSTALLATION. + + +AFP Client Libraries +-------------------- +You should be able to build afpc without any configuration changes. + + +*************** +SAMPLE PROGRAMS +*************** + +You should be able to get away without modifying the default +compilation parameters. See samples/README for futher information. + +*********** +CONTRIBUTED +*********** + +See contrib/README for further information. + +************ +APPLICATIONS +************ + +See the README files in the various directories for further +information about configuration of these programs. + +It is highly recommended that you look at the default configuration +for Aufs. This holds in particular if you are not running a virgin +BSD4.3 or BSD 4.2 system (have NFS added, etc): there is a good chance +you can configure parameters in that will result in better performance +and/or more functionality. + diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..07e3ec3 --- /dev/null +++ b/NOTICE @@ -0,0 +1,38 @@ +NOTICE + +The Columbia AppleTalk Package for UNIX, referred to as CAP +and which consists of files in the enclosed MANIFEST, is Copyright +1986, 1987, and 1988 by the Trustees of Columbia University in the +City of New York. CAP is provided with certain restrictions as +outlined below. + +COPYRIGHT NOTICE + +Copyright (c) 1986, 1987, 1988, The Trustees of Columbia University in the +City of New York. Charlie C. Kim, User Services Group, Academic +Information Services Division, Libraries and Center for Computing +Activities & Bill Schilit, Computer Research Facilities, Computer +Science Department. + +Permission is granted to any individual or institution to use, copy, +or redistribute this software so long as it is not sold for profit, +provided that this notice and the original copyright notices are +retained. Columbia University makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + + +The following notices apply to certain of the files in the CAP +distribution and such files are clearly marked. + +Portions Copyright (c) 1985, Stanford Univ. SUMEX project. +May be used but not sold without permission. + +Portions Copyright (c) 1984, Apple Computer Inc. +Gene Tyacke, Alan Oppenheimer, G. Sidhu, Rich Andrews. + +Portions Copyright (c) 1988, Carnegie Mellon University. Rob Chandhok, +Computer Science Department. + +Portions Copyright (c) 1990, 1991, The University of Melbourne. + diff --git a/PORTING b/PORTING new file mode 100644 index 0000000..c232185 --- /dev/null +++ b/PORTING @@ -0,0 +1,154 @@ +The following notes point out some of the items to watch out for when +porting CAP to different variants of BSD and other machines. Most of +these are items that have been "hit" in the past. + +Index: GENERAL COMPATIBILITY + SYSTEM V + LIBRARIES + APPLICATIONS + CONFIGURATION + +********************* +GENERAL COMPATIBILITY +********************* + +sysvcompat.h in netat/sysvcompat.h attempts to allow the various +modules to mask incompatibilities between System V machines and bsd +machines. Except for some very basic translations (e.g. strchr vs. +index), all the mappings are done under a machine dependency (e.g. +ifdef hpux). Many different parts of CAP make use of this file, +though the largest client by far is Aufs. Most of the samples and +applications have not been tested or modified for System V +compatibility. NOTE: Aufs does not attempt to deal with the file name +length restrictions in System V: file names are simply truncated. + +The machines in sysvcompat.h at present are: hpux and aux. Note: some +of the defintions for hpux are for older versions of hpux. This +points to a need to redo this file at a future point. + +compat.h in netat/compat.h attempts to masks differences between +various bsd machine. To be more specific, it defines an additional +set of "macros", etc. that aren't found on all bsd based machines such +as sigmask, etc. + +******** +SYSTEM V +******** + +You can use conf.sysv.sh to generate a set of "ifdefs" suitable for +inclusion in sysvcompat.h for a particular machine. conf.sysv.sh +takes as an argument, the output file. The default is sysv.cpp. + +Allowable ifdefs in sysvcompat.h are: + + B2S_STRING_MAPON - Set if no strings.h or rindex or index. Must have +string.h and strrchr, strchr in this case. + + B2S_BSTRING_MAPON - Set if no bcopy, bcmp, bzero. Then you must have +memcpy, memcmp, and memset. + + USECHOWN - set you may chown a file to another + + NEEDFCNTLDOTH - set if you need to include fcntl to get O_READONLY, etc. + + USETIMES - use times to get process usage times instead of the +preferred bsd getrusage. Also assume that wait3 does not return usage +information. + + NOWAIT3 - set if no wait3 in system - use wait instead which isn't as +flexible. + + NODUP2 - No DUP2 call. Emulate with "close/dup" + + NOLSTAT - no lstat for symbolic links. Use stat or don't use at all. + + USERAND - don't use the bsd random call, use srand, rand instead + + USEGETCWD - use the system 5 getcwd call to get the current working +directory instead of the bsd getwd call. + + NOUTIMES - use utime to set file times instead of bsd utimes + + NOPGRP - set if either setpgrp or killpg is missing. If one is, both +probably are, but it might be like hpux where one is in libc and one +in libBSD (in which case you need not define this, but modify +libspecial to have libBSD (SLIB in makefiles)) + + NOVFORK - system doesn't have vfork - what a shame. Use fork instead. + +********* +LIBRARIES +********* + +CAP LIBRARIES +------------- +Most of the machine dependencies are encoded into the modules and deal +with issues of structure byte alignment, incompatibilities with BSD +Unix, etc. Most of these are dealt with by doing "ifdef machine" and +triggering internal defines. Following is a module by module +description of some of the more important of these. + +abkip.c has four major "defines". NORECVMSG and NOSENDMSG are set by +Configure and say whether sendmsg and recvmsg exist in the system +(they do scatter/gather io). NEEDMSGHDR should also be set if + doesn't define it (as in old versions of aux). +NOFFS means that the target machine does not have the "ffs" subroutine +defined (find first set bit) and that we must use our own copy. hpux +and aux use NOFFS. + +abddp.c has one major "define": INLINECHKSUM. INLINECHKSUM attempts +to speed up the ddp checksumming by defining the code in-line. This +been optimized for a vax and only tested on a vax. It is only set for +a vax :-). Note: it is possible to turn off ddp checksumming by +compiling abddp.c with "DONT_DOCHKSUM". This is NOT recommended. If +you have an application that requires turning off ddp checksumming, +you can call "checksum_error" with FALSE to ignore checksum errors. + +abnbp.h in netat has an ifdef important for byteswapped machines. +Specifically, there is a bitfield definition and the order of the +items may depend upon the byte ordering of the target. You may need +to add an ifdef for your machine. + +AFP Libraries +------------- +The AFP libraries contain three main components at the present time: +(1) a set of packing and unpacking routines that attempts to hide byte +swapping and struct packing differences between machines, (2) the des +subroutines, and (3) the locking routines. There are all routines +that might be used by either the client or server AFP software. + + o Packing and unpacking + +afpcmd.c is a driver for the packing and unpacking routines that +attempts to mask byte ordering and structure alignments. Currently +there are two defines in this module: BYTESWAPPED and +LOOSE_BYTESWAPPED. Like the defines in the libraries, these are also +hidden. The first simply says the machine is byte swapped like a vax. +The second allows cheating in dealing with byte swapping and should +only be defined if you are byte swapping and you can be sure that a +pointer to a "byte" will be usuable as pointer to a word or double +word. Currently, both defines are set for a vax. Sequents (nsxxxxx +and 386 based machines) and ns16000/ns32000 machines have BYTESWAPPED +on. pyramids, ibm032 (ibm rt pc), and 680xx based machines (sun, +aux/mac-ii, hpux/hp-9000 series) do not need either define. Note: you +should be able to turn on LOOSE_BYTESWAPPED for the nsxxxx processors, +but it is not done by default. + +o locking + +The two defines for locking are "NOLOCKF" and "NOFLOCK" that should be +set appropriately for you machine. Most BSD systems should have flock +available. For the implications of what happens when you do or don't +have these, see the documentation on the various client and server +(Aufs) programs. + + +************ +APPLICATIONS +************ + +Aufs has a seperate document on porting/installation in +applications/aufs/INSTALLATION. However, it is worth mentioning that +most of the Aufs porting requirements are covered through the use of +sysvcompat.h and compat.h as mentioned in "GENERAL COMPATIBILITY". + diff --git a/README b/README new file mode 100644 index 0000000..9d5ec4f --- /dev/null +++ b/README @@ -0,0 +1,338 @@ +The purpose of setting up CAP on GitHub is to build an AppleTalk +protocol stack for OS X 10.6 and newer that is compatible with +Classic Mac OS. That is, AFP-version up to 2.x and transport via DDP +(despite Apple removing DDP from OS X since 10.6). + +This will enable AppleTalk networking with emulated machines running +Classic Mac OS as well as printing on non-IP, AppleTalk-only printers. + +It should be possible to make this work using Homebrew. + +- 'mabam', March 2015 + +---------------------------------------------------------------------- + + CAP Distribution 6.0, Patch Level 198 + 'asip1', March 1999 + +In 'CAP.faq' it is stated that the following interim patches are +available for download from munnari.OZ.AU and can be reversed once +patch 199 is completed: + + asip.patch - AppleShareIP support + cicon.patch - Color Desktop Icon support + extnd.patch - Extended volume size support + desktop.patch - Desktop tool color icon support + +It appears that patch 199 has never been released and the interim +patches are not available anymore at munnari.OZ.AU. + +They are, however, available for download at + ftp://iubio.bio.indiana.edu/util/cap/ (log in as guest) +with the first patch substituted for a fixed version called +'asip1.patch'. +In this therefore unofficial, though fully patched version of CAP 6.0 +these patches have been applied in the required, above stated order on +top of the patches 001 to 198. + +See also 'cap60.pl198+asip.README' for the original information by +Don Gilbert from the Department of Biology, Indiana University +Bloomington. + +- 'mabam', March 2015 + +---------------------------------------------------------------------- + + CAP - Columbia AppleTalk Package for UNIX + + o RELEASE NOTES + o CAP Distribution 6.0, Patch Level 198, June 1996 + +Notice +------ + +Copyright (c) 1986, 1987, 1988, The Trustees of Columbia University in +the City of New York. Charlie C. Kim, User Services Group, Academic +Information Services Division, Libraries and Center for Computing +Activities and Bill Schilit, formerly of Computer Research Facilities, +Computer Science Department. + +Permission is granted to any individual or institution to use, copy, +or redistribute this software so long as it is not sold for profit, +provided that this notice and the original copyright notices are +retained. Columbia University makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + +-- + +Portions Copyright (c) 1985, Stanford Univ. SUMEX project. +May be used but not sold without permission. + +Portions Copyright (c) 1984, Apple Computer Inc. +Gene Tyacke, Alan Oppenheimer, G. Sidhu, Rich Andrews. + +Portions Copyright (c) 1990 - 1996 The University of Melbourne + +Modules copyright in part or whole by any other entity than Columbia +University are clearly marked as such. + +-- + +Portions are of the CAP distribution are public domain software. The +specific items are: + extras/att_getopt.c + extras/des.c + +Portions of the CAP distribution are contributed by other sites including: + Rob Chandhok, Computer Science Department, Carnegie Mellon University + Ed Moy, University of California at Berkeley + David Hornsby, The University of Melbourne + Rakesh Patel, Rutgers University + Paul Campbell + + +ABSTRACT +-------- + +CAP was written for BSD 4.2 Unix and derivatives. CAP implements a +library containing a portion of Apple Computer's AppleTalk protocols. +In order to use this package you may need an AppleTalk/Ethernet bridge +(e.g. Shiva FastPath, Webster MultiPort Gateway). CAP includes a number +of applications that can be used to print to a LaserWriter, spool for a +LaserWriter, and act as Unix based AppleShare compatible file server. +CAP also includes a number of sample programs and contributed software. + +CAP library routines are structured, for the most part, the same as +the Apple routines described in "Inside AppleTalk" and "Inside +LaserWriter." Refer to the Apple documents and the procedure comments +for a complete description of the routines and how to call them. + +Bill Croft's original work in this area provided the inspiration for CAP. + +Prerequisties +------------ + + o CAP as originally shipped needs a gateway capable of supporting + IPTalk (the transmission of AppleTalk DDP packets inside IP UDP packets) + to translate (gateway) IPTalk packets to/from EtherTalk or LocalTalk. + Suitable candidates include ... + + * Webster MultiPort Gateway + * Cayman Gatorbox + * Shiva FastPath + * Cisco Router + + o This CAP version supports Native EtherTalk, UAR (Phase 1 or Phase 2) and + Kernel AppleTalk, UAB (Phase 1) on certain hosts. A gateway as listed + above is only required to access LocalTalk services. + + o baseline host system: Ultrix 2.0-1. Most will work under BSD 4.2, + BSD 4.3, Ultrix 1.0-1.2, Sun OS 3.2 or higher, ACIS 4.2, A/UX, IBM + RISC 6000, IRIS/IRIX, HP/Apollo Domain (BSD environment), OSF/Alpha, + 386/BSD, FreeBSD and other systems with BSD like networking + facilities with varying levels of functionality. Under certain + systems, only portions will work. + +Where +----- + +CAP can be obtained by anonymous FTP from + +munnari.OZ.AU mac/{cap60.pl100.tar.Z,cap.patches/*} +ftp-ns.rutgers.EDU pub/cap/{cap60.pl100.tar.Z,cap.patches/*} +gatekeeper.DEC.COM pub/net/appletalk/cap/{cap60.pl100.tar.Z,cap.patches/*} +ftp.kuis.kyoto-u.AC.JP net/cap/{cap60.pl100.tar.Z,cap60.patches/*.Z} +src.doc.ic.AC.UK packages/multigate/{cap60.pl100.tar.Z,cap.patches/*} + +Please choose an appropriate site and an off-peak time for the transfer. + +The patches are available individually or as the files "patches.1-100.tar.Z", +"patches.101-126.tar.Z", "patches.127-143.tar.Z", "patches.144-154.tar.Z", +"patches.155-162.tar.Z", "patches.163-182.tar.Z" & "patches.183-192.tar.Z". +Additionally, for new users, a partially patched source file is available +as "cap60.pl100.tar.Z" (beware: the file cap60.tar.Z is totally unpatched). + +Patches +------- + +To make the process of patching easier, you should get the 'patch' utility +written by Larry Wall, it is normally available from sites that archive +comp.sources.unix in volume7/patch2 and at GNU archive sites as +patch-2.1.tar.gz (which requires gzip-1.2.2.tar for unpacking). + +For each of the patches, run 'patch -p < cap60.patchNNN' from the top level +cap60 directory, for example, in csh + + foreach i (cap60.patches/cap60.patch*) + patch -p < $i >>& /tmp/patches + end + +and check the /tmp/patches file for patching errors (look for the strings +"rej", "failed", "offset", "fuzz" - should be none). To remove the *.orig +files that patch leaves behind (containing the original version of the file), +run 'make spotless' from the top level directory (note that spotless also +removes all makefiles so gen.makes needs to be run to regenerate them). + +Information +----------- + +There is no CAP mailing list. Instead, notices and information about +CAP are posted to the mailing list info-appletalk which is gatewayed +with the USENET news group comp.protocols.appletalk. If you don't +have access to comp.protocols.appletalk and have access to the +ARPANET, you can get on the mailing list by sending mail to +info-appletalk-request@andrew.cmu.edu. + +Information about CAP and related UNIX AppleTalk packages is available +via the World Wide Web using + + http://www.cs.mu.OZ.AU/appletalk/atalk.html + +The CAP FAQ (Frequently Asked Questions) file is available via FTP +from munnari.OZ.AU as the file + + mac/CAP.faq + +Documentation +------------- + +Important documentation resides in: + + doc/install.ms - stepwise installation document: assumes + you have read NOTES and this document + doc/print.cookbook - simple steps to implement CAP printing + man/* - UNIX manual entries for the various CAP programs + NOTES - installation notes: READ THIS BEFORE STARTING INSTALLATION + PORTING - notes on porting CAP to machines it doesn't know about + +What's in CAP +------------- + +The Columbia AppleTalk Package consists of a number of libraries, a +number of programs, and associated documentation. Following is a list +of the main parts along with a brief description. + + o NOTES for a general overview of installation and some overview material. + + o PORTING for information about making CAP work on systems not listed + in NOTES + + netat - general header files used by various parts of CAP + man - man pages for some of the programs + doc - documentation + lib/cap - main appletalk libraries: ASP, PAP, ATP, NBP, DDP + lib/afp - generic AppleTalk Filing Protocol (AFP) routines + lib/afpc - AFP client libraries + lib/xenix - compatibility routines for XENIX use + etc - support programs: only atis - support program for NBP + extras - code and materials not necessarily related to AppleTalk + samples - sample programs: allow simple interaction with lw, appleshare + server, etc. See README there. + contrib - contributed programs + support - alterative LAP delivery support, Native EtherTalk, + Kernel AppleTalk and UAB. + applications - main applications. + +The following programs in applications are in regular use at Columbia +and are a main part of the reason we work on CAP: + papif - UNIX lpd "input" filter for spooling to appletalk + - also includes sample "output" filter and printcap entry + - Note: this is a very bare bones filter + lwsrv - Simple LaserWriter spooler suitable for extension + aufs - AppleTalk Filing Protocol Unix File Server + + NOTE: You must have the AppleShare 1.1 or 2.0 client code installed in + your Macintosh to use this. You must obtain this from Apple - + we do not and do not plan to supply this. The client code is + a lot of work and Apple's already done an excellent job here. + +Bug Reports +----------- + +Send bug report, comments, etc. to cap@munnari.OZ.AU or uunet!munnari!cap + +Notes +----- +Hasn't been througly checked out on any system except Ultrix 2.0 & SunOS. +It it known to have run or should be able to run under: BSD 4.2, BSD +4.3, Ultrix 1.0, 1.1, 1.2, 2.2, Sun OS 3.2 or higher, Pyramid's Unix +under the BSD universe, ACIS 4.2 or 4.3 for the IBM RT PC, A/UX, HP-UX +for the series 9000 (release 6.0), Convex Unix V6.1, Sequents, IBM AIX +on the RISC 6000, Silicon Graphics IRIS/IRIX, HP/Apollo Domain (BSD), +OSF/1 Alpha, 386/BSD, FreeBSD and the Encore Multimax. + +LAP - will probably never be implemented +DDP - don't try to use it directly +Documentation - in shorter supply than it should really be + + +TODO list +--------- +a) Complete NBP - completed. +b) Complete PAP - completed. +c) Complete ATP - completed. +d) Complete DDP - essentially completed, but some minor parts missing. +e) Complete ASP - completed. +f) Start AFP - client side needs to be redone, server side okay. +g) Start ZIP work. KIP modified to allow under rev 1/88. +h) Start RTMP work. Not need under KIP. +i) miscellanous other fixes and cleanup + + +Credits +------- + +Thanks to the User Services staff at Columbia University Center for +Computing Activies for patiently testing all the broken software that +was foisted on them as "working" with special thanks going to: + Rob Cartolano for testing Aufs beyond the call of duty + Alan Crosswell for making papif die more than anyone else and + letting me use his RT. + Lisa Covi and Jeff Eldredge for living with the software in + our Mac MicroLab + Mark Kennedy, Tom Chow, and Richard Sacks for giving Charlie + the support and time to work on CAP +and Father Larry "Mac" McCormick from the Columbia University +Macintosh Users Group for his inspiration and support. + +And to the following list of people for their +support, help, commentary, and bug fixes: + Bill Croft, SUMEX, Stanford University + Janet Tornow, Apple Computer + Dan Tappan, Bolt Beranek and Newman + Rakesh Patel, Rutgers University + Charles Hedrick, Rutgers University + Robert Elz, University of Melbourne, Australia + Dan Sahlin, Swedish Institute of Computer Science, Sweden + Scooter Morris, Genentech + Mike Byron, Adobe Systems Incorporated + Tom Mallory, Adobe Systems Incorporated + Phil Farrell, School of Earth Sciences, Stanford University + Mark Davies, VUW, New Zealand + Roy Smith, Public Health Research Institute, NYC + Ritch Ruff, Oregeon State + Dan Lanciani, Harvard University + Ravinder (Rob) Chandhok, Carnegie Mellon University + Dwight Mckay, Purdue University + Steve Fram, CITI, University of Michigan + Paul Campbell, Unisoft + Edward Moy, WSSG, University of California at Berkelely + Tharen Debold, Georgia Tech + Jim Guyton, The Rand Corporation + and any we might accidently left out of this list +our thanks! + + +Further CAP 6.0 thanks to + William Roberts, Queen Mary & Westfield College, UK + Edward Moy, University of California at Berkeley + Steve P. Andrewartha, University of Tasmania + Tom Evans, Webster Computer Corp. + Phil Budne, Shiva + Rakesh Patel, Rutgers University + Chip Salzenberg, Teltronics/TCT + Dan Oscarsson, Lund Institute of Technology, Sweden + Bridget Rogers, University of MN, Duluth + Matthew Lewis, University of Amsterdam + Max Tardiveau, University of St. Thomas +and lots of nice people at the University of Melbourne, Australia. diff --git a/applications/Makefile.m4 b/applications/Makefile.m4 new file mode 100644 index 0000000..9c5a3ef --- /dev/null +++ b/applications/Makefile.m4 @@ -0,0 +1,29 @@ + + +all: + (cd lwsrv; make) + (cd papif; make) + (cd aufs; make) + +install: + (cd lwsrv; make install) + (cd papif; make install) + (cd aufs; make install) + +clean: + -(cd lwsrv; make clean) + -(cd papif; make clean) + -(cd aufs; make clean) + +spotless: + -rm -f *.orig Makefile makefile + -(cd lwsrv; make spotless) + -(cd papif; make spotless) + -(cd aufs; make spotless) + +dist: + @cat todist + @(cd papif; make dist) + @(cd lwsrv; make dist) + @(cd aufs; make dist) + diff --git a/applications/README b/applications/README new file mode 100644 index 0000000..0faab0b --- /dev/null +++ b/applications/README @@ -0,0 +1,12 @@ +Installation notes + +lwsrv - lwsrv is a spooling agent - it pretends to be a LaserWriter. + See lwsrv/README for installation notes. + +aufs - aufs is a Appletalk Filing Protocol Unix based File Server + (AppleShare compatible) + +papif - contains a set of programs that can be "plugged" into the + Berkeley lpd to allow printing to a laserwriter from unix + via appletalk. See papif/README for installation notes. + diff --git a/applications/aufs/INSTALLATION b/applications/aufs/INSTALLATION new file mode 100644 index 0000000..eb155da --- /dev/null +++ b/applications/aufs/INSTALLATION @@ -0,0 +1,234 @@ +Aufs Installation notes + +Note: Installation of Aufs assumes you have the CAP libraries already +installed. + +Aufs installation on BSD 4.2, BSD 4.3, Ultrix and Sun machines +shouldn't be that hard, but you do have to go through the various +possible defines and decide if you need them or not. Also included +here is information on the password look aside file. Finally, if you +aren't running on a machine as listed above see the section on +porting. + + +Previously, the Makefile built two versions of Aufs. The second +version was used to slow down sends from the host system. Under this +version of Aufs, you can use the "S" switch to limit the number of +packets sent. See the section Fastpath below. + + +FASTPATH +******** + +The Kinetics FastPath box MAY have problems keeping up with packets +from a 8600 or faster machine with a high speed ethernet interface. +We can't quantify which machines will have this problem (other than to +say we saw it on a 8650 with a DELUA running Ultrix 1.2 and an 8700 +with a DEBNT running Ultrix 2.0, but didn't on a MicroVax II's running +BSD 4.3 and Ultrix 2.0 with DEQNA's or a VAX750 running Ultrix 2.0 +with a DEUNA), but if you see extensive delays, then you may have this +problem. You can check things out by running aufs with the debugging +command "-a server" - if you see a long delay between "Sending reply +..." and "done", then you probably need to reduce the packet burst +number via the "-S" switch. [You can also turn on atp debugging and +watch for "incoming bitmap" messages -- if they tend to be most of the +bits, then the same holds]. The standard (and maxiumum) is 8. You'll +probably have to reduce it to one. (Note: other causes might be +problems on your ethernet, etc.). If this doesn't fix things, then +something else in your environment has problems. + +LOCKING +******** + +For a full explanation of how locking is managed, see "design.notes", +but here's the basic information. The modules that handles locking is +actually in libafp(afposlock) to allow its use by client programs that +go into Aufs volumes. + +If you have both lockf and flock available on your system, then you +are set. You can have shared writeable volumes with the following +caveat - the open fork command does not implement "deny" permissions, +thus you should probably not have shared "writable" applications +unless they are AppleShare compatible (e.g. don't write to +themselves). In addition, programs that depend on "deny" permissions +being set on data files will also cause problems (MacWrite 4.6 +actually sets a byte range lock on the files it uses (but locks for +0x7fffffff instead of using -1 for the entire file)). + +If you only have lockf on your system, then some coordination of +shared volumes is possible and Byte Range Lock will be available. +Allowing general write access to a volume is feasible, but not +recommended since setting a lock blocks. Also, files that are +read-only by one class of users and writable by others may result in +the read-only users getting corrupted data since they have to way to +tell the writers not to write because they are reading. This applies +mainly to the Desktop and finderinfo files. + +If you have flock, then you don't have byte range locks. flock is +better than lockf in dealing with shared files like the desktop and +finderinfo files - you should be able to share writable volumes as if +you had both flock and lockf except you cannot do Byte Range Locks, so +be careful (e.g. probably a bad idea to share data files). + +NFS: lockf and/or flock must be coordinated across machines with a +special daemon outside of the standard NFS protocol. If this is not +done, errors that designate problems because of "remote files system" +are ignored and the code proceeds on the assumption the lock has been +made (just like the case when one or both are not available)--but you +should know that they haven't! In particular, don't have multiple +writers. + + +DEFINES +******* + +The m4.features file in the top level CAP directory can be used to +select additional server features. Configure offers the opportunity to +customise this file at configuration time. See CAP60.README. + +Defines can also be set by changing m4.setup and reconfiguring your +makefiles. See [smartunixfinderinfo] and [aufsosdefs] in m4.setup. + +All the configurable options listed below go in "OSDEFS" in the MakeFile. + +International Character Sets +---------------------------- +Provision has now been made to handle international character sets or +rather "ascii"s that are different from what most people in the U.S. +see. The idea (and tables) come from Dan Sahlin of the Swedish +Institute of Computer Science. + +There are three major character set translation tables defined: + Swedish D47 file ends in .swe and has a type of TEXT + Swedish-Finnish E47 file ends in .fin and has a type of TEXT + ISO 8859-1 Latin 1 file ends in .latin1 and has a type of TEXT +These are only active if "FULL_NCS_SUPPORT" is defined in OSDEFS. + +In addtion, there is our standard unix to macintosh definition that +maps between carriage returns and line feeds which happens when the +file has a type of "TEXT" and a creator of "unix". This cannot be +turned off without editing the source. + +The action should really be specifiable on a per volume basis instead +of a server wide basis. + + +End of line translation +----------------------- +The automatic cr/lf translate feature for read a line a time mode can +be turned off by defining NONXLATE. + +Unix file types +--------------- +You can make Aufs "guess" the file type by turning on +"SMART_UNIX_FINDERINFO". This is not turned on by default because of +the heavy penalty involved - you must open and read every file when +getting the finderinfo (at least the first time). All this is handled +in afpudb.c which knows about a couple of different file types. You +can add icons here if you wish and we will include them in the +distribution if we like them if you send them to us. Note: this is +highly unix version dependent. + +VOLUME INFO +----------- +If you have the statfs or getmnt systems calls available on your +system, then you will be able to get information on space used and +free on a volume. The information returned is for space free on the +file system that the root directory of the volume is on. Note that +volumes may span file systems - in this case, the information will be +misleading and incorrect. To use statfs or getmnt simply define +USESTATFS or USEGETMNT respectively. + +You can define USEQUOTA if you want Aufs to return the person's quota +if it exists. + +NOTE ON SUNS (and other other system that uses the Sun quota code +instead of the Melbourne quota code) : On current versions of SunOS +(possibly all, I don't know), the quota code used differs from the +Berkeley code (which is derived from and/or is the Melbourne code) - +it is their own private version. The major external difference as far +as Aufs is concerned is that system call and some of the arguments are +different. (SunOS's system call is quotactl vs. quota in BSD). Code +under the USESUNQUOTA define masks the differences by implementing a +quota subroutine that calls quotactl with the proper arguments. On +Suns, USESUNQUOTA is the default and need not be turned on. (Systems +other than Suns that run SunOS quota code must turn on the code by +defining USESUNQUOTA). If this behavior is unwanted, then you +shouldn't define "USEQUOTA"! + +WARNING: the USESUNQUOTA code builds a table of mounted file systems - +the table length is taken from param.h as NMOUNT. If you are +compiling for another system or multiple systems with differing values +of NMOUNT, you should define MAXUFSMOUNTED to the larger of these +values. + +You can define USTAT if you have the ustat system call on your system. +However, ustat returns the free space left including the safe margin +(usually 10%) that is only writable by root. It also doesn't say how +much is used. The problem is that the first time around the query is +for the free space and total space and subsequent queries are for free +space only. This means that if space frees up, then the free space +(to the mac) goes negative. We must be able to return the real total +space to make this work right and we can't without mucho work (that +requires munging through the kernel). THIS OPTION IS NOT RECOMMENDED. + +Edward Moy has supplied us with a "server" based method of getting +volume information (that uses one server per Aufs process). It is +workable, but isn't all that efficient (or inefficient). Some method +of using this in conjuction with the ustat call should probably be +figured out. To include, use -DSIZESERVER. See sizeserver.shar in the +patches/Moy@Berkeley directory. + +PASSWORD +******** +Aufs normally uses the unix password file. This means we are limited +to 8 characters for the login name and we don't know the "real" +password. Thus, we have encrypted passwords. + +Aufs now allows a password lookaside file that will be used under the +following conditions: + o specified at startup with the -P option + o des encryption supplied (cf. afp/README) + o it is owned by root and not readable by world + +The format of this file is very rigid (and stupid). Each entry must +be formatted as (scanf format): + "USER: %s\tUNIXUSER: %s\tPASSWORD: %s" +where user is the username that must be supplied by the AFP client, +unixuser is the user name that user runs under and password is the +password to use. No comments, etc. are allowed. + + +PORTING +******* + +It is entirely possible to run Aufs under other versions of Unix than +listed before. One we have tried is hpux - a System V machine with +BSD networking extensions. There are number of problems with it +(especially in older versions (pre-6.0) where you have to supply a +rename function, etc), but it does work. Most of the configuration +options that will allow us to run on a System V machine with BSD +networking extensions are in cap/netat/sysvcompat.h (there because +some of the defines also apply to other code). + +It is worth nothing that we by-pass the problem of how to map the 32 +character files names from the Mac to the 14 character file names of +System V R2 and the 32 character file names of System V R3 by simply +not allowing names longer than the operating system limit. This limit +is encoded in afpdid.c by using the MAXNAMLEN (assumed to be in +) + +Some assumptions: + off_t is defined in and encodes the "whence" + and return values of lseek and tell. Furthermore, it must + be at least a signed double word (32 bits) in size. + groups (gid_t) is short or unsigned short + +Late breaking news: + +Some systems may have getgroups defined to return an array of type +gid_t instead of type int. This will cause problems if typeof(gid_t) +!= typeof(int) (which is generally true). To get around this problem, +define use "-DGGTYPE=gid_t" in afposdefs. + + diff --git a/applications/aufs/Makefile.m4 b/applications/aufs/Makefile.m4 new file mode 100644 index 0000000..d921121 --- /dev/null +++ b/applications/aufs/Makefile.m4 @@ -0,0 +1,193 @@ +CFLAGS=cflags() bigcflags() specialcflags() +I=includedir() +# +# SEE INSTALLATION for documentation +# + +# valid are NONXLATE,FULL_NCS_SUPPORT,USECHOWN +# USESTATFS or USEGETMNT +# USEQUOTA or USESUNQUOTA +# and GGTYPE="gid_t" +OSDEFS=aufsosdefs() +AFPLIB=libafp() +CAPLIB=libcap() + +# for other libraries (like BSD on hpux) +SLIB=libspecial() + +# for Rutgers +RULIB=libru() + +# used mainly for debugging +CAPFILES= + +# aufs.c definitions: USEVPRINTF - use vprintf in logging +AUFSDEFS=ifdef([usevprintf],[-DUSEVPRINTF ]) + +# to get "more" information about files with a speed penalty +# Also, is specific to 4.2 BSD. May not work on some machines +ifdef([smartunixfinderinfo],[],[#])AFPUDB=-DSMART_UNIX_FINDERINFO + +#For hpux (you have you may need to supply a routine that does rename) +# (Other limitations apply!!!!) +# RENAME=rename.o + +# make sure that you define point getopt to att_getopt.o if your system +# doesn't have it builtin +GETOPT=ifdef([needgetopt],[needgetopt]) + +# This encodes the assumed location of certain directories +EXTRAS=../../extras +# Set the following approriately +DESTDIR=capsrvrdestdir() + +# +# End of configurable options +# +SRCS=afpos.c afpvols.c afpfile.c afpdir.c afpfork.c \ + afpmisc.c afpserver.c aufsicon.c abmisc2.c \ + afpdt.c afpdid.c afposenum.c afpavl.c \ + afposfi.c afpgc.c afppasswd.c afposlock.c aufsv.c \ + afpudb.c afposncs.c afpspd.c afpfid.c afpdsi.c aufscicon.c +OBJS=afpos.o afpvols.o afpfile.o \ + afpmisc.o afpserver.o aufsicon.o abmisc2.o \ + afpdt.o afpdir.o afpfork.o afpdid.o afposenum.o afpavl.o \ + afposfi.o afpgc.o afppasswd.o aufsv.o \ + afpudb.o afposncs.o afpspd.o afpfid.o afpdsi.o aufscicon.o +SYMLINKS=att_getopt.c + +all: aufs sizeserver afpidsrvr afpidlist afpidtool + +aufs: aufs.o $(OBJS) $(CAPFILES) ${RENAME} $(GETOPT) + ${CC} $(LFLAGS) -o aufs aufs.o $(OBJS) $(CAPFILES) ${RENAME} \ + $(GETOPT) ${AFPLIB} ${CAPLIB} ${SLIB} ${RULIB} + +sizeserver: sizeserver.o + ${CC} ${LFLAGS} -o sizeserver sizeserver.o ${SLIB} ${RULIB} + +sizeserver.o: sizeserver.c sizeserver.h + ${CC} ${OSDEFS} ${CFLAGS} -c sizeserver.c + +afpidsrvr: afpidsrvr.c ../../lib/afp/afpidaufs.h + ${CC} ${OSDEFS} ${CFLAGS} -o afpidsrvr afpidsrvr.c \ + ${GETOPT} ${AFPLIB} ${SLIB} + +afpidlist: afpidlist.c ../../lib/afp/afpidaufs.h + ${CC} ${OSDEFS} ${CFLAGS} -o afpidlist afpidlist.c \ + ${GETOPT} ${AFPLIB} ${SLIB} + +afpidtool: afpidtool.c ../../lib/afp/afpidaufs.h + ${CC} ${OSDEFS} ${CFLAGS} -o afpidtool afpidtool.c \ + ${GETOPT} ${AFPLIB} ${SLIB} + +newver: + /bin/sh aufs_vers.sh `cat aufs_vers` aufs_vers aufsv.c + make all + +aufsv.c: aufs_vers + /bin/sh aufs_vers.sh `cat aufs_vers` useold aufsv.c + +clean: + -rm -f *.o aufs sizeserver afpidsrvr afpidlist afpidtool ${SYMLINKS} + +spotless: + -rm -f *.o *.orig aufs sizeserver afpidsrvr afpidlist afpidtool \ + ${SYMLINKS} Makefile makefile + +lint: aufs.c $(SRCS) + lint aufs.c $(SRCS) + +install: aufs sizeserver + -strip aufs + ifdef([sysvinstall],[install -f $(DESTDIR) aufs], + [${INSTALLER} aufs $(DESTDIR)]) + -strip sizeserver + ifdef([sysvinstall],[install -f $(DESTDIR) sizeserver], + [${INSTALLER} sizeserver $(DESTDIR)]) + -strip afpidsrvr afpidlist afpidtool + ifdef([sysvinstall],[install -f $(DESTDIR) afpidsrvr afpidlist afpidtool], + [${INSTALLER} afpidsrvr afpidlist afpidtool $(DESTDIR)]) + +dist: + @cat todist + +att_getopt.o: att_getopt.c + +att_getopt.c: + ln -s ${EXTRAS}/att_getopt.c + +afpos.o: afpos.c + ${CC} ${OSDEFS} ${CFLAGS} -c afpos.c + +afposncs.o: afposncs.c + ${CC} ${OSDEFS} ${CFLAGS} -c afposncs.c + +afpudb.o: afpudb.c + ${CC} ${CFLAGS} ${AFPUDB} -c afpudb.c + +aufs.o: aufs.c + ${CC} ${OSDEFS} ${CFLAGS} ${AUFSDEFS} -c aufs.c + +afpserver.o: afpserver.c + ${CC} ${OSDEFS} ${CFLAGS} ${AUFSDEFS} -c afpserver.c + +# Dependencies +afpos.o: afpos.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h \ + afpvols.h $I/netat/afpcmd.h +afpudb.o: afpudb.c $I/netat/appletalk.h afpudb.h +afpfork.o: afpfork.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpntoh.h +afpdir.o: afpdir.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpntoh.h +afposfi.o: afposfi.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h afpgc.h afpudb.h +afpvols.o: afpvols.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpvols.h \ + afpntoh.h +afpfile.o: afpfile.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpntoh.h +afpmisc.o: afpmisc.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h +afpserver.o: afpserver.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpntoh.h +aufsicon.o: aufsicon.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h +afpcmd.o: afpcmd.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h $I/netat/afpcmd.h +abmisc2.o: abmisc2.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h \ + $I/netat/afpcmd.h +afpdt.o: afpdt.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h \ + $I/netat/afp.h $I/netat/afpcmd.h \ + afpvols.h afpdt.h afpavl.h \ + afpntoh.h afpudb.h +afpdid.o: afpdid.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h +afposenum.o: afposenum.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + afpdt.h afpavl.h +afppacks.o: afppacks.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h $I/netat/afpcmd.h +afpavl.o: afpavl.c afpavl.h +afperr.o: afperr.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h +afpgc.o: afpgc.c afpgc.h +afppasswd.o: afppasswd.c $I/netat/sysvcompat.h afppasswd.h +afposncs.o: afposncs.c $I/netat/appletalk.h $I/netat/afp.h \ + afposncs.h afps.h +afpdsi.o: afpdsi.c $I/netat/appletalk.h ../../lib/cap/abasp.h \ + afpdsi.h diff --git a/applications/aufs/NOTES b/applications/aufs/NOTES new file mode 100644 index 0000000..1f2e409 --- /dev/null +++ b/applications/aufs/NOTES @@ -0,0 +1,172 @@ +What does AUFS do? + +Through the use of the AFP protocol Aufs maps the macintosh file +system into the Unix file system. + +Things to note about Aufs: + +User info: + o Resource Fork, and finder information are stored in subdirectories + (.resource, .finderinfo). The data fork is stored as is. For + example the file "dir:Hey" on the mac is stored as three files + dir/Hey, dir/.resource/Hey, and dir/.finderinfo/Hey. + + o Login requires a username and password. The password is verified + and no more than 8 chars (AFP spec). This is your unix user name + and passowrd and is transmitted in clear text. You will not be + able to login if your unix password is longer than 8 chars. + Also, no randnum password encryption is used due to the lack of a + key on the unix side :-(. (Note: there is a mechanism to allow a + "lookaside password file so you can have long user ids, encrypted + passwords, etc".) + + o Volumes are defined by an "afpvols" file. The vols file contains + lines with the volume's name, the path, and an optional password. + The root directory of the volume is specified by the path. (New: + Aufs version 3 will search for the file afpvols first and then + .afpvols). + + o When a user logs into the server the user's home directory is + searched for an afpvols file. The names in this file can then + be mounted by the Mac client. See the sample afpvols file "afpvols". + + o If the user does not have a afpvols file then their home + directory will be added as a volume. + + o A server wide vols file can be specified on the aufs command + line. The volumes in this file are always seen by clients + and could contain general (maybe read-only) mac utilities + and files. + + o Case does not matter on Mac filenames, to make Aufs implement this + feature, define NOCASEMATCH. Beware of the performance degradation. + + o Since unix file names may not contain 8 bit chars and slash, + Aufs translates special characters in file names into colon + followed by two hex digits. + + o Unix files are given the type "TEXT" and creator "unix" and the + standard unix end of line "linefeed" is translated to a "carriage + return" when sending to the mac and vice versa. + + o National character sets may be defined using afposncs.c -- see + INSTALLATION and design.notes and afposncs.c + + o Since mac file names may not be longer than 31 chars, Aufs + simply skips long file names on the unix side. + + o Two files are maintained by Aufs for the desktop database, + these are .IDeskTop and .ADeskTop. .IDeskTop stores icon + information, .ADeskTop stores APPL information. Both of + these files are created in the volumes root directory. + + o Bytes used, and bytes free on a volume are set to arbitrary + values (unless you are running a machine with "statfs" or "getmnt"). + + o Depending on your system, there may be no file or desktop locking. + Share writable volumes at your own risk in these situations. (cf. + INSTALLATION for information). + + o Aufs will follow symbolic links for directories and files. + However, for directories, Aufs will not permit more than about 4 + symbolic links to be followed in any path. When the limit is + reached, the symbolic links to directories will not appear to be + there. However, be careful - you can do really strange things with + symbolic links. + +Admin info: + + o Aufs disassociates itself when run without debugging turned on. + o Aufs forks a new child process for each session. + o For other bugs, problems, etc. see todo and design.notes + o See the design.notes for info on protections. + o You cannot run many programs on a write locked directory/disk. + o Be careful when moving volumes around: tar has a very small limit + on path names. rdist and dump work well though. + +What Aufs does not do? +---------------------- + +It does not handle the problem of 14 character file names under System +V - file names are simply truncated. + +Known problems +-------------- + +Here's the big one: we finally realized that the .ADeskTop, .IDeskTop +and .finderinfo files are NOT written in network order. This means +you can't transport directly between machines with different byte +orders! (Not tar, dump, or NFS). Aufs version 3 will destroy the old +desktop files. Aufs version 3 will also rewrite the old .finderinfo +files when it sees them. To ensure things are okay, rebuild your +desktop. + +Both DeskTop files grow without bounds. The only way to prune them is +to delete then and rebuild the desktop from the Mac. + +The Applications mapping database can quickly get out of sync with +reality - not enough information is stored to keep in line (and even +we did store enough it would be costly to recover). Problem comes +when you start moving directories holding Applications around - it may +or may not work out - remember though, you can alway rebuild the +desktop (painful as it might be..). + +The file creator "unix" and file type "TEXT" are not registered with +Apple. + +read/writes and many other operation are blocking - this may cause +problems! However, making them run async is a lot of work. The +"solution" is to stay away from things that might block for a long +long time - e.g. ttys. For the most part though, it wouldn't cause +problems. + +Aufs uses a complete path name specification to deal with files in +general. When it exceeds the maximum path name on a system, this will +cause serious problems - Aufs should really enforce this restriction +or remove it, but it doesn't. + +Unix systems keep three dates on files: ctime - last status change +time, mtime - last modification time, and atime - last access time. +Mac wants modification and creation. Modification is really later of +mtime and ctime. Creation time is unknown and we give the earliest of +ctime, mtime and atime to give an approximation. + +How to Run +---------- + +Aufs can be run with no arguments in which case it registers the +name "Hostname Aufs:AFPServer@*." + +A logfile is created as "object".log in your currently connected +directory. + +If you are not running aufs from root, then other users will not +be able to login since it tries to do a setuid, etc. + +Other useful options are: + + -a for AFP debugging by level (or setenv AUFSDEBUG): + All DeskTop Directory File Fork OS Server Volume debug + The option "debug" disables forking among other things. + -t for packet traces (or setenv AUFSTRACE): + [I|O|B]CmdName + -n for setting the server's name + -V volfile - set the server wide volumes file + -U # - set the maximum number of asp/afp sessions allowed + (default is 10) + -G to specify an id to use as "" (disallowed o.w.) + -P specify an auxillary password file to override the standard one + (this is a mess) + -c specify directory to put coredumps into (note: if you specify + this, be sure to give absolute path names for -P) + -s for statistics after run + +Example: aufs -t 'bdelete irename' -a 'file fork dir' -s -n Billy + +Above example would trace delete packets on both input and output, +rename packets on input only. File, fork and directory routines will +print their own information. Statistics will be printed at the end of +the run. The server will register as "Billy:AFPServer@*". + +See the man page in cap/man for more information. + diff --git a/applications/aufs/README b/applications/aufs/README new file mode 100644 index 0000000..cd6b1f4 --- /dev/null +++ b/applications/aufs/README @@ -0,0 +1,59 @@ + + AUFS - AppleTalk to Unix File Server + + o RELEASE NOTES + o Aufs, version 3.00, Feb. 1988 + +Introduction +------------ + +Aufs provides file service for a Mac client running AppleShare software. +Aufs implements most of the AppleTalk Filing Protocol, as specified by +protocol version 1.1, February 17, 1987. AFP 2.0 is also supported, except +for the (optional) password change feature. It is possible to add code for +this, but is dangerous to use with clear text passwords across networks. + +Though this version has been extensively tested, but definitely +contain bugs - it is far to big not to. + +Installation +============ +See INSTALLATION in this directory for notes on installation. + + +What Aufs Does +============= +See the Aufs man page and see NOTES in this directory + +The actual design parameters are mapped in design.notes. + + +MAJOR CHANGES since Version 2.0: + +Here's the big one: we finally realized that the .ADeskTop, .IDeskTop +and .finderinfo files are NOT written in network order. This means +you can't transport directly between machines with different byte +orders! (Not tar, dump, or NFS). Aufs version 3 will destroy the old +desktop files. Aufs version 3 will also rewrite the old .finderinfo +files when it sees them. To ensure things are okay, rebuild your +desktop. + +Accepts .afpvols if no afpvols. + +Provision has now been made to handle international character sets or +rather "ascii"s that are different from what most people in the U.S. +see. The idea (and tables) come from Dan Sahlin of the Swedish +Institute of Computer Science. + +Turning on SMART_FINDERINFO in afpudb.c will yield more information; +however, it is unix variant (BSD) dependent and slows things down +considerably. + +Simple minded shutdown mechanism added. + +Disallow "." and ".." as file names. + +Kinetics Box too slow code dropped in favor of settable values. + +Update to use sun quota system properly + diff --git a/applications/aufs/abmisc2.c b/applications/aufs/abmisc2.c new file mode 100644 index 0000000..5d01ff7 --- /dev/null +++ b/applications/aufs/abmisc2.c @@ -0,0 +1,83 @@ +/* + * $Author: djh $ $Date: 91/02/15 21:05:37 $ + * $Header: abmisc2.c,v 2.1 91/02/15 21:05:37 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * abmisc2.c - miscellaneous, but nevertheless useful routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * + * Edit History: + * + * March 1987 Schilit Created. + * + */ + +#include +#include +#include +#include +#include +#include + + +/* Should be put in abnbp.c */ +/* cck - is server dependent - don't want in libraries */ + +/* + * OSErr SrvrRegister(int skt, char *name, char *type, char *zone) + * + * Register the name and socket using NBP. + * + * If type is NULL then 'name' is a compound name in the form "name:type@zone" + * otherwise 'name', 'type' and 'zone' point to the respective parts of the + * name. + * + */ + +#define SRV_RTI (sectotick(1)) /* retransmit interval is 4 seconds */ +#define SRV_RTC 5 /* retransmit count */ + + +OSErr +SrvrRegister(skt,name,type,zone, en) +int skt; +char *name,*type,*zone; +EntityName *en; +{ + nbpProto nbpr; + NBPTEntry nbpt[1]; + int err; + + if (type == NULL) /* if no type then compound name */ + create_entity(name,en); /* create entity */ + else { /* otherwise already split */ + strcpy(en->objStr.s,name); + strcpy(en->typeStr.s,type); + strcpy(en->zoneStr.s,zone); + } + + nbpr.nbpAddress.skt = skt; + nbpr.nbpRetransmitInfo.retransInterval = SRV_RTI; + nbpr.nbpRetransmitInfo.retransCount = SRV_RTC; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + nbpr.nbpDataField = 1; + nbpr.nbpEntityPtr = en; /* entity */ + return(NBPRegister(&nbpr,FALSE)); /* register the name */ +} + +OSErr +SrvrShutdown(en) +EntityName *en; +{ + return(NBPRemove(en)); +} + + diff --git a/applications/aufs/afpavl.c b/applications/aufs/afpavl.c new file mode 100644 index 0000000..e0b6acd --- /dev/null +++ b/applications/aufs/afpavl.c @@ -0,0 +1,316 @@ +/* + * $Author: djh $ $Date: 91/02/15 21:05:43 $ + * $Header: afpavl.c,v 2.1 91/02/15 21:05:43 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * afpavl.c - Appletalk Filing Protocol AVL Tree Management + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * + * Edit History: + * + * Mar 30, 1987 Schilit Created, based on older versions + * + * + */ + + +#include +#include "afpavl.h" + +/* + * The compare routines for the following MUST be used as: + * compare(userdata, node->userdata) + * This is because some of the callers may want to "cheat" on copying + * data and only send the key as the userdata. Since AVLInsert returns + * the AVLNode, the user data there can be modified after the fact + * if the userdata passed is not quite what we wanted (was only a search + * key) + */ +#define TRUE 1 +#define FALSE 0 + +AVLNode * +AVLLookup(r,udata,compar) +AVLNodePtr r; /* root, start of search */ +AVLUData *udata; /* check for this user data */ +int (*compar)(); /* comparison routine */ +{ + int cmp; + + while (r != NILAVL) { /* until we runnout... */ + cmp = (*compar)(udata,r->b_udata); /* compare the keys */ + if (cmp < 0) /* key < r */ + r = r->b_l; /* move left */ + else if (cmp > 0) /* key > r */ + r = r->b_r; /* move right */ + else /* key = r */ + return(r); /* return the node */ + } + return(NULL); /* not found, return null */ +} + +AVLNode * +AVLNew(udata) +AVLUData *udata; +{ + AVLNode *new; + + new = (AVLNode *) malloc(sizeof(AVLNode)); + new->b_bf = 0; /* init balance factor */ + new->b_l = /* and left, right pointer */ + new->b_r = NILAVL; /* fields in new node */ + new->b_udata = udata; /* install user data */ + return(new); /* and return new ptr */ +} + +/* + * int AVLInsert(AVLNode **root, AVLUData *udata, int (*compar)()) + * + * Create a new node with the user's data attached to it (AVLUData). + * Use the compar routine to decide where this entry goes in the + * tree. Always returns user data of node inserted or found. + * + * Rewrote to match closer to Knuth version: cf. Searching and Sorting, + * The Art of Computer Programming, Volume 3, pp. 455-457. + * + */ + +AVLNode * +AVLInsert(root,udata,compar) +AVLNode **root; /* handle on root node */ +AVLUData *udata; /* user data for new node */ +int (*compar)(); /* comparison routine */ +{ + AVLNodePtr t,s,p,q,r; + int bf,cmp; /* balance factor */ + + if ((p = *root) == NILAVL) { /* check for empty tree */ + *root = AVLNew(udata); /* if so then insert and */ + return(*root); /* return... */ + } + +/* + * p move down the tree to locate the insertion + * point for new. + * + * s is the node to rebalance around: the closest node to new which + * has a balance factor different from zero. t is the father of s. + * If t stays null then we want to rebalance around "root" + */ + + /* A1 */ + t = NILAVL; + s = p; + + do { + /* A2 */ + if ((cmp = (*compar)(udata,p->b_udata)) == 0) + return(p); + if (cmp < 0) { /* comparison base */ + /* A3 */ + if ((q = p->b_l) == NILAVL) { /* go left unless NIL */ + p->b_l = q = AVLNew(udata); /* if nil, then insert */ + break; /* and goto rest of code */ + } + } else { /* cmp > 0 */ + /* A4 */ + if ((q = p->b_r) == NILAVL) { /* go right unless NIL */ + p->b_r = q = AVLNew(udata); + break; + } + } + if (q->b_bf != 0) { + t = p; /* new rebalance point */ + s = q; + } + p = q; + } while (1); + + /* A6 */ + /* adjust the balance factors of nodes on path from next of s to q */ + + /* remember bf at balance point */ + bf = ((*compar)(udata,s->b_udata) < 0) ? -1 : 1; + if (bf < 0) /* take the first step... */ + r = p = s->b_l; /* to get past the balance point */ + else + r = p = s->b_r; + + /* follow p down to q and set each balance factor to 1 or -1. + * no addition is required since balance factors of all nodes after + * s are zero (s is the closest non zero node). + */ + + while (p != q) { /* follow the path from p to new */ + /* set balance factors for nodes */ + cmp = (*compar)(udata,p->b_udata); +#ifdef DEBUG + if (cmp == 0) { /* p == q !! */ + fprintf(stderr, "Can't happen p == q in AVLInsert A6\n"); + exit(9999); + } +#endif + p->b_bf = cmp < 0 ? -1 : 1; /* adjust */ + p = cmp < 0 ? p->b_l : p->b_r; /* and follow correct path */ + } + + /* A7 */ + /* check if the tree is unbalanced */ + + if (s->b_bf == 0) { /* at balance point check bf */ + s->b_bf = bf; /* was balanced now only 1 off */ + return(q); /* tree is ok (return(q)) */ + } + + if (s->b_bf == -bf) { /* at balance point did we improve? */ + s->b_bf = 0; /* yes, set balance factor to 0 */ + return(q); /* and return (q - new node ) */ + } + + /* balance factor went to -2 or +2, rebalance the tree */ + if (r->b_bf == bf) { /* single rotation */ + p = r; + if (bf < 0) { + s->b_l = r->b_r; + r->b_r = s; + } else { + s->b_r = r->b_l; + r->b_l = s; + } + s->b_bf = r->b_bf = 0; /* subtree is now balanced */ + } else { + /* double rotation */ + if (bf < 0) { + p = r->b_r; + r->b_r = p->b_l; + p->b_l = r; + s->b_l = p->b_r; + p->b_r = s; + } else { + p = r->b_l; + r->b_l = p->b_r; + p->b_r = r; + s->b_r = p->b_l; + p->b_l = s; + } + if (p->b_bf == 0) + s->b_bf = r->b_bf = 0; + else { + if (p->b_bf == bf) { /* b(p) = a */ + s->b_bf = -bf; + r->b_bf = 0; + } else { /* b(p) = -a */ + s->b_bf = 0; + r->b_bf = bf; + } + } + p->b_bf = 0; + } + + /* A10 */ + /* Finishing touch: s points to the new root and t points to the + * father of the old root of the rebalanced subtree. Make t point + * to the head of the balanced subtree. + */ + + if (t == NILAVL) /* rebalance around the root node? */ + *root = p; /* yes, set a new root */ + else { + if (s == t->b_r) /* did we rebalance on right? */ + t->b_r = p; /* yes, then set new subtree there */ + else + t->b_l = p; /* else rebalanced on left */ + } + return(q); /* return new node */ +} + +#ifdef notdef +AVLNode * +AVLLookN(r,udata,n,comp) +AVLNode *r; +AVLUData *udata; +int *n; +int (*comp)(); +{ + AVLNode *s; + int cmp; + + while (r != NILAVL) { /* until we runnout... */ + cmp = (*comp)(udata,r->b_udata); /* compare the keys */ + if (cmp < 0) /* key < r */ + r = r->b_l; /* move left */ + else if (cmp > 0) /* key > r */ + r = r->b_r; /* move right */ + else /* key = r */ + break; /* found a match */ + } + if (r == NILAVL) /* check if runnout */ + return(NILAVL); /* yes... then not found */ + if (--(*n) <= 0) /* no... found something, decr n */ + return(r); /* it was the one we wanted */ + s = AVLLookN(r->b_l,udata,n,comp); /* else check left subtree */ + if (s != NILAVL) /* found it there? */ + return(s); /* return the node */ + return(AVLLookN(r->b_r,udata,n,comp)); /* else check right subtree */ +} + +/* + * AVLLookupNth(AVLNode *root, AVLUData *udata, int n, int (*compare)()) + * + * Call the specified comparison routine to locate the n'th node + * that matches user data. The primary key used by compare must + * match the primary key used by AVLInsert. + * + */ + +AVLUData * +AVLLookupNth(r,udata,n,comp) +AVLNodePtr r; +AVLUData *udata; +int n; +int (*comp)(); +{ + AVLNode *s,*f; /* subtree */ + int cmp; + + f = NILAVL; + while (r != NILAVL && f == NILAVL) { /* locate first matching */ + cmp = (*comp)(udata,r->b_udata); /* compare the keys */ + if (cmp < 0) /* key < r */ + r = r->b_l; /* move left */ + else if (cmp > 0) /* key > r */ + r = r->b_r; /* move right */ + else /* key = r */ + f = r; /* found it set f exits loop */ + } + if (f == NILAVL) + return((AVLUData *) 0); /* nothing found */ + s = AVLLookN(f,udata,&n,comp); /* call locator routine */ + if (s != NILAVL) /* did we find it? */ + return(s->b_udata); /* yes, return user data */ + return((AVLUData *) 0); /* return failure */ +} + +#endif + +void +AVLMapTree(root,pnode,uhdl) +AVLNodePtr root; +void (*pnode)(); +char *uhdl; +{ + if (root == NILAVL) + return; + (*pnode)(root->b_udata,uhdl); /* call with udata */ + if (root->b_l != NILAVL) + AVLMapTree(root->b_l,pnode,uhdl); /* do left nodes */ + if (root->b_r != NILAVL) + AVLMapTree(root->b_r,pnode,uhdl); /* do right nodes */ +} diff --git a/applications/aufs/afpavl.h b/applications/aufs/afpavl.h new file mode 100644 index 0000000..7198807 --- /dev/null +++ b/applications/aufs/afpavl.h @@ -0,0 +1,38 @@ +/* + * $Author: djh $ $Date: 91/02/15 21:05:50 $ + * $Header: afpavl.h,v 2.1 91/02/15 21:05:50 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * afpavl.h - Appletalk Filing Protocol AVL Tree Management definitions + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * + * Edit History: + * + * Mar 30, 1987 Schilit Created, based on older versions + * + * + */ + +typedef char AVLUData; /* baggage for user */ + +typedef struct avl_node { + short b_bf; /* balance factor 0, 1, or -1 */ + struct avl_node *b_l; /* left child */ + struct avl_node *b_r; /* right child */ + AVLUData *b_udata; /* user data (baggage) */ +} AVLNode, *AVLNodePtr; + +#define NILAVL ((AVLNodePtr) 0) + +AVLNode *AVLInsert(); +AVLNode *AVLLookup(); +void AVLPrintTree(); +void AVLMapTree(); + diff --git a/applications/aufs/afpdid.c b/applications/aufs/afpdid.c new file mode 100644 index 0000000..f468a0b --- /dev/null +++ b/applications/aufs/afpdid.c @@ -0,0 +1,1190 @@ +/* + * $Author: djh $ $Date: 1996/04/27 12:03:04 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpdid.c,v 2.13 1996/04/27 12:03:04 djh Rel djh $ + * $Revision: 2.13 $ + * + */ + +/* + * afpdid.c - Directory id routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * December 1990 djh tidy up for AFP 2.0 + * + */ + +#include +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif _TYPES +#include +#include +#include +#include "afps.h" + +#ifdef USEDIRENT +# include +#else USEDIRENT +# ifdef xenix5 +# include +# else xenix5 +# include +# endif xenix5 +#endif USEDIRENT + +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +/* + * + * EtoIdirid() - external to internal translation of directory id. + * ItoEdirid() - internal to external translation of directory id. + * EtoIfile() - return info on file + * Idirid() - given a path, return an internal directory id. + * Ipdirid() - given a directory id, return parent's directory id. + * Idndirid() - given a directory id and a directory name, return + * the directory id of the "directory", creating did if + * non-existant + * Idrdirid() - remove the specified directory + * pathstr() - given a directory id, return a path. + * ppathstr() - given a "root" dirid and dirid, return a path relative + * to root dirid + * InitDID() - called once to init this module. + * InitDIDVol - mark the volume of a dirid + * ItoEName() - translate internal to external name + * EtoIName() - translate external to internal name + * ENameLen() - figure length of interal file name in external terms + * EModified() - mark dirid as modified + * + */ + +#define TRUE 1 +#define FALSE 0 + +private IDirP rootd; + +#ifdef FIXED_DIRIDS +/* + * We use a separate database and server to provide persistant + * external numbers. This allows the external ids to be + * common accross several instances of aufs - coincidental ones + * as well as repeated calls by the same user. The database + * is read-only from this program - a separate aufsExt server + * is used to create new entries etc. Some backup functions in + * the library allow for when the server or database are (for + * some reason) absent or not responding. However, these should + * not be used normally. The scheme below is preferable if you + * don't wish to have the server running. + * + */ + +#include "../../lib/afp/afpidaufs.h" + +#else FIXED_DIRIDS + +/* + * ExtDir structure maps external directory numbers into internal (IDir) + * pointers. It is not a good idea to simply pass back the IDir pointer + * since the mac expects forever constant ids and will remember these + * ids between logins to the server. + * + * Although we can't generate forever constant ids the ExtDir table will + * at least verify that we are not looking at a random pointer address. + * + * Failure from the table lookup generates a aeParamErr which is usually + * handled ok the mac by restarting the selection process at root dir. + * + */ + +struct { /* Maps external to internal safely */ + int xd_base; /* base edir number */ + int xd_count; /* number in use */ + IDirP *xd_idirs; /* pointers to the internal dirids */ + int xd_idsize; /* size of idirs array */ +} ExtDir; + +#endif FIXED_DIRIDS + +private char *ipathstr(); +private sdword NewEDirid(); + +/* + * nfind scans and finds whether a particular directory "dir" has a + * subdirectory by the name "nam" + * + * use a simple hash of "nam" to avoid multiple strcmp() calls + * + */ + +private IDirP +nfind(dir,nam) +IDirP dir; +char *nam; +{ + register long hash; + register char *p; + + p = nam; + hash = 0; + while (*p) { + hash <<= 2; + hash += *p++; + } + + for ( ; dir != (IDirP)0; dir = dir->next) { + if (dir->hash != hash) + continue; + if (strcmp(dir->name, nam) == 0) + break; + } + + return(dir); +} + + +/* + * Create a new directory entry under the specified parent + * (Should we check existance?) + * + */ + +private IDirP +mkdirset(parent,name) +IDirP parent; +char *name; +{ + IDirP dir; + register long hash; + register char *p, *t; + + dir = (IDirP)malloc(sizeof(IDir)); + t = dir->name = (char *)malloc((unsigned) strlen(p = name)+1); + hash = 0; + while (*p) { + hash <<= 2; + hash += (*t++ = *p++); /* copy string while making hash */ + } + *t = '\0'; + dir->hash = hash; /* remember name hash value */ + dir->pdir = parent; /* set pointer to parent in node */ + dir->subs = NILDIR; /* no children */ + if (parent != NILDIR) { /* if parent different than root */ + V_COPY(dir->volbm,parent->volbm); /* duplicate info... */ + dir->next = parent->subs; /* make us a sub dir of parent */ + parent->subs = dir; /* ... */ + } else { /* if no parent then */ + V_ZERO(dir->volbm); /* no ownership */ + dir->next = NILDIR; /* no siblings */ + } + dir->flags = 0; /* no flags yet */ + dir->modified = 0; + dir->eceidx = NOECIDX; /* no index for enum cache */ + dir->edirid = -1; /* create new external dirid */ + OSValidateDIDDirInfo(dir); /* validate directory info */ + if (parent && (dir->flags & DID_DATA) == 0) { + parent->subs = dir->next; /* unlink it */ + free(dir->name); /* nada */ + free(dir); /* nada */ + return(NILDIR); + } + if (parent) + OSValidateDIDDirInfo(parent); /* revalidate parent */ + return(dir); +} + + +IDirP +Ipdirid(dir) +IDirP dir; +{ + return(dir->pdir); /* return parent */ +} + +IDirP lastcd = NILDIR; + +/* + * return an absolute path + * +*/ +char * +pathstr(cd) +IDirP cd; +{ + static char paths[MAXPATHLEN]; + + if (lastcd == cd) /* same as last request? */ + return(paths); /* yes.. just return old paths */ + lastcd = cd; /* else set new dir */ + (void)ipathstr(cd,paths); /* and do the work... */ + return(paths); /* return new path */ +} + +private char * +ipathstr(cd,p) +IDirP cd; +register char *p; +{ + register char *t; + + /* check for root directory */ + if (cd == rootd) { + /* init path */ + *p++ = '/'; + *p = '\0'; + return(p); + } + + /* recurse on parent until root */ + p = ipathstr(cd->pdir, p); + + /* insert new path component */ + if (cd->pdir != rootd) + *p++ = '/'; + t = cd->name; + while (*t) + *p++ = *t++; + *p = '\0'; + + /* return a pointer */ + return(p); +} + +#ifdef FIXED_DIRIDS +/* + * dir path and filename concatanated + * + */ + +private char * +filpathstr(cd, name) +IDirP cd; +char *name; +{ + char *p; + int len; + + p = pathstr(cd); /* get dir path */ + len = strlen(p); + if (p[len-1] != '/') /* add / if needs */ + p[len++] = '/'; + strcpy(p+len, name); /* add given name */ + lastcd = NILDIR; /* have corrupted cd area */ + return p; +} +#endif FIXED_DIRIDS + +/* + * return a path relative to vroot + * +*/ +char * +ppathstr(vrootd,cd) +IDirP vrootd,cd; +{ + static char path[MAXPATHLEN]; + int len; + + if (cd == vrootd) /* check for root directory */ + strcpy(path,""); /* at top of tree, init path */ + else { + (void) ppathstr(vrootd,cd->pdir); /* recurse on parent until root */ + len = strlen(path); + if (cd->pdir != vrootd) /* ... */ + path[len++] = '/'; /* add path component */ + strcpy(path+len, cd->name); /* concatenate current dir to path */ + } + return(path); +} + + +/* + * void printtree(dir,start) + * + * Print the directory tree starting from dir, start is a copy + * of dir. + * + */ + +private void +printtree(dir,start) +IDirP dir,start; +{ + if (dir == NILDIR) + return; + printf("Path %s Idirid = 0x%x = %d\n",pathstr(dir),dir,dir); + printtree(dir->subs,start); + if (dir != start) + printtree(dir->next,start); +} + +/* + * void InitDID() + * + * Called once to initialize directory id mechanism. + * + * Create root directory in rootd. + * + */ +void +InitDID() +{ +#ifdef FIXED_DIRIDS + aufsExtInit(); +#else FIXED_DIRIDS + ExtDir.xd_count = 0; /* no known external dirs */ + ExtDir.xd_base = 320; /* anything will do above EROOTD */ + ExtDir.xd_idsize = 100; /* inital size of table */ + ExtDir.xd_idirs = (IDirP *) malloc(sizeof(IDirP)*100); +#endif FIXED_DIRIDS + rootd = mkdirset(NILDIR,""); /* allocate root directory */ +} + +/* + * InitDIDVol(dirid) +*/ +InitDIDVol(dirid, volid) +IDirP dirid; +int volid; +{ + V_BITSET(dirid->volbm,volid); /* remember ownership */ +} + +/* + * IDirP Idirid(char *path,char *dn) + * + * Make sure path is valid + * +*/ + +IDirP +Idirid(path) +char *path; +{ + char c, *p; + IDirP cd, ncd; + int len; + + if (*path == '\0') { /* any argument specified? */ + logit(0, "internal error: Idirid has a bad arg count\n"); + return(NILDIR); + } + + /* scan down path, creating dirids as necessary */ + if (*path != '/') { /* check if root is starting point? */ + if (DBDIR) + logit(0, "internal error: Idirid has bad starting point %s\n",path); + } + len = strlen(path); /* get length of path */ + while ((len-1) && path[len-1] == '/') /* chew off trailing /s */ + len--; + cd = rootd; + do { + while (*path == '/') /* step to start of component */ + path++,len--; + + /* find end */ + for (p = path; *p != '/' && len; p++, len--) + /* NULL */; + c = *p; /* remember char here */ + *p = '\0'; /* change to null */ + ncd = nfind(cd->subs, path); /* find component if it exists */ + if (ncd == NILDIR) + cd = mkdirset(cd, path); /* create new */ + else + cd = ncd; + *p = c; /* set back to org. */ + path = p; + } while (len && cd != NILDIR); + return(cd); /* final point */ +} + +/* + * + * IDirP Idndirid(IDirP idirid,char *dn) + * + * Return an internal directory ID for the directory "path/dn" -- + * where path specifies the parent path and dn the directory name. + * The string dn may be null in which case a directory id for path + * is returned. + * + * This routine always returns an internal directory id, creating a + * new IDir node if necessary. One of only two routine which causes + * the creation of new IDir nodes. + * + * WILL RETURN NULL IF PATH IS BAD + */ +IDirP +Idndirid(parent,dn) +IDirP parent; +char *dn; +{ + IDirP dp; + +#ifdef SHORT_NAMES + if (DBDIR && parent != NULL) + printf("Idndirid parent->name %s dn %s\n",parent->name, + (dn == NULL) ? "" : dn); +#endif SHORT_NAMES + if (dn == NULL || *dn == '\0') /* is the name specified? */ + return(parent); /* no, so return parent */ + dp = nfind(parent->subs,dn); /* see if already present */ + if (dp != NILDIR) /* yes... */ + return(dp); /* return info */ + return(mkdirset(parent,dn)); /* create new directory */ +} + +/* + * Idname(pdir, dirid) + * + * return name of directory dirid in directory pdir + * + * MODIFY return value at your own risk! + * +*/ +char * +Idname(pdir, dirid) +IDirP pdir; +IDirP dirid; +{ + register IDirP p = pdir->subs; +#ifdef SHORT_NAMES + if (DBDIR && dirid != NULL && pdir != NULL) + printf("Idname pdir->name %s, dirid->name %s\n",pdir->name,dirid->name); +#endif SHORT_NAMES + do { + if (p == dirid) + return(p->name); + p = p->next; + } while (p != NILDIR); + return(NULL); +} + +/* + * sdword NewEDirid(IDirP idir) + * + * Allocate a new unique external directory id for a newly created + * directory. The id is is used as an index into a table of internal + * directory ids. The returned external id is this index plus some + * random base number to map past EROOTD (2). + * + * If the table of internal directory pointers overgrows its bounds + * then realloc the table at a larger size. + * + */ + +#ifdef FIXED_DIRIDS +sdword +Edirid(idir) +IDirP idir; +{ + if (idir == rootd) /* shortcut */ + return(rootEid); + else { + if (idir->edirid == -1) + idir->edirid = NewEDirid(idir); + return(idir->edirid); + } +} +#endif FIXED_DIRIDS + +private sdword +NewEDirid(idir) +IDirP idir; +{ +#ifdef SHORT_NAMES + if (DBDIR && idir != NULL) + printf("NewEDirid idir->name %s\n",idir->name); +#endif SHORT_NAMES +#ifdef FIXED_DIRIDS + if (idir == rootd) + return(rootEid); /* root has no parent! */ + return(aufsExtEDirId(Edirid(idir->pdir), idir->name)); +#else FIXED_DIRIDS + { + sdword edir; + + if (ExtDir.xd_count >= ExtDir.xd_idsize) { + ExtDir.xd_idirs = (IDirP *) /* realloc larger table */ + realloc((char *) ExtDir.xd_idirs, + sizeof(IDirP)*((unsigned) ExtDir.xd_idsize+100)); + ExtDir.xd_idsize += 100; /* larger table now */ + if (DBDIR) + printf("NewEDirid: Realloc occured.\n"); + } + edir = ExtDir.xd_count; /* current counter is idx */ + ExtDir.xd_count++; /* increment count */ + ExtDir.xd_idirs[edir] = idir; /* save new handle */ + return(ExtDir.xd_base+edir); /* return base + idx */ + } +#endif FIXED_DIRIDS +} + +/* + * IDirP GetIDirid(sdword edir) + * + * Given an external ID check to see if we know about the external->internal + * mapping by verifying the edir as an index into a table. + * + * If no known mapping then return NILDIR. + * + */ + +private IDirP +GetIDirid(edir) +sdword edir; +{ +#ifdef SHORT_NAMES + if (DBDIR) + printf("GetIDirid: edir:%d\n",edir); +#endif SHORT_NAMES +#ifdef FIXED_DIRIDS + { + char *path; + + path = aufsExtPath(edir); + if (DBDIR) + printf("GetIDirid gave '%s' for %d\n", path, edir); + if (path) + return(Idirid(path)); + if (DBDIR) + printf("GetIDirid: Bad directory idx %d\n", edir); + return(NILDIR); + } +#else FIXED_DIRIDS + edir -= ExtDir.xd_base; /* subtract base */ + if (edir < 0 || /* below base? */ + edir > ExtDir.xd_count-1) { /* or above count? */ + if (DBDIR) /* then invalid */ + printf("GetIDirid: Bad directory idx %d, highest is %d\n", + edir,ExtDir.xd_count-1); + return(NILDIR); /* yes... not allocated */ + } + return(ExtDir.xd_idirs[edir]); /* return handle */ +#endif FIXED_DIRIDS +} + + +/* + * IDirP EtoIdirid(sdword dirid,word volid) + * + * Convert an external directory id (dirid) to an internal directory + * id. + * + * Internal directory ids are pointers of type IDir into the directory + * tree built and maintained by these procedures. There is only one + * directory tree, and each AFP volume contains a root id which is the + * mount point for that volume (which may be different from the + * directory trees root of "/"). + * + * There are two special external directory IDs, EROOTD (1) and + * EPROOTD (2), for the volume's root directory and parent of root + * directory. + * + * If the external id is neither of these call GetIDirid to + * do the real work. + * + */ + +IDirP +EtoIdirid(dirid,volid) +sdword dirid; +int volid; +{ + IDirP VolRootD(),idir; + + if (DBDIR) + printf("EtoIdirid: volid %d dirid:%d \n",volid,dirid); + if (dirid == EROOTD) /* check for root id */ + return(VolRootD(volid)); /* yes, so return volumes root */ + if (dirid == EPROOTD) /* check for parent of root */ + return(Ipdirid(VolRootD(volid))); /* yes, so return that too... */ + idir = GetIDirid(dirid); /* get internal dirid */ + if (idir != NILDIR && /* if directory exists and */ + V_BITTST(idir->volbm,volid)) /* is on specified volume... */ + return(idir); /* then return dirid */ + return(NILDIR); /* else return nil directory */ +} + +/* + * sdword ItoEdirid(IDirP dirid, word volid) + * + * Convert an internal directory id to an external directory id. + * + * See EtoIdirid() above. + * + */ + +sdword +ItoEdirid(dirid,volid) +IDirP dirid; +int volid; +{ + IDirP rd,VolRootD(); + +#ifdef SHORT_NAMES + if (DBDIR && dirid != NULL) + printf("ItoEdirid: dirid->name %s, volid %d\n",dirid->name,volid); +#endif SHORT_NAMES + rd = VolRootD(volid); /* fetch volumes root directory id */ + if (dirid == rd) /* converting root id? */ + return(EROOTD); /* yes, return special code */ + if (dirid == Ipdirid(rd)) /* converting parent of root? */ + return(EPROOTD); /* yes, return code for that too */ + + if (DBDIR && !V_BITTST(dirid->volbm,volid)) /* know this ownership? */ + printf("ItoEdirid: bad volid for %s\n",pathstr(dirid)); + + if (dirid->edirid == -1) { + if (DBDIR) + printf("ItoEdirid: calling NewEDirid\n"); + dirid->edirid = NewEDirid(dirid); + if (DBDIR) + printf("ItoEdirid: NewEDirid returns %d\n", dirid->edirid); + } + if ((dirid->flags & DID_VALID) == 0) /* validate info if invalid */ + OSValidateDIDDirInfo(dirid); + + return(dirid->edirid); /* else return external form */ +} + +/* + * OSErr EtoIFile(char *file, IDirP *idir, IDirP *ipdir, int *ivol + * sdword edir, word evol, byte eptype, byte *epath); + * + * Convert external arguments which specify a file/dir into internal + * handles. + * + * edir, evol, eptype and epath are the external ancestor directory + * id, the external volume id, the path type and the external path. + * + * Returned values are file (the last component of the path), idir, + * the directory handle if file is a directory, ipdir, the ancestor + * handle, and ivol the internal volume id. + * + * This is the major routine for converting volume, dir, path type, + * and path into unix form and proceeds as follows: + * + * Convert volume and ancestor directory (ivol, ipdir from evol and edir). + * + * For each component in the path, convert the component name to internal + * form and locate the component in the IDir tree. + * + * Return the last component name in file, and if this component exists + * in the IDir tree, return the handle in idir. Set ipdir to be the + * ancestor of the file. + * + * Only allow the volume root directory (dirid EROOTD) in the volume + * root parent directory (dirid EPROOTD) + * + */ +OSErr +EtoIfile(file,idir,ipdir,ivol,edir,evol,eptype,epath) +char *file; +IDirP *idir,*ipdir; +int *ivol; +sdword edir; +word evol; +byte eptype; +byte *epath; +{ + int pl; + byte efn[MAXLFLEN+1],*fn; + char c,lc; + OSErr err; + IDirP id; + IDirP iprootd, irootd; /* has parent of root and root respectively */ + + if (DBDIR) { + int i = 1; + pl = *epath; + printf("EtoIfile: ivol = %d, epath = ", *ivol); + while (pl--) { + printf("(%c)", *(epath+i)); + i++; + } + printf("\n"); + } + + *ivol = EtoIVolid(evol); /* set internal volid */ + if (*ivol < 0) + return(*ivol); /* error */ + + id = EtoIdirid(edir,*ivol); /* fetch internal id */ + if (id == NILDIR) /* exists? */ + return(aeParamErr); /* no.... */ + irootd = VolRootD(*ivol); /* get root directory internal id */ + /* assert(irootd != NULL) */ + iprootd = Ipdirid(irootd); /* and then its parent */ + pl = *epath++; /* pascal string length */ + lc = ' '; /* non-null last char */ + + for (fn=efn; pl-- > 0;) { + c = *fn++ = *epath++; /* copy the char */ + if (c == '\0') { /* found a component separator? */ + if (lc == '\0') { /* yes.... was last char also null? */ + /* yes... then want parent since we have "null,null" */ + if (id == iprootd) /* can't go above here! */ + return(aeObjectNotFound); + id = Ipdirid(id); /* okay, move to parent */ + } else /* otherwise */ + if (*efn != '\0') { /* check if we have a component */ + if (id == iprootd) { + /* only possible subdir */ +#ifdef SHORT_NAMES + if (eptype == 0x02) { + if (strcmp((char *)efn, (char *)VolName(*ivol)) != 0) + return(aeObjectNotFound); + } else { + if (strcmp((char *)efn, (char *)VolSName(*ivol)) != 0) + return(aeObjectNotFound); + } +#else SHORT_NAMES + if (strcmp((char *)efn, (char *)VolName(*ivol)) != 0) + return(aeObjectNotFound); +#endif SHORT_NAMES + id = irootd; + } else { +#ifdef SHORT_NAMES + if (DBDIR) + printf("EtoIfile, eptype 0x%x\n",eptype); + if (eptype == 0x02) { + if ((err=EtoIName(efn,file)) != noErr) /*convert e to i */ + return(err); + } + else { /*shortname*/ + if ((err=EtoIName_Short(id,efn,file)) != noErr) + return(err); + } +#else SHORT_NAMES + if ((err=EtoIName(efn,file)) != noErr) /* convert e to i */ + return(err); +#endif SHORT_NAMES + id = Idndirid(id,file); /* and move to new */ + } + } + fn = efn; /* init ptr for next component */ + } + lc = c; /* remember last char */ + if (id == NILDIR) { /* did we hit something bad? */ + printf("No such component %s\n",fn); + return(aeParamErr); + } + } + *fn = '\0'; /* tie off with a null */ + if (id == iprootd && *efn != '\0') { /* special check */ +#ifdef SHORT_NAMES + if (eptype == 0x02) { + if (strcmp((char *)efn, (char *)VolName(*ivol)) != 0) + return(aeObjectNotFound); + } else { + if (strcmp((char *)efn, (char *)VolSName(*ivol)) != 0) + return(aeObjectNotFound); + } +#else SHORT_NAMES + if (strcmp((char *)efn, (char *)VolName(*ivol)) != 0) + return(aeObjectNotFound); +#endif SHORT_NAMES + *file = '\0'; + id = irootd; + } else { +#ifdef SHORT_NAMES + if (DBDIR) + printf("EtoIfile, eptype 0x%x\n",eptype); + if (eptype == 0x2) { + if ((err=EtoIName(efn,file)) != noErr) /* convert last file component */ + return(err); + } + else { + if ((err=EtoIName_Short(id,efn,file)) != noErr) /* shortname */ + return(err); + } +#else SHORT_NAMES + if ((err=EtoIName(efn,file)) != noErr) /* convert last file component */ + return(err); +#endif SHORT_NAMES + } + + if (*file == '\0') { /* did we have a file name? */ + strcpy(file, id->name); + if (id == iprootd) /* tch, tch - don't want above here */ + return(aeObjectNotFound); + *idir = id; /* set directory name */ + *ipdir = Ipdirid(id); /* set parent id */ + } else { + /* disallow "." or ".." */ + if (file[0] == '.' && + (file[1] == '\0' || (file[1] == '.' && file[2] == '\0'))) + return(aeObjectNotFound); + *ipdir = id; /* else parent is dir itself */ + *idir = Idndirid(id,file); /* and dir is possibly the file */ + /* can only access "rootd" in parent of root directory */ + if (id == iprootd && *idir != irootd) + return(aeObjectNotFound); + } + return(noErr); +} + + +/* + * dir_link(IDirP pdir, IDirP dir) + * + * Make the directory dir a subdirectory of parent pdir. + * + */ + +private void +dir_link(pdir,dir) +IDirP pdir,dir; +{ +#ifdef SHORT_NAMES + if (DBDIR && pdir != NULL && dir != NULL) + printf("dir_link pdir->name %s, dir->name %s\n",pdir->name,dir->name); +#endif SHORT_NAMES + dir->pdir = pdir; /* set parent */ + dir->next = pdir->subs; /* link to front */ + pdir->subs = dir; + +} + +/* + * dir_unlink(IDirP pdir, IDirP dir) + * + * Remove the directory dir from it's parent pdir. + * + */ + +private void +dir_unlink(pdir,dir) +IDirP pdir,dir; +{ + IDirP prev,this; + +#ifdef SHORT_NAMES + if (DBDIR && pdir != NULL && dir != NULL) + printf("dir_unlink pdir->name %s, dir->name %s\n",pdir->name, dir->name); +#endif SHORT_NAMES + for (prev = NILDIR, this = pdir->subs; + this != NILDIR && this != dir; + prev = this, this = this->next); + + if (this == NILDIR) { /* moby trouble */ + printf("dir_unlink: dir %s not found\n",this); + return; + } + + if (prev == NILDIR) /* first on the list? */ + pdir->subs = this->next; /* yes... link to parent */ + else + prev->next = this->next; /* else link to prev */ + this->next = NILDIR; /* next no longer valid */ + this->pdir = NILDIR; /* parent no longer known */ +} + +Idrdirid(ipdir, idir) +IDirP ipdir, idir; +{ + /* simple solution - simply unlink from extdir list and "mark" */ + /* it is as invalid - set the idir's extdir bad and when used */ + /* we will "revalidate" it. Sufficient checking is done to */ + /* prevent returnning "bad" info anyway. Basically, the whole */ + /* point is to prevent mac side from getting to entries which aren't */ + /* valid - this will simply eliminate work */ +#ifdef FIXED_DIRIDS + if (idir->edirid == -1) + aufsExtDel(pathstr(idir)); + else + aufsExtDelId(idir->edirid); +#else FIXED_DIRIDS + ExtDir.xd_idirs[idir->edirid - ExtDir.xd_base] = NILDIR; +#endif FIXED_DIRIDS + idir->edirid = -1; + idir->flags &= ~DID_VALID; +#ifdef SHORT_NAMES + if (DBDIR && ipdir != NULL && idir != NULL) + printf("Idrdirid ipdir->name %s, idir->name %s\n",ipdir->name,idir->name); +#endif SHORT_NAMES +#ifdef notdef + /* until problem of "old" ones is figured out */ + /* the fix is to make all things that "stash" dirids check validity */ + /* possibly store a refcnt in it to figure when we may kill off */ + dir_unlink(ipdir, idir); /* unlink from the tree */ + ExtDir.xd_idirs[idir->edirid - ExtDir.xd_base] = NILDIR; /* set to nil */ + /* would like to delete space, but bad thing to do */ + idir->flags &= ~DID_VALID; /* mark as not valid */ + idir->subs = NILDIR; /* make sure not valid (shouldn't be) */ +#endif notdef +} + +/* + * Idmove(IDirP fpdir, char *from, IDirP tpdir, char *to) + * + * Maintains consistency in internal structures when a directory + * is renamed or moved. + * + * The directory "from" in parent fpdir is being renamed to be + * "to" in the parent tpdir. Because directory ids may not + * change we must modify the tree instead of recreating nodes. + * + */ +void +Idmove(fpdir,from,tpdir,to) +IDirP fpdir,tpdir; +char *from,*to; +{ + IDirP fdir; +#ifdef FIXED_DIRIDS + sdword toEid; + char *orig_name = NULL; +#endif FIXED_DIRIDS + +#ifdef SHORT_NAMES + if (DBFIL && fpdir != NULL) + printf("Idmove fpdir->name %s, tpdir->name %s, from %s, to %s\n",fpdir->name, tpdir->name, from, to); +#endif SHORT_NAMES + if (DBFIL) { + printf("Idmove: changing path=%s, file=%s",pathstr(fpdir),from); + if (tpdir == fpdir) + printf(" to new name %s\n",to); + else + printf(" to path=%s, file=%s\n",pathstr(tpdir),to); + } + + fdir = nfind(fpdir->subs,from); /* locate source dir */ + + if (fdir == NILDIR) { + printf("Idmove: no known directory %s\n",from); + return; + } + +#ifdef FIXED_DIRIDS + if (fdir->edirid == -1) /* will need full name later */ + orig_name = string_copy(pathstr(fdir)); +#endif FIXED_DIRIDS + + if (strcmp(from,to) != 0) { /* if different names then... */ + register long hash; + register char *p, *t; + + free(fdir->name); /* release the old name */ + t = fdir->name = (char *)malloc((unsigned) strlen(p = to)+1); + hash = 0; + while (*p) { + hash <<= 2; + hash += (*t++ = *p++); /* copy string while making hash */ + } + *t = '\0'; + /* record hash */ + fdir->hash = hash; + } + + if (fpdir != tpdir) { /* if different parents then... */ + if (nfind(tpdir->subs,to) != NILDIR) + printf("Idmove: name already exists %s\n",to); + else { + dir_unlink(fpdir,fdir); /* unlink from old parent */ + dir_link(tpdir,fdir); /* relink into new parent */ +#ifdef FIXED_DIRIDS + toEid = Edirid(tpdir); + if (fdir->edirid == -1) + aufsExtMoveId(orig_name, toEid, to); + else + aufsExtMoveIds(fdir->edirid, toEid, to); +#endif FIXED_DIRIDS + } + } +#ifdef FIXED_DIRIDS + else { /* effectively a rename */ + if (fdir->edirid == -1) + aufsExtRename(orig_name, to); + else + aufsExtRenameId(fdir->edirid, to); + } + if (orig_name != NULL) + free(orig_name); +#endif FIXED_DIRIDS + lastcd = NILDIR; /* names have changed */ +} + +static char *hexdigits = "0123456789abcdef"; + +/* problem with length of enp */ + +#ifdef SHORT_NAMES +byte * +ItoEName_Short(idir,in,enp) +IDirP *idir; +char *in; +byte *enp; +{ + char c,c2; + char *cp,*cp2; + byte *en = enp; + + if (in[0] != '\0') + Get_name(idir,in,0,en); + else + *en++ = '\0'; + return(enp); +} +#endif SHORT_NAMES + +byte * +ItoEName(in,enp) +register char *in; +byte *enp; +{ + byte c,c2; + char *cp,*cp2; + byte *en = enp; +#ifdef ISO_TRANSLATE + extern u_char ISO2Mac[]; +#endif ISO_TRANSLATE + + while ((c = *in++) != '\0') { + if (c != ':') { +#if defined (ISO_TRANSLATE) & defined (ISO_FILENAMES) + *en++ = (c & 0x80) ? ((c2 = ISO2Mac[c]) ? c2 : 0x3f) : c; +#else /* ISO_TRANSLATE & ISO_FILENAMES */ + *en++ = c; +#endif /* ISO_TRANSLATE & ISO_FILENAMES */ + } else { + /* must convert to external form */ + if ((c = *in++) == '\0' || (c2 = *in++) == '\0') { + *en++ = '|'; + if (c != '\0') + *en++ = (c == ':') ? '|' : c; /* found null, deposit c or | */ + break; /* done with while */ + } + if ((cp = index(hexdigits,c)) == NULL || + (cp2 = index(hexdigits,c2)) == NULL) + { + *en++ = '|'; /* deposit initial | */ + *en++ = (c == ':') ? '|' : c; /* deposit c or | */ + *en++ = (c2 == ':') ? '|' : c2; /* deposit c2 or | */ + continue; /* continue with while */ + } + +#ifndef hpux + *en++ = ((cp-hexdigits) << 4) | (cp2-hexdigits); +#else /* hpux */ + /* hpux compiler barfs on the above */ + *en = ((cp-hexdigits) << 4); + *en |= (cp2-hexdigits); + en++; +#endif /*hpux*/ + } + } + *en++ = '\0'; + return(enp); +} + +#ifdef SHORT_NAMES +OSErr +EtoIName_Short(idir,en,inp) +IDirP idir; +register byte *en; /* max is 31 or so */ +char *inp; +{ + byte c; /* unsigned char */ + register char *in = inp; + register int cnt = 0; + + if ((*en) != '\0') + Get_name(idir,en,1,in); + else + *in++ = '\0'; + return(noErr); +} +#endif SHORT_NAMES + +OSErr +EtoIName(en,inp) +register byte *en; /* max is 31 or so */ +char *inp; +{ + byte c; /* unsigned char */ + register u_char *in = (u_char *)inp; + register int cnt = 0; +#ifdef ISO_TRANSLATE + extern u_char Mac2ISO[]; +#endif ISO_TRANSLATE + + while ((c = *en++) != '\0') { + if (isascii(c) && !iscntrl(c) && isprint(c) && c != '/') { + *in++ = c; + cnt++; + } else { +#if defined (ISO_TRANSLATE) & defined (ISO_FILENAMES) + if ((c & 0x80) && (*in = Mac2ISO[c])) { + cnt += 1; + in++; + } else +#endif /* ISO_TRANSLATE & ISO_FILENAMES */ + { + /* must convert to 3 char external form */ + *in++ = ':'; /* : */ + *in++ = hexdigits[(c >> 4) & 0xf]; + *in++ = hexdigits[(c & 0xf)]; + cnt += 3; + } + } + } + *in++ = '\0'; + if (cnt > MAXNAMLEN) + return(aeAccessDenied); /* bogus */ + return(noErr); +} + +/* + * Given an internal file name, compute the length of the external + * file name + */ +int +ENameLen(in) +register char *in; +{ + register int len = 0; + register char c; + register char c2; + + while ((c = *in++) != '\0') { + if (c != ':') + len++; + else { + /* must convert to external form */ + if ((c = *in++) == '\0' || (c2 = *in++) == '\0') { + len++; + if (c != '\0') + len++; + break; /* done with while */ + } + if (index(hexdigits,c) == NULL || index(hexdigits,c2) == NULL) + len += 3; + else + len++; + } + } + return(len); +} + +void +EModified(dirid) +IDirP dirid; +{ + /* what to do on overflow of 2^35-1? */ + dirid->modified++; /* push the modified flag */ + VolModified(dirid->volbm); /* and here too! */ +} diff --git a/applications/aufs/afpdid.h b/applications/aufs/afpdid.h new file mode 100644 index 0000000..0675893 --- /dev/null +++ b/applications/aufs/afpdid.h @@ -0,0 +1,21 @@ +/* + * $Author: djh $ $Date: 91/02/15 21:06:05 $ + * $Header: afpdid.h,v 2.1 91/02/15 21:06:05 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * afpdid.h - header file for AFP directory id mechanism. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1987 by The Trustees of Columbia University in + * the City of New York. + * + * Edit History: + * + * Mon Mar 23, 1987 Schilit Created + * + */ + + diff --git a/applications/aufs/afpdir.c b/applications/aufs/afpdir.c new file mode 100644 index 0000000..715a95d --- /dev/null +++ b/applications/aufs/afpdir.c @@ -0,0 +1,1070 @@ +/* + * $Author: djh $ $Date: 1996/06/19 04:26:25 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpdir.c,v 2.8 1996/06/19 04:26:25 djh Rel djh $ + * $Revision: 2.8 $ + * + */ + +/* + * afpdir.c - Appletalk Filing Protocol Directory Level Routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * December 1990 djh tidy up for AFP 2.0 + * + */ + +/* + * Non OS dependant support routines for: + * + * FPGetDirParms() + * FPSetDirParms() + * FPOpenDir() + * FPCloseDir() + * FPEnumerate() + * FPCreateDir() + * FPGetFileDirInfo() + * + */ + +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif +#include +#include +#include +#include "afpntoh.h" +#include "afps.h" /* common server header */ + +#ifdef DEBUG_AFP_CMD +#include +#include +extern FILE *dbg; +#endif /* DEBUG_AFP_CMD */ + +private int EnumPack(); + +/* + * OSErr FPGetDirParms(byte *p,byte *r,int *rl) + * + * This call is used to retrieve parameters for a particular directory. + * + */ + +OSErr +FPGetFileDirParms(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + GetFileDirParmsPkt gfdp; + FileDirParm fdp; + IDirP idir,ipdir; + char file[MAXUFLEN]; + int ivol,err; + + ntohPackX(PsGetFileDirParms,p,l,(byte *) &gfdp); + + err = EtoIfile(file,&idir,&ipdir,&ivol,gfdp.gdp_dirid, + gfdp.gdp_volid,gfdp.gdp_ptype,gfdp.gdp_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_bmap(); + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", gfdp.gdp_volid); + fprintf(dbg, "\tDirID: %08x\n", gfdp.gdp_dirid); + fprintf(dbg, "\tFBMap: %04x\t", gfdp.gdp_fbitmap); + dbg_print_bmap(gfdp.gdp_fbitmap, 0); + fprintf(dbg, "\tDBMap: %04x\t", gfdp.gdp_dbitmap); + dbg_print_bmap(gfdp.gdp_dbitmap, 1); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", gfdp.gdp_ptype, + (gfdp.gdp_ptype == 1) ? "Short" : "Long"); + dbg_print_path(gfdp.gdp_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) { + if (DBFIL || DBDIR) + printf("FPGetFileDirParms: EtoIfile returns %d\n",err); +#ifdef SHORT_NAMES + if (gfdp.gdp_dirid > 1000) + return(aeObjectNotFound); +#endif SHORT_NAMES + return(err); + } + + fdp.fdp_pdirid = ItoEdirid(ipdir,ivol); + fdp.fdp_dbitmap = gfdp.gdp_dbitmap; + fdp.fdp_fbitmap = gfdp.gdp_fbitmap; + fdp.fdp_zero = 0; + err = OSFileDirInfo(ipdir,idir,file,&fdp,ivol); /* fill in information */ + if (err != noErr) { + if (DBFIL || DBDIR) + printf("FPGetFileDirParms: OSFileDirInfo returns %d on %s/%s\n", + err,pathstr(ipdir),file); + return(err); + } + if (FDP_ISDIR(fdp.fdp_flg)) { + *rl = htonPackX(DirParmPackR,(byte *)&fdp,r); + *rl += htonPackX(DirPackR,(byte *)&fdp,r+(*rl)); + } + if (!FDP_ISDIR(fdp.fdp_flg)) { + *rl = htonPackX(DirParmPackR,(byte *)&fdp,r); + *rl += htonPackX(FilePackR,(byte *)&fdp,r+(*rl)); + } + + /* + * check for bogus bitmaps & truncate response + * (DON'T return aeBitMapErr) + * + */ + if ((!FDP_ISDIR(fdp.fdp_flg) && gfdp.gdp_dbitmap && !gfdp.gdp_fbitmap) + || (FDP_ISDIR(fdp.fdp_flg) && !gfdp.gdp_dbitmap && gfdp.gdp_fbitmap)) + *rl =6; + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_parm(); + void dbg_print_bmap(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tFBMap: %04x\t", fdp.fdp_fbitmap); + dbg_print_bmap(fdp.fdp_fbitmap, 0); + fprintf(dbg, "\tDBMap: %04x\t", fdp.fdp_dbitmap); + dbg_print_bmap(fdp.fdp_dbitmap, 1); + fprintf(dbg, "\tFDFlg: %02x\t(%s)\n", fdp.fdp_flg, + FDP_ISDIR(fdp.fdp_flg) ? "Directory" : "File"); + if (*rl == 6) + fprintf(dbg, "\t\n"); + else + dbg_print_parm(FDP_ISDIR(fdp.fdp_flg) ? fdp.fdp_dbitmap:fdp.fdp_fbitmap, + r+6, (*rl)-6, FDP_ISDIR(fdp.fdp_flg) ? 1 : (idir) ? 1 : 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/* + * OSErr FPEnumerate(...) + * + * This call is used to enumerate the contents of a directory. The + * reply is composed of a number of file and/or directory parameter + * structures. + * + */ + +OSErr +FPEnumerate(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + FileDirParm fdp; + EnumeratePkt enup; + EnumerateReplyPkt enpr; + IDirP idir,ipdir; + char file[MAXUFLEN]; + byte *cntptr; + int ivol,stidx,idx,maxidx; + word cnt; + int reqcnt,maxreply,len,elen,err; + extern int sqs; /* maximum send qs */ + + ntohPackX(PsEnumerate,p,l,(byte *) &enup); + + err = EtoIfile(file,&idir,&ipdir,&ivol,enup.enu_dirid, + enup.enu_volid,enup.enu_ptype,enup.enu_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_bmap(); + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", enup.enu_volid); + fprintf(dbg, "\tDirID: %08x\n", enup.enu_dirid); + fprintf(dbg, "\tFBMap: %04x\t", enup.enu_fbitmap); + dbg_print_bmap(enup.enu_fbitmap, 0); + fprintf(dbg, "\tDBMap: %04x\t", enup.enu_dbitmap); + dbg_print_bmap(enup.enu_dbitmap, 1); + fprintf(dbg, "\tRqCnt: %04x\n", enup.enu_reqcnt); + fprintf(dbg, "\tStart: %04x\n", enup.enu_stidx); + fprintf(dbg, "\tMaxRp: %04x\n", enup.enu_maxreply); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", enup.enu_ptype, + (enup.enu_ptype == 1) ? "Short" : "Long"); + dbg_print_path(enup.enu_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + if (idir == NILDIR) + return(aeDirNotFound); + + /* set the bitmaps for return message and packing */ + + fdp.fdp_fbitmap = enpr.enur_fbitmap = enup.enu_fbitmap; + fdp.fdp_dbitmap = enpr.enur_dbitmap = enup.enu_dbitmap; + + /* set the parent dirid to the enumerated directory */ + + fdp.fdp_pdirid = ItoEdirid(idir,ivol); + + /* fetch the max size of a reply packet, start index, request count */ + + maxreply = enup.enu_maxreply; + maxreply = min(sqs, maxreply); + stidx = enup.enu_stidx; + reqcnt = enup.enu_reqcnt; + + maxidx = OSEnumInit(idir); /* init, and fetch count of entries */ + if (maxidx < 0) /* error? */ + return(maxidx); /* return error */ + + if (stidx > maxidx) { /* start index gt count of entries? */ + OSEnumDone(idir); + return(aeObjectNotFound); /* yes... object not found then */ + } + + cntptr = &r[ENUR_ACTCNT_OFF]; /* address of packed actcnt word */ + + len = htonPackX(EnumPackR,(byte *)&enpr,r); + + /* starting with the file/directory at stidx, load upto reqcnt + * entries into the reply buffer. The size of the reply buffer must + * not exceed maxreply bytes. Do not include a file/directory if + * the associated bitmap is zero. + */ + + for (idx=stidx,cnt=0; idx <= maxidx && cnt < (word)reqcnt; idx++) { + elen = EnumPack(idir,&fdp,ivol,idx,&r[len]); + if (elen > 0) { /* something packed for this entry? */ + if (len+elen > maxreply) /* yes... check if overflow */ + break; /* yes.. break out */ + cnt++; /* else include entry in count */ + len += elen; /* include entry in len */ + if (len > maxreply-30) /* if close to the limit */ + break; /* then break out now */ + } + } + + OSEnumDone(idir); /* finished with enumerate */ + + if (cnt == 0) /* filter tossed all */ + return(aeObjectNotFound); + + PackWord(cnt,cntptr); /* pack the actual count */ + *rl = len; /* length of packet for reply */ + + if (DBDIR) + printf("OSEnum: maxreply=%d, ourreply=%d, reqcnt=%d, ourcnt=%d\n", + maxreply,len,reqcnt,cnt); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + int i = cnt; + byte *q = r + 6; + void dbg_print_bmap(); + void dbg_print_parm(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tFBtMap: %04x\t", enpr.enur_fbitmap); + dbg_print_bmap(enpr.enur_fbitmap, 0); + fprintf(dbg, "\tDBtMap: %04x\t", enpr.enur_dbitmap); + dbg_print_bmap(enpr.enur_dbitmap, 1); + fprintf(dbg, "\tActCnt: %d\n", cnt); + while (i-- > 0) { + fprintf(dbg, "\t----------\n"); + fprintf(dbg, "\tStrcLn: %d\n", (int)*q); + fprintf(dbg, "\tEnType: %02x\t(%s)\n", *(q+1), + (*(q+1) & 0x80) ? "Directory" : "File"); + dbg_print_parm((*(q+1) & 0x80) ? enpr.enur_dbitmap : enpr.enur_fbitmap, + q+2, (*q)-2, (*(q+1) & 0x80) ? 1 : 0); + q += *q; + } + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); /* all ok */ +} + +/* + * int EnumPack(IDirP ipdir, FileDirParm *fdp, int ivol, + * int idx, byte *r); + * + * Pack a single enumeration entry as specified by the enumeration + * idx and the enumeration direcotry ipdir. + * + * Fetch file/directory information for the entry as specified by + * the parent directory (ipdir), the volume (ivol), and the name + * from enumeration index (idx). + * + * If the file bitmap passed in fdp is zero and the entry is a file + * then return 0. If the directory bitmap passed in fdp is zero and + * the entry is a directory then return 0. + * + * In the normal case pack a length byte, directory flag byte, and + * the file/directory information as specified by the fdp bitmaps + * int r and return the length of of this entry. + * + */ + +private int +EnumPack(ipdir,fdp,ivol,idx,r) +IDirP ipdir; +FileDirParm *fdp; +int ivol,idx; +byte *r; +{ + char *fn; + int len; + int err; + + fn = (char *) OSEnumGet(ipdir,idx); /* get the file name */ + /* and the info for this entry (nildir - no info on whether */ + /* directory or not) */ + err = OSFileDirInfo(ipdir,NILDIR,fn,fdp,ivol); + if (err == aeAccessDenied) /* if no access */ + return(0); /* then forget the entry */ + + if (FDP_ISDIR(fdp->fdp_flg)) { /* if a directory */ + if (fdp->fdp_dbitmap == 0) /* and dir bitmap is zero */ + return(0); /* then skip the entry */ + len = htonPackX(DirPackR,(byte *) fdp,r+2); /* else pack */ + } else { /* else, if a file */ + if (fdp->fdp_fbitmap == 0) /* and file bitmap is zero */ + return(0); /* then skip the entry */ + len = htonPackX(FilePackR,(byte *) fdp,r+2); /* else pack */ + } + + len += 2; /* include size, flg into sum */ + if ((len % 2) != 0) /* if odd number of bytes */ + r[len++] = 0; /* then even out the length */ + r[0] = (byte) len; /* store length of structure */ + r[1] = fdp->fdp_flg; /* directory flag */ + return(len); /* return length of this item */ +} + +/*ARGSUSED*/ +OSErr +FPSetDirParms(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + SetDirParmsPkt sdp; + FileDirParm fdp; + IDirP ipdir,idir; + int ivol,len,err; + char file[MAXUFLEN]; + + len = ntohPackX(PsSetDirParms,p,l,(byte *) &sdp); + ntohPackXbitmap(ProtoDirAttr,p+len,l-len, + (byte *) &fdp,sdp.sdp_bitmap); + + err = EtoIfile(file,&idir,&ipdir,&ivol,sdp.sdp_dirid, + sdp.sdp_volid,sdp.sdp_ptype,sdp.sdp_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + char *pathstr(); + void dbg_print_bmap(); + void dbg_print_parm(); + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", sdp.sdp_volid); + fprintf(dbg, "\tDirID: %08x\n", sdp.sdp_dirid); + fprintf(dbg, "\tFBMap: %04x\t", sdp.sdp_bitmap); + dbg_print_bmap(sdp.sdp_bitmap, 1); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", sdp.sdp_ptype, + (sdp.sdp_ptype == 1) ? "Short" : "Long"); + dbg_print_path(sdp.sdp_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + dbg_print_parm(sdp.sdp_bitmap, p+len, l-len, 1); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + fdp.fdp_dbitmap = sdp.sdp_bitmap; /* using this bitmap */ + + if (DBDIR) + printf("FPSetDirParms: path=%s, file=%s, bm=%d\n", + pathstr(ipdir),file,sdp.sdp_bitmap); + + err = OSSetDirParms(ipdir,file,&fdp); /* set dirparms */ + if (err == noErr) + VolModified(ivol); + return(err); +} + +#ifdef SHORT_NAMES +OSErr +FPOpenDir(p,l,r,rl) +byte *p, *r; +int l; +int *rl; +{ + OpenDirPkt ODPkt; + IDirP idir,ipdir; + char file[MAXUFLEN]; + int ivol,err; + + ntohPackX(PsOpenDir,p,l,(byte*)&ODPkt); + + err = EtoIfile(file,&idir,&ipdir,&ivol,ODPkt.odr_dirid, ODPkt.odr_volid, + ODPkt.odr_ptype,ODPkt.odr_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", ODPkt.odr_volid); + fprintf(dbg, "\tDirID: %08x\n", ODPkt.odr_dirid); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", ODPkt.odr_ptype, + (ODPkt.odr_ptype == 1) ? "Short" : "Long"); + dbg_print_path(ODPkt.odr_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((err != noErr) && (DBDIR)) + printf("error returned from EtoIfile\n"); + PackDWord(ItoEdirid(idir,ivol),r); + *rl = 4; + return(noErr); +} + +#else SHORT_NAMES +OSErr +FPOpenDir() +{ + return(aeParamErr); +} +#endif SHORT_NAMES + +OSErr +FPCloseDir() +{ + return(aeParamErr); +} + + +OSErr +FPCreateDir(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + CreateDirPkt crd; + IDirP idir,ipdir; + int ivol,err; + char file[MAXUFLEN]; + + ntohPackX(PsCreateDir,p,l,(byte *) &crd); + + err = EtoIfile(file,&idir,&ipdir,&ivol,crd.crd_dirid, + crd.crd_volid,crd.crd_ptype,crd.crd_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", crd.crd_volid); + fprintf(dbg, "\tDirID: %08x\n", crd.crd_dirid); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", crd.crd_ptype, + (crd.crd_ptype == 1) ? "Short" : "Long"); + dbg_print_path(crd.crd_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + if (DBFIL) + printf("FPCreateDir: create path=%s name=%s\n",pathstr(ipdir),file); + + if ((err = OSCreateDir(ipdir,file, &idir)) == noErr) { + if (DBFIL) + printf("FPCreateDir: create path=%s name=%s edirID=%d\n", + pathstr(ipdir),file,idir->edirid); + PackDWord(ItoEdirid(idir,ivol),r); + *rl = 4; + } + if (err == noErr) /* if success */ + VolModified(ivol); /* then volume modified */ + return(err); +} + + +/* + * Preliminary version - just does files + * +*/ + +/*ARGSUSED*/ +OSErr +FPSetFileDirParms(p,l,r,rl) +byte *p, *r; +int l; +int *rl; +{ + SetFileDirParmsPkt scp; + FileDirParm fdp; + IDirP idir,ipdir; + char file[MAXUFLEN]; + int ivol,len,err; + + len = ntohPackX(PsSetFileDirParms,p,l,(byte *) &scp); + ntohPackXbitmap(ProtoFileDirAttr,p+len,l-len,(byte *) &fdp,scp.scp_bitmap); + + err = EtoIfile(file,&idir,&ipdir,&ivol,scp.scp_dirid, + scp.scp_volid,scp.scp_ptype,scp.scp_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_bmap(); + void dbg_print_parm(); + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", scp.scp_volid); + fprintf(dbg, "\tDirID: %08x\n", scp.scp_dirid); + fprintf(dbg, "\tBtMap: %04x\t", scp.scp_bitmap); + dbg_print_bmap(scp.scp_bitmap, 0); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", scp.scp_ptype, + (scp.scp_ptype == 1) ? "Short" : "Long"); + dbg_print_path(scp.scp_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + dbg_print_parm(scp.scp_bitmap, p+len, l-len, (idir) ? 1 : 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + fdp.fdp_dbitmap = fdp.fdp_fbitmap = scp.scp_bitmap; + + if (DBFIL) + printf("FPSetFileDirParms: setting bm=%d for %s %s\n", + scp.scp_bitmap,pathstr(ipdir),file); + + return(OSSetFileDirParms(ipdir,idir,file,&fdp)); +} + +#ifdef DEBUG_AFP_CMD +/* + * describe FPGetFileDirParms/FPGetFileInfo bitmaps + * + */ + +void +dbg_print_bmap(bmap, typ) +u_short bmap; +int typ; +{ + int i, j; + + if (dbg != NULL) { + fprintf(dbg, "("); + for (i = 0, j = 0; i < 16; i++) { + if (bmap & (0x0001 << i)) { + bmap &= ~(0x0001 << i); + switch (i) { + case 0: + fprintf(dbg, "Attributes"); + j++; + break; + case 1: + fprintf(dbg, "Parent DID"); + j++; + break; + case 2: + fprintf(dbg, "Creat Date"); + j++; + break; + case 3: + fprintf(dbg, "Modif Date"); + j++; + break; + case 4: + fprintf(dbg, "Bakup Date"); + j++; + break; + case 5: + fprintf(dbg, "Findr Info"); + j++; + break; + case 6: + fprintf(dbg, "Long Name"); + j++; + break; + case 7: + fprintf(dbg, "Short Name"); + j++; + break; + case 8: + if (typ) + fprintf(dbg, "Direct ID"); + else + fprintf(dbg, "File Numbr"); + j++; + break; + case 9: + if (typ) + fprintf(dbg, "OffSpr Cnt"); + else + fprintf(dbg, "DFork Len"); + j++; + break; + case 10: + if (typ) + fprintf(dbg, "Owner ID"); + else + fprintf(dbg, "RFork Len"); + j++; + break; + case 11: + if (typ) + fprintf(dbg, "Group ID"); + j++; + break; + case 12: + if (typ) { + fprintf(dbg, "Acs Rights"); + j++; + } + break; + case 13: + fprintf(dbg, "ProDos Inf"); + j++; + break; + default: + fprintf(dbg, "Unknwn Bit"); + j++; + break; + } + if (bmap) + fprintf(dbg, ", "); + if (bmap && (j % 4) == 0) + fprintf(dbg, "\n\t\t\t"); + } + } + fprintf(dbg, ")\n"); + } + + return; +} + +/* + * dump parameters described by bitmap + * + */ + +#define get2(s) (u_short)(((s)[0]<<8)|((s)[1])) +#define get4(s) (u_int)(((s)[0]<<24)|((s)[1]<<16)|((s)[2]<<8)|((s)[3])) + +void +dbg_print_parm(bmap, r, rl, typ) +u_short bmap; +byte *r; +int rl, typ; +{ + int i, j; + byte *p, *q; + short offset; + void dbg_print_attr(); + void dbg_print_date(); + void dbg_print_accs(); + void dbg_print_fndr(); + + p = r; /* parameters */ + + if (dbg != NULL) { + for (i = 0; i < 16 && rl > 0; i++) { + if (bmap & (0x0001 << i)) { + switch (i) { + case 0: + fprintf(dbg, "\tAttributes: "); + dbg_print_attr(get2(r), typ); + rl -= 2; + r += 2; + break; + case 1: + fprintf(dbg, "\tParent DID: %08x\n", get4(r)); + rl -= 4; + r += 4; + break; + case 2: + fprintf(dbg, "\tCreat Date: "); + dbg_print_date(get4(r)); + rl -= 4; + r += 4; + break; + case 3: + fprintf(dbg, "\tModif Date: "); + dbg_print_date(get4(r)); + rl -= 4; + r += 4; + break; + case 4: + fprintf(dbg, "\tBakup Date: "); + dbg_print_date(get4(r)); + rl -= 4; + r += 4; + break; + case 5: + fprintf(dbg, "\tFindr Info:\n"); + dbg_print_fndr(r, typ); + rl -= 32; + r += 32; + break; + case 6: + fprintf(dbg, "\t Long Name: \""); + offset = get2(r); + q = p + offset; + for (j = 0; j < (int)*q; j++) + fprintf(dbg, "%c", *(q+j+1)); + fprintf(dbg, "\"\n"); + rl -= 2; + r += 2; + break; + case 7: + fprintf(dbg, "\tShort Name: "); + offset = get2(r); + q = p + offset; + for (j = 0; j < (int)*q; j++) + fprintf(dbg, "%c", *(q+j+1)); + fprintf(dbg, "\"\n"); + rl -= 2; + r += 2; + break; + case 8: + if (typ) + fprintf(dbg, "\t Direct ID: %08x\n", get4(r)); + else + fprintf(dbg, "\tFile Numbr: %08x\n", get4(r)); + rl -= 4; + r += 4; + break; + case 9: + if (typ) { + fprintf(dbg, "\tOffSpr Cnt: %d\n", get2(r)); + rl -= 2; + r += 2; + } else { + fprintf(dbg, "\t DFork Len: %d\n", get4(r)); + rl -= 4; + r += 4; + } + break; + case 10: + if (typ) + fprintf(dbg, "\t Owner ID: %08x\n", get4(r)); + else + fprintf(dbg, "\t RFork Len: %d\n", get4(r)); + rl -= 4; + r += 4; + break; + case 11: + if (typ) { + fprintf(dbg, "\t Group ID: %08x\n", get4(r)); + rl -= 4; + r += 4; + } + break; + case 12: + if (typ) { + fprintf(dbg, "\tAcs Rights: "); + dbg_print_accs(r); + rl -= 4; + r += 4; + } + break; + case 13: + fprintf(dbg, "\tProDos Inf:\n"); + rl -= 6; + r += 6; + break; + default: + fprintf(dbg, "\t\n", i); + break; + } + } + } + } + + return; +} + +/* + * Print the 16-bit File/Dir attributes + * + */ + +void +dbg_print_attr(attr, typ) +u_int attr; +int typ; +{ + int i, j; + + if (dbg != NULL) { + fprintf(dbg, "%04x (", attr); + for (i = 0, j = 0; i < 16; i++) { + if (attr & (0x0001 << i)) { + attr &= ~(0x0001 << i); + switch (i) { + case 0: + fprintf(dbg, "Invisible"); + j++; + break; + case 1: + if (typ) + fprintf(dbg, "IsExpFolder"); + else + fprintf(dbg, "MultiUser"); + j++; + break; + case 2: + fprintf(dbg, "System"); + j++; + break; + case 3: + if (typ) + fprintf(dbg, "Mounted"); + else + fprintf(dbg, "DAlrdyOpen"); + j++; + break; + case 4: + if (typ) + fprintf(dbg, "InExpFolder"); + else + fprintf(dbg, "RAlrdyOpen"); + j++; + break; + case 5: + if (typ) + fprintf(dbg, "", i); + else + fprintf(dbg, "RDOnly/WrtInhib"); + j++; + break; + case 6: + fprintf(dbg, "BackupNeeded"); + j++; + break; + case 7: + fprintf(dbg, "RenameInhib"); + j++; + break; + case 8: + fprintf(dbg, "DeleteInhib"); + j++; + break; + case 9: + if (typ) + fprintf(dbg, ""); + else + fprintf(dbg, "CopyProtect"); + j++; + break; + case 15: + fprintf(dbg, "Set/Clear"); + j++; + break; + default: + fprintf(dbg, "", i); + j++; + break; + } + if (attr) + fprintf(dbg, ", "); + if (attr && (j % 4) == 0) + fprintf(dbg, "\n\t\t\t"); + } + } + fprintf(dbg, ")\n"); + } + + return; +} + +/* + * print Finder Information + * + */ + +void +dbg_print_fndr(f, typ) +byte *f; +int typ; +{ + void dbg_print_type(); + + /* + * File Finder Info + * + */ + if (typ == 0) { + dbg_print_type("\t FilTyp:", f); + f += 4; + dbg_print_type("\t Creatr:", f); + f += 4; + fprintf(dbg, "\t FdrFlg: %04x\n", get2(f)); + f += 2; + fprintf(dbg, "\t Locatn: %04x %04x\n", get2(f), get2(f+2)); + f += 4; + fprintf(dbg, "\t Window: %04x\n", get2(f)); + f += 2; + fprintf(dbg, "\t IconID: %04x\n", get2(f)); + f += 2; + f += 8; /* unused */ + fprintf(dbg, "\t CommID: %04x\n", get2(f)); + f += 2; + fprintf(dbg, "\t HomeID: %08x\n", get4(f)); + + return; + } + + /* + * Directory Finder Info + * + */ + if (typ == 1) { + fprintf(dbg, "\t DiRect: %04x %04x %04x %04x\n", + get2(f), get2(f+2), get2(f+4), get2(f+6)); + f += 8; + fprintf(dbg, "\t FdrFlg: %04x\n", get2(f)); + f += 2; + fprintf(dbg, "\t Locatn: %04x %04x\n", get2(f), get2(f+2)); + f += 4; + fprintf(dbg, "\t FdView: %04x\n", get2(f)); + f += 2; + fprintf(dbg, "\t Scroll: %04x %04x\n", get2(f), get2(f+2)); + f += 4; + fprintf(dbg, "\t DChain: %08x\n", get4(f)); + f += 4; + fprintf(dbg, "\t Script: %02x\n", *f); + fprintf(dbg, "\t XFlags: %02x\n", *(f+1)); + f += 2; + fprintf(dbg, "\t CommID: %04x\n", get2(f)); + f += 2; + fprintf(dbg, "\t HomeID: %08x\n", get4(f)); + + return; + } + + return; +} + +/* + * print 4 byte access rights + * + */ + +void +dbg_print_accs(accs) +byte *accs; +{ + if (dbg != NULL) { + if (accs[0] & 0x80) + fprintf(dbg, "OWNER, "); + if (accs[0] & 0x10) + fprintf(dbg, "BLANK ACCESS, "); + fprintf(dbg, "UARights %02x, World %02x, Group %02x, Owner %02x\n", + accs[0] & 0x07, accs[1], accs[2], accs[3]); + } + + return; +} + +/* + * print the AFP date + * + */ + +void +dbg_print_date(date) +time_t date; +{ + char *ctime(); + time_t offset; + struct timeval tp; + struct timezone tzp; + + if (dbg != NULL) { + if (date == 0x80000000) { + fprintf(dbg, "\n"); + return; + } +#ifdef SOLARIS + tzset(); + offset = timezone; +#else /* SOLARIS */ + gettimeofday(&tp, &tzp); + offset = (time_t)(tzp.tz_minuteswest*60); +#endif /* SOLARIS */ + date = date - (((30*365+7)*(-24)*3600L)) + offset; + fprintf(dbg, "%s", ctime(&date)); + } + + return; +} + +/* + * print a path name (pascal string) + * + */ + +void +dbg_print_path(path) +char *path; +{ + int i; + + if (dbg != NULL) { + fprintf(dbg, "\tMFile: \""); + for (i = 0; i < *path; i++) + if (isprint(*(path+i+1))) + fprintf(dbg, "%c", *(path+i+1)); + else + fprintf(dbg, "<0x%02x>", *(path+i+1)); + fprintf(dbg, "\"\n"); + } +} +#endif /* DEBUG_AFP_CMD */ diff --git a/applications/aufs/afpdsi.c b/applications/aufs/afpdsi.c new file mode 100644 index 0000000..7d25f8c --- /dev/null +++ b/applications/aufs/afpdsi.c @@ -0,0 +1,2029 @@ +/* + * $Author: djh $ $Date: 91/03/14 13:45:20 $ + * $Header: afpdsi.c,v 2.2 91/03/14 13:45:20 djh Exp $ + * $Revision: 2.2 $ + * + */ + +/* + * afpdsi.c - Data Stream Interface + * + * AFP via a Transport Protocol (eg: TCP/IP) + * + * AppleTalk package for UNIX + * + * The following routines implement a lightweight extra + * layer between AFP (as embodied in the AUFS code), the + * original ASP via ATP layer, and delivery via other + * Transport Protocol layers, currently only TCP/IP. + * + * Refer: "AppleTalk Filing Protocol 2.2 & + * AFP over TCP/IP Specification" + * + * SSS == Server Session Socket + * SLS == Session Listening Socket + * WSS == Workstation Session Socket + * + * Copyright (c) 1997 The University of Melbourne + * David Hornsby + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../lib/cap/abasp.h" /* urk */ +#include "afpdsi.h" + +#ifdef HAVE_WRITEV +#include +#endif /* HAVE_WRITEV */ + +/* + * aufs AFP routines + * + */ +int dsiInit(); +int dsiGetSession(); +int dsiFork(); +int dsiTickleUserRoutine(); +int dsiGetRequest(); +int dsiWrtContinue(); +int dsiWrtReply(); +int dsiCmdReply(); +int dsiAttention(); +int dsiGetNetworkInfo(); +int dsiGetParms(); +int dsiCloseSession(); +int dsiShutdown(); +int dsiTCPIPCloseSLS(); + +/* + * AppleTalk Session Protocol routines + * (including some formerly 'private') + * + */ +int SPInit(); +int SPGetSession(); +int SPFork(); +int SPTickleUserRoutine(); +int SPGetRequest(); +int SPWrtContinue(); +int SPWrtReply(); +int SPCmdReply(); +int SPAttention(); +int SPGetNetworkInfo(); +int SPGetParms(); +int SPCloseSession(); +int SPShutdown(); + +ASPSSkt *aspsskt_find_slsrefnum(); +ASPSkt *aspskt_find_sessrefnum(); +ASPSkt *aspskt_find_active(); +ASPQE *create_aq(); + +void stopasptickle(); +void stop_ttimer(); +void delete_aq(); +void Timeout(); + +/* + * local TCP/IP routines + * + */ +int dsiTCPIPInit(); +int dsiTCPIPFork(); +int dsiTCPIPGetRequest(); +int dsiTCPWrtContinue(); +int dsiTCPIPWrtReply(); +int dsiTCPIPCmdReply(); +int dsiTCPIPReply(); +int dsiTCPIPTickleUserRoutine(); +int dsiTCPIPCloseSession(); +int dsiTCPIPAttention(); +int dsiTCPIPGetNetworkInfo(); +int dsiTCPIPWrite(); +int dsiTCPIPCloseSLS(); + +/* + * globals + * + */ +#ifdef DEBUG_AFP_CMD +extern FILE *dbg; +#endif /* DEBUG_AFP_CMD */ + +extern int errno; +extern int asip_enable; +extern char *dsiTCPIPFilter; +private struct dsi_sess *dsi_session = NULL; + +/* + * DSI transport-layer demultiplexing + * + */ + +/* + * Set up a Server Listening Socket (SLS) for both + * ASP/ATP and TCP/IP. + * + * SLSEntityIdentifier - server AppleTalk address + * ServiceStatusBlock - pointer to ServerInfo data + * ServiceStatusBlockSize - ServerInfo data size + * SLSRefNum - return a session RefNum + * + */ + +int +dsiInit(SLSEntityIdentifier, ServiceStatusBlock, + ServiceStatusBlockSize, SLSRefNum) +AddrBlock *SLSEntityIdentifier; /* SLS Net id */ +char *ServiceStatusBlock; /* block with status info */ +int ServiceStatusBlockSize; /* size of status info */ +int *SLSRefNum; /* sls ref num return place */ +{ + int result; + extern int numasp; + + /* + * allocate & initialise space for DSI session data + * + */ + if (numasp <= 0) + return(-1); + + if ((dsi_session = (struct dsi_sess *) + malloc(sizeof(struct dsi_sess)*numasp)) == NULL) + return(-1); + + bzero((char *)dsi_session, sizeof(struct dsi_sess)*numasp); + + /* + * allocate SLSRefNum, initialise AppleTalk Session Protocol SLS + * + */ + result = SPInit(SLSEntityIdentifier, ServiceStatusBlock, + ServiceStatusBlockSize, SLSRefNum); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "** SPInit(): (PID %d)\n", getpid()); + fprintf(dbg, "\tSLSRefNum: %d\n", *SLSRefNum); + fprintf(dbg, "\tServiceStatusBlockSize: %d\n", ServiceStatusBlockSize); + fprintf(dbg, "\tresult: %d\n", result); + fprintf(dbg, "\n\n"); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (result != noErr) + return(result); + + /* + * if enabled, setup TCP/IP SLS (uses same SLSRefNum) + * + */ + if (asip_enable) + if (dsiTCPIPInit(SLSEntityIdentifier, ServiceStatusBlock, + ServiceStatusBlockSize, SLSRefNum) != noErr) + asip_enable = 0; + + return(noErr); +} + +/* + * set up to wait for a new session to start + * + * SLSRefNum - Session Listening Socket RefNum + * SessRefNum - returns new session reference number + * comp - completion flag/error + * + */ + +int +dsiGetSession(SLSRefNum, SessRefNum, comp) +int SLSRefNum; +int *SessRefNum; +int *comp; +{ + int result; + + /* + * get a session reference number, + * + */ + result = SPGetSession(SLSRefNum, SessRefNum, comp); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "** SPGetSession(): (PID %d)\n", getpid()); + fprintf(dbg, "\tSLSRefNum: %d\n", SLSRefNum); + fprintf(dbg, "\tSessRefNum: %d\n", *SessRefNum); + fprintf(dbg, "\tcomp: %d\n", *comp); + fprintf(dbg, "\tresult: %d\n", result); + fprintf(dbg, "\n\n"); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (result != noErr) + return(result); + + /* + * assume that this session is going to be + * AppleTalk, until we find out otherwise + * (this depends on what type of OpenSession + * packet actually arrives) + * + */ + dsi_session[*SessRefNum].sesstype = DSI_SESSION_ATALK; + + /* + * initialise data structure for DSI state + * + */ + dsi_session[*SessRefNum].timeout = 0; + dsi_session[*SessRefNum].aspqe = NULL; + dsi_session[*SessRefNum].sess_id_in = 0; + dsi_session[*SessRefNum].sess_id_out = 0; + dsi_session[*SessRefNum].state = DSI_STATE_HDR; + dsi_session[*SessRefNum].lenleft = sizeof(struct dsi_hdr); + dsi_session[*SessRefNum].ptr = (char *)&dsi_session[*SessRefNum].hdr; + + return(noErr); +} + +/* + * fork and create new process to handle session + * + * SessRefNum - session reference number + * stickle - want server tickle + * ctickle - want client tickle + * + */ + +int +dsiFork(SessRefNum, stickle, ctickle) +int SessRefNum; +int stickle; +int ctickle; +{ + /* + * if AppleTalk, hand off to Session Protocol + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) + return(SPFork(SessRefNum, stickle, ctickle)); + + /* + * handle locally for TCP/IP + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) + return(dsiTCPIPFork(SessRefNum, stickle, ctickle)); + + return(ParamErr); +} + +/* + * set the user-timeout routine and argument + * + * this needs to be handled in the parent + * process for AppleTalk connections and in + * the child process for TCP/IP connections + * + * 'pid' was obtained from the approriate fork() + * + */ + +int +dsiTickleUserRoutine(SessRefNum, routine, pid) +int SessRefNum; +int (*routine)(); +int pid; +{ + /* + * AppleTalk + * + */ + if (pid) + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) + return(SPTickleUserRoutine(SessRefNum, routine, pid)); + + /* + * TCP/IP + * + */ + if (!pid) + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) + return(dsiTCPIPTickleUserRoutine(SessRefNum, routine, getpid())); + + return(noErr); +} + +/* + * set up to wait for a request on SSS + * + * SessRefNum - session reference number + * ReqBuff - request command buffer + * ReqBuffSize - request command buffer size + * ReqRefNum - pointer to a special command block + * SPReqType - returns command request type + * ActRcvdReqLen - returns command length + * comp - completion flag/error + * + */ + +int +dsiGetRequest(SessRefNum, ReqBuff, ReqBuffSize, + ReqRefNum, SPReqType, ActRcvdReqLen, comp) +int SessRefNum; +char *ReqBuff; +int ReqBuffSize; +ASPQE **ReqRefNum; +int *SPReqType; +int *ActRcvdReqLen; +int *comp; +{ + /* + * AppleTalk + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) + return(SPGetRequest(SessRefNum, ReqBuff, ReqBuffSize, + ReqRefNum, SPReqType, ActRcvdReqLen, comp)); + + /* + * TCP/IP + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) + return(dsiTCPIPGetRequest(SessRefNum, ReqBuff, ReqBuffSize, + ReqRefNum, SPReqType, ActRcvdReqLen, comp)); + + return(ParamErr); +} + +/* + * 'read' data sent by client + * + * SessRefNum - session reference number + * ReqRefNum - client connection details (addr, TID) + * Buffer - final location for data + * BufferSize - maximum amount of data we can handle + * ActLenRcvd - actual amount of date received + * atptimeout - ATP get data timeout + * comp - completion flag/error + * + */ + +int +dsiWrtContinue(SessRefNum, ReqRefNum, Buffer, BufferSize, + ActLenRcvd, atptimeout, comp) +int SessRefNum; +ASPQE *ReqRefNum; +char *Buffer; +int BufferSize; +int *ActLenRcvd; +int atptimeout; +int *comp; +{ + /* + * AppleTalk + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) + return(SPWrtContinue(SessRefNum, ReqRefNum, Buffer, + BufferSize, ActLenRcvd, atptimeout, comp)); + + /* + * TCP/IP + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) + return(dsiTCPIPWrtContinue(SessRefNum, ReqRefNum, Buffer, + BufferSize, ActLenRcvd, atptimeout, comp)); + + return(ParamErr); +} + +/* + * reply to a write request sent to our SSS + * + * SessRefNum - session reference number + * ReqRefNum - client connection details (addr, TID) + * CmdResult - return result + * CmdReplyData - return data + * CmdReplyDataSize - return data size + * comp - completion flag/error + * + */ + +int +dsiWrtReply(SessRefNum, ReqRefNum, CmdResult, + CmdReplyData, CmdReplyDataSize, comp) +int SessRefNum; +ASPQE *ReqRefNum; +dword CmdResult; +char *CmdReplyData; +int CmdReplyDataSize; +int *comp; +{ + /* + * AppleTalk + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) + return(SPWrtReply(SessRefNum, ReqRefNum, CmdResult, + CmdReplyData, CmdReplyDataSize, comp)); + + /* + * TCP/IP + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) + return(dsiTCPIPWrtReply(SessRefNum, ReqRefNum, CmdResult, + CmdReplyData, CmdReplyDataSize, comp)); + + return(ParamErr); +} + +/* + * Reply to a command request sent to our SSS + * + * SessRefNum - session reference number + * ReqRefNum - client connection details (addr, TID) + * CmdResult - return result + * CmdReplyData - return data + * CmdReplyDataSize - return data size + * comp - completion flag/error + * + */ + +int +dsiCmdReply(SessRefNum, ReqRefNum, CmdResult, + CmdReplyData, CmdReplyDataSize, comp) +int SessRefNum; +ASPQE *ReqRefNum; +dword CmdResult; +char *CmdReplyData; +int CmdReplyDataSize; +int *comp; +{ + /* + * AppleTalk + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) + return(SPCmdReply(SessRefNum, ReqRefNum, CmdResult, + CmdReplyData, CmdReplyDataSize, comp)); + + /* + * TCP/IP + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) + return(dsiTCPIPCmdReply(SessRefNum, ReqRefNum, CmdResult, + CmdReplyData, CmdReplyDataSize, comp)); + + return(ParamErr); +} + +/* + * send an Attention signal to WSS + * + * SessRefNum - session reference number + * AttentionCode - attention message + * atpretries - ATP Retries + * atptimeout - ATP Timeout + * comp - completion falg/error + * + */ + +int +dsiAttention(SessRefNum, AttentionCode, atpretries, atptimeout, comp) +int SessRefNum; +word AttentionCode; +int atpretries; +int *comp; +{ + /* + * AppleTalk + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) + return(SPAttention(SessRefNum, AttentionCode, + atpretries, atptimeout, comp)); + + /* + * TCP/IP + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) + return(dsiTCPIPAttention(SessRefNum, AttentionCode, + atpretries, atptimeout, comp)); + + return(ParamErr); +} + +/* + * return remote address of session client + * + */ + +int +dsiGetNetworkInfo(SessRefNum, addr) +int SessRefNum; +AddrBlock *addr; +{ + /* + * AppleTalk + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) + return(SPGetNetworkInfo(SessRefNum, addr)); + + /* + * TCP/IP + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) + return(dsiTCPIPGetNetworkInfo(SessRefNum, addr)); + + return(ParamErr); +} + +/* + * Get server operating parameters (these numbers are used + * to malloc() the appropriate amount of buffer space). + * + * MaxCmdSize - maximum single packet size + * QuantumSize - maximum outstanding data + * + * For ASP/ATP: + * MaxCmdSize = atpMaxData (578) + * QuantumSize = atpMaxData*atpMaxNum (578*8) + * + * For TCP/IP: + * MaxCmdSize = AFP Command Size (1500) + * QuantumSize = Data Chunk Size (65536) + * + */ + +int +dsiGetParms(MaxCmdSize, QuantumSize) +int *MaxCmdSize; +int *QuantumSize; +{ + if (asip_enable) { + *MaxCmdSize = DSI_SRVR_CMD; + *QuantumSize = DSI_SRVR_MAX; + return(noErr); + } + + return(SPGetParms(MaxCmdSize, QuantumSize)); +} + +/* + * Close down a SSS + * + * SessRefNum - Session reference number + * atpretries - ATP Retries + * atptimeout - ATP Timeout + * comp - completion flag/error + * + */ + +int +dsiCloseSession(SessRefNum, atpretries, atptimeout, comp) +int SessRefNum; +int atpretries; +int atptimeout; +int *comp; +{ + /* + * AppleTalk + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) + return(SPCloseSession(SessRefNum, atpretries, atptimeout, comp)); + + /* + * TCP/IP + * + */ + if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) + return(dsiTCPIPCloseSession(SessRefNum, atpretries, atptimeout, comp)); + + return(ParamErr); +} + +/* + * shutdown session + * + * SessRefNum - session reference number + * + */ + +int +dsiShutdown(SessRefNum) +int SessRefNum; +{ + /* + * clean up our session data + * + */ + if (dsi_session[SessRefNum].aspqe != NULL) + delete_aspaqe(dsi_session[SessRefNum].aspqe); + + dsi_session[SessRefNum].timeout = 0; + dsi_session[SessRefNum].aspqe = NULL; + dsi_session[SessRefNum].sess_id_in = 0; + dsi_session[SessRefNum].sess_id_out = 0; + dsi_session[SessRefNum].state = DSI_STATE_HDR; + dsi_session[SessRefNum].sesstype = DSI_SESSION_ATALK; + dsi_session[SessRefNum].lenleft = sizeof(struct dsi_hdr); + dsi_session[SessRefNum].ptr = (char *)&dsi_session[SessRefNum].hdr; + + /* + * then clean up the ASP stuff + * + */ + return(SPShutdown(SessRefNum)); +} + +#ifdef DEBUG_AFP_CMD +/* + * return descriptive command name string + * + */ +char * +dsi_cmd(cmd) +int cmd; +{ + switch (cmd) { + case DSIGetStatus: + return("DSIGetStatus"); + break; + case DSIOpenSession: + return("DSIOpenSession"); + break; + case DSICommand: + return("DSICommand"); + break; + case DSIWrite: + return("DSIWrite"); + break; + case DSIAttention: + return("DSIAttention"); + break; + case DSITickle: + return("DSITickle"); + break; + case DSICloseSession: + return("DSICloseSession"); + break; + } + return("UNKNOWN"); +} +#endif /* DEBUG_AFP_CMD */ + +/* + * TCP/IP related routines + * + */ + +/* + * open and initialise TCP/IP SLS port + * + * "The interface will register the AFP server on a well-known + * (static) data stream port. In case of TCP, it will be TCP + * port number 548". + * + */ + +private int slsskt = -1; +private struct sockaddr_in lsin; + +int +dsiTCPIPInit(SLSEntityIdentifier, ServiceStatusBlock, + ServiceStatusBlockSize, SLSRefNum) +AddrBlock *SLSEntityIdentifier; /* SLS Net id */ +char *ServiceStatusBlock; /* block with status info */ +int ServiceStatusBlockSize; /* size of status info */ +int *SLSRefNum; /* sls ref num return place */ +{ + int aport, len; + extern u_int asip_addr; + extern u_short asip_port; + private int dsiTCPIPSLSListener(); + struct protoent *t, *getprotobyname(); + + /* + * open a stream socket + * + */ + if ((slsskt = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return(slsskt); + + bzero((char *)&lsin, sizeof(lsin)); + + lsin.sin_family = AF_INET; + lsin.sin_port = htons(asip_port); + lsin.sin_addr.s_addr = htonl(asip_addr); + + /* + * want to send data as it becomes available + * + */ + len = 1; + t = getprotobyname("tcp"); + aport = (t == NULL) ? IPPROTO_TCP : t->p_proto; + setsockopt(slsskt, aport, TCP_NODELAY, (char *)&len, sizeof(int)); + + /* + * bind to ipaddr:port selected by AUFS -B option + * (defaults to INADDR_ANY:548) + * + */ + if (bind(slsskt, (struct sockaddr *)&lsin, sizeof(lsin)) != 0) { + close(slsskt); + return(-1); + } + + /* + * start listening for connection attempts + * + */ + if (listen(slsskt, 5) != 0) { + close(slsskt); + return(-1); + } + + /* + * register a callback routine to handle SLS connection requests + * + */ + fdlistener(slsskt, dsiTCPIPSLSListener, NULL, *SLSRefNum); + + return(noErr); +} + +/* + * fdlistener() callback routine for TCP/IP connection attempts + * + * accept() the connection and register a data listener for + * incoming connection/getstatus packets. + * + */ + +private int +dsiTCPIPSLSListener(fd, none, SLSRefNum) +int fd; +caddr_t none; +int SLSRefNum; +{ + int len, acc; + int illegal = 0; + struct sockaddr_in rsin; + extern u_short asip_port; + private int dsiTCPIPIllegalIP(); + private int dsiTCPIPSSSListener(); + + len = sizeof(struct sockaddr_in); + if ((acc = accept(fd, (struct sockaddr *)&rsin, &len)) < 0) + return(acc); + + /* + * check our IP address filter for + * a disallowed source IP address + * + */ + if (!dsiTCPIPIllegalIP(&rsin)) + fdlistener(acc, dsiTCPIPSSSListener, NULL, SLSRefNum); + else + close(acc), illegal = 1; + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, + "** AppleShareIP connection attempt to port %d from %s:%d (PID %d)\n", + asip_port, inet_ntoa(rsin.sin_addr), ntohs(rsin.sin_port), getpid()); + if (illegal) + fprintf(dbg, "** Rejected by IP address filter (%s)\n", dsiTCPIPFilter); + fprintf(dbg, "\n\n"); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/* + * fdlistener() callback routine for incoming DSI requests + * + * "An AFP server expects two command types, that is, DSIOpenSession + * or DSIGetStatus after the data stream connection establishment. + * DSIOpenSession command confirms the clients commitment to open an + * actual DSI session. There is a 1-to-1 mapping between the data + * stream connection and a DSI session. DSIGetSTatus command replies + * the server status followed by the connection tear down by an AFP + * server". + * + * This handler is interested only in DSIGetStatus or DSIOpenSession + * requests. Once the session is open, we unregister the fd with this + * handler and re-register it with the generic session handler. + * + */ + +private int +dsiTCPIPSSSListener(fd, none, SLSRefNum) +int fd; +caddr_t none; +int SLSRefNum; +{ + int len; + int optlen; + ASPSkt *as; + ASPSSkt *sas; + int SessRefNum; + char reply_opt[8]; + struct dsi_hdr hdr; + char *optptr, *reqst_opt; + private int dsiTCPIPSessListener(); + + /* + * hopefully there are at least sizeof(hdr) bytes available to read + * (of course, there may not be, but the extra trouble of keeping a + * per stream partial header for just DSIGetStatus and DSIOpenSession + * isn't really worth it). + * + */ + if ((len = read(fd, (char *)&hdr, sizeof(hdr))) != sizeof(hdr)) { + fdunlisten(fd); + close(fd); + return(-1); + } + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + char *dsi_cmd(); + fprintf(dbg, "<< AppleShareIP DSI header (PID %d, session startup):\n", + getpid()); + fprintf(dbg, "\tFlags: %02x (%s)\n", hdr.dsi_flags, + (hdr.dsi_flags == DSI_REQ_FLAG) ? "Request" : "REPLY!!"); + fprintf(dbg, "\tCommand: %02x (%s)\n", hdr.dsi_command, + dsi_cmd(hdr.dsi_command)); + fprintf(dbg, "\tRequestID: %d\n", ntohs(hdr.dsi_requestID)); + fprintf(dbg, "\tErrCode/DataOffset: %d\n", ntohl(hdr.dsi_err_offset)); + fprintf(dbg, "\tDataLength: %d\n", ntohl(hdr.dsi_data_len)); + fprintf(dbg, "\tReserved: %d\n\n\n", ntohl(hdr.dsi_reserved)); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + /* + * not interested in Replies + * (should be none) + * + */ + if (hdr.dsi_flags != DSI_REQ_FLAG) + return(noErr); + + /* + * process the request + * + */ + switch (hdr.dsi_command) { + case DSIGetStatus: + /* dig out saved server status info for this SLS */ + if ((sas = aspsskt_find_slsrefnum(SLSRefNum)) != NULL) { + hdr.dsi_flags = DSI_REP_FLAG; + hdr.dsi_err_offset = htonl(noErr); + hdr.dsi_data_len = htonl(sas->ssbl); + hdr.dsi_reserved = htonl(0x00000000); + /* send server status information */ + dsiTCPIPWrite(fd, &hdr, (char *)sas->ssb, sas->ssbl); + } + /* fall through */ + default: /* tear down connection */ + fdunlisten(fd); + close(fd); + break; + case DSIOpenSession: + /* search for SLS next waiting session */ + if ((as = aspskt_find_active(SLSRefNum)) == NULL) { + hdr.dsi_flags = DSI_REP_FLAG; + hdr.dsi_err_offset = htonl(ServerBusy); + hdr.dsi_data_len = htonl(0x00000000); + hdr.dsi_reserved = htonl(0x00000000); + dsiTCPIPWrite(fd, &hdr, NULL, 0); + fdunlisten(fd); + close(fd); + break; + } + /* check for incoming OpenSession options */ + if ((optlen = ntohl(hdr.dsi_data_len)) > 0) { + if ((reqst_opt = (char *)malloc(optlen)) != NULL) { + optptr = reqst_opt; + while (optlen > 0) { + if ((len = read(fd, optptr, optlen)) < 0) { + fdunlisten(fd); + close(fd); + break; + } + optlen -= len; + optptr += len; + } + /* + * one day we might actually care + * + dsi_parse_option(optptr); + * + */ + free(reqst_opt); + } + } + /* start session */ + as->ss = fd; + as->state = SP_STARTED; + SessRefNum = as->SessRefNum; + /* mark this session as type TCP/IP */ + dsi_session[SessRefNum].sesstype = DSI_SESSION_TCPIP; + /* set up state for this session */ + dsi_session[SessRefNum].state = DSI_STATE_HDR; + dsi_session[SessRefNum].lenleft = sizeof(struct dsi_hdr); + dsi_session[SessRefNum].ptr = (char *)&dsi_session[SessRefNum].hdr; + dsi_session[SessRefNum].sess_id_in = ntohs(hdr.dsi_requestID)+1; + dsi_session[SessRefNum].sess_id_out = 0; + dsi_session[SessRefNum].aspqe = NULL; + dsi_session[SessRefNum].timeout = 0; + /* set OpenSession reply option */ + optlen = DSI_OPT_REQLEN+2; + reply_opt[0] = DSI_OPT_REQQ; + reply_opt[1] = DSI_OPT_REQLEN; + reply_opt[2] = (DSI_SRVR_MAX >> 24) & 0xff; + reply_opt[3] = (DSI_SRVR_MAX >> 16) & 0xff; + reply_opt[4] = (DSI_SRVR_MAX >> 8) & 0xff; + reply_opt[5] = (DSI_SRVR_MAX) & 0xff; + /* setup response header */ + hdr.dsi_flags = DSI_REP_FLAG; + hdr.dsi_err_offset = htonl(noErr); + hdr.dsi_data_len = htonl(optlen); + hdr.dsi_reserved = htonl(0x00000000); + /* send OpenSession Reply */ + dsiTCPIPWrite(fd, &hdr, reply_opt, optlen); + /* move fd to session handler */ + fdunlisten(fd); + fdlistener(fd, dsiTCPIPSessListener, (caddr_t)as, SessRefNum); + *as->comp = noErr; + return(noErr); + break; + } + + return(noErr); +} + +/* + * data listener for opened sessions + * + * At any time the data listener can be in one of four states, + * waiting until the expected amount of data has arrived, or a + * reply has been sent, freeing the header for re-use: + * + * DSI_STATE_HDR - reading the 16-byte DSI header + * DSI_STATE_AFP - reading the AFP command data + * DSI_STATE_DAT - reading the DSIWrite data + * DSI_STATE_REP - waiting until reply is sent + * + */ + +private int +dsiTCPIPSessListener(fd, as, SessRefNum) +int fd; +ASPSkt *as; +int SessRefNum; +{ + int len; + int comp; + char *ptr; + ASPQE *aspqe; + atpProto *ap; + struct dsi_hdr *hdr; + + /* + * better have a waiting request + * + */ + if ((aspqe = dsi_session[SessRefNum].aspqe) == NULL) { + logit(0, "Incoming TCP/IP data but no pending request"); + dsiTCPIPCloseSession(SessRefNum, 0, 0, &comp); + return(-1); + } + + /* + * ignore available data until reply is sent + * (reply uses the sessionID in DSI header) or + * dsiTCPIPWrtContinue() changes the state to + * DSI_STATE_DAT + * + */ + if (dsi_session[SessRefNum].state == DSI_STATE_REP) + return(noErr); + + /* + * read DSI header or data from the + * tcp/ip stream as it comes in + * + */ + len = dsi_session[SessRefNum].lenleft; + ptr = dsi_session[SessRefNum].ptr; + + if ((len = read(fd, ptr, len)) < 0) { + logit(0, "TCP/IP read() returned %d (errno %d)", len, errno); + dsiTCPIPCloseSession(SessRefNum, 0, 0, &comp); + *aspqe->comp = SessClosed; + return(len); + } + + dsi_session[SessRefNum].lenleft -= len; + dsi_session[SessRefNum].ptr += len; + + if (dsi_session[SessRefNum].lenleft > 0) + return(noErr); + + /* + * sanity check + * + */ + if (dsi_session[SessRefNum].lenleft < 0) { + logit(0, "mismatch in expected amount of read data"); + dsiTCPIPCloseSession(SessRefNum, 0, 0, &comp); + *aspqe->comp = SessClosed; + return(-1); + } + + hdr = &dsi_session[SessRefNum].hdr; + + /* + * finished reading something, deal with it + * + */ + switch (dsi_session[SessRefNum].state) { + case DSI_STATE_HDR: + /* now have a complete DSI hdr */ + if (ntohl(hdr->dsi_data_len) > 0) { + /* and AFP hdr to follow */ + ap = &aspqe->abr.proto.atp; + dsi_session[SessRefNum].ptr = ap->atpDataPtr; + if (hdr->dsi_command == DSIWrite && ntohl(hdr->dsi_err_offset) != 0) + dsi_session[SessRefNum].lenleft = ntohl(hdr->dsi_err_offset); + else + dsi_session[SessRefNum].lenleft = ntohl(hdr->dsi_data_len); + dsi_session[SessRefNum].state = DSI_STATE_AFP; + return(noErr); + break; + } + /* fall through */ + case DSI_STATE_AFP: + /* have DSI hdr and optional AFP header */ + dsi_session[SessRefNum].ptr = (char *)hdr; + dsi_session[SessRefNum].lenleft = sizeof(struct dsi_hdr); + if (hdr->dsi_flags == DSI_REQ_FLAG) + dsi_session[SessRefNum].state = DSI_STATE_REP; + else + dsi_session[SessRefNum].state = DSI_STATE_HDR; + break; + case DSI_STATE_DAT: + /* have all DSIWrite data, reset state, tell client */ + dsi_session[SessRefNum].aspqe = NULL; + dsi_session[SessRefNum].ptr = (char *)hdr; + dsi_session[SessRefNum].lenleft = sizeof(struct dsi_hdr); + dsi_session[SessRefNum].state = DSI_STATE_REP; + *aspqe->ActRcvdReqLen = ntohl(hdr->dsi_data_len); + *aspqe->ActRcvdReqLen -= ntohl(hdr->dsi_err_offset); + *aspqe->comp = noErr; + delete_aspaqe(aspqe); + return(noErr); + break; + default: + /* huh ? */ + break; + } + + /* + * process DSI header and optional AFP data + * + */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + char *dsi_cmd(); + fprintf(dbg, "<< AppleShareIP DSI header (PID %d, session #%d):\n", + getpid(), SessRefNum); + fprintf(dbg, "\tFlags: %02x (%s)\n", hdr->dsi_flags, + (hdr->dsi_flags == DSI_REQ_FLAG) ? "Request" : "Reply"); + fprintf(dbg, "\tCommand: %02x (%s)\n", hdr->dsi_command, + dsi_cmd(hdr->dsi_command)); + fprintf(dbg, "\tRequestID: %d\n", ntohs(hdr->dsi_requestID)); + fprintf(dbg, "\tErrCode/DataOffset: %d\n", ntohl(hdr->dsi_err_offset)); + fprintf(dbg, "\tDataLength: %d\n", ntohl(hdr->dsi_data_len)); + fprintf(dbg, "\tReserved: %d\n\n\n", ntohl(hdr->dsi_reserved)); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + /* + * reset tickle timer + * + */ + dsi_session[SessRefNum].timeout = 0; + + /* + * ignore packet replies, rely on TCP to + * deliver in-order, that's what it's for. + * + */ + if (hdr->dsi_flags == DSI_REP_FLAG) + return(noErr); + + /* + * must be request, check if incoming + * session ID is what we are expecting + * + */ + if (ntohs(hdr->dsi_requestID) != dsi_session[SessRefNum].sess_id_in) { + logit(0, "unexpected incoming TCP/IP session ID"); + *aspqe->comp = ParamErr; + return(-1); + } + dsi_session[SessRefNum].sess_id_in++; + + /* + * only 3 valid commands to pass on to client + * handle DSITickle locally, 'cause it's simple + * + */ + switch (hdr->dsi_command) { + case DSICommand: + case DSIWrite: + *aspqe->comp = noErr; + break; + case DSICloseSession: + *aspqe->comp = SessClosed; + break; + case DSITickle: + hdr->dsi_flags = DSI_REP_FLAG; + dsiTCPIPWrite(fd, hdr, NULL, 0); + dsi_session[SessRefNum].state = DSI_STATE_HDR; + return(noErr); + break; + default: + logit(0, "unexpected incoming DSI cond (%d)", hdr->dsi_command); + *aspqe->comp = ParamErr; + break; + } + + /* + * tell the client how much data + * came in and the command type + * + */ + if (hdr->dsi_command == DSIWrite && ntohl(hdr->dsi_err_offset) != 0) + *aspqe->ActRcvdReqLen = ntohl(hdr->dsi_err_offset); + else + *aspqe->ActRcvdReqLen = ntohl(hdr->dsi_data_len); + *aspqe->SPReqType = hdr->dsi_command; + *aspqe->ReqRefNum = aspqe; + + /* + * free previous GetRequest aspqe + * + */ + delete_aspaqe(aspqe); + dsi_session[SessRefNum].aspqe = NULL; + + return(noErr); +} + +/* + * fork and create new process to handle TCP/IP session + * + * SessRefNum - session reference number + * stickle - want server tickle + * ctickle - want client tickle + * + * In the server code (parent) close all but SLS + * In the child code forget about SLS, just listen + * for SSS requests + * + */ + +int +dsiTCPIPFork(SessRefNum, stickle, ctickle) +int SessRefNum; +int stickle; +int ctickle; +{ + int i, pid; + ASPSSkt *sas; + extern int sqs; + ASPSkt *as, *bs; + extern int numasp; + private void dsiTCPIPTimer(); + + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) + return(-1); + + if (as->state != SP_STARTED) + return(-1); + + /* + * make a new process + * + */ + if ((pid = fork()) < 0) + return(pid); + + /* + * if in parent process: + * close SSS + * + * if in child process: + * close both SLS (AppleTalk and TCP/IP) + * start tickle timer for our client session + * stop tickle timers for sibling ATALK sessions + * + */ + if (pid) { + /* close SSS */ + if (as->ss != -1) { + fdunlisten(as->ss); + close(as->ss); + as->ss = -1; + } + as->state = SP_HALFCLOSED; + } else { /* in child */ + if (as->type != SP_SERVER) + return(noErr); + /* close TCP/IP SLS */ + dsiTCPIPCloseSLS(); + /* kill sibling AT timeouts */ + for (i = 0; i < numasp; i++) { + if (i != SessRefNum) { + if ((bs = aspskt_find_sessrefnum(i)) != NULL) { + if (bs->tickling) + stopasptickle(bs); + stop_ttimer(bs); + } + } + } + /* close AppleTalk SLS */ + if ((sas = aspsskt_find_slsrefnum(as->SLSRefNum)) != NULL) + ATPCloseSocket(sas->addr.skt); + /* set a new read quantum */ + sqs = DSI_SRVR_MAX; + /* start our tickle timer */ + Timeout(dsiTCPIPTimer, numasp, DSI_TIMEOUT); + } + + return(pid); +} + +/* + * set up to wait for a request on TCP/IP SSS + * + * SessRefNum - session reference number + * ReqBuff - request command buffer + * ReqBuffSize - request command buffer size + * ReqRefNum - pointer to a special command block + * SPReqType - returns command request type + * ActRcvdReqLen - returns command length + * comp - completion flag/error + * + */ + +int +dsiTCPIPGetRequest(SessRefNum, ReqBuff, ReqBuffSize, + ReqRefNum, SPReqType, ActRcvdReqLen, comp) +int SessRefNum; +char *ReqBuff; +int ReqBuffSize; +ASPQE **ReqRefNum; +int *SPReqType; +int *ActRcvdReqLen; +int *comp; +{ + ASPSkt *as; + atpProto *ap; + ASPQE *aspqe; + + /* + * check state of connection + * and validity of descriptor + * + */ + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + if (as->state != SP_STARTED) { + *comp = SessClosed; + return(SessClosed); + } + if (as->ss == -1) { + *comp = ParamErr; + return(ParamErr); + } + + /* + * subsequent GetRequests from TREL_TIMEOUT code + * on this SessRefNum will never get a callback + * (because we don't need them for TCP/IP use) + * + */ + if (dsi_session[SessRefNum].aspqe != NULL) { + *comp = 1; + return(noErr); + } + + /* + * save GetRequest args for data arrival + * + */ + aspqe = create_aspaqe(); + + aspqe->type = tSPGetRequest; + aspqe->SessRefNum = SessRefNum; + aspqe->ReqRefNum = ReqRefNum; + aspqe->SPReqType = SPReqType; + aspqe->ActRcvdReqLen = ActRcvdReqLen; + aspqe->comp = comp; + + ap = &aspqe->abr.proto.atp; + ap->atpReqCount = ReqBuffSize; + ap->atpDataPtr = ReqBuff; + + dsi_session[SessRefNum].aspqe = aspqe; + + *comp = 1; + return(noErr); +} + +/* + * arrange to put the 'read' data into Buffer + * + * SessRefNum - session reference number + * ReqRefNum - client connection details (addr, TID) + * Buffer - final location for data + * BufferSize - maximum amount of data we can handle + * ActLenRcvd - actual amount of date received + * atptimeout - ATP get data timeout + * comp - completion flag/error + * + */ + +dsiTCPIPWrtContinue(SessRefNum, ReqRefNum, Buffer, + BufferSize, ActLenRcvd, atptimeout, comp) +int SessRefNum; +ASPQE *ReqRefNum; +char *Buffer; +int BufferSize; +int *ActLenRcvd; +int atptimeout; +int *comp; +{ + ASPSkt *as; + ASPQE *aspqe; + struct dsi_hdr *hdr; + + /* + * sanity checks + * + */ + if (BufferSize < 0) { + *comp = ParamErr; + return(ParamErr); + } + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + if (as->state != SP_STARTED) { + *comp = SessClosed; + return(SessClosed); + } + if (as->ss == -1) { + *comp = ParamErr; + return(ParamErr); + } + + /* + * save WrtContinue args for + * completion of data arrival + * + */ + aspqe = create_aspaqe(); + + aspqe->type = tSPWrtContinue; + aspqe->SessRefNum = SessRefNum; + aspqe->ActRcvdReqLen = ActLenRcvd; + aspqe->comp = comp; + + /* + * reset state & continue data reads + * + */ + hdr = &dsi_session[SessRefNum].hdr; + dsi_session[SessRefNum].aspqe = aspqe; + dsi_session[SessRefNum].state = DSI_STATE_DAT; + dsi_session[SessRefNum].lenleft = ntohl(hdr->dsi_data_len); + dsi_session[SessRefNum].lenleft -= ntohl(hdr->dsi_err_offset); + dsi_session[SessRefNum].ptr = Buffer; + + *comp = 1; + return(noErr); +} + +/* + * reply to a write request sent to our TCP/IP SSS + * + * SessRefNum - session reference number + * ReqRefNum - client connection details (addr, TID) + * CmdResult - return result + * CmdReplyData - return data + * CmdReplyDataSize - return data size + * comp - completion flag/error + * + */ + +int +dsiTCPIPWrtReply(SessRefNum, ReqRefNum, CmdResult, + CmdReplyData, CmdReplyDataSize, comp) +int SessRefNum; +ASPQE *ReqRefNum; +dword CmdResult; +char *CmdReplyData; +int CmdReplyDataSize; +int *comp; +{ + return(dsiTCPIPReply(DSIWrite, SessRefNum, ReqRefNum, + CmdResult, CmdReplyData, CmdReplyDataSize, comp)); +} + +/* + * Reply to a command request sent to our TCP/IP SSS + * + * SessRefNum - session reference number + * ReqRefNum - client connection details (addr, TID) + * CmdResult - return result + * CmdReplyData - return data + * CmdReplyDataSize - return data size + * comp - completion flag/error + * + */ + +int +dsiTCPIPCmdReply(SessRefNum, ReqRefNum, CmdResult, + CmdReplyData, CmdReplyDataSize, comp) +int SessRefNum; +ASPQE *ReqRefNum; +dword CmdResult; +char *CmdReplyData; +int CmdReplyDataSize; +int *comp; +{ + return(dsiTCPIPReply(DSICommand, SessRefNum, ReqRefNum, + CmdResult, CmdReplyData, CmdReplyDataSize, comp)); +} + +/* + * common reply code + * + */ + +int +dsiTCPIPReply(dsiType, SessRefNum, ReqRefNum, CmdResult, + CmdReplyData, CmdReplyDataSize, comp) +int dsiType; +int SessRefNum; +ASPQE *ReqRefNum; +dword CmdResult; +char *CmdReplyData; +int CmdReplyDataSize; +int *comp; +{ + ASPSkt *as; + struct dsi_hdr hdr; + + /* + * some sanity checking + * + */ + if (CmdReplyDataSize < 0) { + *comp = ParamErr; + return(ParamErr); + } + if (CmdReplyDataSize > DSI_SRVR_MAX) { + *comp = SizeErr; + return(SizeErr); + } + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + if (as->state != SP_STARTED) { + *comp = ParamErr; + return(ParamErr); + } + if (as->ss == -1) { + *comp = ParamErr; + return(ParamErr); + } + + /* + * setup DSI response header + * (the requestID is already + * in network byte order) + * + */ + hdr.dsi_flags = DSI_REP_FLAG; + hdr.dsi_command = dsiType; + hdr.dsi_requestID = dsi_session[SessRefNum].hdr.dsi_requestID; + hdr.dsi_err_offset = htonl(CmdResult); + hdr.dsi_data_len = htonl(CmdReplyDataSize); + hdr.dsi_reserved = htonl(0x00000000); + + /* + * session hdr can be re-used now + * + */ + dsi_session[SessRefNum].state = DSI_STATE_HDR; + + /* + * send it ... + * + */ + if (dsiTCPIPWrite(as->ss, &hdr, CmdReplyData, CmdReplyDataSize) < 0) { + *comp = ParamErr; + return(ParamErr); + } + + *comp = noErr; + return(noErr); +} + +/* + * setup tickle timeout callback + * + */ + +int +dsiTCPIPTickleUserRoutine(SessRefNum, routine, arg) +int SessRefNum; +int (*routine)(); +int arg; +{ + ASPSkt *as; + + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) + return(ParamErr); + + as->tickle_timeout_user = routine; + as->ttu_arg = arg; + + return(noErr); +} + +/* + * Close down a TCP/IP Service Socket socket + * + * SessRefNum - Session reference number + * atpretries - ATP Retries + * atptimeout - ATP Timeout + * comp - completion flag/error + * + */ + +private struct dsi_hdr shut_hdr; + +int +dsiTCPIPCloseSession(SessRefNum, atpretries, atptimeout, comp) +int SessRefNum; +int atpretries; +int atptimeout; +int *comp; +{ + ASPSkt *as; + + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + + switch (as->state) { + case SP_STARTED: + break; + case SP_HALFCLOSED: + break; + default: + as->active = FALSE; /* aspskt_free(as); */ + return(noErr); + break; + } + + /* + * set up the DSI header + * + */ + shut_hdr.dsi_flags = DSI_REQ_FLAG; + shut_hdr.dsi_command = DSICloseSession; + shut_hdr.dsi_requestID = htons(dsi_session[SessRefNum].sess_id_out++); + shut_hdr.dsi_err_offset = htonl(0x00000000); + shut_hdr.dsi_data_len = htonl(0x00000000); + shut_hdr.dsi_reserved = htonl(0x00000000); + + /* + * and send it ... + * + */ + if (dsiTCPIPWrite(as->ss, &shut_hdr, NULL, 0) < 0) { + *comp = ParamErr; + return(ParamErr); + } + + as->state = SP_INACTIVE; + as->active = FALSE; /* aspskt_free(as); */ + + if (as->ss != -1) { + fdunlisten(as->ss); + close(as->ss); + as->ss = -1; + } + + *comp = noErr; + return(noErr); +} + +/* + * send a TCP/IP Attention signal to WSS + * + * SessRefNum - session reference number + * AttentionCode - attention message + * atpretries - ATP Retries + * atptimeout - ATP Timeout + * comp - completion falg/error + * + */ + +private struct dsi_hdr attn_hdr; + +int +dsiTCPIPAttention(SessRefNum, AttentionCode, atpretries, atptimeout, comp) +int SessRefNum; +word AttentionCode; +int atpretries; +int *comp; +{ + ASPSkt *as; + char attn[2]; + + /* + * some sanity checking + * + */ + if (AttentionCode == 0x00) { + *comp = ParamErr; + return(ParamErr); + } + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + if (as->state == SP_STARTING) { + *comp = ParamErr; + return(ParamErr); + } + + /* + * set up the DSI attention header, + * + */ + attn_hdr.dsi_flags = DSI_REQ_FLAG; + attn_hdr.dsi_command = DSIAttention; + attn_hdr.dsi_requestID = htons(dsi_session[SessRefNum].sess_id_out++); + attn_hdr.dsi_err_offset = htonl(0x00000000); + attn_hdr.dsi_data_len = htonl(sizeof(attn)); + attn_hdr.dsi_reserved = htonl(0x00000000); + + /* + * the attention field + * + */ + attn[0] = AttentionCode >> 8; + attn[1] = AttentionCode & 0xff; + + /* + * and send it ... + * + */ + if (dsiTCPIPWrite(as->ss, &attn_hdr, attn, sizeof(attn)) < 0) { + *comp = ParamErr; + return(ParamErr); + } + + *comp = noErr; + return(noErr); +} + +/* + * return peer name of session client + * + * (NB: function return value is positive TCP/IP port number, + * to distinguish this from a real AppleTalk GetNetworkInfo + * call which returns noErr. The IP address is returned in + * the four bytes of the AddrBlock) + * + */ + +int +dsiTCPIPGetNetworkInfo(SessRefNum, addr) +int SessRefNum; +AddrBlock *addr; +{ + ASPSkt *as; + struct sockaddr_in name; + int len = sizeof(struct sockaddr); + + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) + return(ParamErr); + + if (as->ss == -1) + return(ParamErr); + + if (getpeername(as->ss, (struct sockaddr *)&name, &len) != 0) + return(ParamErr); + + if (name.sin_family != AF_INET) + return(ParamErr); + + name.sin_addr.s_addr = ntohl(name.sin_addr.s_addr); + addr->net = ((name.sin_addr.s_addr & 0xff000000) >> 16); + addr->net |= ((name.sin_addr.s_addr & 0x00ff0000) >> 16); + addr->node = ((name.sin_addr.s_addr & 0x0000ff00) >> 8); + addr->skt = (name.sin_addr.s_addr & 0x000000ff); + + return(ntohs(name.sin_port)); +} + +/* + * write data to client via TCP/IP stream + * + * We deliberately don't use non-blocking I/O + * because the majority of the large data transfers + * happen in a process dedicated to a single client. + * + */ + +int +dsiTCPIPWrite(fd, hdr, data, len) +int fd; +struct dsi_hdr *hdr; +char *data; +int len; +{ + int cc, cd; + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + char *dsi_cmd(); + fprintf(dbg, ">> AppleShareIP DSI header (PID %d)\n", getpid()); + fprintf(dbg, "\tFlags: %02x (%s)\n", hdr->dsi_flags, + (hdr->dsi_flags == DSI_REQ_FLAG) ? "Request" : "Reply"); + fprintf(dbg, "\tCommand: %02x (%s)\n", hdr->dsi_command, + dsi_cmd(hdr->dsi_command)); + fprintf(dbg, "\tRequestID: %d\n", ntohs(hdr->dsi_requestID)); + fprintf(dbg, "\tErrCode/DataOffset: %d\n", ntohl(hdr->dsi_err_offset)); + fprintf(dbg, "\tDataLength: %d\n", ntohl(hdr->dsi_data_len)); + fprintf(dbg, "\tReserved: %d\n", ntohl(hdr->dsi_reserved)); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + /* + * writev() is more efficient but + * is less portable than write() + * + */ +#ifdef HAVE_WRITEV + { struct iovec iov[2]; + iov[0].iov_base = (caddr_t)hdr; + iov[0].iov_len = sizeof(struct dsi_hdr); + iov[1].iov_base = (caddr_t)data; + iov[1].iov_len = len; + cc = writev(fd, iov, (data == NULL) ? 1 : 2); + } +#else /* HAVE_WRITEV */ + if ((cc = write(fd, (char *)hdr, sizeof(struct dsi_hdr))) >= 0) { + if (data != NULL) { + if ((cd = write(fd, data, len)) >= 0) + cc += cd; + else + cc = cd; + } + } +#endif /* HAVE_WRITEV */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + extern int errno; + if (cc < 0) + fprintf(dbg, "** dsiTCPIPWrite(): %d bytes returns %d (errno %d)", + len+sizeof(struct dsi_hdr), cc, errno); + fprintf(dbg, "\n\n\n"); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(cc); +} + +/* + * Tickle Timeout timer + * + */ + +private void +dsiTCPIPTimer(numsess) +int numsess; +{ + int i; + ASPSkt *as; + static int inited = 0; + static struct dsi_hdr tick_hdr; + void Timeout(); + + /* + * set-up the invariant + * fields of the tickle hdr + * + */ + if (!inited) { + tick_hdr.dsi_flags = DSI_REQ_FLAG; + tick_hdr.dsi_command = DSITickle; + tick_hdr.dsi_err_offset = htonl(0x00000000); + tick_hdr.dsi_data_len = htonl(0x00000000); + tick_hdr.dsi_reserved = htonl(0x00000000); + inited = 1; + } + + /* + * check for idle TCP/IP sessions + * + */ + for (i = 0; i < numsess; i++) { + if (dsi_session[i].sesstype == DSI_SESSION_TCPIP) { + dsi_session[i].timeout += DSI_TIMEOUT; + if (dsi_session[i].timeout >= ASPCONNECTIONTIMEOUT) { + if ((as = aspskt_find_sessrefnum(i)) != NULL) { + if (as->tickle_timeout_user != NULL) + (*as->tickle_timeout_user)(i, as->ttu_arg); + else { /* no user routine */ + as->state = SP_INACTIVE; + as->active = FALSE; /* aspskt_free(as); */ + dsiShutdown(i); + } + } + } else { /* not timed out, but time to send a tickle ? */ + if ((dsi_session[i].timeout % (ASPTICKLETIMEOUT)) == 0) { + if ((as = aspskt_find_sessrefnum(i)) != NULL) { + tick_hdr.dsi_requestID = htons(dsi_session[i].sess_id_out++); + dsiTCPIPWrite(as->ss, &tick_hdr, NULL, 0); + } + } + } + } + } + + Timeout(dsiTCPIPTimer, numsess, DSI_TIMEOUT); + + return; +} + +/* + * close the SLS (called from either the + * AppleTalk or TCP/IP child processes) + * + */ + +int +dsiTCPIPCloseSLS() +{ + if (slsskt != -1) { + fdunlisten(slsskt); + close(slsskt); + slsskt = -1; + } + + return(noErr); +} + +/* + * IP address filter + * + * compatible with, and stolen from, + * the ARNS remote access package + * + * http://www.cs.mu.OZ.AU/appletalk/atalk.html + * + */ + +private int ipFilters = 0; +private struct ipFilter *ipFilter = NULL; + +/* + * read the specified IP address filter file + * + */ + +private void +dsiTCPIPBuildFilterList() +{ + FILE *fp; + char line[160]; + char *mask, *addr; + unsigned long inet_addr(); + + ipFilters = 0; + + if (dsiTCPIPFilter != NULL) { + if (ipFilter == NULL) + if ((ipFilter = + (struct ipFilter *)malloc(MAXIPFILTSIZ*MAXIPFILTERS)) == NULL) + return; + if ((fp = fopen(dsiTCPIPFilter, "r")) != NULL) { + while (fgets(line, sizeof(line), fp) != NULL) { + if (line[0] == '#') + continue; + if ((mask = (char *)index(line, '\n')) != NULL) + *mask = '\0'; + mask = line+1; + while (*mask != '\0' && isspace(*mask)) + mask++; /* skip spaces */ + addr = mask; + while (*addr != '\0' && !isspace(*addr)) + addr++; /* skip mask */ + while (*addr != '\0' && isspace(*addr)) + addr++; /* skip spaces */ + if (line[0] == '+' || line[0] == '*' || line[0] == '-') { + ipFilter[ipFilters].perm = line[0]; + ipFilter[ipFilters].addr = (*addr == '\0') ? 0L : inet_addr(addr); + ipFilter[ipFilters].mask = (*mask == '\0') ? 0L : inet_addr(mask); + if (++ipFilters >= MAXIPFILTERS) + break; + } + } + (void)fclose(fp); + } + } + + return; +} + +/* + * check the IP address filter, if any + * + */ + +private int +dsiTCPIPIllegalIP(from) +struct sockaddr_in *from; +{ + int i; + u_long addr; + + dsiTCPIPBuildFilterList(); + + if (ipFilters == 0 + || ipFilter == NULL) + return(0); + + addr = from->sin_addr.s_addr; + + for (i = 0 ; i < ipFilters ; i++) { + if (ipFilter[i].addr != 0L) { + if ((addr & ipFilter[i].mask) == ipFilter[i].addr) + return(ipFilter[i].perm == '-'); + } else { + if ((addr & ipFilter[i].mask) == addr) + return(ipFilter[i].perm == '-'); + } + } + + return(0); +} diff --git a/applications/aufs/afpdsi.h b/applications/aufs/afpdsi.h new file mode 100644 index 0000000..1032201 --- /dev/null +++ b/applications/aufs/afpdsi.h @@ -0,0 +1,108 @@ +/* + * $Author: djh $ $Date: 91/03/14 13:45:20 $ + * $Header: afpdsi.h,v 2.2 91/03/14 13:45:20 djh Exp $ + * $Revision: 2.2 $ + * + */ + +/* + * afpdsi.h - Data Stream Interface Includes + * + * AFP via a Transport Protocol (eg: TCP/IP) + * + * AppleTalk package for UNIX + * + * The following routines implement a lightweight extra + * layer between AFP (as embodied in the AUFS code), the + * original ASP via ATP layer, and delivery via other + * Transport Protocol layers, currently only TCP/IP. + * + * Refer: "AppleTalk Filing Protocol 2.2 & + * AFP over TCP/IP Specification" + * + * SSS == Server Session Socket + * SLS == Session Listening Socket + * WSS == Workstation Session Socket + * + * Copyright (c) 1997 The University of Melbourne + * David Hornsby + * + */ + +/* + * options + * + */ +#define DSI_OPT_REQQ 0x00 +#define DSI_OPT_ATTQ 0x01 + +#define DSI_OPT_REQLEN 4 +#define DSI_OPT_ATTLEN 4 + +/* + * quantum sizes + * + */ +#define DSI_ATTN_SIZ 2 +#define DSI_SRVR_CMD 1500 +#define DSI_SRVR_MAX 64*1024 + +/* + * the DSI header will be inserted in front of + * every AFP request or reply packet + * + */ + +struct dsi_hdr { + byte dsi_flags; /* used to determine packet type */ +#define DSI_REQ_FLAG 0x00 +#define DSI_REP_FLAG 0x01 + byte dsi_command; /* similar to ASP commands, except WrtCont */ +#define DSICloseSession 1 +#define DSICommand 2 +#define DSIGetStatus 3 +#define DSIOpenSession 4 +#define DSITickle 5 +#define DSIWrite 6 +#define DSIAttention 8 + word dsi_requestID; /* req ID on per-session basis, wraps */ + dword dsi_err_offset; /* error for reply, offset for write, else 0 */ + dword dsi_data_len; /* total data length following dsi_hdr */ + dword dsi_reserved; /* reserved for future, should be zero */ +}; + +/* + * per-session demux info + * + */ +struct dsi_sess { + int sesstype; +#define DSI_SESSION_ATALK 0x01 +#define DSI_SESSION_TCPIP 0x02 + int state; /* type of DSI data expected */ +#define DSI_STATE_HDR 0x01 +#define DSI_STATE_AFP 0x02 +#define DSI_STATE_DAT 0x03 +#define DSI_STATE_REP 0x04 + char *ptr; /* where we have to put incoming data */ + int lenleft; /* amount of data expected to arrive */ + int timeout; /* per-session tickle timer */ +#define DSI_TIMEOUT 5*4 + word sess_id_out; /* outgoing session ID (0-65535) */ + word sess_id_in; /* incoming session ID (0-65535) */ + struct dsi_hdr hdr; /* current incoming header (for reply) */ + ASPQE *aspqe; /* callback data for GetRequest etc. */ +}; + +/* + * IP filter list + * + */ +#define MAXIPFILTERS 100 +#define MAXIPFILTSIZ sizeof(struct ipFilter) + +struct ipFilter { + sword perm; + dword mask; + dword addr; +}; diff --git a/applications/aufs/afpdt.c b/applications/aufs/afpdt.c new file mode 100644 index 0000000..0f79728 --- /dev/null +++ b/applications/aufs/afpdt.c @@ -0,0 +1,2159 @@ +/* + * $Author: djh $ $Date: 1996/06/19 04:29:14 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpdt.c,v 2.15 1996/06/19 04:29:14 djh Rel djh $ + * $Revision: 2.15 $ + * + */ + +/* + * afpdt.c - Appletalk Filing Protocol Desktop Interface + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * + * + */ + +/* + * Desktop management routines: + * + * FPOpenDT - Open the icon desktop. + * FPCloseDT - Close the icon desktop. + * FPAddIcon - Add an icon bitmap to the desktop. + * FPGetIcon - Retrieve an icon bitmap from the desktop. + * FPGetIconInfo - Retrieve icon info from the desktop. + * FPAddAPPL - Add application info to desktop. + * FPRemoveAPPL - Remove application info from desktop. + * FPGetAPPL - Retrieve application info from desktop. + * FPAddComment - Add comment info to the desktop. + * FPRemoveComment - Remove comment info from the desktop. + * FPGetComment - Retrieve comment info from the desktop. + * + */ + +#include +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif _TYPES +#include +#include +#include +#include +#include +#ifdef NEEDFCNTLDOTH +# include +#endif NEEDFCNTLDOTH +#ifdef SOLARIS +#include +#endif SOLARIS +#ifdef linux +#include +#endif linux +#include +#include +#include "afpntoh.h" +#include "afpgc.h" +#include "afps.h" +#include "afpdt.h" +#include "afpudb.h" + + +#ifdef DEBUG_AFP_CMD +extern FILE *dbg; +#endif /* DEBUG_AFP_CMD */ + +import int errno; + +private DeskTop *DeskTab[MAXVOLS]; /* table of open desktops by volume */ + +private APPLNode *dtBindFCtoAPPL(); +private IconNode *dtBindFCtoIcon(); +private APPLNode *dtFindAPPLList(); +private IconNode *dtFindIconList(); +private IconNode *dtIconInsertPoint(); +private IconNode *dtIconInsert(); +private APPLNode *dtAPPLInsertPoint(); +private APPLNode *dtAPPLInsert(); +private byte *IDFetch(); +private void CacheAdd(); + +#define APPLFILEMODE 0644 +#define ICONFILEMODE 0664 + +#define REMOVEAPPLIDB 0 +#define REMOVEICONIDB 1 + +private ReadIDesk(), ReadADesk(); + +#ifdef AUFS_README +import char *aufsreadme; +import char *aufsreadmename; +#endif AUFS_README + +/* + * PrintINode(AVLUData *node) + * + * Print out information about the Icon node pointed to by node. + * + */ + +private void +PrintINode(in) +IconNode *in; +{ + char cbuff[5],tbuff[5]; + IconInfo *ii = &in->in_info; /* handle on icon info */ + + strncpy(cbuff,(char *)ii->i_FCreator,4); + cbuff[4] = '\0'; + strncpy(tbuff,(char *)ii->i_FType,4); + tbuff[4] = '\0'; + printf("IconNode Creator=%s Type=%s IType=%d iloc=%d bmsize=%d\n", + cbuff,tbuff,ii->i_IType,in->in_iloc,ii->i_bmsize); +} + +PrintIconInfo(fc, ft) +byte fc[], ft[]; +{ + printf("Icon Info: Creator = %c%c%c%c, Type = %c%c%c%c\n", + fc[0], fc[1], fc[2], fc[3], ft[0], ft[1], ft[2], ft[3]); +} + +/* + * PrintANode(AVLUData *node) + * + * Print out information about the APPL node pointed to by node. + * + */ + +private void +PrintANode(udata) +AVLUData *udata; +{ + char cbuff[5]; + APPLNode *an = (APPLNode *) udata; /* cast to APPL record */ + APPLInfo *ai = &an->an_info; /* handle on APPL info */ + + strncpy(cbuff,(char *)ai->a_FCreator,4); + cbuff[4] = '\0'; + printf("APPLNode node Creator=%s path=%s name=%s\n", + cbuff,pathstr(an->an_ipdir),an->an_fnam); +} + +/* + * int OpenDesk(DeskTop *dt, char *dtfn, int trunc, int mode, int *wrtok) + * + * Open or create a desktop as specified by the path in dt->dt_rootd + * and the desktop file name, dtfn. + * + * First try opening in read/write mode. If that fails because of + * an access error then try opening read only. If read/write fails + * because the file does not exist then try creating the file. + * + * Return the file handle or < 0, and set wrtok to TRUE if we obtained + * write access on this file. + * + * Top level volume must have ".finderinfo" directory or we + * won't create the files. + * +*/ +private int +OpenDesk(dt,dtfn,trunc,mode,wrtok) +DeskTop *dt; +char *dtfn; +int trunc; /* if true, then open in truncate mode */ +int *wrtok; /* if file is writeable */ +int mode; /* mode to open file in */ +{ + int dtfd; + char path[MAXPATHLEN]; + int fmode; +#ifdef AUFS_README + char path2[MAXPATHLEN]; +#endif AUFS_README + + *wrtok = TRUE; /* assume we can write */ + OSfname(path,dt->dt_rootd,dtfn,F_DATA); /* create file name */ + if (DBDSK) + printf("OpenDesk: opening %s\n",path); + + fmode = O_RDWR; +#ifdef O_TRUNC + if (trunc) { + if (DBDSK) + printf("OpenDesk: truncating %s\n",dtfn); + fmode |= O_TRUNC; + } +#endif + if ((dtfd=open(path,fmode)) >= 0) + return(dtfd); /* success! */ + + if (errno == EACCES || errno == EROFS) { /* access error? */ + *wrtok = FALSE; /* indicate for caller no write */ + return(open(path,O_RDONLY)); /* just try reading */ + } +#ifdef AUFS_README + if(aufsreadme && strcmp(dtfn, DESKTOP_APPL) == 0 && + access(aufsreadme, R_OK) == 0) { + OSfname(path2,dt->dt_rootd,aufsreadmename,F_DATA); + if(access(path2, F_OK) < 0 && errno == ENOENT) + if(symlink(aufsreadme, path2) < 0) { + if (DBDSK) + printf("OpenDesk: symbolic link %s failed\n",path2); + } else { + if (DBDSK) + printf("OpenDesk: linking %s\n",aufsreadme); + } + } +#endif AUFS_README + + if ((dt->dt_rootd->flags & DID_FINDERINFO) == 0) + return(-1); + + dtfd = open(path,O_RDWR|O_CREAT, mode); /* try creating*/ + if (dtfd < 0) /* did we open anything? */ + *wrtok = FALSE; /* if not then can't write */ + return(dtfd); +} + +/* + * Intialize desktop manager for particular volume + * +*/ +private +DTInit(dt) +DeskTop *dt; +{ + int wok; /* desktop is writeable flag */ + int err; + + dt->dt_avlroot = (AVLNode *) 0; + dt->dt_avllast = (AVLNode *) 0; + dt->dt_ifd = -1; + dt->dt_afd = -1; + dt->dt_rootd = VolRootD(dt->dt_ivol); + +#ifdef STAT_CACHE + OScd(pathstr(dt->dt_rootd)); +#endif STAT_CACHE + + dt->dt_afd = OpenDesk(dt,DESKTOP_APPL,FALSE,APPLFILEMODE,&wok); /* open or create */ + if (dt->dt_afd >= 0) { /* success on open? */ + OSLockFileforRead(dt->dt_afd); + err = ReadADesk(dt); /* yes... read APPL database */ + OSUnlockFile(dt->dt_afd); + (void)close(dt->dt_afd); /* close APPL data base */ + if (err < 0) { + DeskRemove(dt, REMOVEAPPLIDB); + if (wok) { + err = OpenDesk(dt,DESKTOP_APPL, TRUE, APPLFILEMODE, &wok); + close(err); + } + } + dt->dt_afd = -1; /* no longer here... */ + } + + dt->dt_ifd = OpenDesk(dt,DESKTOP_ICON, FALSE,ICONFILEMODE,&wok); + if (dt->dt_ifd >= 0) { /* success on open? */ + OSLockFileforRead(dt->dt_ifd); + err = ReadIDesk(dt); /* yes... read Icon database */ + OSUnlockFile(dt->dt_ifd); + if (err < 0) { + close(dt->dt_ifd); + dt->dt_ifd = -1; + DeskRemove(dt, REMOVEICONIDB); + if (wok) + dt->dt_ifd = OpenDesk(dt,DESKTOP_ICON, TRUE,ICONFILEMODE,&wok); + } +#ifdef notdef + /* yes, there is - geticon uses this fd */ + if (!wok) { /* writeable? */ + (void)close(dt->dt_ifd); /* no, so no reason to keep open */ + dt->dt_ifd = -1; + } +#endif + } + unixiconalways(dt); + return(noErr); +} + +unixiconalways(dt) +DeskTop *dt; +{ + IconNode *in,*replaced; + extern struct ufinderdb uf[]; + extern int uf_len; + int i; + + in = dtBindFCtoIcon(dt, (byte *)DEFFCREATOR); + for (i = 0 ; i < uf_len; i++) { + if (uf[i].ufd_icon == NULL) + continue; + in = dtIconInsert(in, uf[i].ufd_ftype, (byte)1, &replaced); + /* should only be called before desktop is inited */ + in->in_iloc = -1; /* no file pos! */ + in->in_mloc = NOGCIDX; /* no icon index */ + in->in_uloc = uf[i].ufd_icon; /* remember icon */ + in->in_info.i_bmsize = uf[i].ufd_iconsize; /* icon size */ + in->in_info.i_ITag[0] = 0x12; + } +} + +/* + * ReadIDesk - Initialize Icon Desktop. + * + */ +private +ReadIDesk(dt) +DeskTop *dt; +{ + IconFileRecord ifr; + IconNode *in, *replaced, *inhead; + IconInfo *ii; + int cnt,badcnt; + off_t bmz,fpos; + + if (dt->dt_ifd < 0) + return(0); + + (void)lseek(dt->dt_ifd, 0L, 0); /* got to start */ + for (fpos = 0, badcnt = 0;;) { /* read length of record */ + cnt = read(dt->dt_ifd, (char *)&ifr,IFRLEN); + if (cnt != IFRLEN) + break; + fpos += IFRLEN; + if (ntohl(ifr.ifr_magic) != IFR_MAGIC) { + if (badcnt > 10) { + printf("ICON Desktop corrupt or out of revision\n"); + return(-1); + } + printf("ICON Desktop entry has bad magic number... skipping\n"); + badcnt++; + return(-1); + } + /* find the head of our icon list */ + ii = &ifr.ifr_info; + ii->i_bmsize = ntohl(ii->i_bmsize); + inhead = dtBindFCtoIcon(dt, ii->i_FCreator); + /* now go through list and find place to insert and insert if */ + /* we can */ + in = dtIconInsert(inhead, ii->i_FType, ii->i_IType, &replaced); + /* what to do with replaced entry? (possibly place on free list?) */ + in->in_iloc = fpos; /* index to find bm in file */ + in->in_mloc = NOGCIDX; /* no cache index for icon */ + in->in_uloc = NULL; + bcopy((char *)ii, (char *)&in->in_info, sizeof(IconInfo)); + bmz = in->in_info.i_bmsize; /* this is the bitmap size */ + (void)lseek(dt->dt_ifd,bmz,L_INCR); /* skip over bitmap */ + fpos += bmz; /* increment past bml */ + if (DBDSK) { + printf("ReadIDesk: Loading "); + PrintINode(in); + } + } + return(0); +} + +private +ReadADesk(dt) /* read APPL database */ +DeskTop *dt; +{ + APPLFileRecord afr; /* file format APPL info */ + APPLNode *an, *ahead, *replaced; + char pdir[MAXPATHLEN]; /* create parent dir here */ + int len,cnt; + off_t floc; + IDirP ipdir; + char *fnam; + + if (dt->dt_afd < 0) + return(0); + + (void)lseek(dt->dt_afd, 0L, 0); /* seek to home */ + /* directory string in APPLFileRecord is relative to volumes rootd */ + strcpy(pdir,pathstr(dt->dt_rootd)); /* copy volumes rootd */ + len = strlen(pdir); /* fetch the length of this */ + + while ((cnt = read(dt->dt_afd, (char *)&afr,AFRLEN)) == AFRLEN) { + if (ntohl(afr.afr_magic) != AFR_MAGIC) { + if (DBDSK) { + printf("ReadADesk: APPL Desktop out of revision or bad\n"); + printf("ReadADesk: Skipping rest...\n"); + } + return(-1); + } + /* remember where we are */ + floc = lseek(dt->dt_afd, 0L, 1) - ((off_t)sizeof(afr)); + afr.afr_pdirlen = ntohl(afr.afr_pdirlen); + afr.afr_fnamlen = ntohl(afr.afr_fnamlen); + + if (afr.afr_pdirlen > 0) + pdir[len] = '/'; /* deposit a directory term */ + else pdir[len] = '\0'; + /* Get directory component */ + cnt = read(dt->dt_afd, &pdir[len+1],afr.afr_pdirlen); + if (cnt != afr.afr_pdirlen) { + if (DBDSK) + printf("ReadADesk: unable to read directory name\n"); + return(-1); + } + + ipdir = Idirid(pdir); /* find or create dirid */ + + fnam = malloc((unsigned)afr.afr_fnamlen+1); + cnt = read(dt->dt_afd, fnam, afr.afr_fnamlen); + if (cnt != afr.afr_fnamlen) { + printf("ReadADesk: unable to read filename\n"); + free(fnam); + return(-1); + } + if (ipdir == NILDIR) { + free(fnam); + continue; + } + if (DBDSK) + printf("DeskARead: dir = %s, fnam = %s\n", pathstr(ipdir),fnam); + + ahead = dtBindFCtoAPPL(dt, afr.afr_info.a_FCreator); + an = dtAPPLInsert(ahead, ipdir, fnam, &replaced); + if (replaced) { + /* should free space */ +#ifdef notdef + if (DBDSK) + printf("DeskARead: warning: old undeleted entry: %s/%s\n", + pathstr(ipdir), fnam); +#endif + } + /* ipdir, fnam, and next already done */ + an->an_flags = 0; + bcopy((char *)&afr.afr_info,(char *)&an->an_info,sizeof(APPLInfo)); + an->an_iloc = floc; /* remember file position */ + } + return(0); +} + +/* + * OSErr FPAddComment(...) + * + * This call adds a comment for a file or directory to the desktop + * database. + * + * Inputs: + * dtrefnum desktop refnum. + * dirid ancestor directory id. + * pathType Long/short path indicator. + * path pathname to the file or directory. + * clen Length of the comment. + * ctxt text of the comment. + * + * Errors: + * ParamErr Unknown desktop refnum, bad pathname. + * ObjectNotFound Input params do not point to an existing file. + * AccessDenied User does not have access. + * + * If the comment is greater than 199 bytes then the comment will be + * truncated to 199 bytes. + * + */ + +/*ARGSUSED*/ +OSErr +FPAddComment(p,l,r,rl) +byte *p,*r; +int *rl; +int l; +{ + AddCommentPkt adc; + IDirP idir,ipdir; + int err; + char file[MAXUFLEN]; + int ivol; + DeskTop *dt; + + if (DBDSK) + printf("FPAddComment: ...\n"); + + ntohPackX(PsAddComment,p,l,(byte *) &adc); + if ((dt = VolGetDesk(adc.adc_dtrefnum)) == NILDT) + return(aeParamErr); + + err = EtoIfile(file,&idir,&ipdir,&ivol,adc.adc_dirid, + dt->dt_evol,adc.adc_ptype,adc.adc_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + int i; + void dbg_print_path(); + fprintf(dbg, "\tDTRef: %d\n", adc.adc_dtrefnum); + fprintf(dbg, "\tDirID: %08x\n", adc.adc_dirid); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", adc.adc_ptype, + (adc.adc_ptype == 1) ? "Short" : "Long"); + dbg_print_path(adc.adc_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fprintf(dbg, "\tComnt: (len %d) \"", *adc.adc_comment); + for (i = 0; i < (int)*adc.adc_comment; i++) + fprintf(dbg, "%c", *(adc.adc_comment+i+1)); + fprintf(dbg, "\"\n"); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + err = OSSetComment(ipdir,file,adc.adc_comment+1,adc.adc_comment[0]); + return(err); +} + +OSErr +FPGetComment(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + GetCommentPkt gcm; + IDirP idir,ipdir; + int err,ivol; + char file[MAXUFLEN]; + DeskTop *dt; + + ntohPackX(PsGetComment,p,l,(byte *) &gcm); + if ((dt = VolGetDesk(gcm.gcm_dtrefnum)) == NILDT) + return(aeParamErr); + + err = EtoIfile(file,&idir,&ipdir,&ivol,gcm.gcm_dirid, + dt->dt_evol,gcm.gcm_ptype,gcm.gcm_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tDTRef: %d\n", gcm.gcm_dtrefnum); + fprintf(dbg, "\tDirID: %08x\n", gcm.gcm_dirid); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", gcm.gcm_ptype, + (gcm.gcm_ptype == 1) ? "Short" : "Long"); + dbg_print_path(gcm.gcm_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + err = OSGetComment(ipdir,file,r+1,r); + if (err != noErr) + return(err); + *rl = pstrlen(r)+1; + if (DBDSK) + printf("FPGetComment: returns comment of length %d\n", *rl); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_name(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tComnt: (len %d)", *rl); + dbg_print_name("", r); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/* + * OSErr FPAddAPPL(...) + * + * This call adds an APPL mapping to the desktop database. + * + * Inputs: + * dtrefnum Desktop reference number. + * dirid Ancestor directory id + * Creator 4 byte creator + * APPLTag 4 byte user tag, to be stored with the APPL + * pathType Long/short path + * path pathname to the application being added. + * + * Errors: + * ObjectNotFound Input params do not point to an existing file. + * AccessDenied User does not have access. + * + * An entry is added to the APPL desktop. If an entry for the + * application exists (same creator, file and directory) then it + * is replaced. + * + */ +/*ARGSUSED*/ +OSErr +FPAddAPPL(p,l,r,rlen) +byte *p; +byte *r; +int l; +int *rlen; +{ + AddAPPLPkt aap; + APPLNode *an,*replaced, *ahead; + DeskTop *dt; + int ivol,err; + IDirP idir,ipdir; + char file[MAXUFLEN], *fnam; + + ntohPackX(PsAddAPPL,p,l,(byte *) &aap); + dt = VolGetDesk(aap.aap_dtrefnum); + if (dt == NILDT) + return(aeParamErr); + + err = EtoIfile(file,&idir,&ipdir,&ivol,aap.aap_dirid, + dt->dt_evol,aap.aap_ptype,aap.aap_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_type(); + void dbg_print_path(); + fprintf(dbg, "\tDTRef: %d\n", aap.aap_dtrefnum); + fprintf(dbg, "\tDirID: %08x\n", aap.aap_dirid); + dbg_print_type("\tCreat:", aap.aap_fcreator); + fprintf(dbg, "\tApTag: %08x\n", aap.aap_apptag); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", aap.aap_ptype, + (aap.aap_ptype == 1) ? "Short" : "Long"); + dbg_print_path(aap.aap_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + ahead = dtBindFCtoAPPL(dt, aap.aap_fcreator); + fnam = malloc((unsigned)strlen(file)+1); + /* assert(fnam != NULL) */ + strcpy(fnam, file); /* copy it */ + an = dtAPPLInsert(ahead, ipdir, fnam, &replaced); + bcopy((char *)aap.aap_fcreator,(char *)an->an_info.a_FCreator,4); + bcopy((char *)&aap.aap_apptag,(char *)an->an_info.a_ATag,4); + an->an_flags = AN_MOD; /* modified, but not deleted */ +#ifdef notdef + /* already done */ + an->an_ipdir = ipdir; + an->an_iloc = -1; +#endif + + if (DBDSK) { + char fc[5]; + bcopy((char *)an->an_info.a_FCreator,(char *)fc,4); + fc[4] = '\0'; + printf("FPAddAPPL: path=%s, file=%s, Creator=%s\n", + pathstr(an->an_ipdir),an->an_fnam,fc); + } + + + if (replaced != NULL) { /* just inserted, naught else */ + /* proably should have better handling */ + free((char *)replaced->an_fnam); + free((char *)replaced); + } + return(noErr); +} + +/* + * OSErr FPGetAPPL(...) + * + * This call is used to retrieve information about a particular + * application from the desktop database. + * + * Inputs: + * dtrefnum desktop reference number. + * fcreator 4 bytes of file creator + * APPLIdx index of the APPL entry to be retrieved. + * bitmap bitmap of parms to be returned for file, same + * as FPGetFileDirParms call. + * + * Errors: + * ParamErr + * ObjectNotFound + * AccessDenied + * + * + * Outputs: + * Bitmap + * APPLTag + * File Parameters + * + */ +OSErr +FPGetAPPL(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + int aidx; + GetAPPLPkt gap; + APPLNode *ahead; + DeskTop *dt; + FileDirParm fdp; + dword appltag; + int err; + + ntohPackX(PsGetAPPL,p,l,(byte *) &gap); + + if ((dt = VolGetDesk(gap.gap_dtrefnum)) == NILDT) + return(aeParamErr); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_type(); + void dbg_print_bmap(); + fprintf(dbg, "\tDTRef: %d\n", gap.gap_dtrefnum); + dbg_print_type("\tCreat:", gap.gap_fcreator); + fprintf(dbg, "\tAPidx: %d\n", gap.gap_applidx); + fprintf(dbg, "\tBtMap: %04x\t", gap.gap_bitmap); + dbg_print_bmap(gap.gap_bitmap, 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (DBDSK) { + char fc[5]; + bcopy((char *)gap.gap_fcreator,(char *)fc,4); + fc[4] = '\0'; + + printf("FPGetAPPL: FCreator=%s, Idx=%d\n",fc,gap.gap_applidx); + } + + if ((ahead = dtFindAPPLList(dt, gap.gap_fcreator)) == NULL) + return(aeItemNotFound); + /* "an APPL index of zero returns the first APPL mapping" */ + aidx = (gap.gap_applidx == 0) ? 1 : gap.gap_applidx; + while ((ahead = ahead->an_next) != NULL) + if ((ahead->an_flags & AN_DEL) == 0 && --aidx == 0) + break; + while (ahead != NULL) { + if (ahead->an_flags & AN_DEL) + err = aeItemNotFound; + else + err = OSAccess(ahead->an_ipdir,ahead->an_fnam,OFK_MRD); + if (err == noErr) + break; /* found readable entry */ + if (DBDSK) + printf("AFPGetAPPL: No Access to %s %s error %s\n", + pathstr(ahead->an_ipdir),ahead->an_fnam,afperr(err)); + ahead = ahead->an_next; + } + if (ahead == NULL) + return(aeItemNotFound); + + if (DBDSK) + printf("FPGetAPPL: Found path=%s, file=%s\n", + pathstr(ahead->an_ipdir),ahead->an_fnam); + + fdp.fdp_pdirid = ItoEdirid(ahead->an_ipdir,dt->dt_ivol); + fdp.fdp_fbitmap = gap.gap_bitmap; + fdp.fdp_dbitmap = 0; + + err = OSFileDirInfo(ahead->an_ipdir,NILDIR,ahead->an_fnam,&fdp,dt->dt_ivol); + + if (err != noErr) + return(aeItemNotFound); + + if (FDP_ISDIR(fdp.fdp_flg)) /* should not happen */ + return(aeItemNotFound); + + bcopy((char *)ahead->an_info.a_ATag, (char *)&appltag, 4); + PackWord(fdp.fdp_fbitmap, r); + PackDWord(appltag, r+2); + *rl = 6 + htonPackX(FilePackR, (byte *)&fdp, r+6); + + if (DBDSK) + printf("FPGetAPPL: ...\n"); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_bmap(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tBtMap: %04x\t", fdp.fdp_fbitmap); + dbg_print_bmap(fdp.fdp_fbitmap, 0); + fprintf(dbg, "\tAPTag: %08x\n", appltag); + dbg_print_parm(fdp.fdp_fbitmap, r+6, (*rl)-6, 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/*ARGSUSED*/ +OSErr +FPRmvAPPL(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + RemoveAPPLPkt rma; + DeskTop *dt; + APPLNode *ahead, *found; + char file[MAXUFLEN]; + IDirP idir,ipdir; + int ivol,err; + + ntohPackX(PsRmvAPPL,p,l,(byte *) &rma); + + if ((dt = VolGetDesk(rma.rma_refnum)) == NILDT) + return(aeParamErr); + + err = EtoIfile(file,&idir,&ipdir,&ivol,rma.rma_dirid, + dt->dt_evol,rma.rma_ptype,rma.rma_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_type(); + void dbg_print_path(); + fprintf(dbg, "\tDTRef: %d\n", rma.rma_refnum); + fprintf(dbg, "\tDirID: %08x\n", rma.rma_dirid); + dbg_print_type("\tCreat:", rma.rma_fcreator); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", rma.rma_ptype, + (rma.rma_ptype == 1) ? "Short" : "Long"); + dbg_print_path(rma.rma_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + ahead = dtFindAPPLList(dt, rma.rma_fcreator); + if (ahead == NULL) + return(aeItemNotFound); + ahead = dtAPPLInsertPoint(ahead, ipdir, file, &found); + if (found != (APPLNode *) 0) { /* found something... */ + found->an_flags |= (AN_DEL|AN_MOD); /* indicate it is deleted */ + if (DBDSK) + printf("FPRmvAPPL: Removing %s\n",ahead->an_fnam); + return(noErr); + } + if (DBDSK) + printf("FPRmvAPPL: No name %s found for remove\n",file); + return(aeObjectNotFound); +} + + +/* + * OSErr FPOpenDT(...); + * + */ + +OSErr +FPOpenDT(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + OpenDTPkt odt; + DeskTop *dt; + int ivol; + OSErr dterr; + + ntohPackX(PsOpenDT,p,l,(byte *) &odt); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tVolID: %04x\n", odt.odt_volid); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + ivol = EtoIVolid(odt.odt_volid); + if (ivol < 0) + return(ivol); + + if (DBDSK) + printf("FPOpenDT: ...\n"); + + if ((dt = VolGetDesk(odt.odt_volid)) == NILDT) { + dt = (DeskTop *) malloc(sizeof(DeskTop)); + dt->dt_ivol = ivol; + dt->dt_evol = odt.odt_volid; + if ((dterr = DTInit(dt)) != noErr) { /* init desktop */ + free((char *)dt); + return(dterr); + } + } + VolSetDesk(odt.odt_volid,dt); /* tell volume about desktop */ + PackWord(odt.odt_volid,r); /* return volid as DTRefnum */ + *rl = sizeof(word); /* length of result */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tDTRef: %d\n", odt.odt_volid); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); /* all done */ +} + +/* + * FPCloseDT(...) + * + * This call is used to disassociate a user from the volume's desktop + * database. + * + * Inputs: + * dtrefnum Desktop reference number. + * + * Errors: + * ParamErr unknown desktop reference number. + * + * + */ + +/*ARGSUSED*/ +OSErr +FPCloseDT(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + DeskTop *dt; + CloseDTPkt cdt; + + ntohPackX(PsCloseDT,p,l,(byte *) &cdt); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tDTRef: %d\n", cdt.cdt_dtrefnum); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (DBDSK) + printf("FPCloseDT: ...\n"); + + if ((dt = VolGetDesk(cdt.cdt_dtrefnum)) == NILDT) /* fetch desk */ + return(aeParamErr); + + VolClrDesk(cdt.cdt_dtrefnum); /* release volume's handle */ + /* need to write out stuff */ + if (dt->dt_ifd >= 0) { /* if open, then */ + (void)close(dt->dt_ifd); /* close desktop file */ + dt->dt_ifd = -1; + } +#ifdef notdef + /* really need to map thorugh and release all storage */ + free((char *)dt); /* release desktop storage */ +#endif /* need to release tree */ + return(noErr); +} + +private +WriteAPPL(dt,an) +DeskTop *dt; +APPLNode *an; +{ + APPLFileRecord afr; + char *pstr; + int fd; + off_t pos; + int pdirlen, fnamlen; + + if (DBDSK) + printf("WriteAPPL: writing APPL\n"); + + if ((fd = dt->dt_afd) < 0) { +#ifdef notdef + /* code to be used when rewritting entire file */ + fd = OpenDesk(dt, DESKTOP_APPL, FALSE, APPLFILEMODE, &wok); + if (fd < 0) { + if (DBDSK) + printf("Can't open desktop\n"); + return; + } + if (!wok) { + if (DBDSK) + printf("Desktop is write protected\n"); + (void)close(fd); + return; /* can't */ + } +#else + return; +#endif + } + /* copy APPL info */ + bcopy((char *)&an->an_info,(char* )&afr.afr_info, sizeof(APPLInfo)); + afr.afr_magic = htonl(AFR_MAGIC); /* insert the magic number */ + pstr = (char *)ppathstr(dt->dt_rootd,an->an_ipdir); + pdirlen = strlen(pstr)+1; + afr.afr_pdirlen = htonl(pdirlen); /* length of path */ + fnamlen = strlen(an->an_fnam)+1; + afr.afr_fnamlen = htonl(fnamlen); /* length of file */ + + OSLockFileforWrite(fd); + /* shouldn't keep going after an error! */ + if (an->an_iloc < 0) { + if ((pos = lseek(fd, 0L, L_XTND)) < 0L) { + printf("WriteAPPL failed\n"); + OSUnlockFile(fd); + return; + } + } else { + if ((pos=lseek(fd, an->an_iloc, L_SET)) < 0L) { + printf("WriteAPPL failed\n"); + OSUnlockFile(fd); + return; + } + } + + if (write(fd,(char *)&afr,AFRLEN) != AFRLEN) { /* write the file record */ + printf("WriteAPPL failed on afr\n"); + OSUnlockFile(fd); + return; + } + if (write(fd,pstr,pdirlen) != pdirlen) { /* write dir */ + printf("WriteAPPL failed on path\n"); + OSUnlockFile(fd); + return; + } + /* write file name */ + if (write(fd,an->an_fnam,fnamlen) != fnamlen) { + printf("WriteAPPL failed on file\n"); + OSUnlockFile(fd); + return; + } + OSUnlockFile(fd); + an->an_iloc = pos; + an->an_flags &= ~(AN_MOD); /* not modified anymore */ +} + +private +WriteIcon(fd,in,icon,ilen) +int fd; +IconNode *in; +byte *icon; +int ilen; +{ + off_t pos, start; + IconFileRecord ifr; + + if (DBDSK) + printf("WriteIcon: writing icon\n"); + + in->in_info.i_bmsize = ilen; /* set bitmap size */ + if (fd < 0) + return; + + OSLockFileforWrite(fd); + if (in->in_iloc < 0) { + if ((pos = lseek(fd, 0L, L_XTND)) < 0L) { /* seek to end */ + in->in_iloc = -1; /* mean no disk icon (default) */ + OSUnlockFile(fd); + return; + } + } else { + start = in->in_iloc - ((off_t)IFRLEN); + if ((pos = lseek(fd, start, L_SET)) < 0L) { /* seek to pos */ + in->in_iloc = -1; /* mean no disk icon (default) */ + OSUnlockFile(fd); + return; + } + } + /* copy the icon information to file record */ + bcopy((char *)&in->in_info, (char *)&ifr.ifr_info, sizeof(IconInfo)); + ifr.ifr_magic = htonl(IFR_MAGIC); /* for checking on read-in */ + ifr.ifr_info.i_bmsize = htonl(ifr.ifr_info.i_bmsize); + if (write(fd, (char *)&ifr, IFRLEN) != IFRLEN) { /* write the file record */ + in->in_iloc = -1; /* mean no disk icon (default) */ + printf("WriteIcon failed\n"); + OSUnlockFile(fd); + return; + } + /* write out the icon */ + if (write(fd,(char *)icon,ilen) != ilen) { + in->in_iloc = -1; /* mean no disk icon (default) */ + printf("WriteIcon icon failed\n"); + OSUnlockFile(fd); + return; + } + in->in_iloc = pos+IFRLEN; /* location of icon */ + OSUnlockFile(fd); +} + +/* + * OSErr FPAddIcon(...) + * + * This call is used to add an icon to the desktop database. + * + * Inputs: + * dtrefnum desktop reference number. + * fcreator 4 bytes of file creator + * ftype 4 bytes of file type + * icontype type of icon being added. + * icontag 4 bytes of user information associated with icon. + * bitmapsize size of the bitmap for this icon. + * + * Errors: + * ParamErr unknown desktop refnum. + * IconTypeErr new icon size is different from existing icon size. + * AccessDenied User does not have access. + * + * A new icon is added to the icon database with the specified file type + * and creator. If an icon with the same file type, creator and icontype + * already exists then that icon is replaced. If the new icons size is + * different from the old icon size then IconTypeErr is returned. + * + * The bitmap is sent to the server in a subsequent exchange of Session + * Protocol packets. + * + */ + +/*ARGSUSED*/ +OSErr +FPAddIcon(p,l,r,rl,cno,reqref) +byte *p,*r; +int l; +int *rl; +int cno; +ReqRefNumType reqref; +{ + DeskTop *dt; + AddIconPkt adi; + IconInfo *ii; + IconNode *in,*ihead, *replaced; + byte *icon, *oldicon; + int rcvlen,comp,err; + + ntohPackX(PsAddIcon,p,l,(byte *) &adi); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_type(); + fprintf(dbg, "\tDTRef: %d\n", adi.adi_dtref); + dbg_print_type("\tCreat:", adi.adi_fcreator); + dbg_print_type("\tFlTyp:", adi.adi_ftype); + fprintf(dbg, "\tIcTyp: %d\n", adi.adi_icontype); + fprintf(dbg, "\tIcTag: %d\n", adi.adi_icontag); + fprintf(dbg, "\tBMSiz: %d\n", adi.adi_iconsize); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((dt = VolGetDesk(adi.adi_dtref)) == NULL) + return(aeParamErr); + + ihead = dtBindFCtoIcon(dt, adi.adi_fcreator); + /* assert ihead != NULL */ + icon = (byte *) malloc(adi.adi_iconsize); + + if (DBDSK) { + printf("FPAddIcon: for icon size %d ", adi.adi_iconsize); + PrintIconInfo(adi.adi_fcreator,adi.adi_ftype); + } + + err = dsiWrtContinue(cno,reqref,icon,adi.adi_iconsize,&rcvlen,-1,&comp); + if (err != noErr) { + free((char *)icon); + return(err); + } + do { abSleep(4,TRUE); } while (comp > 0); + if (comp < 0) { + free((char *)icon); + return(comp); + } + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_icon(); + if (adi.adi_icontype == 1) { + fprintf(dbg, "\tAIcon:\n"); + dbg_print_icon(icon, adi.adi_iconsize); + fprintf(dbg, "\n"); + } + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + /* Insert the new entry */ + in = dtIconInsert(ihead, adi.adi_ftype, adi.adi_icontype, &replaced); + /* assert in != NULL */ + in->in_mloc = NOGCIDX; /* no cache yet */ + in->in_iloc = -1; /* initially none */ + in->in_uloc = NULL; + ii = &in->in_info; + /* redunancy */ + bcopy((char *)adi.adi_fcreator,(char *)ii->i_FCreator,FCreatorSize); + /* bcopy(adi.adi_ftype,ii->i_FType,FTypeSize); - already done */ + /* ii->i_IType = adi.adi_icontype; - already done */ + bcopy((char *)&adi.adi_icontag,(char *)ii->i_ITag,ITagSize); + ii->i_bmsize = adi.adi_iconsize; + + if (replaced != 0) { + if (DBDSK) + printf("FPAddIcon: trying to reuse old ICON entry\n"); + if (in->in_info.i_bmsize == replaced->in_info.i_bmsize) { + oldicon = IDFetch(dt->dt_ifd, replaced); + if (oldicon && bcmp((char *)oldicon, (char *)icon, + in->in_info.i_bmsize) == 0 && + bcmp((char *)replaced->in_info.i_ITag,(char *)in->in_info.i_ITag, + ITagSize) == 0) { + if (DBDSK) + printf("New icon matches an old one!\n"); + /* was no different! */ + in->in_iloc = replaced->in_iloc; + in->in_mloc = replaced->in_mloc; + in->in_uloc = replaced->in_uloc; + CacheAdd(in, (byte *)NULL); /* just mark as replacement */ + free((char *)icon); /* unused */ + free((char *)replaced); /* no longer wanted or needed */ + return(noErr); + } + /* mark be rewritten on disk */ + if (DBDSK) + printf("New icon data or user tags different, reusing space\n"); + /* must be rewritten to disk */ + in->in_iloc = replaced->in_iloc; + free((char *)replaced); /* taken care of */ + } else { + /* should mark as "bad" in file */ + /* what to do with replaced? */ + } + } + WriteIcon(dt->dt_ifd,in,icon,rcvlen); /* store in file */ + CacheAdd(in,icon); /* add this to the icon cache */ + return(noErr); +} + +/* + * OSErr FPGetIcon(...) + * + */ +OSErr +FPGetIcon(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + IconNode *ihead, *tofind; + GetIconPkt gic; + DeskTop *dt; + byte *ip; + int len; + + ntohPackX(PsGetIcon,p,l,(byte *) &gic); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_type(); + fprintf(dbg, "\tDTRef: %d\n", gic.gic_dtrefnum); + dbg_print_type("\tCreat:", gic.gic_fcreator); + dbg_print_type("\tFlTyp:", gic.gic_ftype); + fprintf(dbg, "\tIcTyp: %d\n", gic.gic_itype); + fprintf(dbg, "\tIcLen: %d\n", gic.gic_length); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((dt = VolGetDesk(gic.gic_dtrefnum)) == NILDT) + return(aeParamErr); + + if (DBDSK) { + printf("FPGetIcon: Get - "); + PrintIconInfo(gic.gic_fcreator, gic.gic_ftype); + } + ihead = dtFindIconList(dt, gic.gic_fcreator); + if (ihead == NULL) + return(aeItemNotFound); + (void)dtIconInsertPoint(ihead, gic.gic_ftype, gic.gic_itype, &tofind); + if (tofind == NULL) + return(aeItemNotFound); + + if (DBDSK) { + printf("FPGetIcon: Got - "); + PrintINode(tofind); + } + + len = min(tofind->in_info.i_bmsize,gic.gic_length); + + if ((ip = IDFetch(dt->dt_ifd, tofind)) != NULL) + bcopy((char *)ip,(char *)r,len); + else { + if (tofind->in_uloc != NULL) { + len = min(gic.gic_length, tofind->in_info.i_bmsize); + bcopy((char *)tofind->in_uloc, (char *)r, len); + } else + return(aeItemNotFound); + } + *rl = len; + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_icon(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tIcLen: %d\n", *rl); + dbg_print_icon(r, *rl); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/* + * OSErr FPRmvComment(...) + * + */ + +/*ARGSUSED*/ +OSErr +FPRmvComment(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + RemoveCommentPkt rmc; + IDirP idir,ipdir; + int err; + char file[MAXUFLEN]; + int ivol; + DeskTop *dt; + + if (DBDSK) + printf("FPRmvComment: ...\n"); + ntohPackX(PsRmvComment, p, l, (byte *)&rmc); + if ((dt = VolGetDesk(rmc.rmc_dtrefnum)) == NILDT) + return(aeParamErr); + + err = EtoIfile(file,&idir,&ipdir,&ivol,rmc.rmc_dirid, + dt->dt_evol,rmc.rmc_ptype,rmc.rmc_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tDTRef: %d\n", rmc.rmc_dtrefnum); + fprintf(dbg, "\tDirID: %08x\n", rmc.rmc_dirid); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", rmc.rmc_ptype, + (rmc.rmc_ptype == 1) ? "Short" : "Long"); + dbg_print_path(rmc.rmc_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + /* set comment to empty */ + err = OSSetComment(ipdir,file,(byte *)"\0",0); + + return(err); +} + + +OSErr +FPGetIconInfo(p,l,r,rl) +byte *p,*r; +int l; +int *rl; +{ + GetIconInfoPkt gii; + GetIconInfoReplyPkt giir; + DeskTop *dt; + IconNode *ihead; + int idx; + + ntohPackX(PsGetIconInfo,p,l,(byte *) &gii); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_type(); + fprintf(dbg, "\tDTRef: %d\n", gii.gii_dtrefnum); + dbg_print_type("\tCreat:", gii.gii_fcreator); + fprintf(dbg, "\tIcIdx: %d\n", gii.gii_iidx); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((dt = VolGetDesk(gii.gii_dtrefnum)) == NILDT) + return(aeParamErr); + + if (DBDSK) { + printf("FPGetIconInfo: Nth = %d ",gii.gii_iidx); + PrintIconInfo(gii.gii_fcreator, (byte *)"none"); + } + ihead = dtFindIconList(dt, gii.gii_fcreator); + if (ihead == NULL) + return(aeItemNotFound); + idx = gii.gii_iidx; + while ((ihead = ihead->in_next) != NULL && --idx > 0) + /* NULL */; + if (ihead == NULL) + return(aeItemNotFound); + + /* return this info */ + if (DBDSK) { + printf("FPGetIconInfo: Got - "); + PrintINode(ihead); + } + bcopy((char *)ihead->in_info.i_ITag,(char *)&giir.giir_itag, 4); + bcopy((char *)ihead->in_info.i_FType,(char *)giir.giir_ftype, 4); + giir.giir_itype = ihead->in_info.i_IType; + giir.giir_zero = 0; + giir.giir_size = ihead->in_info.i_bmsize; + *rl = htonPackX(PsGetIconInfoReply, (byte *)&giir, r); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_type(); + fprintf(dbg, "\tIcTag: %d\n", giir.giir_itag); + dbg_print_type("\tFlTyp:", giir.giir_ftype); + fprintf(dbg, "\tIcTyp: %d\n", giir.giir_itype); + fprintf(dbg, "\tIcLen: %d\n", giir.giir_size); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + + +private DeskTop * +VolGetDesk(volid) +word volid; +{ + int ivol = EtoIVolid(volid); + if (ivol < 0) + return(NILDT); + return(DeskTab[ivol]); +} + +private void +VolSetDesk(volid,dt) +word volid; +DeskTop *dt; +{ + int ivol = EtoIVolid(volid); + if (ivol < 0) + return; + dt->dt_opened = TRUE; + DeskTab[ivol] = dt; +} + +private void +VolClrDesk(volid) +word volid; +{ + int ivol = EtoIVolid(volid); + if (ivol < 0) + return; + if (DeskTab[ivol]) + DeskTab[ivol]->dt_opened = FALSE; +} + + +/* + * The following routines manage a tree of "File Creators". The bind routines + * simply return the "correct instance" a File Creator, inserting into the + * tree if necessary. The look routines do not insert. + * +*/ + +typedef struct { + byte adt_FCreator[FCreatorSize]; + APPLNode adt_ahead; + IconNode adt_ihead; +} AVLdt; + + +/* + * Compare two 4 byte entities and return: + * < 0 if a < b + * = 0 if a = b + * > 0 if a > b + * Assumes args are points to the two entities. + * +*/ +private int +FCompare(a, b) +byte *a; +byte *b; +{ + int i; + + /* don't use strncmp, because it will terminate on NULL */ + for (i = 0; i < 4; i++, a++, b++) { + if (*a == *b) + continue; + if (*a < *b) + return(-1); + return(1); + } + return(0); +} +/* + * Uses above to compare when first input argument is a pointer to + * byte[4] and the second is of type AVLdt. Only valid for File Creators. + * +*/ +private int +FCompare1(aa, b) +byte *aa; +AVLdt *b; +{ + return(FCompare(aa, b->adt_FCreator)); +} + +/* + * Given a file creator, give the binding in the ordered tree + * If the binding does not exist, then create it. + * +*/ +private AVLdt * +dtBindFC(dt, FCreator) +DeskTop *dt; +byte FCreator[]; +{ + AVLdt *newnode = NULL; + AVLNode *fcn; + + fcn = AVLInsert(&dt->dt_avlroot, FCreator, FCompare1); + /* assert fcn != NULL */ + if ((byte *)fcn->b_udata != FCreator) + return((AVLdt *)fcn->b_udata); + newnode = (AVLdt *)malloc(sizeof(AVLdt)); + bcopy((char *)FCreator, (char *)newnode->adt_FCreator, FCreatorSize); + newnode->adt_ahead.an_next = NULL; + newnode->adt_ihead.in_next = NULL; + fcn->b_udata = (AVLUData *)newnode; + return(newnode); +} + +/* + * Find (and possilby bind) the APPL node chain + * +*/ +private APPLNode * +dtBindFCtoAPPL(dt, FCreator) +DeskTop *dt; +byte FCreator[]; +{ + AVLdt *p; + + p = dtBindFC(dt, FCreator); + /* assert (p != NULL) */ + /* probably should do insertion sort at this point */ + return(&p->adt_ahead); +} + +/* + * Find (and possibly bind) the Icon node chain + * +*/ +private IconNode * +dtBindFCtoIcon(dt, FCreator) +DeskTop *dt; +byte FCreator[]; +{ + AVLdt *p; + + p = dtBindFC(dt, FCreator); + /* assert (p!=NULL) */ + /* probably should do insertion sort at this point */ + return(&p->adt_ihead); +} + +/* + * Given a file creator, give the binding in the ordered tree + * Cache - high probability that lookus on same File Creator will occur. + * +*/ +private AVLdt * +dtLookFC(dt, FCreator) +DeskTop *dt; +byte FCreator[]; +{ + if (dt->dt_avllast == NULL || FCompare(FCreator, dt->dt_oFCreator) != 0) { + bcopy((char *)FCreator, (char *)dt->dt_oFCreator, FCreatorSize); + dt->dt_avllast = AVLLookup(dt->dt_avlroot, FCreator, FCompare1); + } + if (dt->dt_avllast) + return((AVLdt *)dt->dt_avllast->b_udata); + return(NULL); +} + + +/* + * Find the APPL node chain + * +*/ +private APPLNode * +dtFindAPPLList(dt, FCreator) +DeskTop *dt; +byte FCreator[]; +{ + AVLdt *p; + + p = dtLookFC(dt, FCreator); + if (p) + return(&p->adt_ahead); + return(NULL); +} + + +/* + * Find the ICon node chain + * +*/ +private IconNode * +dtFindIconList(dt, FCreator) +DeskTop *dt; +byte FCreator[]; +{ + AVLdt *p; + + p = dtLookFC(dt, FCreator); + if (p) + return(&p->adt_ihead); + return(NULL); +} + + +/* + * rewrite a list of APPLnodes + * done if: + * (a) entry modified + * (b) parent directory of directory APPL is in has changed (e.g. APPL + * moved) + * +*/ +private void +FlushAPPL(adt,dt) +AVLdt *adt; +DeskTop *dt; +{ + register APPLNode *an; + register IDirP appdir, rppdir; /* supposed and real parent directory */ + /* of parent of entry */ + + an = &adt->adt_ahead; /* get first entry */ + while ((an = an->an_next) != NULL) { + if (DBDSK) + printf("FlushAPPL: parpath=%s, file=%s, deleted %s, modified %s\n", + ppathstr(dt->dt_rootd,an->an_ipdir),an->an_fnam, + (an->an_flags & AN_DEL) ? "yes" : "no", + (an->an_flags & AN_MOD) ? "yes" : "no"); + appdir = an->an_ippdir; /* get remember parent */ + rppdir = Ipdirid(an->an_ipdir); /* get real */ + if (an->an_flags & AN_MOD || appdir != rppdir) + WriteAPPL(dt,an); + if (appdir != rppdir) + an->an_ippdir = rppdir; /* reset to real parent */ + } +} + +/* + * Flush the desk top for the specified volume - e.g. flush the various + * cache to disk. Whenever called will (a) try to open for write and + * if it can (b) will run through all APPL mappings, rewritting them. + * +*/ +FlushDeskTop(volid) +int volid; +{ + DeskTop *dt; + int wok; + + dt = VolGetDesk(ItoEVolid(volid)); + if (dt == NILDT) { /* happens when vol mounted */ + if (DBDSK) /* from application */ + printf("FlushDeskTop: Unknown volid %d\n",volid); + return; + } + + /* + * Rewrite the entire APPL desktop on a flush when entries have + * been modified. Will be a nop is can't open desktop for write + * This is necessary even though we have a write through cache + * because old entries stay around (will have to figure something + * better out - maybe keep track of file position and write (invalid) + * flag or something. + * (Actually, just rewrite modified entries) + */ + if (dt->dt_afd < 0) { + dt->dt_afd = OpenDesk(dt, DESKTOP_APPL, FALSE, APPLFILEMODE, &wok); + if (dt->dt_afd < 0) + return; + if (!wok) { + (void)close(dt->dt_afd); + dt->dt_afd = -1; + return; + } + } + /* yes... so flush APPL records */ + OSLockFileforWrite(dt->dt_afd); /* lock it for write */ + AVLMapTree(dt->dt_avlroot, FlushAPPL,(char *) dt); + OSUnlockFile(dt->dt_afd); + (void)close(dt->dt_afd); /* close desktop */ + dt->dt_afd = -1; +} + + +/* + * Finds the insertion point for a new icon node. Returns the node + * whose "next" pointer should be replaced with the new node. + * "replaced" is set to the next node iff that node should be "replaced" + * in the list because it is a duplicate. + * + * FType is the primary key - multiple instanaces of this can occur + * IType is the secondary key - only one instance (per FType) may exist + * in the list. +*/ +private IconNode * +dtIconInsertPoint(h, FType, IType, replaced) +IconNode *h; +byte FType[]; +byte IType; +IconNode **replaced; +{ + IconNode *p, *c; /* previous and current respectively */ + int cmp; + + *replaced = NULL; + /* Scan until we find FType == node's FType */ + for (p = h, c = h->in_next; c != NULL; p = c, c = c->in_next) + if ((cmp = FCompare(FType, c->in_info.i_FType)) >= 0) + break; + /* assert(FCompare(FType, p->in_info.i_FType) < 0) */ + /* if c isn't null then we may also assert that: */ + /* assert(FCompare(FType, c->in_info.i_FType) >= 0) */ + + /* If previous assert is "=" then we check down secondary key */ + /* (if only FType, then replace we replace in this case) */ + /* If previous assert is ">", then insert after previous */ + + /* Thus p points to node to link after if only FTypes were considered */ + /* c points to node >= new node in terms of FType */ + if (cmp == 0 && c != NULL) { /* previous FType was an exact match? */ + do { + if (IType >= c->in_info.i_IType) + break; + p = c; + if ((c = c->in_next) == NULL) + break; + } while ((cmp = FCompare(FType, c->in_info.i_FType)) == 0); + /* at this point we can assert (unless c is null) */ + /* FCompare(FType, p->in_info.i_FType) = 0 */ + /* FCompare(FType, c->in_info.i_FType) >= 0 */ + /* IType >= c->in_info.i_IType && IType < p->in_info.i_IType */ + + if (cmp == 0 && c != NULL) + /* Assert FType == c->in_info.i_FType */ + /* IF Itype == c->in_info.i_IType, then replace c */ + if (IType == c->in_info.i_IType) { + *replaced = c; + } /* else iType > c->in_info and we want to insert after p */ + } + return(p); +} +/* + * Insert a new Icon node into a list. Old instances get "deleted" + * (not replaced - this allows management of the "deleted" space) + * Base key information is inserted into the node and caller is responsible + * for filling in the rest. Replaced icon entry is returned for + * recovery. +*/ +private IconNode * +dtIconInsert(h, FType, IType, replaced) +IconNode *h; +byte FType[]; +byte IType; +IconNode **replaced; +{ + IconNode *p; + IconNode *new; /* new entry */ + + p = dtIconInsertPoint(h, FType, IType, replaced); + if (*replaced) + p->in_next = (*replaced)->in_next; /* unlink node */ + new = (IconNode *)malloc(sizeof(IconNode)); + new->in_next = p->in_next; + p->in_next = new; + /* make sure new node has key info (at least) */ + bcopy((char *)FType, (char *)new->in_info.i_FType, FTypeSize); + new->in_info.i_IType = IType; + return(new); +} + +/* + * Insert APPLNode to chain - append to end if not in list + * otherwise replace entry. + * + * scan list and check for match, otherwise just insert at end + * if deleted (and same) unmark deleted bit want to keep deleted + * entries incore so we can reuse the space +*/ +private APPLNode * +dtAPPLInsertPoint(h, ipdir, fnam, replaced) +APPLNode *h; +IDirP ipdir; +char *fnam; +APPLNode **replaced; +{ + APPLNode *p, *np; + + *replaced = NULL; + for ( p = h, np = h->an_next ; np != NULL ; p = np, np = np->an_next) { + /* strcmp should be case insenstive here, but other considerations.. */ + if (np->an_ipdir == ipdir && strcmp(np->an_fnam, fnam) == 0) { + *replaced = np; + break; + } + } + return(p); +} + +private APPLNode * +dtAPPLInsert(h, ipdir, fnam, replaced) +APPLNode *h; +IDirP ipdir; +char *fnam; +APPLNode **replaced; +{ + APPLNode *p, *np; + + p = dtAPPLInsertPoint(h, ipdir, fnam, replaced); + if (*replaced) + p->an_next = (*replaced)->an_next; + /* insert at this point */ + if ((np = (APPLNode *)malloc(sizeof(APPLNode))) == NULL) + logit(0,"internal error: Memory allocation failure: dtAPPLInsert\n"); + /* hopefully will coredump on next */ + np->an_ipdir = ipdir; + np->an_ippdir = Ipdirid(ipdir); /* remember parent too */ + np->an_fnam = fnam; + np->an_iloc = -1; /* no iloc */ + np->an_next = p->an_next; /* should be null */ + p->an_next = np; /* link in */ + return(np); +} + + +/* + * Cache handling routines +*/ + +private GCHandle *gci; /* general cache for icon data */ +private IconCacheEntry *fice; /* maybe free cache entry */ +/* + * int gci_compare(IconCacheEntry *ice, IconCacheEntry *key) + * + * General cache compare routine for icon bitmap data. + * + */ + +private int +gci_compare(ice,key) +IconCacheEntry *ice,*key; +{ + return(ice->ib_node == key->ib_node); /* compare owners */ +} + +/* + * void gci_purge(IconCacheEntry *ice) + * + * general cache purge routine for icon bitmap data. + * + */ + +private void +gci_purge(ice) +IconCacheEntry *ice; +{ + if (DBDSK) + printf("gci_purge: Purging icon at %d\n",ice->ib_node); + + (ice->ib_node)->in_mloc = NOGCIDX; /* tell iconnode no longer cached */ + if (fice) { + if (ice->ib_size > fice->ib_size) { + fice->ib_data = ice->ib_data; /* use larger */ + fice->ib_size = ice->ib_size; + } else + if (ice->ib_data) + free((char *)ice->ib_data); /* release the data */ + free((char *) ice); /* free entry itself */ + } else + fice = ice; +} + +private void gci_flush() {} /* noop */ +private GCUData *gci_load() { return((GCUData *)0); } /* noop */ +private int gci_valid() { return(TRUE); } /* noop */ + +/* + * InitIconCache() + * + * Create a general purpose cache for storing icon bitmaps. The + * IconNode record contains a cache index when the icon is in memory. + * + */ + +void +InitIconCache() +{ + gci = GCNew(ICSize,gci_valid,gci_compare,gci_load,gci_purge,gci_flush); +} + +/* + * void CacheAdd(IconNode *in, char *icon) + * + * Add the icon bitmap (icon) associated with the IconNode in + * to the cache and store the cache index in IconNode in for + * quick reference. If cache index is valid, be nice about it. + * If no icon data and cache index is valid, then we have "replaced" + * the icon node and should simply rechain things. + * + * Used for adding icons not read from disk. (e.g. sent by remote) + * + */ +private void +CacheAdd(in,icon) +IconNode *in; +byte *icon; +{ + IconCacheEntry *ice; + + if (DBDSK) + printf("CacheAdd: Adding icon at %d\n",in); + + if (in->in_mloc != NOGCIDX) { + ice = (IconCacheEntry *)GCGet(gci, in->in_mloc); /* get cache entry */ + /* if new icon data and free entry, then maximize space in free entry */ + if (icon) { + if (fice && (ice->ib_size > fice->ib_size)) { + if (fice->ib_data) /* Get rid of any left */ + free((char *)fice->ib_data); + fice->ib_data = ice->ib_data; /* use larger */ + fice->ib_size = ice->ib_size; + } else + if (ice->ib_data) + free((char *)ice->ib_data); /* release the data */ + } + } else { + if (icon == NULL) /* no new icon data */ + return; /* nothing means nop here */ + if (fice) { /* free entry? */ + ice = fice; + if (fice->ib_data) + free((char *)fice->ib_data); /* data no good */ + fice = NULL; + } else { /* nope, must allocate */ + ice = (IconCacheEntry *) malloc(sizeof(IconCacheEntry)); + } + } + if (icon) + ice->ib_data = icon; /* here is the bitmap */ + ice->ib_node = in; /* remember parent */ + ice->ib_size = in->in_info.i_bmsize; /* remember size */ + if (in->in_mloc == NOGCIDX) + in->in_mloc = GCAdd(gci,(GCUData *) ice); /* set node to be cache idx */ +} + + +/* + * char *IDFetch( int fd,IconNode *in) + * + * The icon is either cached in memory or in the file fd. + * + * Check IconNode in to see if the icon is at a cache index, if so + * return the cached icon. If the icon is not in memory then read + * in from file fd and add to the cache. + * + */ +private byte * +IDFetch(fd,in) +int fd; +IconNode *in; +{ + IconCacheEntry *ice; + + if (in->in_mloc != NOGCIDX) { /* data in memory? */ + /* yes... get cached */ + ice = (IconCacheEntry *)GCGet(gci,in->in_mloc); /* icon data at index */ + if (DBDSK) + printf("IDFetch: hit on icon at %d\n",in); + return(ice->ib_data); /* return bitmap data */ + } + + if (fd < 0) /* no fp? */ + return(NULL); /* then can't get icon */ + + if (fice) { /* use last freed if there */ + ice = fice; + fice = NULL; + } else { + ice = (IconCacheEntry *) malloc(sizeof(IconCacheEntry)); + if (ice == NULL) { + printf("Memory allocation error"); + } + ice->ib_data = NULL; + ice->ib_size = -1; + } + if (ice->ib_size < in->in_info.i_bmsize) { + if (ice->ib_data) + free((char *)ice->ib_data); /* get rid of too small region */ + /* allocate */ + ice->ib_data = (byte *) malloc((unsigned)in->in_info.i_bmsize); + if (ice->ib_data == NULL) { + printf("Memory allocation error"); + } + } + + OSLockFileforRead(fd); + if (lseek(fd,in->in_iloc,L_SET) < 0L) { /* offset in file to data */ + fice = ice; + OSUnlockFile(fd); + return(NULL); + } + if (read(fd,(char *)ice->ib_data,in->in_info.i_bmsize) < 0) { + fice = ice; + OSUnlockFile(fd); + return(NULL); + } + OSUnlockFile(fd); + ice->ib_node = in; + in->in_mloc = GCAdd(gci, (GCUData *) ice); /* add to cache */ + return(ice->ib_data); /* return buffer pointer */ +} + + +RemoveAPPLIDB(adt, dt) +AVLdt *adt; +DeskTop *dt; +{ + APPLNode *an, *ant; + + for (an = adt->adt_ahead.an_next; an ; ) { + ant = an; + an = an->an_next; + free(ant->an_fnam); + free(ant); + } + adt->adt_ahead.an_next = NULL; +} + +RemoveICONIDB(adt, dt) +AVLdt *adt; +DeskTop *dt; +{ + IconNode *in, *intemp; + + for (in = adt->adt_ihead.in_next; in ; ) { + intemp = in; + in = in->in_next; + if (intemp->in_uloc) + free(intemp->in_uloc); + free(intemp); + } + adt->adt_ihead.in_next = NULL; +} + +DeskRemove(dt, wh) +DeskTop *dt; +int wh; +{ + AVLMapTree(dt->dt_avlroot, + wh == REMOVEAPPLIDB ? RemoveAPPLIDB : RemoveICONIDB, (char *) dt); +} + +#ifdef DEBUG_AFP_CMD +/* + * print a 4 byte Creator or File Type + * + */ + +void +dbg_print_type(str, typ) +char *str; +byte *typ; +{ + int i; + + if (dbg != NULL) { + fprintf(dbg, "%s '", str); + for (i= 0; i < 4; i++) + fprintf(dbg, "%c", (isprint(typ[i])) ? typ[i] : '?'); + fprintf(dbg, "'\n"); + } + + return; +} + +/* + * print out a representation of the ICN# + * + */ + +#define get2(s) (u_short)(((s)[0]<<8)|((s)[1])) +#define get4(s) (u_int)(((s)[0]<<24)|((s)[1]<<16)|((s)[2]<<8)|((s)[3])) + +void +dbg_print_icon(icn, len) +byte *icn; +int len; +{ + u_char *p; + int i, j, k; + u_long data1, data2, mask; + + if (len != 256) + return; + + if (dbg != NULL) { + for (i = 0, p = icn; i < 32; i += 2, p += 8) { + for (j = 0; j < 2; j++) { + if (j == 1) + fprintf(dbg, "\t "); + data1 = get4(p+(j*128)); + data2 = get4(p+4+(j*128)); + for (k = 0; k < 32; k++) { + mask = ((u_long)0x80000000 >> k); + if (data1 & mask && data2 & mask) + fprintf(dbg, "8"); + else + if (data1 & mask) + fprintf(dbg, "\""); + else + if (data2 & mask) + fprintf(dbg, "o"); + else + fprintf(dbg, " "); + } + } + fprintf(dbg, "\n"); + } + } + + return; +} +#endif /* DEBUG_AFP_CMD */ diff --git a/applications/aufs/afpdt.h b/applications/aufs/afpdt.h new file mode 100644 index 0000000..f1d9666 --- /dev/null +++ b/applications/aufs/afpdt.h @@ -0,0 +1,148 @@ +/* + * $Author: djh $ $Date: 1994/10/10 09:02:04 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpdt.h,v 2.2 1994/10/10 09:02:04 djh Rel djh $ + * $Revision: 2.2 $ + * + */ + +/* + * afpdt.h - Appletalk Filing Protocol Desktop definitions + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Mar 30, 1987 Schilit Created. + * + * + */ + +#include "afpavl.h" /* relies strickly on avl structs */ + +#define FF_ICON 00 +#define FF_APPL 01 +#define FF_COMM 02 + +#define FCreatorSize 4 +#define FTypeSize 4 +#define ITagSize 4 + +typedef struct { /* Icon Information */ + sdword i_bmsize; /* 4: size of the icon bitmap */ + byte i_FCreator[FCreatorSize]; /* 4[8]: file's creator type */ + byte i_FType[FTypeSize]; /* 4[12] file's type */ + byte i_IType; /* 1[13] icon type */ + byte i_pad1; /* 1[14] */ + byte i_ITag[ITagSize]; /* 4[18] user bytes */ + byte i_pad2[2]; /* 2[20] pad out to double word boundry */ +} IconInfo; + +typedef struct { /* APPL information */ + byte a_FCreator[4]; /* creator of application */ + byte a_ATag[4]; /* user bytes */ +} APPLInfo; + +/* never use zero or 0x1741 as the major version */ +#define AFR_MAGIC 0x00010002 /* version 1.2 (don't use 1.1, 2.2, etc) */ +/* version 1.0 (version 0x1741.0000/0x1741) */ + + +typedef struct { /* File Format APPL record */ + dword afr_magic; /* magic number for check */ + APPLInfo afr_info; /* the appl info */ + sdword afr_pdirlen; /* length of (relative) parent directory */ + sdword afr_fnamlen; /* length of application name */ + /* name follows */ +} APPLFileRecord; + +/* never use zero or 0x2136 as the major version */ +#define IFR_MAGIC 0x00010002 /* Version 1.2, skip 1.1, 2.2, etc. */ +/* version 1.0: 0x2136.0000/0x2136 */ + +typedef struct { /* File Format ICON record */ + dword ifr_magic; /* the magic check */ + IconInfo ifr_info; /* the icon info */ + /* bitmap follows this */ +} IconFileRecord; + +struct IconNodeStruct { /* Internal format Icon record */ + IconInfo in_info; /* the icon info */ + off_t in_iloc; /* file location */ + int in_mloc; /* cache index */ + byte *in_uloc; /* if set, then pointer to unix icon */ + struct IconNodeStruct *in_next; /* pointer to next in chain */ +}; +typedef struct IconNodeStruct IconNode; + + +struct APPLNodeStruct { /* Internal format APPL record */ + struct APPLNodeStruct *an_next; + APPLInfo an_info; /* Appl info */ + IDirP an_ipdir; /* parent directory */ + IDirP an_ippdir; /* parent of parent */ + off_t an_iloc; /* location in .ADeskTop */ + int an_flags; /* flags */ +#define AN_DEL 0x1 /* entry is deleted - really need AVLDelete */ +#define AN_MOD 0x2 /* entry is "new" - modified or added */ + /* after readadesktop */ + char *an_fnam; /* file name pointer */ +}; + +typedef struct APPLNodeStruct APPLNode; + + +typedef struct { + int dt_ifd; /* handle on desktop file */ + int dt_afd; /* handle on desktop file */ + int dt_ivol; /* desktop belongs to this volume */ + word dt_evol; /* external value of above */ + AVLNode *dt_avlroot; /* root of avl tree mapping file creators */ + byte dt_oFCreator[FCreatorSize]; /* key for last cache entry of above */ + AVLNode *dt_avllast; /* node pointed to by key */ + IDirP dt_rootd; /* volumes root dir */ + int dt_opened; /* true if desktop is open */ +} DeskTop, *DeskTopP; + +#define IFRLEN (sizeof(IconFileRecord)) +#define AFRLEN (sizeof(APPLFileRecord)) + +#define NILDT ((DeskTop *) 0) + +private DeskTop *VolGetDesk(); +private void VolSetDesk(); +private void VolClrDesk(); + +typedef struct { + IconNode *ib_node; /* pointer to owner node */ + byte *ib_data; /* pointer to the icon data */ + int ib_size; /* size of icon data */ +} IconCacheEntry, *ICEP; + +/* + * ICSize is the size of the icon cache which is used to store icon + * bitmaps in memory. The size of the cache is dependant on the + * following: + * + * Memory: + * size of each entry is about 256 bytes (icon data size for b&w icons) + + * Sizeof(IconCacheEntry). + * + * Work: + * Reference to an icon bitmap in memory is constant time since the + * cache index is stored in the IconNode. + * + * Reference to an icon bitmap in a file causes a scan of the entire + * cache in order to find the minimum entry for replacement. + * + * Performance: + * A good cache size would try to contain all the icons on the desktop + * (considering the above constaints) since the finder will try to read + * the icon bitmaps when returning to the desktop. + * + */ + +#define ICSize 100 /* number of icon cache entries */ + diff --git a/applications/aufs/afpfid.c b/applications/aufs/afpfid.c new file mode 100644 index 0000000..e112edb --- /dev/null +++ b/applications/aufs/afpfid.c @@ -0,0 +1,99 @@ +/* + * $Author: djh $ $Date: 1996/04/27 12:03:04 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpfid.c,v 2.2 1996/04/27 12:03:04 djh Rel djh $ + * $Revision: 2.2 $ + * + */ + +/* + * afpfid.c - File ID routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * September 1995 scooter Created + * + */ + +#include +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif _TYPES +#include +#include +#include +#include "afps.h" + +#ifdef FIXED_DIRIDS +#include "../../lib/afp/afpidaufs.h" +#endif /* FIXED_DIRIDS */ + +/* + * FIdmove(IDirP fpdir, char *from, IDirP tpdir, char *to) + * + * Maintains consistency in database structures when a file + * is renamed or moved. + * + * The file "from" in parent fpdir is being renamed to be + * "to" in the parent tpdir. Because file ids may not + * change we must modify the tree instead of recreating nodes. + * + */ + +#ifndef FIXED_DIRIDS +void +FIdmove(fpdir,from,tpdir,to) +IDirP fpdir,tpdir; +char *from,*to; +{ + return; +} +#else /* FIXED_DIRIDS */ +void +FIdmove(fpdir,from,tpdir,to) +IDirP fpdir,tpdir; +char *from,*to; +{ + IDirP fdir; + sdword toEid, fileID; + char path[MAXUFLEN]; + +#ifdef SHORT_NAMES + if (DBFIL && fpdir != NULL) + printf("FIdmove fpdir->name %s, tpdir->name %s, from %s, to %s\n",fpdir->name, tpdir->name, from, to); +#endif SHORT_NAMES + + if (DBFIL) { + printf("FIdmove: changing path=%s, file=%s",pathstr(fpdir),from); + if (tpdir == fpdir) + printf(" to new name %s\n",to); + else + printf(" to path=%s, file=%s\n",pathstr(tpdir),to); + } + + sprintf(path, "%s/%s", pathstr(fpdir), from); + + /* Look up from to see if we have a FileID */ + if ((fileID = aufsExtFindId(path)) < 0) { + if (DBFIL) + printf("FIdmove: no ID for %s/%s",pathstr(fpdir),from); + return; /* Nope, we're done */ + } + + /* We've got one, now rename it! */ + + if (fpdir != tpdir) { /* if different parents then... */ + toEid = Edirid(tpdir); + aufsExtMoveIds(fileID, toEid, to); + } else { /* effectively a rename */ + aufsExtRenameId(fileID, to); + } +} +#endif FIXED_DIRIDS diff --git a/applications/aufs/afpfile.c b/applications/aufs/afpfile.c new file mode 100644 index 0000000..4cb14e9 --- /dev/null +++ b/applications/aufs/afpfile.c @@ -0,0 +1,726 @@ +/* + * $Author: djh $ $Date: 1996/06/19 04:20:09 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpfile.c,v 2.11 1996/06/19 04:20:09 djh Rel djh $ + * $Revision: 2.11 $ + * + */ + +/* + * afpfile.c - Appletalk Filing Protocol File Level Routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * + */ + +/* + * Non OS dependant support routines for: + * + * FPExchangeFiles() + * FPSetFileParms() + * FPCreateFile() + * FPCopyFile() + * FPRename() + * FPDelete() + * FPMove() + * + */ + +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif +#include +#include +#include +#include "afpntoh.h" +#include "afps.h" + +#ifdef DEBUG_AFP_CMD +extern FILE *dbg; +#endif /* DEBUG_AFP_CMD */ + +/* + * OSErr FPSetFileParms(...) + * + */ + +/*ARGSUSED*/ +OSErr +FPSetFileParms(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + SetFileParmsPkt sfp; + FileDirParm fdp; + IDirP idir,ipdir; + char file[MAXUFLEN]; + int ivol,len,err; + + len = ntohPackX(PsSetFileParms,p,l,(byte *) &sfp); + ntohPackXbitmap(ProtoFileAttr,p+len,l-len,(byte *) &fdp,sfp.sfp_bitmap); + + err = EtoIfile(file,&idir,&ipdir,&ivol,sfp.sfp_dirid, + sfp.sfp_volid,sfp.sfp_ptype,sfp.sfp_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_bmap(); + void dbg_print_parm(); + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", sfp.sfp_volid); + fprintf(dbg, "\tDirID: %08x\n", sfp.sfp_dirid); + fprintf(dbg, "\tBtMap: %08x\t", sfp.sfp_bitmap); + dbg_print_bmap(sfp.sfp_bitmap); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", sfp.sfp_ptype, + (sfp.sfp_ptype == 1) ? "Short" : "Long"); + dbg_print_path(sfp.sfp_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + dbg_print_parm(sfp.sfp_bitmap, p+len, l-len, 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + fdp.fdp_fbitmap = sfp.sfp_bitmap; /* set file bitmap */ + + if (DBFIL) + printf("FPSetFileParms: setting bm=%d for %s %s\n", + fdp.fdp_fbitmap,pathstr(ipdir),file); + + return(OSSetFileParms(ipdir,file,&fdp)); +} + +/* + * OSErr FPCreateFile(byte *p,byte *r,int *rl) + * + * This call is used to create a file. + * + */ + + +/*ARGSUSED*/ +OSErr +FPCreateFile(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + CreateFilePkt crf; + int ivol,delf,err; + IDirP idir,ipdir; + char file[MAXUFLEN]; + + ntohPackX(PsCreateFile,p,l,(byte *) &crf); + err = EtoIfile(file,&idir,&ipdir,&ivol,crf.crf_dirid, + crf.crf_volid,crf.crf_ptype,crf.crf_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", crf.crf_volid); + fprintf(dbg, "\tDirID: %08x\n", crf.crf_dirid); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", crf.crf_ptype, + (crf.crf_ptype == 1) ? "Short" : "Long"); + dbg_print_path(crf.crf_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + delf = (crf.crf_flg & CRF_HARD) != 0; + + if (DBFIL) + printf("FPCreateFile flg=%02x, delf=%d, path=%s, fn=%s\n", + crf.crf_flg,delf,pathstr(ipdir),file); + + err = OSCreateFile(ipdir,file,delf); + if (err == noErr) /* if success */ + VolModified(ivol); /* then volume modified */ + return(err); +} + +#ifdef FIXED_DIRIDS +/* + * OSErr FPCreateID(byte *p,byte *r,int *rl) + * + * This call is used to create a file ID. + * + */ + + +/*ARGSUSED*/ +OSErr +FPCreateID(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + CreateIDPkt crid; + int ivol,delf,err; + IDirP idir,ipdir; + char file[MAXUFLEN]; + sdword fileID; + + ntohPackX(PsCreateID,p,l,(byte *) &crid); + + err = EtoIfile(file,&idir,&ipdir,&ivol,crid.crid_dirid, + crid.crid_volid,crid.crid_ptype,crid.crid_path); + + if (err == noErr) + fileID = aufsExtEFileId(ipdir->edirid, file); +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", crid.crid_volid); + fprintf(dbg, "\tDirID: %08x\n", crid.crid_dirid); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", crid.crid_ptype, + (crid.crid_ptype == 1) ? "Short" : "Long"); + dbg_print_path(crid.crid_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fprintf(dbg, "\tFileID: %08x\n", fileID); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + + if ((err != noErr) && (DBFIL)) + printf("error returned from EtoIfile\n"); + PackDWord(fileID,r); + *rl = 4; + return(noErr); +} + +/* + * OSErr FPResolveID(byte *p,byte *r,int *rl) + * + * This call is used to resolve a file ID. + * + */ + + +/*ARGSUSED*/ +OSErr +FPResolveID(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + ResolveIDPkt rid; + int ivol,delf,err; + IDirP idir,ipdir; + FileDirParm fdp; + char file[MAXUFLEN]; + sdword fileID; + char *path, *filep; + extern char *strrchr(); + extern char *aufsExtPath(); + + idir = NULL; + + ntohPackX(PsRslvID,p,l,(byte *) &rid); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_bmap(); + fprintf(dbg, "\tVolID: %04x\n", rid.rid_volid); + fprintf(dbg, "\tFilID: %04x\n", rid.rid_fileid); + fprintf(dbg, "\tFBMap: %04x\t", rid.rid_fbitmap); + dbg_print_bmap(rid.rid_fbitmap, 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + /* + * get the path from the database + */ + if ((path = aufsExtPath(rid.rid_fileid)) == NULL) + return(aeIDNotFound); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tFName: %s\n", path); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + /* + * devide into file and dir + */ + filep = strrchr(path, '/'); + if (filep) + *filep++ = '\0'; + + /* + * get the Volume ID + */ + ivol = EtoIVolid(rid.rid_volid); /* set internal volid */ + + /* + * get the directory ID + */ + ipdir = Idirid(path); + + /* + * call OSFileDirInfo + */ + fdp.fdp_pdirid = ItoEdirid(ipdir,ivol); + fdp.fdp_dbitmap = 0; + fdp.fdp_fbitmap = rid.rid_fbitmap; + fdp.fdp_zero = 0; + err = OSFileDirInfo(ipdir,idir,filep,&fdp,ivol); /* fill in information */ + if (err != noErr) { + if (DBFIL) + printf("FPResolveID: OSFileDirInfo returns %d on %s/%s\n", + err,pathstr(ipdir),file); + return(err); + } + PackWord(rid.rid_fbitmap,r); + *rl = 2; + *rl += htonPackX(FilePackR,(byte *) &fdp,r+(*rl)); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_parm(); + void dbg_print_bmap(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tFBMap: %04x\t", fdp.fdp_fbitmap); + dbg_print_bmap(fdp.fdp_fbitmap, 0); + fprintf(dbg, "\tFDFlg: %02x\t(%s)\n", fdp.fdp_flg, + FDP_ISDIR(fdp.fdp_flg) ? "Directory" : "File"); + if (*rl == 2) + fprintf(dbg, "\t\n"); + else + dbg_print_parm(fdp.fdp_fbitmap, r+2, (*rl)-2, 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + + +/* + * OSErr FPDeleteID(byte *p,byte *r,int *rl) + * + * This call is used to delete a file ID. + * + */ + + +/*ARGSUSED*/ +OSErr +FPDeleteID(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + return(aeParamErr); +} +#endif /* FIXED_DIRIDS */ + +/* + * FPCopyFile(byte *p,byte *r,int *rl) [NOP] + * + * Optional, may not be supported on all servers. + * + * + */ + +/*ARGSUSED*/ +OSErr +FPCopyFile(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + CopyFilePkt cpf; + char sfile[MAXUFLEN],dfile[MAXUFLEN],newname[MAXUFLEN]; + char *dfilep; + IDirP sidir,sipdir,didir,dipdir; + int sivol,divol; + int err; + + ntohPackX(PsCopyFile,p,l,(byte *) &cpf); + err = EtoIfile(sfile,&sidir,&sipdir,&sivol,cpf.cpf_sdirid, + cpf.cpf_svolid,cpf.cpf_sptype,cpf.cpf_spath); + if (err != noErr) + return(err); + err = EtoIfile(dfile,&didir,&dipdir,&divol,cpf.cpf_ddirid, + cpf.cpf_dvolid,cpf.cpf_dptype,cpf.cpf_dpath); + if (err != noErr) + return(err); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tSVolID: %04x\n", cpf.cpf_svolid); + fprintf(dbg, "\tSDirID: %08x\n", cpf.cpf_sdirid); + fprintf(dbg, "\tDVolID: %04x\n", cpf.cpf_dvolid); + fprintf(dbg, "\tDDirID: %08x\n", cpf.cpf_ddirid); + fprintf(dbg, "\tSPType: %d\t(%s Names)\n", cpf.cpf_sptype, + (cpf.cpf_sptype == 1) ? "Short" : "Long"); + dbg_print_path(cpf.cpf_spath); + fprintf(dbg, "\tSUPath: \"%s/%s\"\n", pathstr(sipdir), sfile); + fprintf(dbg, "\tDPType: %d\t(%s Names)\n", cpf.cpf_dptype, + (cpf.cpf_dptype == 1) ? "Short" : "Long"); + dbg_print_path(cpf.cpf_dpath); + fprintf(dbg, "\tDUPath: \"%s/%s\"\n", pathstr(dipdir), dfile); + fprintf(dbg, "\tNPType: %d\t(%s Names)\n", cpf.cpf_newtype, + (cpf.cpf_newtype == 1) ? "Short" : "Long"); + fprintf(dbg, "\tNMFile: \"%s\"\n", cpf.cpf_newname); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (didir == NILDIR) /* destination directory */ + return(aeParamErr); /* must be around */ + +#ifdef SHORT_NAMES +/* i don't have to worry about this if we are doing short names */ + if ((err = EtoIName(cpf.cpf_newname,newname)) != noErr) + return(err); +#else SHORT_NAMES + if ((err = EtoIName(cpf.cpf_newname,newname)) != noErr) + return(err); +#endif SHORT_NAMES + + if (*newname != '\0') /* if new name was specified */ + dfilep = newname; /* then use that */ + else /* otherwise if no new name */ + dfilep = sfile; /* then use the source file name */ + + if (DBFIL) { + printf("FPCopyFile: srcvol=%d, srcdir=%s srcfil=%s\n", + sivol,pathstr(sipdir),sfile); + printf("FPCopyFile: dstvol=%d, dstdir=%s, dstname=%s\n", + divol,pathstr(didir),dfilep); + } + + err = OSCopyFile(sipdir,sfile,didir,dfilep); + if (err == noErr) /* if success */ + VolModified(divol); /* then dest volume modified */ + return(err); +} + +/* + * FPRename(byte *p,byte *r,int *rl) + * + * This call is used to rename either a file or directory. + * + */ + +/*ARGSUSED*/ +OSErr +FPRename(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + RenamePkt ren; + IDirP idir,ipdir,nidir,nipdir; + int ivol,err; + char file[MAXUFLEN],nfile[MAXUFLEN]; + + ntohPackX(PsRename,p,l,(byte *) &ren); + + err = EtoIfile(file,&idir,&ipdir,&ivol,ren.ren_dirid, + ren.ren_volid,ren.ren_ptype,ren.ren_path); + if (err != noErr) + return(err); + + err = EtoIfile(nfile,&nidir,&nipdir,&ivol,ren.ren_dirid, + ren.ren_volid,ren.ren_ntype,ren.ren_npath); + if (err != noErr) + return(err); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", ren.ren_volid); + fprintf(dbg, "\tDirID: %08x\n", ren.ren_dirid); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", ren.ren_ptype, + (ren.ren_ptype == 1) ? "Short" : "Long"); + dbg_print_path(ren.ren_path); + fprintf(dbg, "\tPPath: \"%s/%s\"\n", pathstr(ipdir), file); + fprintf(dbg, "\tNType: %d\t(%s Names)\n", ren.ren_ntype, + (ren.ren_ntype == 1) ? "Short" : "Long"); + dbg_print_path(ren.ren_npath); + fprintf(dbg, "\tNPath: \"%s/%s\"\n", pathstr(nipdir), nfile); + fprintf(dbg, "\tNwTyp: %d\t(%s Names)\n", ren.ren_ntype, + (ren.ren_ntype == 1) ? "Short" : "Long"); + dbg_print_path(ren.ren_npath); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (DBFIL) { + printf("FPRename path=%s, name=%s,",pathstr(ipdir),file); + printf("to path=%s, name=%s\n",pathstr(nipdir),nfile); + } + + if (ipdir != nipdir) { + printf("FPRename: different parent directory\n"); + return(aeParamErr); + } + err = OSRename(ipdir,file,nfile); + if (err == noErr) { + OFNFIXUP(ipdir, file, ipdir, nfile); + } + return(err); +} + + +/* + * FPDelete(byte *p,byte *r,int *rl) + * + * This call is used to delete either a file or directory. + * + */ + +/*ARGSUSED*/ +OSErr +FPDelete(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + DeletePkt del; + IDirP idir,ipdir; + int ivol,err; + char file[MAXUFLEN]; + + ntohPackX(PsDelete,p,l,(byte *) &del); + + err = EtoIfile(file,&idir,&ipdir,&ivol,del.del_dirid, + del.del_volid,del.del_ptype,del.del_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tVolID: %04x\n", del.del_volid); + fprintf(dbg, "\tDirID: %08x\n", del.del_dirid); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", del.del_ptype, + (del.del_ptype == 1) ? "Short" : "Long"); + dbg_print_path(del.del_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + if (DBFIL) + printf("FPDelete: path=%s file=%s\n",pathstr(ipdir),file); + + if (is_open_file(ipdir, file)) + return(aeFileBusy); + +#ifdef STAT_CACHE + OScd(pathstr(ipdir)); /* so we don't try and delete current directory */ +#endif STAT_CACHE + + err = OSDelete(ipdir,idir,file); + + if (err == noErr) /* if success */ + VolModified(ivol); /* then volume modified */ + + return(err); +} + +/* + * FPMove(...) + * + * This call is used to move (not just copy) a directory or file + * to another location on a single volume (source and destination + * must be on the same volume). An object cannot be moved from one + * volume to another with this call, even though both volumes may be + * managed by the server. The destination of the move is specified + * by providing a DirID and Pathname that indicates the object's new + * Parent Directory. + * + */ + +/*ARGSUSED*/ +OSErr +FPMove(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + MovePkt mov; + IDirP idir,ipdir,nidir,nipdir; + int ivol,err; + char file[MAXUFLEN],nfile[MAXUFLEN]; + char *nf; +#ifdef SHORT_NAMES + char tempfile[MAXUFLEN]; + int i; +#endif SHORT_NAMES + + ntohPackX(PsMove,p,l,(byte *) &mov); + + err = EtoIfile(file,&idir,&ipdir,&ivol,mov.mov_sdirid, + mov.mov_volid,mov.mov_sptype,mov.mov_spath); + if (err != noErr) + return(err); + + err = EtoIfile(nfile,&nidir,&nipdir,&ivol,mov.mov_ddirid, + mov.mov_volid,mov.mov_dptype,mov.mov_dpath); + if (err != noErr) + return(err); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tSVolID: %04x\n", mov.mov_volid); + fprintf(dbg, "\tSDirID: %08x\n", mov.mov_sdirid); + fprintf(dbg, "\tDVolID: %04x\n", mov.mov_volid); + fprintf(dbg, "\tDDirID: %08x\n", mov.mov_ddirid); + fprintf(dbg, "\tSPType: %d\t(%s Names)\n", mov.mov_sptype, + (mov.mov_sptype == 1) ? "Short" : "Long"); + dbg_print_path(mov.mov_spath); + fprintf(dbg, "\tSUPath: \"%s/%s\"\n", pathstr(ipdir), file); + fprintf(dbg, "\tDPType: %d\t(%s Names)\n", mov.mov_dptype, + (mov.mov_dptype == 1) ? "Short" : "Long"); + dbg_print_path(mov.mov_dpath); + fprintf(dbg, "\tDUPath: \"%s/%s\"\n", pathstr(nipdir), nfile); + fprintf(dbg, "\tNPType: %d\t(%s Names)\n", mov.mov_newtype, + (mov.mov_newtype == 1) ? "Short" : "Long"); + dbg_print_path(mov.mov_newname); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + /* nidir, nfile must be a directory! */ + if (nidir == NILDIR) + return(aeParamErr); + /* where should this be done if not here? */ +#ifdef SHORT_NAMES + if ((mov.mov_newtype != 0x2) && (mov.mov_newname[0]!= '\0')) { + for (i = 1; i <= (int)mov.mov_newname[0]; i++) + tempfile[i-1] = mov.mov_newname[i]; + tempfile[mov.mov_newname[0]] = '\0'; + if (DBFIL) + printf("tempfile %s\n",tempfile); + EtoIName_Short(nidir,tempfile, nfile); + nf = nfile; + }else if (mov.mov_newname[0] != '\0'){ + EtoIName(mov.mov_newname, nfile); + nf = nfile; +#else SHORT_NAMES + if (mov.mov_newtype != 0x2) + return(aeParamErr); + if (mov.mov_newname[0] != '\0') { + EtoIName(mov.mov_newname, nfile); + nf = nfile; +#endif SHORT_NAMES + } else nf = file; + + if (DBFIL) { + printf("FPMove path=%s, name=%s,",pathstr(ipdir),file); + printf("to path=%s, name=%s\n",pathstr(nidir),nf); + } + + err = OSMove(ipdir,file,nidir,nf); + if (err == noErr) { /* if success */ + OFNFIXUP(ipdir, file, nidir, nfile); + VolModified(ivol); /* then volume modified */ + } + return(err); +} + +/* + * FPExchangeFiles(...) + * + * Atomic exchange of two files. Swaps the data and resource forks + * but not the finder info. + * + */ + +/*ARGSUSED*/ +OSErr +FPExchangeFiles(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + ExchPkt xch; + IDirP aidir,aipdir,bidir,bipdir; + int ivol,err; + char afile[MAXUFLEN],bfile[MAXUFLEN]; + char *nf; + + ntohPackX(PsExchange,p,l,(byte *)&xch); + + err = EtoIfile(afile,&aidir,&aipdir,&ivol,xch.exc_adirid, + xch.exc_volid,xch.exc_aptype,xch.exc_apath); + if (err != noErr) + return(err); + + err = EtoIfile(bfile,&bidir,&bipdir,&ivol,xch.exc_bdirid, + xch.exc_volid,xch.exc_bptype,xch.exc_bpath); + if (err != noErr) + return(err); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_path(); + fprintf(dbg, "\tEVolID: %04x\n", xch.exc_volid); + fprintf(dbg, "\tSDirID: %08x\n", xch.exc_adirid); + fprintf(dbg, "\tDDirID: %08x\n", xch.exc_bdirid); + fprintf(dbg, "\tSType: %d\t(%s Names)\n", xch.exc_aptype, + (xch.exc_aptype == 1) ? "Short" : "Long"); + dbg_print_path(xch.exc_apath); + fprintf(dbg, "\tDType: %d\t(%s Names)\n", xch.exc_bptype, + (xch.exc_bptype == 1) ? "Short" : "Long"); + dbg_print_path(xch.exc_bpath); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + /* + * Neither a nor b should be directories. + * + */ + if (aidir != NILDIR || bidir != NILDIR) + return(aeObjectTypeErr); + + if (DBFIL) { + printf("FPExchangeFiles path=%s, name=%s,\n",pathstr(aipdir),afile); + printf(" with path=%s, name=%s\n",pathstr(bipdir),bfile); + } + + err = OSExchangeFiles(aipdir,afile,bipdir,bfile); + + if (err == noErr) { /* if success */ + VolModified(ivol); /* then volume modified */ +#ifdef FIXED_DIRIDS + { + char a_path[MAXPATHLEN], b_path[MAXPATHLEN]; + sprintf(a_path, "%s/%s", pathstr(aipdir),afile); + sprintf(b_path, "%s/%s", pathstr(bipdir),bfile); + aufsExtExchange(a_path, b_path); + } +#endif /* FIXED_DIRIDS */ + } + + return(err); +} diff --git a/applications/aufs/afpfork.c b/applications/aufs/afpfork.c new file mode 100644 index 0000000..a1e23f1 --- /dev/null +++ b/applications/aufs/afpfork.c @@ -0,0 +1,705 @@ +/* + * $Author: djh $ $Date: 1995/06/26 05:49:55 $ + * $Header: /local/mulga/mac/src/cap60/applications/aufs/RCS/afpfork.c,v 2.8 1995/06/26 05:49:55 djh Rel djh $ + * $Revision: 2.8 $ +*/ + +/* + * afpfork.c - Appletalk Filing Protocol Fork Level Routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * + */ + +/* + * Non OS dependant support routines for: + * + * FPGetForkParms() + * FPSetForkParms() + * FPOpenFork() + * FPCloseFork() + * FPRead() + * FPWrite() + * FPFlushFork() + * FPByteRangeLock() + * + */ + +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif +#include +#include +#include +#include "afps.h" +#include "afpntoh.h" +#include "afposncs.h" + +#ifdef DEBUG_AFP_CMD +extern FILE *dbg; +#endif /* DEBUG_AFP_CMD */ + +typedef struct { + int ofn_fd; /* file descriptor */ + sdword ofn_pos; /* file position */ + IDirP ofn_ipdir; + int ofn_ivol; + int ofn_efn; /* external file number */ + char ofn_fnam[MAXUFLEN]; /* file name */ + int ofn_mode; + int ofn_type; /* data or rsrc */ + int ofn_flgs; /* file flags */ +#define OF_EOF 0x020 /* eof on file */ +#define OF_NOTOPEN 0x02 /* to support non-existent files */ +#define OF_INUSE 0x04 /* OFN is in use */ + int ofn_trans_table; /* Translation table if any */ +} OFN; + +#define MaxOFN 30 + +private OFN OFNTBL[MaxOFN]; + +private int oinit = FALSE; + +private +iniofn() +{ + int i; + + for (i=0; i < MaxOFN; i++) { + OFNTBL[i].ofn_flgs = 0; + OFNTBL[i].ofn_pos = -1; + OFNTBL[i].ofn_trans_table = -1; /* no index */ + OFNTBL[i].ofn_efn = i+1; + } + oinit = TRUE; +} + +private word +ItoEOFN(o) +OFN *o; +{ + return(o->ofn_efn); +} + +private OFN * +EtoIOFN(i) +int i; +{ + i = i-1; + if (i > MaxOFN || i < 0) { + printf("EtoIOFN: bad file number\n"); + return(NULL); + } + if (!oinit || (OFNTBL[i].ofn_flgs & OF_INUSE) == 0) /* ofn assigned? */ + return(NULL); /* no, mark as bad */ + return(&OFNTBL[i]); +} + +private void +relofn(ofn) +OFN *ofn; +{ + ofn->ofn_flgs = 0; + ofn->ofn_pos = -1; + ofn->ofn_trans_table = -1; /* delete */ +} + +private OFN * +asnofn() +{ + int i; + if (!oinit) + iniofn(); + for (i=0; i < MaxOFN; i++) + if ((OFNTBL[i].ofn_flgs & OF_INUSE) == 0) { + OFNTBL[i].ofn_trans_table = -1; /* paranoia: no index */ + OFNTBL[i].ofn_flgs = OF_INUSE; + return(&OFNTBL[i]); + } + return(0); +} + +/* + * This is a horrible hack that is necessary to ensure that renames + * get reflected back. Really, really, points to the need for internal + * directory of names.... Can't use fstat because file name is required + * to return some of the information (from finderinfo). Sigh... + * +*/ +OFNFIXUP(oipdir, ofile, nipdir, nfile) +IDirP oipdir; +char *ofile; +IDirP nipdir; +char *nfile; +{ + OFN *ofn; + if (!oinit) + return(-1); + + for (ofn = OFNTBL; ofn < OFNTBL+MaxOFN; ofn++) + if (ofn->ofn_flgs & OF_INUSE) { + if (ofn->ofn_ipdir == oipdir && strcmp(ofn->ofn_fnam, ofile) == 0) { + ofn->ofn_ipdir = nipdir; /* remember new directory */ + strcpy(ofn->ofn_fnam, nfile); /* remember new file */ + } + } + return(0); +} + +/* + * determine if file is already open + * + */ + +int +is_open_file(ipdir, file) +IDirP ipdir; +char *file; +{ + OFN *ofn; + + if (!oinit) + return(0); + + for (ofn = OFNTBL; ofn < OFNTBL+MaxOFN; ofn++) + if (ofn->ofn_flgs & OF_INUSE) + if (ofn->ofn_ipdir == ipdir + && strcmp(ofn->ofn_fnam, file) == 0) + return(1); + + return(0); +} + +/* + * OSErr FPGetForkParms(...) + * + * This call is used to retrieve parameters for a file associated with + * a particular open fork. + * + * Inputs: + * refnum Open fork refnum + * bitmap Bitmap describing which parameters are to be retrieved. + * This field is the same as the FPGetFileDirParms call. + * + * Outputs: + * Same as FPGetFileDirParms. + * + * Errors: + * ParamErr, BitMapErr, AccessDenied. + * + * The fork must be open for read. + * + */ + +OSErr +FPGetForkParms(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + GetForkParmsPkt gfp; + FileDirParm fdp; + int err; + OFN *ofn; + + ntohPackX(PsGetForkParms,p,l,(byte *) &gfp); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_bmap(); + fprintf(dbg, "\tRefNum: %d\n", gfp.gfp_refnum); + fprintf(dbg, "\tBitMap: %04x\t", gfp.gfp_bitmap); + dbg_print_bmap(gfp.gfp_bitmap, 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((ofn = EtoIOFN(gfp.gfp_refnum)) == NULL) + return(aeParamErr); + + fdp.fdp_pdirid = ItoEdirid(ofn->ofn_ipdir,ofn->ofn_ivol); + fdp.fdp_dbitmap = 0; + fdp.fdp_fbitmap = gfp.gfp_bitmap; + fdp.fdp_zero = 0; + err = OSFileDirInfo(ofn->ofn_ipdir,NILDIR,ofn->ofn_fnam,&fdp,ofn->ofn_ivol); + if (err != noErr) + return(err); + PackWord(gfp.gfp_bitmap,r); + *rl = 2; + *rl += htonPackX(FilePackR,(byte *) &fdp,r+(*rl)); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_parm(); + fprintf(dbg, " Return Parameters:\n"); + dbg_print_parm(fdp.fdp_fbitmap, r+2, (*rl)-2, 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/* + * OSErr FPSetForkParms(...) + * + * Inputs: + * + * refnum Open fork refnum. + * bitmap Bitmap describing which params are to be set. + * + * Output: + * Function Result. + * + * Errors: + * ParamErr, BitMapErr, DiskFull, LockErr, AccessDenied. + * + * The bitmap is the same as FPSetFileDirParms, however in AFP 1.1 + * only the fork length may be set. + * + * The fork must be opened for write. + * + */ + +/*ARGSUSED*/ +OSErr +FPSetForkParms(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + SetForkParmsPkt sfkp; + OFN *ofn; + sdword len; + + ntohPackX(PsSetForkParms,p,l,(byte *) &sfkp); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_bmap(); + fprintf(dbg, "\tRefNum: %d\n", sfkp.sfkp_refnum); + fprintf(dbg, "\tBitMap: %04x\t", sfkp.sfkp_bitmap); + dbg_print_bmap(sfkp.sfkp_bitmap, 0); + fprintf(dbg, "\tForkLn: %d\n", + (sfkp.sfkp_bitmap & FP_DFLEN) ? sfkp.sfkp_dflen : sfkp.sfkp_rflen); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((ofn = EtoIOFN(sfkp.sfkp_refnum)) == NULL) + return(aeParamErr); + + if (DBFRK) + printf("FPSetForkParms: ofn=%d\n",sfkp.sfkp_refnum); + + if ((sfkp.sfkp_bitmap & ~(FP_DFLEN|FP_RFLEN)) != 0 || sfkp.sfkp_bitmap==0) { + printf("FPSetForkParms: bad bitmap %x\n",sfkp.sfkp_bitmap); + return(aeBitMapErr); + } + + len = (sfkp.sfkp_bitmap & FP_DFLEN) ? sfkp.sfkp_dflen : sfkp.sfkp_rflen; + /* can only set fork length for now */ + return((ofn->ofn_flgs&OF_NOTOPEN) ? noErr : OSSetForklen(ofn->ofn_fd, len)); +} + + +OSErr +FPOpenFork(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + OpenForkPkt ofk; + OpenForkReplyPkt ofr; + FileDirParm fdp; + int ivol,err,err1,flg,fhdl,len, ttidx; + IDirP idir,ipdir; + char file[MAXUFLEN]; + byte finderinfo[FINFOLEN]; + OFN *ofn; + extern PackEntry ProtoOFkRP[]; + + ntohPackX(PsOpenFork,p,l,(byte *) &ofk); + + err = EtoIfile(file,&idir,&ipdir,&ivol,ofk.ofk_dirid, + ofk.ofk_volid,ofk.ofk_ptype,ofk.ofk_path); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_mode(); + void dbg_print_bmap(); + void dbg_print_path(); + fprintf(dbg, "\tOFork: %s\n", (ofk.ofk_rdflg) ? "Resource" : "Data"); + fprintf(dbg, "\tVolID: %04x\n", ofk.ofk_volid); + fprintf(dbg, "\tDirID: %08x\n", ofk.ofk_dirid); + fprintf(dbg, "\tBtMap: %04x\t", ofk.ofk_bitmap); + dbg_print_bmap(ofk.ofk_bitmap, 0); + fprintf(dbg, "\tAMode: %04x\t", ofk.ofk_mode); + dbg_print_mode(ofk.ofk_mode); + fprintf(dbg, "\tPType: %d\t(%s Names)\n", ofk.ofk_ptype, + (ofk.ofk_ptype == 1) ? "Short" : "Long"); + dbg_print_path(ofk.ofk_path); + if (err == noErr) + fprintf(dbg, "\tUPath: \"%s/%s\"\n", pathstr(ipdir), file); + else + fprintf(dbg, "\tUPath: \n", err); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (err != noErr) + return(err); + + if (DBFRK) + printf("OpenFork: rdflg=%d, mode=%d, path=%s file=%s\n", + ofk.ofk_rdflg,ofk.ofk_mode,pathstr(ipdir),file); + + flg = (ofk.ofk_rdflg == 0) ? F_DATA : F_RSRC; + + err = OSOpenFork(ipdir,file,ofk.ofk_mode,flg,&fhdl); + + if (err != noErr + && err != aeDenyConflict + && !(flg == F_RSRC && err == aeObjectNotFound)) + return(err); + + /* + * now, if data fork exists and resource + * open failed, allow go ahead + * + */ + if (err == aeObjectNotFound) { +#ifdef notdef + /* why did I do this? */ + if (ofk.ofk_mode & OFK_MWR) /* don't allow write access */ + return(aeAccessDenied); +#endif + if (OSFileExists(ipdir, file, F_DATA) != noErr) + return(aeObjectNotFound); + else { + if (DBFRK) + printf("Allowing fake access to resource fork\n"); + } + } + + if ((ofn = asnofn()) == 0) { + if (err == noErr) + OSClose(fhdl); + return(aeTooManyFilesOpen); + } + ofn->ofn_fd = fhdl; + ofn->ofn_mode = ofk.ofk_mode; + ofn->ofn_type = flg; + ofn->ofn_ipdir = ipdir; + ofn->ofn_ivol = ivol; + + if (err == aeObjectNotFound + || err == aeDenyConflict) + ofn->ofn_flgs |= OF_NOTOPEN; + + if (flg == F_DATA) { + /* Never translate resource fork */ + OSGetFNDR(ipdir, file, finderinfo); /* check file type */ +#ifdef SHORT_NAMES + if (DBFRK) + printf("afpfork, ofk.ofk_path %d\n",ofk.ofk_ptype); + if ((ttidx = ncs_istrans(finderinfo, file, ofk.ofk_ptype)) >= 0) { +#else SHORT_NAMES + if ((ttidx = ncs_istrans(finderinfo, file)) >= 0) { +#endif SHORT_NAMES + ofn->ofn_trans_table = ttidx; + if (DBFRK) + printf("Open fork: translation table: %s\n", ncs_tt_name(ttidx)); + } + } + + strcpy(ofn->ofn_fnam,file); /* remember file name (UGH!!!!) */ + + fdp.fdp_fbitmap = ofk.ofk_bitmap; + fdp.fdp_dbitmap = 0; + if ((err1 = OSFileDirInfo(ipdir,NILDIR,file,&fdp,ivol)) != noErr) { + if (err != noErr) + OSClose(fhdl); + relofn(ofn); + return(err1); + } + + ofr.ofkr_bitmap = ofk.ofk_bitmap; + ofr.ofkr_refnum = (err == aeDenyConflict) ? 0x0000 : ItoEOFN(ofn); + len = htonPackX(ProtoOFkRP, &ofr, r); /* pack away */ + *rl = len + htonPackX(FilePackR,(byte *) &fdp,r+len); + + if (err == aeDenyConflict) { + relofn(ofn); + return(err); + } + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_parm(); + void dbg_print_bmap(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tFDescr: %d\n", ofn->ofn_fd); + fprintf(dbg, "\tBitMap: %04x\t", ofr.ofkr_bitmap); + dbg_print_bmap(ofk.ofk_bitmap, 0); + fprintf(dbg, "\tRefNum: %d\n", ofr.ofkr_refnum); + dbg_print_parm(ofr.ofkr_bitmap, r+len, *rl, 0); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/*ARGSUSED*/ +OSErr +FPCloseFork(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + CloseForkPkt cfk; + word attr; + OFN *ofn; + int err; + + ntohPackX(PsCloseFork,p,l,(byte *) &cfk); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tRefNum: %d\n", cfk.cfk_refnum); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (DBFRK) + printf("CloseFork: refnum=%d\n",cfk.cfk_refnum); + if ((ofn = EtoIOFN(cfk.cfk_refnum)) == NULL) + return(aeParamErr); + + if ((ofn->ofn_mode & OFK_MWR) != 0) /* if opened for write */ + VolModified(ofn->ofn_ivol); /* then set volume modification dt */ + + err = (ofn->ofn_flgs & OF_NOTOPEN) ? noErr : OSClose(ofn->ofn_fd); + + if (err == noErr) { + OSGetAttr(ofn->ofn_ipdir, ofn->ofn_fnam, &attr); + attr &= ~((ofn->ofn_type == F_DATA) ? FPA_DAO : FPA_RAO); + OSSetAttr(ofn->ofn_ipdir, ofn->ofn_fnam, attr); + } + + relofn(ofn); + return(err); +} + + +/* + * + * FPRead(...) + * + * The fork is read starting offset bytes from the beginning of the fork + * and terminating at the first newline-char encountered. + * + */ + +OSErr +FPRead(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + ReadPkt rp; + OFN *ofn; + extern int sqs; /* get max send quantum size */ + + ntohPackX(PsRead,p,l,(byte *) &rp); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tRefNum: %d\n", rp.rdf_refnum); + fprintf(dbg, "\tOffSet: %d\n", rp.rdf_offset); + fprintf(dbg, "\tReqCnt: %d\n", rp.rdf_reqcnt); + fprintf(dbg, "\tNLMask: %d\n", rp.rdf_flag); + fprintf(dbg, "\tNLChar: 0x%02x\n", rp.rdf_nlchar); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (DBFRK) + printf("Read: ofn=%d, offset=%d, reqcnt=%d, flg=%d, nlc=%02x\n", + rp.rdf_refnum,rp.rdf_offset,rp.rdf_reqcnt, + rp.rdf_flag,rp.rdf_nlchar); + + if ((ofn = EtoIOFN(rp.rdf_refnum)) == NULL) + return(aeParamErr); + + if (ofn->ofn_flgs & OF_NOTOPEN) { + *rl = 0; + return(aeEOFErr); + } + + return(OSRead(ofn->ofn_fd,rp.rdf_offset, + min(rp.rdf_reqcnt,sqs), + rp.rdf_flag,rp.rdf_nlchar,r,rl,&ofn->ofn_pos, + ofn->ofn_trans_table)); +} + +OSErr +FPWrite(p,l,r,rl,cno,reqref) +byte *p,*r; +int l,*rl; +int cno; +ReqRefNumType reqref; +{ + WritePkt wrt; + int rcvlen; + int comp,err; + OFN *ofn; + extern int n_rrpkts; + + ntohPackX(PsWrite,p,l,(byte *) &wrt); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tS/EFlg: %02x\n", wrt.wrt_flag); + fprintf(dbg, "\tRefNum: %d\n", wrt.wrt_refnum); + fprintf(dbg, "\tOffset: %d\n", wrt.wrt_offset); + fprintf(dbg, "\tReqCnt: %d\n", wrt.wrt_reqcnt); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((ofn = EtoIOFN(wrt.wrt_refnum)) == NULL) + return(aeParamErr); + if (DBFRK) + printf("FPWrite: ofn=%d, flg=%d, offs=%d, req=%d\n", + wrt.wrt_refnum,wrt.wrt_flag,wrt.wrt_offset,wrt.wrt_reqcnt); + if (ofn->ofn_flgs & OF_NOTOPEN) { + if (wrt.wrt_reqcnt != 0) /* allow zero length requests */ + return(aeAccessDenied); + } + + err = dsiWrtContinue(cno,reqref,r,n_rrpkts*atpMaxData,&rcvlen,-1,&comp); + if (err != noErr) + return(err); + do { abSleep(4,TRUE); } while (comp > 0); + if (comp < 0) + return(comp); + err = OSWrite(ofn->ofn_fd,r,(sdword) rcvlen, + wrt.wrt_offset,wrt.wrt_flag,&ofn->ofn_pos, + ofn->ofn_trans_table); + if (err != noErr) + return(err); + PackDWord(ofn->ofn_pos,r); + *rl = sizeof(ofn->ofn_pos); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tWrittn: %d\n", ofn->ofn_pos); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/*ARGSUSED*/ +OSErr +FPFlushFork(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + OFN *ofn; + FlushForkPkt flf; + + ntohPackX(PsFlush, p, l, (byte *)&flf); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL ) { + fprintf(dbg, "\tRefNum: %d\n", flf.flf_refnum); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((ofn = EtoIOFN(flf.flf_refnum)) == NULL) + return(aeParamErr); + if (DBFRK) + printf("FLFlush: ofn=%d\n", flf.flf_refnum); + return((ofn->ofn_flgs & OF_NOTOPEN) ? noErr : OSFlushFork(ofn->ofn_fd)); +} + +/*ARGSUSED*/ +OSErr +FPByteRangeLock(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + OFN *ofn; + ByteRangeLockPkt brl; + OSErr err; + + ntohPackX(PsByteRangeLock, p, l, (byte *)&brl); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tULFlag: %s\n", (brl.brl_flg & 0x01) ? "UNLOCK" : "LOCK"); + fprintf(dbg, "\tSEFlag: %s\n", (brl.brl_flg & 0x80) ? "END" : "START"); + fprintf(dbg, "\tRefNum: %d\n", brl.brl_refnum); + fprintf(dbg, "\tOffset: %d\n", brl.brl_offset); + fprintf(dbg, "\tLength: %d\n", brl.brl_length); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((ofn = EtoIOFN(brl.brl_refnum)) == NULL) + return(aeParamErr); + if (DBFRK) + printf("Byte Range Lock: ofn=%d (file %s)\n",ofn->ofn_fd,ofn->ofn_fnam); + if (ofn->ofn_flgs & OF_NOTOPEN) + ofn->ofn_pos = brl.brl_offset; + else { + err = OSByteRangeLock(ofn->ofn_fd, brl.brl_offset, brl.brl_length, + brl.brl_flg, &ofn->ofn_pos); + if (err != noErr) + return(err); + } + PackDWord(ofn->ofn_pos,r); + *rl = sizeof(ofn->ofn_pos); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tRStart: %d\n", ofn->ofn_pos); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +#ifdef DEBUG_AFP_CMD +void +dbg_print_mode(mode) +u_short mode; +{ + if (dbg != NULL) { + fprintf(dbg, "("); + if (mode & 0x0001) + fprintf(dbg, "Read "); + if (mode & 0x0002) + fprintf(dbg, "Write "); + if (mode & 0x0004) + fprintf(dbg, "DenyRead "); + if (mode & 0x0008) + fprintf(dbg, "DenyWrite "); + fprintf(dbg, ")\n"); + } +} +#endif /* DEBUG_AFP_CMD */ diff --git a/applications/aufs/afpgc.c b/applications/aufs/afpgc.c new file mode 100644 index 0000000..f1feeba --- /dev/null +++ b/applications/aufs/afpgc.c @@ -0,0 +1,251 @@ +/* + * $Author: djh $ $Date: 91/02/15 21:07:18 $ + * $Header: afpgc.c,v 2.1 91/02/15 21:07:18 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * afpgc.c - Appletalk Filing Protocol General Cache Manager + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * + * Apr 3, 1987 Schilit Created. + * + */ + +/* + * General Cache Routines. + * + * These routines may be used for building a cache based on LRU + * replacement. + * + * The caller controls the cache by supplying routines for loading, + * purging, comparing, and validating entries. + * + * The cache is initialized and the size is defined by a call to + * GCNew, all the other work is performed by the lookup routine, + * GCLocate. See the comments on those routines for calling + * conventions. + * + */ + +/* + * GCNew - create a new cache + * GCScan - locate cache entry + * GCLocate - locate cache entry, loading if necessary + * GCFlush - flush cache + * GCGet - get cache entry + * GCAdd - add cache entry + +*/ + +#include "afpgc.h" /* include defs for cache */ + +#define TRUE 1 +#define FALSE 0 + +/* + * GCHandle *GCNew(size,int (*valid)(), int (*comp)(), + * char *(*load)(), void (*purge)()); + * + * Create a new cache of size 'size' and define the interface routines + * for validating, comparing, loading, and purging entries. + * + * int valid(ce) - returns TRUE if entry is valid. + * int comp(ce,key) - compare and return 0 if equal. + * char *load(key) - load an entry (allocate stg and/or read from disk). + * void purge(ce) - purge an entry (release stg & write to disk). + * void flush(ce) - flush entry (write to disk) + * + * + * The datatype of a cache entry is defined by the *result* of load(). + * This datatype is stored in the cache and passed to purge(), valid() + * and as the first argument to comp(). + * + * A second datatype may be defined for the lookup key. This is the + * argument to GCLocate and is sent to comp() to compare against an + * existing cache entry. Load() accepts a key and returns the cache + * entry for that key. + * + */ + +GCHandle * +GCNew(size,cvalid,ccomp,cload,cpurge,cflush) +int size; +int (*cvalid)(); +int (*ccomp)(); +char *(*cload)(); +void (*cpurge)(); +void (*cflush)(); +{ + GCHandle *gch; + int i; + + gch = (GCHandle *) malloc(sizeof(GCHandle)); + gch->gch_clock = 0; + gch->gch_size = size; + gch->gch_valid = cvalid; /* validation routine */ + gch->gch_comp = ccomp; /* compare routine */ + gch->gch_load = cload; /* loading routine */ + gch->gch_purge = cpurge; /* release routine */ + gch->gch_flush = cflush; /* write or update */ + gch->gch_lru = (int *) malloc(sizeof(int)*size); + gch->gch_ents = (GCUData **) malloc(sizeof(GCUData *) * (size)); + for (i=0; i < size; i++) + gch->gch_lru[i] = -1; + return(gch); +} + +/* + * boolean GCScan(GCHandle *gch, GCUData *key, int *idx) + * + * Scan the cache (gch) looking for key. + * + * Returns TRUE if the entry was found in the cache, idx is the index + * of the entry. + * + * Returns FALSE if no entry was found in the cache, idx is a free + * entry (may have called user's purge). + * + */ + +/* private */ int +GCScan(gch, key, idx) +GCHandle *gch; +GCUData *key; +int *idx; +{ + register GCUData **ent = gch->gch_ents; + register int *lru = gch->gch_lru; + register int i; + register int mi = 0; + register int mf = -1; + + for (i=0; i < gch->gch_size; i++) { /* scan cache for entry and min */ + if (lru[i] < 0) /* entry in cache? */ + mf = i; /* no, remember free entry */ + else { + if ((*gch->gch_comp)(ent[i],key)) /* compare */ + if ((*gch->gch_valid)(ent[i])) { /* match, see if entry is valid */ + lru[i] = gch->gch_clock++; /* found matching valid entry */ + *idx = i; /* here is the cache index */ + return(TRUE); /* aready in cache return TRUE */ + } else { + mf = i; /* invalid and matching, so reuse it */ + break; /* break out of for loop */ + } + if (lru[i] < lru[mi]) /* no match, check for min entry */ + mi = i; /* if so remember */ + } + } + + /* Miss. cache scan is over without locating the desired entry. */ + /* if we did not find a free entry then free the min lru found */ + /* and load the cache with the desired entry. */ + + if (mf < 0) { /* did we find a free entry */ + (*gch->gch_purge)(ent[mi]); /* no, free the min entry */ + mf = mi; /* now here is a slot */ + } + *idx = mf; /* set index */ + return(FALSE); +} + +/* + * char *GCLocate(GCHandle *gch, char *key) + * + * Locate the entry matching 'key' in the cache 'gch' by calling + * the comparision routine comp() for each cache entry. + * + * If no entry is found, or the matching entry fails the user + * specified valid() check, then add a new cache entry by calling + * the load() procedure. + * + * If the cache is full, then replacement is required and the + * user specified purge() is called to release the LRU entry. + * + */ + +char * +GCLocate(gch,key) +GCHandle *gch; +char *key; +{ + int idx; + + if (GCScan(gch,key,&idx)) /* scan for entry */ + return(gch->gch_ents[idx]); /* found it, so return */ + gch->gch_lru[idx] = gch->gch_clock++; /* else free entry, set clock */ + gch->gch_ents[idx] = (*gch->gch_load)(key); /* load it in */ + return(gch->gch_ents[idx]); /* and return it */ +} + +/* + * flush is called when OSFlush gets called -- could and should be + * done without releasing cache entries by using user defined flush + * routine: in other words, it is a prime opportunity to scan for "bad" + * items + * + * allows passing of userdata (single long) + * + */ +void +GCFlush(gch, udata) +GCHandle *gch; +unsigned long udata; +{ + int i; + char **ent = gch->gch_ents; + int *lru = gch->gch_lru; + + for (i=0; i < gch->gch_size; i++) /* scan cache for entry and min */ + if (lru[i] >= 0) /* entry in cache? */ + if ((*gch->gch_valid)(ent[i])) /* yes... entry valid? */ + (*gch->gch_flush)(ent[i], udata); /* yes.. then flush */ + +} + +/* + * GCUData *GCGet(GCHandle *gch, int idx) + * + * Return the cache entry at index idx. + * + */ + +GCUData * +GCGet(gch,idx) +GCHandle *gch; +int idx; +{ + return(gch->gch_ents[idx]); +} + +/* + * int GCAdd(GCHandle *gch, GCUData *udata) + * + * Add the entry udata to the cache. Returns cache index. + * + * Adding an entry may cause user's flush routine to be called + * if the cache is full. + * + */ + +int +GCAdd(gch, udata) +GCHandle *gch; +GCUData *udata; +{ + int idx; + + if (GCScan(gch,udata,&idx)) /* scan for entry in cache */ + return(idx); /* found it, oh well.. */ + gch->gch_lru[idx] = gch->gch_clock++; /* else free entry, set clock */ + gch->gch_ents[idx] = udata; /* store entry */ + return(idx); /* and return index */ +} diff --git a/applications/aufs/afpgc.h b/applications/aufs/afpgc.h new file mode 100644 index 0000000..80473a2 --- /dev/null +++ b/applications/aufs/afpgc.h @@ -0,0 +1,48 @@ +/* + * $Author: djh $ $Date: 91/02/15 21:07:27 $ + * $Header: afpgc.h,v 2.1 91/02/15 21:07:27 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * afpgc.c - Appletalk Filing Protocol General Cache Manager Definitions + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * + * Apr 3, 1987 Schilit Created. + * + */ + +/* GCHandle is the general cache manager's handle on a cache */ + +typedef char GCUData; + +typedef struct { + int gch_clock; /* the current cache clock */ + int gch_size; /* the size of the cache */ + int (*gch_valid)(); /* valid function */ + int (*gch_comp)(); /* compare function */ + char *(*gch_load)(); /* load function */ + void (*gch_purge)(); /* free function */ + void (*gch_flush)(); /* write to disk function */ + int *gch_lru; /* clocks for each entry */ + GCUData **gch_ents; /* data for each entry */ +} GCHandle; + +/* given a pointer cache pointer and an index, returns an entry */ +#define GCidx2ent(gch,idx) ((gch)->gch_ents[(idx)]) + +GCHandle *GCNew(); /* create a new cache */ +GCUData *GCLocate(); /* locate an entry in the cache */ +void GCFlush(); /* flush the cache */ + +GCUData *GCGet(); /* direct access get by index */ +int GCAdd(); /* direct access add to cache */ + +#define NOGCIDX -1 /* NULL cache index */ diff --git a/applications/aufs/afpid.notes b/applications/aufs/afpid.notes new file mode 100644 index 0000000..3f3acda --- /dev/null +++ b/applications/aufs/afpid.notes @@ -0,0 +1,160 @@ +afpidsrvr +--------- + +This provides aufs with permanent external directory and file id's. + +Rather than remembering pathnames, Mac's often store a number for +the directory and the name of the file. These id's must be mapped +by aufs onto the Unix file system - thus the term "external id's". +External id's are used extensively by the alias manager, and also +affect QuickTime. aufs's problem is that it forgets the id's after +each session. This causes many problems, the greatest being that +aliases do not work properly. + +The previous, and still default, version created external id's as +required in a linear manner. These are mapped onto pointers - +internally aufs holds the directory picture as a tree. An array of +these pointers, indexed by the external id, was used to perform the +external to internal mapping. The reverse mapping was achieved +primarily by a field in the internal directory records. + +This new version uses a central database to hold the external id's. +Various schemes were examined, including the use of inode numbers. +Most were rejected on the realisation that a single scheme had to +cover the whole file system: you cannot assign numbers by volumes. +The database is single-writer/multi-reader. aufs processes read +id/path pairs from the database, but if they want to add entries they +send a request to the 'afpidsrvr'. The use of a server at least +ensures consistency is maintained, gets around the basic problem of +the lack of locks, and preserves some security. So far, at least, the +system has worked without locks - aufs does re-open databases after +it detects change, to avoid some problems. Security is not +particularly strict - the server will not create entries if +directories don't exist, and will not remove entries for existing +directories. The system is not foolproof, and can have problems if +the database gets corrupted, but there we are. + +The current version (1.1) of the database is the second go. The first +version contained two sets of entries: fullpath->id and id->fullpath. +This worked fine until the system tried to delete or move directory +trees - it just moved the top one, and left the children in their +original places. The second version mirrors the directory structure +in providing a tree. Moving and deleting now alters the tree +structure. This means that many more accesses are required, but the +records themselves are on average smaller. Each record, looked up by +key N, consists of: + + + +There is also an initial record consisting of the databased version +and the root id. + +As well as a the afpidsrvr server there is an afpidtool, which can +send messages directly to the server, and afpidlist, which will +list the database. + +The system is by no means foolproof. Expect problems if different +people are working on the same area - aufs still cannot detect +automatically that other processes have changed the Unix +filestructure. Since the server routines pass a fullpath back to +aufs itself, and let it then decode that into the internal aufs +directory tree, if the name is changed by another process, then aufs +may see a whole new path. If this proves a problem, a mechanism will +be required to pass back the information to aufs - it is essentially +a problem with the aufs/afpidsrvr interface, rather than with afpidsrvr +itself. One possibility would be to passback a list of extId/name +pairs, rather than a path. If the names changed than aufs would +realise that, and could change its internal record. I've no idea how +it would tell the Mac though! + +Another area of concern is in the integrity of the database. The +current database should not get corrupted, but if it does the +programs are not very resiliant to it - the contents of a record are +believed. It is not particularly clear how this will workout in +practice - my experience with corruptions is limited to problems +during testing, when bugs caused process crashes. If corruptions turn +out to be a problem, then some extra checks will be required. To be +worth while, adding CRC checks to the database records is probably +required. For the moment the simple scheme seems preferable. Do note +that, in the extreme case, you may have to delete the database in +/usr/local/lib/cap and start again. If you do this, ensure you stop both +afpidsrvr and aufs first - neither like this event! This will obviously +cause problems to users. The previous version of afpidsrvr had a clean +function, which was intended to remove unused and faulty entries. +This is currently non-operative, but may be re-instated in the +future. + +In will be noted that afpidsrvr is optional. Why might you not wish to +use this: + + * There is a performance penalty, and you might not wish to pay it. + In practice, there is a slight penalty on startup, and in opening + folders for the first time - especially new ones. The only "bad case" + scenario that actually occurs is the reverse lookup of an id to a + pathname, which is then converted to an internal pointer by a second + tree semi-traversal. My experience with a Sun II server suggests that + this delay is negligable. + + * Reliability. The system has, currently, only been tested on a Sun + II server. There may be minor problems elsewhere, but there is + nothing to suggest that there should be huge problems. I'm still not + sure about the big-endian/little-endian situation, but I believe that + providing the database is not moved from one machine to another there + should be no problem. + + * NFS. We do not use NFS much, and I have little experience of it. I + would only expect problems if the aufs server for the same area ran + on serveral machines, but there are several suspect areas. + + * Portability. The system runs under SunOS4.1. I have not tested it + on other systems - not even Solaris. The chief problem areas may be + in named sockets, which are used to send the information between + client processes and the server. On other systems you my have to use + equivalents. + +On a final point, the modified server does contain emergency code for +use if the server ceases to function. This should never happen in +normal running, but may if errors are hit. This backup mechanism +actually reflects v1.0 if the database, and just records name/id +pairs. It is anything but efficient, and is merely intended to avoid +having to crash the server. Really there ought to be a few bells +ringing if this happens, but there are not! [Perhaps in a future +version, if something thinks of a good interface.] It is suggested +that this should not be used routinely - not least it has problems +still with moving and deleting sub-trees of directories - and is very +inefficient. In case you wonder why not just fall back on the default +scheme, the assumption is that the existing database is fine, but +cannot be modified. + +Installation and Use +-------------------- + +To use the file/directory ID server, select the FIXED_DIRIDS option in +the m4.features file. Re-run gen.makes, 'make clean' and rebuild CAP. + +When compiled and installed, you will get the additional tools afpidsrvr, +afpidlist and afpidtool in your cap bin directory - along with the +modified aufs. + +To bring them into operation, you must: + +* Modify /etc/rc.local or your start-cap-servers file to place the +following lines, or similar, before you start aufs: + + rm /usr/local/lib/cap/afpIDsock + afpidsrvr -l /usr/adm/afpidsrvr.log + +Then restart as appropriate. The first call to afpidsrvr will create +the database, and aufs processes will then use it. Note the first +line is to delete the socket used for communication. This normally +happens when afpidsrvr exits, but may not if the machine crashes. In +normal running, you should be careful about running this - ensure +there is no afpidsrvr server running. This will only happen in normal +running if the afpidsrvr falls over. If this does happen, run +afpidlist first to ensure you can printout the database. If you +have real problems, you may have to close down aufs and delete the +database before continuing. You may prefer to try to restore the +database off backup. + +John Forrest, +jf@ap.co.umist.ac.uk diff --git a/applications/aufs/afpidlist.c b/applications/aufs/afpidlist.c new file mode 100644 index 0000000..fe02ffd --- /dev/null +++ b/applications/aufs/afpidlist.c @@ -0,0 +1,102 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:49:40 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpidlist.c,v 2.3 1996/06/18 10:49:40 djh Rel djh $ + * $Revision: 2.3 $ + * + */ + +/* + * tool that prints out the contents of the AUFS fixed directory ID database + * + * John Forrest + * + */ + +#ifdef FIXED_DIRIDS + +#include +#include +#include + +#include "../../lib/afp/afpidaufs.h" + +void +print_tree(id, prefix) +sdword id; +char *prefix; +{ + char me [MAXPATHLEN]; + sdword parent; + char *name; + int num_children; + sdword *children; + datum key, data; + int i; + + if (id != rootEid) { /* root a bit different, because name is "/" */ + strcpy(me, prefix); + strcat(me, "/"); + } else + strcpy(me, ""); + + key = num_datum(id); +#ifdef USE_GDBM + data = gdbm_fetch(db, key); +#else /* USE_GDBM */ + data = dbm_fetch(db, key); +#endif /* USE_GDBM */ + + if (data.dptr == NULL) { + printf("**Error** id %d not found (prefix '%s')\n", id, prefix); + return; + } + + if (extract_entry(data,&name,&parent,&num_children,&children) < 0) { + printf("**Error** had probs with data for id %d(prefix '%s')\n", + id, prefix); + return; + } + + if (id == rootEid) + printf("RootEid = %d (+%d)\n", id, num_children); + else { + strcat(me, name); + printf("%d = (%d) '%s' (+%d)\n", id, parent, me, num_children); + } + + children = copy_children(children, num_children); + +#ifdef USE_GDBM + free(data.dptr); +#endif /* USE_GDBM */ + + for (i = 0; i < num_children; i++) + print_tree(children[i], me); + + free(children); +} + + +main(argc, argv) +int argc; +char *argv[]; +{ + int ret; + + if ((ret = open_dbase(1)) < 0) { + fprintf(stderr, "Can't open database %s (%d,%d)\n", + aufsDbName, ret, errno); + exit(-1); + } + + print_tree(rootEid, NULL); + close_dbase(); + exit(0); +} +#else FIXED_DIRIDS +#include +main() +{ + printf("afpidlist: not compiled with -DFIXED_DIRIDS\n"); +} +#endif FIXED_DIRIDS diff --git a/applications/aufs/afpidsrvr.c b/applications/aufs/afpidsrvr.c new file mode 100644 index 0000000..38fec7c --- /dev/null +++ b/applications/aufs/afpidsrvr.c @@ -0,0 +1,830 @@ +/* + * $Author: djh $ $Date: 1996/06/19 10:32:13 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpidsrvr.c,v 2.5 1996/06/19 10:32:13 djh Rel djh $ + * $Revision: 2.5 $ + * + */ + +/* + * Server to provide AUFS fixed directory ID database write facilities + * + * John Forrest + * + */ + +#ifdef FIXED_DIRIDS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_GDBM +#include +#else /* USE_GDBM */ +#include +#endif /* USE_GDBM */ +#include +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#include "../../lib/afp/afpidaufs.h" + +int verbose = 0; +int disconnect = 1; +char *log = NULL; +int continue_clean; + +extern char *optarg; +extern int optind, opterr; + +int queries; /* request socket */ +int bound_queries = 0; /* whether to delete socket or not */ +void create_queries(); + +void session(); +void set_signal(); +void do_disconnect(); +void clean_entries(); + +void open_database(); + +void +fatal(message) +char *message; +{ + if (message != NULL) + fprintf(stderr, "%s:", message); + if (errno > 0) { + fprintf(stderr, " %d: ", errno); + perror(""); + } else + putc('\n', stderr); +#ifdef USE_GDBM + if (db != NULL) + gdbm_close(db); +#else /* USE_GDBM */ + if (db != NULL) + dbm_close(db); +#endif /* USE_GDBM */ + if (bound_queries) + unlink(aufsSockname); + exit(-1); +} + +void +fatal2(message, a1) +char *message, *a1; +{ + fprintf(stderr, "afpidsrvr: fatal error: %s%s :", message, a1); + fatal(NULL); +} + +void +clean_exit(n) +int n; +{ +#ifdef USE_GDBM + if (db != NULL) + gdbm_close(db); +#else /* USE_GDBM */ + if (db != NULL) + dbm_close(db); +#endif /* USE_GDBM */ + if (bound_queries) + unlink(aufsSockname); + exit(n); +} + +main(argc, argv) +char *argv[]; +{ + int c; + extern char *optarg; + int doClean = 0; + + while ((c = getopt(argc, argv, "vtcl:")) != -1) { + switch (c) { + case 'v': + verbose = 1; + break; + case 't': + disconnect = 0; + break; + case 'l': + log = optarg; + break; + case 'c': + doClean = 1; + break; + case '?': + fprintf(stderr, + "usage: afpidsrvr [-c] [-v] [-t] [-l logfile]\n"); + exit(-1); + } + } + + set_signal(); + + if (disconnect) + do_disconnect(); + + if (log) { + int fd; + + if ((fd = open(log, O_WRONLY|O_APPEND|O_CREAT)) < 0) + fd = open("/dev/null", O_WRONLY); + + if (fd >= 0) { +#ifndef NODUP2 + dup2(fd, 2); +#else NODUP2 + close(2); + dup(fd); +#endif NODUP2 + close(fd); + } + } + + init_aufsExt(); + + open_database(); + + create_queries(); + + if (doClean) + do + clean_entries(); + while (continue_clean); + +#ifdef USE_GDBM + gdbm_close(db); + db = NULL; +#endif /* USE_GDBM */ + + { + time_t now; + + time(&now); + fprintf(stderr, "Starting afpidsrvr with %s at %s", + aufsDbName, ctime(&now)); + } + + session(); + /* NOTREACHED */ +} + +/* + * create queries "pipe" entry + * + */ + +void +create_queries() +{ + struct sockaddr *addr; + int addrlen; + + if ((queries = query_socket()) < 0) + fatal("Problem creating socket"); + + query_addr(&addr, &addrlen); + if (bind(queries, addr, addrlen) < 0) + fatal2("Problem binding socket ", aufsSockname); + bound_queries = 1; + +#ifdef linux + chmod(aufsSockname, 0666); +#endif /* linux */ + + if (listen(queries, 5) < 0) + fatal("Listen"); + + return; +} + +void +do_exit() +{ + clean_exit(-1); +} + +/* + * force clean exit + * + */ + +void +set_signal() +{ + if (signal(SIGHUP, SIG_IGN)!=SIG_IGN) + signal(SIGHUP, do_exit); + if (signal(SIGINT, SIG_IGN)!=SIG_IGN) + signal(SIGINT, do_exit); + if (signal(SIGTERM, SIG_IGN)!=SIG_IGN) + signal(SIGTERM, do_exit); +} + +void +create_database() +{ + datum initial_key, initial; + datum root, root_datum; + +#ifdef USE_GDBM + if ((db = gdbm_open(aufsDbName, 2048, GDBM_WRCREAT, 0644, 0L)) == NULL) + fatal("Creating db"); +#else /* USE_GDBM */ + if ((db = dbm_open(aufsDbName, O_RDWR|O_CREAT, 0644)) == NULL) + fatal("Creating db"); +#endif /* USE_GDBM */ + + rootEid = valid_id(); /* any will do */ + root_datum = new_entry(aufsRootName, (sdword)0); + root = num_datum(rootEid); +#ifdef USE_GDBM + if (gdbm_store(db, root, root_datum, GDBM_REPLACE) < 0) + fatal("Writing root record"); +#else /* USE_GDBM */ + if (dbm_store(db, root, root_datum, DBM_REPLACE) < 0) + fatal("Writing root record"); +#endif /* USE_GDBM */ + + initial_key.dptr = "I"; + initial_key.dsize = strlen(initial_key.dptr); + initial = create_init(aufsDbVersion, rootEid); +#ifdef USE_GDBM + if (gdbm_store(db, initial_key, initial, GDBM_REPLACE) < 0) + fatal("Writing root record"); +#else /* USE_GDBM */ + if (dbm_store(db, initial_key, initial, DBM_REPLACE) < 0) + fatal("Writing root record"); +#endif /* USE_GDBM */ + + flush_database(); +} + + +void +open_database() +{ + datum initial_key, initial; + char *version; + + initial_key.dptr = "I"; + initial_key.dsize = strlen(initial_key.dptr); + +#ifdef USE_GDBM + db = gdbm_open(aufsDbName, 2048, GDBM_READER, 0644, 0L); +#else /* USE_GDBM */ + db = dbm_open(aufsDbName, O_RDWR, 0644); +#endif /* USE_GDBM */ + if (db == NULL && errno == ENOENT) + create_database(); + if (db == NULL) + fatal("Opening db"); + +#ifdef USE_GDBM + initial = gdbm_fetch(db, initial_key); +#else /* USE_GDBM */ + initial = dbm_fetch(db, initial_key); +#endif /* USE_GDBM */ + if (initial.dptr == NULL) + fatal("Suspect Database"); + if (extract_init(initial, &version, &rootEid) < 0) + fatal("Problem with initial record"); + if (strcmp(version, aufsDbVersion) != 0) + fatal2("Incompatible d/b, can't deal with version ", version); + +#ifdef USE_GDBM + free(initial.dptr); +#endif /* USE_GDBM */ + + return; +} + +void +add_entry(entry) +char *entry; +{ + int ret; + + if (!is_directory(entry)) + return; + + if ((ret = lookup_path(entry, NULL, NULL, 1)) <= 0) + fprintf(stderr, "Failed to create '%s' (%d, %d)\n", + entry, ret, errno); + + return; +} + +void +delete_entry_id(); + +void +add_entry_id(parent, rest) +sdword parent; +char *rest; +{ + int ret; + sdword id; + char *entry; + + if ((ret = lookup_path_id(parent, rest, &id, NULL, 1)) <= 0) { + fprintf(stderr, "Failed to create %d/'%s' (%d, %d)\n", + parent, rest, ret, errno); + return; + } + if (verbose) + fprintf(stderr, "Created entry %d/'%s' (%d, %d) id = %d\n", + parent, rest, ret, errno, id); + + entry = equiv_path(id); + if (!is_directory(entry)) /* check existance in restrospect! */ + delete_entry_id(id); + flush_database(); + + return; +} + +void +add_entry_fid(parent, rest) +sdword parent; +char *rest; +{ + int ret; + sdword id; + char *entry; + + if ((ret = lookup_path_id(parent, rest, &id, NULL, 1)) <= 0) { + fprintf(stderr, "Failed to create %d/'%s' (%d, %d)\n", + parent, rest, ret, errno); + return; + } + entry = equiv_path(id); + if (!is_file(entry)) /* check existance in restrospect! */ + delete_entry_id(id); + flush_database(); + + return; +} + +void +delete_entry(entry) +char *entry; +{ + sdword id, parent; + datum data; + + if (is_directory(entry) || is_file(entry)) + return; + + if (lookup_path(entry, &id, &data, 0) > 0){ + (void) extract_entry(data, NULL, &parent, NULL, NULL); + do_delete_entry(id); + delete_child(parent, id); + flush_database(); + } + + return; +} + +void +delete_entry_id(id) +sdword id; +{ + sdword parent; + datum data; + char *entry = equiv_path(id); + + if (is_directory(entry) || is_file(entry)) + return; + + data = get_datum(id); + if (data.dptr == NULL) + return; + if (extract_entry(data, NULL, &parent, NULL, NULL) < 0) + return; + do_delete_entry(id); + delete_child(parent, id); + flush_database(); + + return; +} + +void +delete_entry_fid(id) +sdword id; +{ + sdword parent; + datum data; + char *entry = equiv_path(id); + + if (is_file(entry)) + return; + + data = get_datum(id); + if (data.dptr == NULL) + return; + if (extract_entry(data, NULL, &parent, NULL, NULL) < 0) + return; + do_delete_entry(id); + delete_child(parent, id); + flush_database(); + + return; +} + +void +move_entry(from, to) +char *from, *to; +{ + datum data; + sdword id, from_parent, to_parent; + char to_directory[MAXPATHLEN]; + char *new_name; + char *name; + char *ptr; + + assert(to[0] == '/'); + + if (is_directory(from) || !is_directory(to)) + if (is_file(from) || !is_file(to)) + return; + + if (lookup_path(from, &id, &data, 0) <= 0) { + /* did not know previously! */ + add_entry(to); + return; + } + + if (lookup_path(to, NULL, NULL, 0) > 0) /* exists already! */ + return; + + extract_entry(data, &name, &from_parent, NULL, NULL); + + name = string_copy(name); /* just in case it gets clobbered */ + + ptr = rindex(to, '/'); + strcpy(to_directory, ""); + strncat(to_directory, to, ptr-to); + new_name = ptr+1; + + assert(new_name[0] != '\0'); + /* will happen for trailing /, so be careful */ + + lookup_path(to_directory, &to_parent, NULL, 1); + + fprintf(stderr, "Moving %d/%s to %d/%s\n", + from_parent, name, to_parent, new_name); + + if (from_parent != to_parent) { + /* NB it may be quicker to try compare the two strings */ + delete_child(from_parent, id); + add_child(to_parent, id); + } + + data = get_datum(id); /* get again - in case overwritten */ + data = modify_parent(data, to_parent); + if (strcmp(name, new_name) != 0) /* name change too */ + data = modify_name(data, new_name); + store_datum(id, data, DBM_REPLACE); + flush_database(); + + return; +} + +void +move_entry_id(from_parent, name, to_parent, new_name) +sdword from_parent, to_parent; +char *name, *new_name; +{ + datum data; + sdword id; + char *ptr; + + + if (lookup_path_id(from_parent, name, &id, NULL, 0) <= 0) { + /* did not know previously! */ + add_entry_id(from_parent, name); + return; + } + + if (lookup_path_id(to_parent, new_name, NULL, NULL, 0) > 0) + /* exists already! */ + return; + + if (is_directory(equiv_path(id))||is_file(equiv_path(id))) + return; + + if (from_parent == to_parent) { + data = get_datum(id); /* get here in case corrupted earlier */ + data = modify_name(data, new_name); + store_datum(id, data, DBM_REPLACE); + } else { + delete_child(from_parent, id); + add_child(to_parent, id); + data = get_datum(id); /* get here in case corrupted earlier */ + data = modify_parent(data, to_parent); + if (strcmp(name, new_name) != 0) /* name change too */ + data = modify_name(data, new_name); + store_datum(id, data, DBM_REPLACE); + } + if (! is_directory(equiv_path(id)) && ! is_file(equiv_path(id))) + delete_entry_id(id); /* does not actually exist */ + else + flush_database(); + + return; +} + +void +rename_entry_id(id, new_name) +sdword id; +char *new_name; +{ + datum data; + int ret; + + if (verbose) + fprintf(stderr, "rename_entry_id: %d -> %s\n", + id, new_name); + data = get_datum(id); + if (data.dptr == NULL) { + if (verbose) + fprintf(stderr, "Unknown id: %d\n", id); + /* did not know previously! */ + return; + } + data = modify_name(data, new_name); + if (ret = store_datum(id, data, DBM_REPLACE) < 0) + if (verbose) + fprintf(stderr, "rename_entry_id: store returned %d\n", + ret); + + if (! is_directory(equiv_path(id)) && ! is_file(equiv_path(id))) { + if (verbose) + fprintf(stderr, "rename_entry_id: Ooops! %s doesn't exist!\n", + equiv_path(id)); + delete_entry_id(id); /* does not actually exist */ + } else + flush_database(); + + return; +} + +void +rename_entry(path, new_name) +char *path, *new_name; +{ + datum data; + sdword id; + char *ptr; + + if (lookup_path(path, &id, &data, 0) <= 0) { + /* did not know previously! */ + return; + } + data = modify_name(data, new_name); + store_datum(id, data, DBM_REPLACE); + + if (! is_directory(equiv_path(id)) && ! is_file(equiv_path(id))) + delete_entry_id(id); /* does not actually exist */ + else + flush_database(); + + return; +} + +void +clean_entries() +{ + /* currently no-op */ + continue_clean = 0; + + return; +} + +void +session() +{ + struct sockaddr addr; + int addrlen; + int sock; + char buff[2*MAXPATHLEN+3]; + char command, arg1[MAXPATHLEN], arg2[MAXPATHLEN]; + sdword id1, id2; + int count; + int prob, args, fileID; + + for (;;) { + fileID = 0; + addrlen = sizeof(struct sockaddr); + if ((sock = accept(queries, &addr, &addrlen)) < 0) + fatal("Accept"); + if ((count = recv(sock, buff, 2*MAXPATHLEN+3, 0)) < 0) + fatal("Recv"); + buff[count] = '\0'; + if (verbose && count > 0) + fprintf(stderr, "Received: '%s'\n", buff); + + prob = 0; + + if (buff[0] != '\0') { + command = buff[0]; + switch (command) { /* first decode arguments */ + case 'A': case 'D': + args = sscanf(buff+1, "%[^\277]\277", arg1); + break; + case 'M': + args = sscanf(buff+1, "%[^\277]\277%[^\277]\277", arg1, arg2); + break; + case 'R': + args = sscanf(buff+1, "%[^\277]\277%[^\277]\277", arg1, arg2); + break; + case 'a': + args = sscanf(buff+1, "%d\277%[^\277]\277", &id1, arg1); + break; + case 'f': + args = sscanf(buff+1, "%d\277%[^\277]\277", &id1, arg1); + break; + case 'd': + args = sscanf(buff+1, "%d\277", &id1); + break; + case 'm': + args = sscanf(buff+1, "%d\277%[^\277]\277%d\277%[^\277]\277", + &id1, arg1, &id2, arg2); + break; + case 'r': + args = sscanf(buff+1, "%d\277%[^\277]\277", &id1, arg1); + break; + } + + switch (command) { + case 'A': + if (args < 1 || arg1[0] != '/') + prob = 1; + else + add_entry(arg1); + break; + case 'a': + if (args < 2) + prob = 1; + else + add_entry_id(id1, arg1); + break; + case 'f': + if (args < 2) + prob = 1; + else + add_entry_fid(id1, arg1); + break; + case 'D': + if (args < 1 || arg1[0] != '/') + prob = 1; + else + delete_entry(arg1); + break; + case 'd': + if (args < 1) + prob = 1; + else + delete_entry_id(id1); + break; + case 'M': + if (args < 2 || arg1[0] != '/' || arg2[0] != '/') + prob = 1; + else + move_entry(arg1, arg2); + break; + case 'm': + if (args < 4) + prob = 1; + else + move_entry_id(id1, arg1, id2, arg2); + break; + case 'R': + if (args < 2 || arg1[0] != '/') + prob = 1; + else + rename_entry(arg1, arg2); + break; + case 'r': + if (args < 2) + prob = 1; + else + rename_entry_id(id1, arg1); + break; + case 'C': + clean_entries(); + break; + default: + prob = 1; + break; + } + } + if (prob) + fprintf(stderr, "Bad command '%s'\n", buff); + close(sock); + } + + return; +} + +/* + * disassociate + * + */ + +void +do_disconnect() +{ + if (fork()) + _exit(0); + { + int f; + + for (f = 0; f < 10; f++) + (void) close(f); + } + (void) open("/", 0); +#ifndef NODUP2 + (void) dup2(0, 1); + (void) dup2(0, 2); +#else NODUP2 + (void)dup(0); /* for slot 1 */ + (void)dup(0); /* for slot 2 */ +#endif NODUP2 +#ifndef POSIX +#ifdef TIOCNOTTY + { + int t; + + if ((t = open("/dev/tty", 2)) >= 0) { + ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } +#endif TIOCNOTTY +#ifdef xenix5 + /* + * USG process groups: + * The fork guarantees that the child is not a process group leader. + * Then setpgrp() can work, whick loses the controllong tty. + * Note that we must be careful not to be the first to open any tty, + * or it will become our controlling tty. C'est la vie. + * + */ + setpgrp(); +#endif xenix5 +#else POSIX + (void) setsid(); +#endif POSIX +} + +/* + * These are here to ensure we pick up these + * versions for the server, and not those in lib_client.c + * + */ + +int +amAufsExt() +{ + return(1); +} + +/* + * flush_database: force any outstanding writes + * + */ + +void +flush_database() +{ +#ifndef USE_GDBM + extern void open_database( /* void */ ); + + dbm_close(db); + open_database(); +#endif /* USE_GDBM */ + + return; +} +#else FIXED_DIRIDS +#include +main() +{ + printf("afpidsrvr: not compiled with -DFIXED_DIRIDS\n"); +} +#endif FIXED_DIRIDS diff --git a/applications/aufs/afpidtool.c b/applications/aufs/afpidtool.c new file mode 100644 index 0000000..a2cb7d4 --- /dev/null +++ b/applications/aufs/afpidtool.c @@ -0,0 +1,216 @@ +/* + * $Author: djh $ $Date: 1996/06/19 10:51:19 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpidtool.c,v 2.3 1996/06/19 10:51:19 djh Rel djh $ + * $Revision: 2.3 $ + * + */ + +/* + * Tell the AUFS fixed directory ID sever to do some basic things + * + * John Forrest + * + */ + +#ifdef FIXED_DIRIDS + +#include +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#include "../../lib/afp/afpidaufs.h" + +int verbose = 0; + +void +fatal(message) +char *message; +{ + if (message != NULL) + fprintf(stderr, "%s:", message); + if (errno > 0) + perror(""); + else + putc('\n', stderr); + exit(-1); +} + +void +fatal2(message, a1) +char *message, *a1; +{ + fprintf(stderr, "afpidtool: fatal error: %s%s :", message, a1); + fatal(NULL); +} + +void +getcdwd(path, result) +char *path, *result; +{ + char command [256]; + FILE *pwd; + + sprintf(command, "cd '%s'; pwd\n", path); + if ((pwd = popen(command, "r")) == NULL) + strcpy(result, ""); + else { + fgets(result, MAXPATHLEN, pwd); + pclose(pwd); + if (result[0] != '/') + strcpy(result, ""); + result[strlen(result)-1] = '\0'; /* remove trailing lf */ + } +} + +void +doFullPath(path, fullPath) +char *path, *fullPath; +{ + char *ptr; + + if (path[0] == '\0') +#ifdef SOLARIS + getcwd(fullPath, MAXPATHLEN); +#else /* SOLARIS */ + getwd(fullPath); +#endif /* SOLARIS */ + else + if (is_directory(path)) + getcdwd(path, fullPath); + else + if ((ptr = (char *)rindex(path, '/')) != NULL) { + *ptr = '\0'; + doFullPath(path, fullPath); + strcat(fullPath, "/"); + strcat(fullPath, ptr+1); + } else { /* directory name only */ + doFullPath("", fullPath); + strcat(fullPath, "/"); + strcat(fullPath, path); + } +} + +char *fullpath(path) +char *path; +{ + char temp[MAXPATHLEN]; + char fullPath[MAXPATHLEN]; + + if (is_directory(path)) + getcdwd(path, fullPath); + else { + strcpy(temp, path); + doFullPath(path, fullPath); + } + + return string_copy(fullPath); +} + +main(argc, argv) +int argc; +char *argv[]; +{ + int c; + extern char *optarg; + extern optind; + char *arg1, *arg2; + sdword id1, id2; + + int res; + + while ((c = getopt(argc, argv, "vn:a:m:d:r:cN:A:D:M:R:")) != -1) { + switch (c) { + case 'v': + verbose = 1; + break; + case 'n': case 'a': + arg1 = fullpath(optarg); + if (verbose) + fprintf(stderr, "New %s\n", arg1); + if (send_new(arg1) < 0) + fatal("Sending new"); + break; + case 'N': case 'A': + id1 = atoi(optarg); + arg1 = argv[optind++]; + if (verbose) + fprintf(stderr, "New %d/%s\n", id1, arg1); + if (send_new_id(id1,arg1) < 0) + fatal("Sending new"); + break; + case 'd': + arg1 = fullpath(optarg); + if (verbose) + fprintf(stderr, "Delete %s\n", arg1); + if (send_delete(arg1) < 0) + fatal("Sending delete"); + break; + case 'D': + id1 = atoi(optarg); + if (verbose) + fprintf(stderr, "Delete %d\n", id1); + if (send_delete_id(id1) < 0) + fatal("Sending delete"); + break; + case 'm': + arg1 = fullpath(optarg); + arg2 = fullpath(argv[optind++]); + if (verbose) + fprintf(stderr, "Move %s -> %s\n", arg1, arg2); + if (send_move(arg1, arg2) < 0) + fatal("Sending move"); + break; + case 'M': + id1 = atoi(optarg); + arg1 = argv[optind++]; + id2 = atoi(argv[optind++]); + arg2 = argv[optind++]; + if (verbose) + fprintf(stderr, "Move %d/%s -> %s\n", + id1, arg1, id2, arg2); + if (send_move_id(id1, arg1, id2, arg2) < 0) + fatal("Sending move"); + break; + case 'r': + arg1 = fullpath(optarg); + arg2 = argv[optind++]; + if (verbose) + fprintf(stderr, "Rename %s -> %s\n", arg1, arg2); + if (send_rename(arg1, arg2) < 0) + fatal("Sending rename"); + break; + case 'R': + id1 = atoi(optarg); + arg1 = argv[optind++]; + if (verbose) + fprintf(stderr, "Rename %d -> %s\n", id1, arg1); + if (send_rename_id(id1, arg1) < 0) + fatal("Sending rename"); + break; + case 'c': + if (verbose) + fprintf(stderr, "Clean\n"); + if (send_clean() < 0) + fatal("Sending clean"); + break; + case '?': + fprintf(stderr, + "usage: afpidtool [-v] [-n path] [-d path] [-m from to]\n"); + exit(-1); + } + } + exit(0); +} +#else FIXED_DIRIDS +#include +main() +{ + printf("afpidtool: not compiled with -DFIXED_DIRIDS\n"); +} +#endif FIXED_DIRIDS diff --git a/applications/aufs/afpmisc.c b/applications/aufs/afpmisc.c new file mode 100644 index 0000000..94f0ed8 --- /dev/null +++ b/applications/aufs/afpmisc.c @@ -0,0 +1,106 @@ +/* + * $Author: djh $ $Date: 91/02/15 21:07:34 $ + * $Header: afpmisc.c,v 2.1 91/02/15 21:07:34 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * afpmisc.c - miscellaneous, but nevertheless useful routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * + * Edit History: + * + * March 1987 Schilit Created. + * + */ + +#include +#include +#include +#include +#include "afps.h" + +int afp_dbug; + +/* + * int SetDBLevel(char *argv) + * + * Process arguments for tracing of AFP levels: fork, file, directory, + * etc. The options are: + * + */ + +struct { + int db_flg; + char *db_nam; +} dbtab[] = { + {DBG_ALL,"All"}, + {DBG_DEBG|DBG_DESK,"DeskTop"}, + {DBG_DEBG|DBG_DIRS,"Directory"}, + {DBG_DEBG|DBG_ENUM,"Enumerate"}, + {DBG_DEBG|DBG_FILE,"File"}, + {DBG_DEBG|DBG_FORK,"Fork"}, + {DBG_DEBG|DBG_OSIN,"OS"}, + {DBG_DEBG|DBG_SRVR,"Server"}, + {DBG_DEBG|DBG_UNIX,"Unix"}, + {DBG_DEBG|DBG_VOLS,"Volume"}, + {DBG_DEBG,"debug"} +}; + +#define DBTABN 11 /* size of debug table */ + +char *DBLevelOpts() +{ + int i; + static char dbopts[100]; + + *dbopts = '\0'; + for (i=0; i < DBTABN; i++) { + strcat(dbopts,dbtab[i].db_nam); + strcat(dbopts," "); + } + return(dbopts); +} + +int SetDBLevel(s) +char *s; +{ + char dbuf[30],*cp; + int i,idx,len; + + while (*s != '\0') { /* until runnout */ + while(*s == ' ' || *s == '\t') /* skip spaces */ + s++; + if (*s == '\0') + break; + for (len=0, cp = dbuf; *s != ' ' && *s != '\t' && *s != '\0' && len < 29; + cp++, s++, len++) + *cp = *s; + *cp++ = '\0'; + /* length should be correct */ + /* len = strlen(dbuf); */ /* find length of command */ + idx = -1; + for (i=0; i < DBTABN; i++) { + if (strncmpci(dbuf,dbtab[i].db_nam,len) == 0) + if (idx > 0) { + printf("SetDBLevel: ambiguous debug '%s' (%s and %s)\n", + dbuf,dbtab[idx].db_nam,dbtab[i].db_nam); + return(FALSE); + } else + idx = i; + } + if (idx < 0) { + printf("SetDBLevel: unknown debug level %s\n",dbuf); + return(FALSE); + } + printf("SetDBLevel: Debugging %s\n",dbtab[idx].db_nam); + afp_dbug |= dbtab[idx].db_flg; /* add the flag */ + } + return(TRUE); +} + diff --git a/applications/aufs/afpntoh.h b/applications/aufs/afpntoh.h new file mode 100644 index 0000000..b486b19 --- /dev/null +++ b/applications/aufs/afpntoh.h @@ -0,0 +1,140 @@ +/* + * $Author: djh $ $Date: 1996/04/25 03:15:24 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpntoh.h,v 2.5 1996/04/25 03:15:24 djh Rel djh $ + * $Revision: 2.5 $ + * + */ + +/* + * afpntoh.h - Server Net to Host Unpacking. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University + * in the City of New York. + * + * Edit History: + * + * Sun Apr 5 Schilit Created, based on afpcmd.c + * + */ + +extern PackEntry ProtoBRL[]; /* 1 ByteRangeLock */ +#define PsByteRangeLock ProtoBRL +extern PackEntry ProtoCVP[]; /* 2 CloseVol */ +#define PsCloseVol ProtoCVP +extern PackEntry ProtoCDP[]; /* 3 CloseDir */ +#define PsCloseDir ProtoCDP +extern PackEntry ProtoCFkP[]; /* 4 CloseFork */ +#define PsCloseFork ProtoCFkP +extern PackEntry ProtoCpFP[]; /* 5 CopyFile */ +#define PsCopyFile ProtoCpFP +extern PackEntry ProtoCRDP[]; /* 6 CreateDir */ +#define PsCreateDir ProtoCRDP +extern PackEntry ProtoCFP[]; /* 7 CreateFile */ +#define PsCreateFile ProtoCFP +extern PackEntry ProtoDFP[]; /* 8 Delete */ +#define PsDelete ProtoDFP +extern PackEntry ProtoEP[]; /* 9 Enumerate */ +#define PsEnumerate ProtoEP +extern PackEntry ProtoFVP[]; /* 10 Flush */ +#define PsFlush ProtoFVP + /* 11 Flush a fork */ +extern PackEntry ProtoGFkPP[]; /* 14 GetForkParms */ +#define PsGetForkParms ProtoGFkPP + /* 15 GetSrvrInfo */ + /* 16 GetSrvrParms */ +extern PackEntry ProtoGVPP[]; /* 17 GetVolParms */ +#define PsGetVolParms ProtoGVPP +extern PackEntry ProtoLP[]; /* 18 Login */ +#define PsLogin ProtoLP +extern PackEntry ProtoLRP[]; /* login reply */ +#define PsLoginReply ProtoLRP +extern PackEntry ProtoLCP[]; /* 19 LoginCont */ +#define PsLoginCont ProtoLCP +extern PackEntry ProtoLCR[]; /* loginCont reply */ +#define PsLoginContR ProtoLCR + /* 20 Logout */ +extern PackEntry ProtoMIP[]; /* 21 MapID */ +#define PsMapID ProtoMIP +extern PackEntry ProtoMNP[]; /* 22 MapName */ +#define PsMapName ProtoMNP +extern PackEntry ProtoMFP[]; /* 23 Move */ +#define PsMove ProtoMFP +extern PackEntry ProtoOVP[]; /* 24 OpenVol */ +#define PsOpenVol ProtoOVP +extern PackEntry ProtoODP[]; /* 25 OpenDir */ +#define PsOpenDir ProtoODP +extern PackEntry ProtoOFkP[]; /* 26 OpenFork */ +#define PsOpenFork ProtoOFkP +extern PackEntry ProtoRP[]; /* 27 Read */ +#define PsRead ProtoRP +extern PackEntry ProtoRFP[]; /* 28 Rename */ +#define PsRename ProtoRFP +extern PackEntry ProtoSDPP[]; /* 29 SetDirParms */ +#define PsSetDirParms ProtoSDPP +extern PackEntry ProtoSFPP[]; /* 30 SetFileParms */ +#define PsSetFileParms ProtoSFPP +extern PackEntry ProtoSFkPP[]; /* 31 SetForkParms */ +#define PsSetForkParms ProtoSFkPP +extern PackEntry ProtoSVPP[]; /* 32 SetVolParms */ +#define PsSetVolParms ProtoSVPP +extern PackEntry ProtoWP[]; /* 33 Write */ +#define PsWrite ProtoWP +extern PackEntry ProtoGFDPP[]; /* 34 GetFileDirParms */ +#define PsGetFileDirParms ProtoGFDPP +extern PackEntry ProtoSFDPP[]; /* 35 SetFileDirParms */ +#define PsSetFileDirParms ProtoSFDPP +extern PackEntry ProtoMsgP[]; /* 38 GetSrvrMsg */ +#define PsGetSrvrMsg ProtoMsgP +extern PackEntry ProtoCreateID[]; /* 39 CreateID */ +#define PsCreateID ProtoCreateID +extern PackEntry ProtoDelID[]; /* 40 DeleteID */ +#define PsDelID ProtoDelID +extern PackEntry ProtoRslvID[]; /* 41 ResolveID */ +#define PsRslvID ProtoRslvID +extern PackEntry ProtoExP[]; /* 42 ExchangeFiles */ +#define PsExchange ProtoExP +extern PackEntry ProtoODT[]; /* 48 OpenDT */ +#define PsOpenDT ProtoODT +extern PackEntry ProtoCDT[]; /* 49 CloseDT */ +#define PsCloseDT ProtoCDT +extern PackEntry ProtoGI[]; /* 51 GetIcon */ +#define PsGetIcon ProtoGI +extern PackEntry ProtoGII[]; /* 52 GetIconInfo */ +#define PsGetIconInfo ProtoGII +extern PackEntry ProtoAAP[]; /* 53 AddAPPL */ +#define PsAddAPPL ProtoAAP +extern PackEntry ProtoRMA[]; /* 54 RmvAPPL */ +#define PsRmvAPPL ProtoRMA +extern PackEntry ProtoGAP[]; /* 55 GetAPPL */ +#define PsGetAPPL ProtoGAP +extern PackEntry ProtoACP[]; /* 56 AddComment */ +#define PsAddComment ProtoACP +extern PackEntry ProtoRMC[]; /* 57 RmvComment */ +#define PsRmvComment ProtoRMC +extern PackEntry ProtoGCP[]; /* 58 GetComment */ +#define PsGetComment ProtoGCP +extern PackEntry ProtoAIP[]; /* 192 AddIcon */ +#define PsAddIcon ProtoAIP + +#define PsChangePassword ProtoCPP +extern PackEntry ProtoCPP[]; +#define PsGetUserInfo ProtoGUIP +extern PackEntry ProtoGUIP[]; +#define PsGetUserInfoReply ProtoGUIRP +extern PackEntry ProtoGUIRP[]; +#define PsGetIconInfoReply ProtoGIIR +extern PackEntry ProtoGIIR[]; +#define PsGetSrvrMsgReply ProtoMsgRP +extern PackEntry ProtoMsgRP[]; + +extern PackEntry ProtoAuthInfo[]; +extern PackEntry DirParmPackR[]; +extern PackEntry FilePackR[]; +extern PackEntry ProtoFileAttr[]; +extern PackEntry DirPackR[]; +extern PackEntry EnumPackR[]; +extern PackEntry ProtoDirAttr[]; +extern PackEntry ProtoFileDirAttr[]; +extern PackEntry ProtoSRP[]; diff --git a/applications/aufs/afpos.c b/applications/aufs/afpos.c new file mode 100644 index 0000000..1a26c59 --- /dev/null +++ b/applications/aufs/afpos.c @@ -0,0 +1,5258 @@ +/* + * $Author: djh $ $Date: 1996/09/10 14:30:03 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpos.c,v 2.80 1996/09/10 14:30:03 djh Rel djh $ + * $Revision: 2.80 $ + * + */ + +/* + * afpos.c - Appletalk Filing Protocol OS Interface. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * March 1988 CCKIM cleanup + * December 1990 djh tidy up for AFP 2.0 + * May 1991 ber,jlm accept a part of the gcos or login name + * (PERMISSIVE_USER_NAME) + * March 1993 jbh/rns Add support for Shadow Password file + * July 1993 pabe/jbh Tidy up Shadow Password support; fix + * combination of same with PERMISSIVE_USER_NAME + * + */ + +/* + * POSSIBLE DEFINES: + * + * NONLXLATE + * Define to turn off translation of \n to \r on line at a time + * reads + * USECHOWN + * System allows user to use chown to give away file ownership + * Following are used to get volume information + * USEGETMNT - DECs getmnt call. Works for either Ultrix 1.2, 2.0 or + * 2.2 (to be verified). Call differs from 2.0 to 2.2 + * USEQUOTA - base volume information on quota for user on file + * system if it exists (messed up by symlinks off the structure). + * This is for the "Melbourne" quota system as usually found in BSD + * systems. + * USESUNQUOTA - running with sun quota system. Basically, just turns on + * emulation of the Melbourne quota call + * USEBSDQUOTA - "new" BSD quota which uses the quotactl call and + * provides quotas for both users and groups + * USEUSTAT - not recommended. returns information about file, but + * information doesn't tell us how much space is there -- only + * how much is free and we need both + * USESTATFS - statfs is the Sun NFS solution to volume information. + * (has modified call arguments and structure elements under SGI IRIX). + * + * PERMISSIVE_USER_NAME - let the Chooser name be from the gcos field + * SHADOW_PASSWD - enable support of shadow password files + * + * aux has a couple of "ifdefs". + * - one to set full BSD compatibility + * - one to protect against rename("file1","file1"): it will + * incorrectly unlink "file1" (period - nothing left afterwards) + * + * OTHER: GGTYPE + * Some versions of unix return a gid_t array in getgroups instead of + * an int array. For those, define GGTYPE to gid_t. In particular, + * this is a problem with (at least some version of) "MORE/BSD" + * from Mt. Xinu. + * + * System V defines + * NOLSTAT - no lstat call - don't try to figure out things with symlinks + * USERAND - use sysv rand call not bsd random + * + */ + +#if (defined(__386BSD__) || defined(__FreeBSD__)) +#define __BSD_4_4__ +#endif /* __386BSD__ */ + +#include +#include +#include +#include +#include +#ifndef _TYPES +# include /* assume included by param.h */ +#endif _TYPES +#include +#include +#include +#include +#include + +#ifdef aux +# include +#endif aux + +#ifdef USEDIRENT +#include +#else USEDIRENT +# ifdef xenix5 +# include +# else xenix5 +# ifndef drsnx +# include +# endif drsnx +# endif xenix5 +#endif USEDIRENT + +#ifdef SHADOW_PASSWD +#include +#endif SHADOW_PASSWD + +#ifdef gould +# define USESUNQUOTA +#endif gould + +#ifdef USESUNQUOTA +# ifndef USEQUOTA +# define USEQUOTA +# endif USEQUOTA +#endif USESUNQUOTA + +#if defined (APPLICATION_MANAGER) || defined (DENYREADWRITE) +# define NEEDFCNTLDOTH +#endif APPLICATION_MANAGER|DENYREADWRITE + +#ifdef NEEDFCNTLDOTH +# include +# ifdef apollo +# include +# endif apollo +#endif NEEDFCNTLDOTH + +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#ifdef USEUSTAT +# include +#endif USEUSTAT + +#ifdef USESTATFS +# ifdef SOLARIS +# include +# else /* SOLARIS */ +# if defined(__BSD_4_4__) || defined(__osf__) +# include +# include +# else /* __BSD_4_4__ || __osf__ */ +# if defined(sgi) || defined(apollo) +# include +# else /* sgi || apollo */ +# include +# endif /* sgi || apollo */ +# endif /* __BSD_4_4__ || __osf__ */ +# endif /* SOLARIS */ +#endif /* USESTATFS */ + +#ifdef AIX +#include +#endif AIX + +#ifdef __BSD_4_4__ +#include +#endif /* __BSD_4_4__ */ + +#ifdef linux +#include +#endif /* linux */ + +#ifdef NeXT +#undef USEQUOTA +#undef USESUNQUOTA +#endif NeXT + +#ifdef USEBSDQUOTA +# include +# include +#endif USEBSDQUOTA + +#ifdef USEQUOTA +# ifdef SOLARIS +# undef USESUNQUOTA +# include +# else SOLARIS +# ifndef USESUNQUOTA +# include +/* + * NOTE: If there is not sys/quota.h and there is a ufs/quota.h + * then you should probably define SUN_QUOTA -- especially if your + * NFS is based on the sun model + * + */ +# else USESUNQUOTA +# include +# include +# endif USESUNQUOTA +# endif SOLARIS +# ifndef Q_GETDLIM +# ifdef Q_GETQUOTA +# define Q_GETDLIM Q_GETQUOTA +# else Q_GETQUOTA + /* + * Error: You have turned on quotas and aren't using the + * bsd or sun quota system. + * + */ +# endif Q_GETQUOTA +# endif Q_GETDLIM +#endif USEQUOTA + +/* assumes that ultrix 1.1 doesn't have getmnt or if it does, then it */ +/* has limits.h and uname */ +#ifdef USEGETMNT +# include +/* the following assumes ultrix 1.2 or above */ +/* well, do you really think dec would license their code to another */ +/* vendor :-) */ +# include +# include +#endif USEGETMNT + +#ifdef drsnx +# ifdef USESTATFS +# undef USESTATFS /* ICL DRS/NX statfs() is a little different */ +# endif USESTATFS +#endif drsnx + +#ifdef SOLARIS +# include +# define NGROUPS NGROUPS_MAX_DEFAULT +#endif SOLARIS + +#include +#include /* flags should be in misc */ +#include "afps.h" /* common includes */ +#include "afpvols.h" +#include "afppasswd.h" /* in case we are using privates */ +#include "afposncs.h" +#include "afpgc.h" + +#ifdef SIZESERVER +#include +#include +#include +#include "sizeserver.h" +#endif SIZESERVER + +#ifdef PERMISSIVE_USER_NAME +#include +#endif PERMISSIVE_USER_NAME + +#ifdef ULTRIX_SECURITY +#include +#include +#endif ULTRIX_SECURITY + +#ifdef DIGITAL_UNIX_SECURITY +#include +#include +#include +#endif DIGITAL_UNIX_SECURITY + +#ifdef LOG_WTMP +# if defined(sgi) || defined(SOLARIS) +# define LOG_WTMPX +# endif /* sgi || SOLARIS */ +# ifdef LOG_WTMPX +# include +# else /* LOG_WTMPX */ +# include +# endif /* LOG_WTMPX */ +#endif /* LOG_WTMP */ + +#ifdef DISTRIB_PASSWDS +#include +#endif /* DISTRIB_PASSWDS */ + +#ifdef MAXBSIZE +# define IOBSIZE MAXBSIZE /* set to max buf entry size by if there */ +#else MAXBSIZE +# ifdef BLKDEV_IOSIZE +# define IOBSIZE BLKDEV_IOSIZE /* set to std block device read size */ +# else BLKDEV_IOSIZE +# define IOBSIZE BUFSIZ /* use stdio bufsiz */ +# endif BLKDEV_IOSIZE +#endif MAXBSIZE + +#define NILPWD ((struct passwd *) 0) + +#ifdef SHADOW_PASSWD +#define NILSPWD ((struct spwd *) 0) +#endif SHADOW_PASSWD + +/* macro to test for directory file */ + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif S_ISDIR + +#define E_IOWN 0x80000000 /* owner of file */ + +#define I_SETUID 04000 /* internal set user ID */ +#define I_SETGID 02000 /* internal set group ID */ + +#define I_RD 04 /* Internal (Unix) bits for read */ +#define I_WR 02 /* write */ +#define I_EX 01 /* execute (search) */ + +#define E_RD 02 /* External bits for read */ +#define E_WR 04 /* write */ +#define E_SR 01 /* search */ + +#define IS_OWNER 6 /* internal owner shift amount */ +#define IS_GROUP 3 /* internal group shift amount */ +#define IS_WORLD 0 /* internal world shift amount */ + +#define ES_USER 24 /* external user shift amount */ + /* which is a calculated access based on: */ +#define ES_WORLD 16 /* external world shift amount */ +#define ES_GROUP 8 /* external group shift amount */ +#define ES_OWNER 0 /* external owner shift amount */ + +/* Mask search (is unix execute) bits for world, group and owner */ + +#define IS_EX_WGO ((I_EX << IS_OWNER) | \ + (I_EX << IS_GROUP) | \ + (I_EX << IS_WORLD)) + +private char *usrnam,*usrdir; +private int usruid; +private int usrgid; +private int ngroups; + +#ifndef GGTYPE +# ifdef SOLARIS +# define GGTYPE gid_t +# else SOLARIS +# define GGTYPE int +# endif SOLARIS +#endif GGTYPE + +private GGTYPE groups[NGROUPS+1]; + +#ifdef USEGETMNT +# ifndef NOSTAT_ONE +/* no big deal if this changes, it just means you can't compile under */ +/* ultrix 2.0/1.2 for ultrix 2.2 (you probably can't for 1.2 anyway) */ +/* WARNING: not 100% sure that there aren't other problems preventing */ +/* a compile from ultrix 2.0 from working on 2.2 */ +# define NOSTAT_ONE 4 /* as of ultrix 2.2 */ +# define ISOLDGETMNT 1 +# else NOSTAT_ONE +# define ISOLDGETMNT 0 +# endif NOSTAT_ONE +private int oldgetmnt = ISOLDGETMNT; /* use new or old format getmnt */ + /* default is based on compiling system */ +# ifndef NUMGETMNTBUF +# define NUMGETMNTBUF 8 /* 8 before, let's not be conservative */ + /* because on systems with many file */ + /* systems we will get killed with 8 */ + /* Back off!!!!! struct fs_data is huge */ +# endif NUMGETMNTBUF +#endif USEGETMNT + +import int errno; + +/* + * AFPOS functions + * + * Generally, a name like: OSXxxxx means that it is a "primary" function + * that accomplishes a afp command (not always though). + * +*/ + +export int OSEnable(); +export void tellaboutos(); +export OSErr OSMapID(); +export OSErr OSMapName(); +export OSErr OSDelete(); +private OSErr os_rmdir(); +private OSErr os_delete(); +export OSErr OSRename(); +export OSErr OSMove(); +private OSErr os_move(); +export OSErr OSFlush(); +export OSErr OSFlushFork(); +export OSErr OSClose(); +export OSErr OSRead(); +export OSErr OSWrite(); +export OSErr OSCreateDir(); +private OSErr os_mkdir(); +export OSErr OSCreateDir(); +export OSErr OSFileDirInfo(); +export OSErr OSDirInfo(); /* from old spec */ +export OSErr OSFileInfo(); /* from old spec */ +export void OSValidateDIDDirInfo(); +export OSErr OSFileExists(); +export OSErr OSSetFileDirParms(); +export OSErr OSSetFileParms(); +export OSErr OSSetDirParms(); +export OSErr OSSetForklen(); +export OSErr OSGetSrvrMsg(); +private OSErr os_chmod(); +private void os_change_all(); +private u_short EtoIPriv(); +private int OSInGroup(); +private u_short EtoIAccess(); +private dword ItoEPriv(); +private dword ItoEAccess(); +export OSErr OSAccess(); +export OSErr OSVolInfo(); +#ifdef USEGETMNT +private OSErr ultrix_volinfo(); +#endif USEGETMNT +export OSErr OSCopyFile(); +private OSErr os_copy(); +export OSErr OSCreateFile(); +export OSErr OSOpenFile(); +export boolean setguestid(); +export boolean setpasswdfile(); +export OSErr OSLoginRand(); +export OSErr OSLogin(); +export word OSRandom(); +export sdword CurTime(); /* current time in internal form */ +export char *tilde(); /* tilde expansion */ +export char *logdir(); /* user login directory */ +export int guestlogin; /* session is a guest login */ +private OSErr unix_rmdir(); +private OSErr unix_unlink(); +private OSErr unix_rename(); +#ifdef XDEV_RENAME +private OSErr xdev_rename(); /* rename across devices */ +#endif XDEV_RENAME +private OSErr unix_open(); +private OSErr unix_close(); +private OSErr unix_mkdir(); +private OSErr unix_create(); +private OSErr unix_createo(); +private OSErr unix_chown(); +private OSErr unix_chmod(); +private OSErr unix_stat(); +private OSErr ItoEErr(); +private int filemode(); +private char *syserr(); + +#ifdef SIZESERVER +private void getvolsize(); +#endif SIZESERVER + +#ifdef LWSRV_AUFS_SECURITY +char *bin, *tempbin ; +#endif LWSRV_AUFS_SECURITY + +#ifdef SHADOW_PASSWD +int shadow_flag; +#endif SHADOW_PASSWD + +#ifdef DENYREADWRITE +struct accessMode { + struct accessMode *next; + char path[MAXPATHLEN]; + int mode; + int fd; +}; +struct accessMode *accessMQueue = (struct accessMode *)NULL; +#endif DENYREADWRITE + +/* + * Enable OS Dependent functions + * + * For now: under AUX, set full BSD compatibility + * under sun quota systems, build a mount table for use in quota call + * +*/ +export int +OSEnable() +{ +#ifdef USEGETMNT + struct utsname unames; +#endif USEGETMNT + +#ifdef USEGETMNT + if (uname(&unames) >= 0) { + /* don't think getmnt was available in ultrix 1.1. If it was */ + /* then we assume it had uname too */ + if (strcmp(unames.sysname, "ULTRIX-32") == 0) { + if (strcmp(unames.release, "2.0") == 0 || + strcmp(unames.release, "1.2") == 0 || + strcmp(unames.release, "1.1") == 0) { + oldgetmnt = TRUE; + } else oldgetmnt = FALSE; + } + } +#endif USEGETMNT +#ifdef aux +/* ensure reliable signals, etc */ +#define BSDCOMPAT COMPAT_BSDNBIO|COMPAT_BSDPROT|COMPAT_BSDSIGNALS|\ +COMPAT_BSDTTY|COMPAT_EXEC|COMPAT_SYSCALLS + setcompat(BSDCOMPAT); +#endif aux +#ifdef USESUNQUOTA + build_mount_table(); +#endif USESUNQUOTA +#ifdef SHADOW_PASSWD + shadow_flag = (access(SHADOW, F_OK) == 0); +#endif SHADOW_PASSWD +} + +void +tellaboutos() +{ + int haveflock; + int havelockf; + + logit(0,"CONFIGURATION"); + getlockinfo(&haveflock, &havelockf); + if (haveflock) + logit(0," Configured: FLOCK"); + if (havelockf) + logit(0," Configured: LOCKF"); +#ifdef USEUSTAT + logit(0," Configured: Volume space information: ustat"); +#endif USEUSTAT +#ifdef USESTATFS + logit(0," Configured: Volume space information: stat[v]fs"); +#endif USESTATFS +#ifdef USEGETMNT + logit(0," Configured: Volume space information: getmnt"); + if (oldgetmnt) + logit(0," using old style (Ultrix 1.2, 2.0) getmnt"); +#endif USEGETMNT + +#ifdef USEQUOTA +# if defined(USESUNQUOTA) || defined(SOLARIS) + logit(0," Configured: SUN quota system"); +# else /* USESUNQUOTA || SOLARIS */ +# ifdef USEBSDQUOTA + logit(0," Configured: New BSD quota system"); +# else /* USEBSDQUOTA */ + logit(0," Configured: Melbourne (BSD) quota system"); +# endif /* USEBSDQUOTA */ +# endif /* USESUNQUOTA || SOLARIS */ +#endif /* USEQUOTA */ + +#ifdef USECHOWN + logit(0," Configured: chown: system allows one to give away ownership of files"); +#endif USECHOWN + if (os_issmartunixfi()) + logit(0," Configured: reading unknown unix files to get finder information"); + logit(0," Configured translation tables are:"); + ncs_table_dump(); +#ifdef DISTRIB_PASSWDS + logit(0," Configured: Using Distributed Passwords for authentication"); +#endif DISTRIB_PASSWDS +} + +/* + * OwnerID = 0 means that the folder is "unowned" or owned by + * . The owner bit of the User Rights summary is always + * set for such a folder. + * + * GroupID = 0 means that the folder has no group affiliation; hence + * the group's access privileges (R, W, S) are ignored for such a + * folder. + * +*/ + +#define NOID (-1) /* internal group/user id for */ + +/* + * Make external and internal ids differ by one. Then 1 (administrator) + * maps to 0 (root), and 0 (any user or group) maps to -1. Thus the + * AFP client thinks user ids are 1 higher than what the server (and the + * unix machine) use internally. + * + */ + +#define ItoEID(iid) ((sdword) (iid + 1)) +#define EtoIID(eid) ((int) (eid - 1)) + +/* + * + * OSErr OSMapID(byte fcn,char *name,dword id) + * + * + * This function is used to map a creator ID to a creator name, or + * a group ID to a group name. + * + * The creator name/id is identical to the user name and UID under + * Unix. + * + * Inputs: + * fcn MapID_C for creator, MapID_G for group. + * id The id to be mapped, either group ID or creator ID. + * + * Outputs: + * OSErr Function result. + * name name corresponding to input ID. + * + * + */ + +export OSErr +OSMapID(fcn,name,idd) +byte fcn; +char *name; +sdword idd; /* 4 bytes */ +{ + struct passwd *pwd; + struct group *grp; + int id = EtoIID(idd); /* convert to internal id */ + + if (DBOSI) + printf("OSMapID fcn=%s id=%d\n", + ((fcn == MapID_C) ? "Creator" : "Group"),id); + + if (idd == 0) { + name[0] = '\0'; + return(noErr); + } + + switch (fcn) { + case MapID_C: + pwd = getpwuid(id); + if (pwd == NULL) + return(aeItemNotFound); + strcpy(name,pwd->pw_name); + break; + + case MapID_G: + grp = getgrgid(id); + if (grp == NULL) + return(aeItemNotFound); + strcpy(name,grp->gr_name); + break; + default: + return(aeParamErr); + } + return(noErr); +} + +/* + * OSErr OSMapName(byte fcn,char *name,sdword *id); + * + * This call is used to map a creator name to a creator ID or + * a group name to a group id. + * + * The creator name/id is identical to the user name and UID under + * Unix. + * + * Inputs: + * fcn MapName_C for creator, MapName_G for group. + * name Item to be mapped, either creator name or group name. + * + * Outputs: + * OSErr Function result. + * id ID corresponding to input name. + * + * + */ + +OSErr +OSMapName(fcn,name,eid) +byte fcn; +char *name; +sdword *eid; /* 4 bytes */ +{ + struct passwd *pwd; + struct group *grp; + int id; + + if (DBOSI) + printf("OSMapName fcn=%s name=%s\n", + (fcn == MapName_C) ? "Creator" : "Group",name); + + if (name[0] == '\0') { + *eid = 0; + return(noErr); + } + + switch (fcn) { + case MapName_C: + pwd = getpwnam(name); + if (pwd == NULL) + return(aeItemNotFound); + id = pwd->pw_uid; /* return user ID */ + break; + + case MapName_G: + grp = getgrnam(name); /* get group entry */ + if (grp == NULL) + return(aeItemNotFound); + id = grp->gr_gid; /* return group ID */ + break; + } + *eid = ItoEID(id); /* convert to external */ + return(noErr); +} + +/* + * return information about a particular user id. + * if doself is true then return information about logged in user + * AFP2.0 + * + */ +OSGetUserInfo(doself, userid, guirp) +boolean doself; +dword userid; +GetUserInfoReplyPkt *guirp; +{ + int bm = guirp->guir_bitmap; + int usr = usruid; + int grp = usrgid; + struct passwd *pwd; + + if (!doself) { + if ((pwd = (struct passwd *)getpwuid(EtoIID(userid))) == NULL) + return(aeItemNotFound); + usr = pwd->pw_uid; + grp = pwd->pw_gid; + } + /* Convert to external form */ + guirp->guir_userid = ItoEID(usr); + guirp->guir_pgroup = ItoEID(grp); + return(noErr); +} + +/* + * get the server or login message from the specified file + * AFP2.1 + * + */ + +#define LOGINMSG 0 +#define SERVERMSG 1 + +OSErr +OSGetSrvrMsg(typ, msg) +word typ; +byte *msg; +{ + int fd, i, len; + char *msgfile; + extern char *motdfile; + extern char *messagefile; + + msg[0] = '\0'; + + if (typ != LOGINMSG && typ != SERVERMSG) + return(noErr); + + msgfile = (typ == LOGINMSG) ? motdfile : messagefile; + + if (msgfile == NULL) + return(noErr); + + if ((fd = open(msgfile, O_RDONLY, 0644)) >= 0) { + if ((len = read(fd, (char *)msg, SRVRMSGLEN-1)) >= 0) { + if (len == SRVRMSGLEN-1) + msg[len-1] = 0xc9; /* ... */ + msg[len] = '\0'; + for (i = 0; i < len; i++) + if (msg[i] == '\n') + msg[i] = '\r'; + } + close(fd); + return(noErr); + } + + sprintf(msg, ""); + + return(noErr); +} + +/* + * OSErr OSDelete(IDirP ipdir, idir ,char *file) + * + * OSDelete is used to delete a file or an empty directory. + * + * Inputs: + * parent directory + * directory id of directory (null if file) + * file name in parent directory + * + * Outputs, OSErr Function result: + * + * ParamErr Bad path. + * ObjectNotFound Path does not point to an existing file or directory. + * DirNotEmpty The directory is not empty. + * FileBusy The file is open. + * AccessDenied User does not have the rights (specified in AFP spec). + * ObjectLocked AFP2.0: file or dir marked DeleteInhibit + * VolLocked AFP2.0: the volume is ReadOnly + * + */ +OSErr +OSDelete(ipdir,idir,file) +IDirP ipdir, idir; +char *file; +{ + int err; + word attr; + extern int sessvers; + char path[MAXPATHLEN]; + + OSfname(path,ipdir,file,F_DATA); /* create data fork name */ +#ifdef NOCASEMATCH + noCaseMatch(path); +#endif NOCASEMATCH + + /* new for AFP2.0 */ + OSGetAttr(ipdir,file,&attr); + if (attr & FPA_DEI) + return((sessvers == AFPVersion1DOT1) ? aeAccessDenied : aeObjectLocked); + + if (DBOSI) + printf("OSDelete file=%s\n",path); + + if (idir) { + err = os_rmdir(path,F_FNDR); /* remove finder dir */ + if (err != noErr) + return(err); + err = os_rmdir(path,F_RSRC); /* delete resource directory */ + if (err != noErr) + return(err); + err = unix_rmdir(path); /* delete the data file */ + if (err != noErr) + return(err); + (void) os_delete(ipdir,file,F_FNDR); /* delete finder fork */ + /* remove the dirid */ + FModified(ipdir, file); + Idrdirid(ipdir, idir); /* idir is invalid after this point */ + return(noErr); /* and return result */ + } + + err = unix_unlink(path); /* rid the data file */ + if (err != noErr) + return(err); + (void) os_delete(ipdir,file,F_RSRC); /* delete resource fork */ + (void) os_delete(ipdir,file,F_FNDR); /* delete finder fork */ + FModified(ipdir, file); + return(noErr); +} + +/* + * OSErr os_rmdir(char *dir, int typ) + * + * Delete a finder/resource directory as specified by type which + * is either F_FNDR or F_RSRC. + * + * If a simple unix_rmdir fails because the directory is not empty + * then scan the directory for "junk" files and remove those. + * + * Junk files are leftovers which exist in our finder/resource + * directory but do not exist in the data directory. They don't + * usually occur under normal operation but cause a headache when + * they do since the folder can stay locked after a delete error. + * + */ +private OSErr +os_rmdir(dir,typ) +char *dir; +int typ; +{ + char path[MAXPATHLEN]; /* resource/finder path */ + char dpath[MAXPATHLEN]; /* data file path */ + DIR *dirp; +#ifdef USEDIRENT + struct dirent *dp, *readdir(); +#else USEDIRENT + struct direct *dp, *readdir(); +#endif USEDIRENT + struct stat stb; + int pl,dpl,err; + + /* create the directory path for this rmdir... either fndr or rsrc dir */ + + strcpy(path,dir); /* local copy of dir name */ + if (typ == F_RSRC) + strcat(path,RFDIR); /* build resource directory name */ + else + strcat(path,FIDIR); /* build finder directory name */ + + /* try deleting the directory */ + + err = unix_rmdir(path); /* try rmdir */ + if (err == aeObjectNotFound) /* does not exist error? */ + err = noErr; /* then ok by use */ + if (err == noErr || /* deleted ok? */ + err != aeDirNotEmpty) /* or unknown cause? */ + return(err); /* then return now */ + + /* directory could not be deleted because it was not empty */ + /* delete dir entries which are not in the data directory and try again */ + + strcpy(dpath,dir); /* local copy of data dir name */ + dpl = strlen(dpath); /* find length */ + dpath[dpl++] = '/'; /* append slash */ + + dirp = opendir(path); /* open the fndr/rsrc dir... */ + if (dirp == NULL) /* does not exist etc? */ + return(noErr); /* then no directory */ + + pl = strlen(path); /* set length of fndr/rsrc dir */ + path[pl++] = '/'; /* add slash for file concats */ + + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + + if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || + (dp->d_name[0] == '.' && dp->d_name[1] == '.' && dp->d_name[2] == '\0')) + continue; /* skip dot and dot dot */ + + strcpy(dpath+dpl,dp->d_name); /* compose name in data dir */ + if (stat(dpath,&stb) == 0) { /* to see if it exists there */ + closedir(dirp); + return(aeDirNotEmpty); /* if so... dir really not empty */ + } + + strcpy(path+pl,dp->d_name); /* otherwise use fndr/rsrc file name */ + if (DBOSI) + printf("os_rmdir: cleaning %s\n",path); + err = unix_unlink(path); /* and remove */ + if (err != noErr) { /* if that failed... */ + closedir(dirp); + return(err); /* then return now */ + } + } + closedir(dirp); /* finished with directory */ + path[pl-1] = '\0'; /* back to fndr/rsrc name */ + return(unix_rmdir(path)); /* try deleting now */ +} + +private OSErr +os_delete(pdir,file,typ) +IDirP pdir; +char *file; +int typ; +{ + char path[MAXPATHLEN]; +#ifdef NOCASEMATCH + register int i; +#endif NOCASEMATCH + + OSfname(path,pdir,file,typ); /* build unix file name */ +#ifdef NOCASEMATCH + if((i = unix_unlink(path)) != noErr) { + noCaseMatch(path); + i = unix_unlink(path); + } + return(i); /* do the work... */ +#else NOCASEMATCH + return(unix_unlink(path)); /* do the work... */ +#endif NOCASEMATCH +} + + +/* + * OSErr OSRename(IDirP pdir, char *from, char *to); + * + * OSRename is used to rename a file or directory. + * + * Inputs: + * pdir parent directory id. + * from name of file or directory to rename. + * to new name for file or directory. + * + * Outputs: + * OSErr Function result. + * + * Pass the work along to os_move, which handles the general case. + * + */ + +OSErr +OSRename(pdir,from,to) +IDirP pdir; /* parent directory id */ +char *from,*to; /* from and to file names */ +{ + return(os_move(pdir,from,pdir,to)); /* do the work */ +} + +/* + * OSErr OSMove(IDirP fpdir, char *from, IDirP tpdir, char *to) + * + * OSMove is used to move a directory or file to another location on + * a single volume (source and destination must be on the same volume). + * The destination of the move is specified by provinding a pathname + * that indicates the object's new parent directory. + * + * Inputs: + * fpdir parent directory id of from. + * from name of file or directory to rename. + * tpdir parent directory id of to. + * to new directory for file or directory. + * + * Outputs: + * OSErr Function result. + * + * Create an internal directory id for the new directory name by + * combining the parent dir id (tpdir), and directory name of the + * destination (to) and call the general routine os_move(). Note + * that the new name is the same as the old name and to specifies + * the destination directory. + * + */ + +export OSErr +OSMove(fpdir,from,tpdir,to) +IDirP fpdir,tpdir; /* from and to parent dirs */ +char *from; /* from file name */ +char *to; /* to file name is dest directory */ +{ + return(os_move(fpdir, from, tpdir, to)); +} + +/* + * OSErr os_move(IDirP fpdir, char *from, IDirP tpdir, char *to) + * + * Move the file. + * + */ +private OSErr +os_move(fpdir,from,tpdir,to) +IDirP fpdir,tpdir; /* from and to parent dirs */ +char *from,*to; /* from and to file names */ +{ + char f_path[MAXPATHLEN]; + char t_path[MAXPATHLEN]; + char fpath[MAXPATHLEN]; + char tpath[MAXPATHLEN]; + extern int sessvers; + struct stat stb; + word attr; + int err,cerr; + int mo; +#ifdef NOCASEMATCH + register char *pp; +#endif NOCASEMATCH + + /* new for AFP2.0 */ + OSGetAttr(fpdir,from,&attr); + if (attr & FPA_RNI) + return((sessvers == AFPVersion1DOT1) ? aeAccessDenied : aeObjectLocked); + + OSfname(f_path,fpdir,from,F_DATA); /* build data file name */ + OSfname(t_path,tpdir,to,F_DATA); /* for from and to files */ + + if (DBOSI) + printf("OSRename from=%s, to=%s\n",f_path,t_path); + + if ((fpdir->flags & DID_FINDERINFO) && (tpdir->flags & DID_FINDERINFO) == 0) + return(aeCantMove); + if ((fpdir->flags & DID_RESOURCE) && (tpdir->flags & DID_RESOURCE) == 0) + return(aeCantMove); + + /* must be able to stat destination */ +#ifdef NOCASEMATCH + if ((err = unix_stat(pp = pathstr(tpdir), &stb)) != noErr) { + noCaseFind(pp); + if ((err = unix_stat(pp, &stb)) != noErr) + return(err); + } +#else NOCASEMATCH + if ((err = unix_stat(pathstr(tpdir), &stb)) != noErr) + return(err); +#endif NOCASEMATCH + mo = filemode(stb.st_mode, stb.st_uid, stb.st_gid); + +#ifdef NOCASEMATCH + if ((err = unix_stat(f_path,&stb)) != noErr) { + noCaseFind(f_path); + if ((err = unix_stat(f_path,&stb)) != noErr) + return(err); + } + noCaseMatch(t_path); +#else NOCASEMATCH + if ((err = unix_stat(f_path,&stb)) != noErr) + return(err); +#endif NOCASEMATCH + + err = unix_rename(f_path,t_path); /* give unix the args */ + if (err != noErr) /* if an error on data files */ + return(err); /* then give up */ + +#ifdef DENYREADWRITE + { + struct accessMode *p = accessMQueue; + + while (p != (struct accessMode *)NULL) { + if (strcmp(f_path, p->path) == 0) { + strcpy(p->path, t_path); + break; + } + p = p->next; + } + } +#endif DENYREADWRITE + + if (!S_ISDIR(stb.st_mode)) { /* directories have no rsrc fork */ + unix_chmod(t_path, mo); /* file: try to reset protection */ + if (fpdir->flags & DID_RESOURCE) { + strcpy(fpath, f_path); + strcpy(tpath, t_path); + toResFork(fpath,from); /* build resource file names */ + toResFork(tpath,to); + err = unix_rename(fpath,tpath); /* give unix a shot at it */ + /* allow non-existant resource */ + if (err != noErr && err != aeObjectNotFound) { /* error on rename? */ + if (DBOSI) + printf("os_rename: failed %s for %s -> %s\n", + afperr(err),fpath,tpath); + cerr = unix_rename(t_path,f_path); /* rename back to orignal */ + if (cerr != noErr && DBOSI) + printf("os_rename: cleanup failed\n"); + unix_chmod(t_path, stb.st_mode&0777); /* file:try to reset protection */ + return(err); + } + } + } + + if (fpdir->flags & DID_FINDERINFO) { + strcpy(fpath, f_path); + strcpy(tpath, t_path); + toFinderInfo(fpath,from); /* build finder info file names */ + toFinderInfo(tpath,to); + err = unix_rename(fpath,tpath); /* give unix a shot at it */ + if (err != noErr && DBOSI) { + printf("os_rename: failed %s for %s -> %s\n", afperr(err),fpath,tpath); + } else { + if (!S_ISDIR(stb.st_mode)) + unix_chmod(tpath, mo); /* file: try to reset protection */ + OSSetMacFileName(tpdir, to); + } + } else + if (DBOSI) + printf("os_rename: no finder info to rename\n"); + + if (S_ISDIR(stb.st_mode)) /* if a directory then need to */ + Idmove(fpdir,from,tpdir,to); /* change internal structure */ + else + FIdmove(fpdir,from,tpdir,to); + + FModified(fpdir, from); /* does an emodified */ + /* EModified(fpdir); */ + /* don't need to mark dest file as modified since mac won't let this */ + /* happen */ + EModified(tpdir); + return(noErr); +} + + +/* + * OSErr OSFlush(int vid) + * + * OSFlush is used to flush to disk any data relating to the specified + * volume that has been modified by the user. + * + * The Unix system call sync is used. We should probably be dumping out + * internal volume buffers instead. + * + * Inputs: + * vid Volume ID. + * + * Outputs: + * OSErr Function Result. + * + */ + +/*ARGSUSED*/ +export OSErr +OSFlush(vid) +int vid; +{ + import GCHandle *fich; /* get FinderInfo cache */ + + GCFlush(fich, NILDIR); /* flush the finderinfo of bad data */ + FlushDeskTop(vid); +/* sync(); /* this is probably a waste */ + /* above is a waste and slows down system unnecessarily */ + return(noErr); +} + +/* + * OSErr OSFlushFork(int fd) + * + * Forces the write to disk of any pending file activity. + * + * Really intended for buffered OSWrites. + * + * Inputs: + * fd File descriptor. + * + * Outputs: + * OSErr Function Result. + * + */ + +OSErr +OSFlushFork(fd) +int fd; +{ + if (DBOSI) + printf("OSFlushFork fd=%d\n",fd); + + return(fsync(fd) == 0 ? noErr : aeMiscErr); +} + + +export OSErr +OSClose(fd) +int fd; +{ + return(unix_close(fd)); /* return OSErr */ +} + +/* + * From the open file referenced by fd + * at the offset offs + * read "reqcnt" bytes + * using the new line mask nlmsk + * and the newline character nlchr + * into the buffer r for at most rl bytes + * keeping the file position in fpos + * translating from lf to cr if unixtomac is nonzero + * +*/ +export OSErr +OSRead(fd,offs,reqcnt,nlmsk,nlchr,r,rl,fpos,trans_table_index) +int fd; +sdword offs,reqcnt; +byte nlmsk,nlchr; +byte *r; +int *rl; +sdword *fpos; +int trans_table_index; +{ + register char c; + int cnt,i; + + if (DBOSI) + printf("OSRead: fd=%d, pos=%d, off=%d, req=%d\n", fd, *fpos, offs, reqcnt); + +#ifdef APPLICATION_MANAGER + { + extern int fdplist[NOFILE]; + extern struct flist *applist; + + if (applist != NULL && fdplist[fd] == 1) { + /* we want Finder copy protection */ + if (offs == 0 && reqcnt > 128) + return(aeAccessDenied); + } + } +#endif APPLICATION_MANAGER + + /* want to probe for eof -- probably there */ + /* back off this. If the request count is zero, then */ + /* don't tell them about EOF because zero length files */ + /* will not get xfered properly */ + if (reqcnt == 0) { + *rl = 0; + return(noErr); + } + + if (offs != *fpos) + *fpos = lseek(fd,(off_t)offs,L_SET); + +#ifdef APPLICATION_MANAGER + /* + * we have to resort to fcntl() lock tests + * because lockf() as used by OSTestLock() + * returns "permission denied" if more than + * one single byte read lock exists on fd. + * This is probably a bug, but since we are + * only interested in any write lock in the + * range it doesn't matter ... + * + */ + { + struct flock flck; + extern struct flist *applist; + + if (applist != NULL) { + flck.l_type = F_RDLCK; + flck.l_whence = 1; /* SEEK_CUR */ +#ifdef DENYREADWRITE + flck.l_start = 4; +#else DENYREADWRITE + flck.l_start = 0; +#endif DENYREADWRITE + flck.l_len = reqcnt; + if (fcntl(fd, F_GETLK, &flck) != -1) { + if (flck.l_type == F_WRLCK) + return(aeLockErr); + } + } else { + if (OSTestLock(fd, reqcnt) != noErr) + return(aeLockErr); + } + } +#else APPLICATION_MANAGER + if (OSTestLock(fd, reqcnt) != noErr) { + return(aeLockErr); + } +#endif APPLICATION_MANAGER + + cnt = read(fd,r,reqcnt); + if (cnt < 0) { + printf("OSRead: Error from read %s\n",syserr()); + return(aeParamErr); + } + + if (cnt == 0) + return(aeEOFErr); + + *fpos += cnt; + + /* under appleshare prior to version 2.0, nlmask was either 0 or 0xff */ + /* so no anding needed to be done (either use or not). shouldn't hurt */ + /* to do it for previous versions though */ + if (nlmsk != 0) { +#ifndef NONLXLATE + if (nlchr == ENEWLINE) { + for (i=0; i < cnt; i++) { + c = r[i] & nlmsk; + if (c == ENEWLINE || c == INEWLINE) + break; + } + if (r[i] == INEWLINE) /* if ended on internal newline */ + r[i] = ENEWLINE; /* then convert to external */ + } else + for (i=0; i < cnt && (r[i]&nlmsk) != nlchr; i++) + /* NULL */; +#else + for (i=0; i < cnt && (r[i]&nlmsk) != nlchr; i++) + /* NULL */; +#endif NONLXLATE + + if (i < cnt) /* found it? */ + cnt = i+1; /* yes count is position plus 1 */ + } + if (trans_table_index >= 0) { + if (DBOSI) + printf("FPRead: translating to macintosh according to: %s\n", + ncs_tt_name(trans_table_index)); + ncs_translate(NCS_TRANS_UNIXTOMAC, trans_table_index, r, cnt); + } + + *rl = cnt; /* store count of bytes read */ + if (cnt < reqcnt && nlmsk == 0) /* less than request and no nlchr */ + return(aeEOFErr); /* means we found eof */ + return(noErr); /* else no error.... */ +} + +/* + * Write to the open file referenced by fd + * the write buffer is wbuf of length wlen + * do the write at offset offs + * keeping file position in fpos + * flg - marks whether offs relative to beginning or end of file + * unixtomax - if non-zero translate cr to lf on writes + * +*/ +OSWrite(fd,wbuf,wlen,offs,flg,fpos,trans_table_index) +int fd; +byte *wbuf; +sdword wlen,offs; +byte flg; +sdword *fpos; +int trans_table_index; +{ + int cnt; + + if (wlen == 0) /* zero byte request? */ + return(noErr); /* yes... no work */ + + if (flg == 0) { + if (offs != *fpos) + *fpos = lseek(fd,(off_t)offs,L_SET); + } else + *fpos = lseek(fd,(off_t)offs,L_XTND); + + if (OSTestLock(fd, wlen) != noErr) { + return(aeLockErr); + } + if (trans_table_index >= 0) { + if (DBOSI) + printf("FPWrite: translating from macintosh according to: %s\n", + ncs_tt_name(trans_table_index)); + ncs_translate(NCS_TRANS_MACTOUNIX, trans_table_index, wbuf, wlen); + } + + cnt = write(fd,wbuf,wlen); + *fpos += cnt; + if (cnt == wlen) + return(noErr); + if (DBOSI) + printf("OSWrite: Error from write %s\n",syserr()); + return(ItoEErr(errno)); +} + +/* + * OSErr OSCreateDir(char *path,dword *dirid) + * + * This function is used to create a new directory. + * + * Inputs: + * path The directory to create. + * + * Outputs: + * OSErr Function result. + * dirid Directory ID of newly created directory. + * + */ + +private OSErr +os_mkdir(pdir,name,mode,typ) +IDirP pdir; /* parent directory */ +char *name; +int typ,mode; +{ + char path[MAXPATHLEN]; + OSErr err; + /* want path/name/.resource */ + /* want path/name/.finderinfo */ + /* want path/name */ + + OSfname(path, pdir, name, F_DATA); /* get path/name */ +#ifdef NOCASEMATCH + noCaseMatch(path); +#endif NOCASEMATCH + switch (typ) { + case F_RSRC: + strcat(path, RFDIR); + break; + case F_FNDR: + strcat(path, FIDIR); + break; + } + if (DBOSI) + printf("os_mkdir: creating mode=o%o dir=%s\n",mode,path); + + err = unix_mkdir(path,mode); /* let unix do the work */ + if (err != noErr) + return(err); + EModified(pdir); + return(noErr); +} + + +OSErr +OSCreateDir(pdir,name, newdirid) +IDirP pdir; /* parent directory id */ +char *name; /* name of new directory */ +IDirP *newdirid; +{ + char path[MAXPATHLEN]; + int err,mo; + struct stat stb; + + if (DBOSI) + printf("OSCreateDir: creating %s\n",name); + + err = unix_stat(pathstr(pdir),&stb); /* get current protections */ + mo = stb.st_mode & 0777; + + err = os_mkdir(pdir,name,mo,F_DATA); /* create the data directory */ + if (err != noErr) /* if that failed... then */ + return(err); /* return the error */ + + if (pdir->flags & DID_FINDERINFO) { + OSfname(path,pdir,name,F_FNDR); /* create finderinfo for folder */ +#ifdef NOCASEMATCH + noCaseMatch(path); +#endif NOCASEMATCH + err = unix_create(path,TRUE,mo); /* do delete if exists... */ + os_mkdir(pdir,name,mo,F_FNDR); /* create the finderinfo directory */ + } + if (pdir->flags & DID_RESOURCE) + os_mkdir(pdir,name,mo,F_RSRC); /* create the resource directory */ + *newdirid = Idndirid(pdir,name); /* now install the directory */ + return(noErr); +} + + +/* + * OSErr OSFileDirInfo(IDirP ipdir, char *fn, + * FileDirParms *fdp, int volid); + * + * Return information for file fn, existing in directory ipdir into + * the fdp structure. + * + * fdp->fdp_dbitmap and fdp->fdp_fbitmap are set with the request + * bitmaps so we don't necessarily need to fetch unwanted (costly) + * items. + * + */ +export OSErr +OSFileDirInfo(ipdir,idir,fn,fdp,volid) +IDirP ipdir; /* parent directory */ +IDirP idir; /* directory id if directory */ +char *fn; /* file name */ +FileDirParm *fdp; /* returned parms */ +int volid; /* volume */ +{ + char path[MAXPATHLEN]; + struct stat buf; + time_t sometime; + + OSfname(path,ipdir,fn,F_DATA); + + if (DBOSI) + printf("OSFileDirInfo on %s, idir = %02x,%02x\n",path,idir,VolRootD(volid)); + +#ifndef STAT_CACHE + if (stat(path,&buf) != 0) { /* file exists? */ +#else STAT_CACHE + if (OSstat(path,&buf) != 0) { /* file exists? */ +#endif STAT_CACHE +#ifdef NOCASEMATCH + noCaseFind(path); /* case-insensitive */ +#ifndef STAT_CACHE + if (stat(path,&buf) != 0) { /* file exists? */ +#else STAT_CACHE + if (OSstat(path,&buf) != 0) { /* file exists? */ +#endif STAT_CACHE + if (idir) /* was in directory tree? */ + Idrdirid(ipdir, idir); /* invalidate the entry then */ + return(aeObjectNotFound); /* no... */ + } +#else NOCASEMATCH + if (idir) /* was in directory tree? */ + Idrdirid(ipdir, idir); /* invalidate the entry then */ + return(aeObjectNotFound); /* no... */ +#endif NOCASEMATCH + } + + /* pick out the earliest date for mac creation time */ + sometime = (buf.st_mtime > buf.st_ctime) ? buf.st_ctime : buf.st_mtime; + fdp->fdp_cdate = (sometime > buf.st_atime) ? buf.st_atime : sometime; + /* pick the later of status change and modification for mac modified */ + fdp->fdp_mdate = (buf.st_mtime < buf.st_ctime) ? buf.st_ctime : buf.st_mtime; + +#ifdef USE_MAC_DATES + { time_t when; + OSGetCDate(ipdir,fn,&fdp->fdp_cdate); + if (OSGetMDate(ipdir,fn,&sometime,&when) == noErr) + if (fdp->fdp_mdate < (when+5)) /* fuzz factor */ + fdp->fdp_mdate = sometime; + } +#endif USE_MAC_DATES + + fdp->fdp_bdate = 0; + fdp->fdp_zero = 0; /* zero the zero byte (?) */ +#ifdef SHORT_NAMES + if (idir == VolRootD(volid)) { + if ((fdp->fdp_dbitmap & DP_SNAME) || (fdp->fdp_fbitmap & FP_SNAME)) { + /* change this for volume names */ + strcpy(fdp->fdp_sname,(char *) VolSName(volid)); + if (DBOSI && fdp != NULL) + printf("dirParms sname %s\n",fdp->fdp_sname); + } + if ((fdp->fdp_dbitmap & DP_LNAME) || (fdp->fdp_fbitmap & FP_LNAME)) { + strcpy(fdp->fdp_lname, (char *) VolName(volid)); + if (DBOSI && fdp != NULL) + printf("dirParms lname %s\n",fdp->fdp_lname); + } + } else { + if ((fdp->fdp_dbitmap & DP_SNAME) || (fdp->fdp_fbitmap & FP_SNAME)) + ItoEName_Short(ipdir,fn,fdp->fdp_sname); + if ((fdp->fdp_dbitmap & DP_LNAME) || (fdp->fdp_fbitmap & FP_LNAME)) + ItoEName(fn,fdp->fdp_lname); + } +#else SHORT_NAMES + if (idir == VolRootD(volid)) + strcpy(fdp->fdp_lname, (char *) VolName(volid)); + else + ItoEName(fn,fdp->fdp_lname); +#endif SHORT_NAMES + + if (S_ISDIR(buf.st_mode)) { /* is this a directory? */ + fdp->fdp_flg = FDP_DIRFLG; /* yes... */ + return(OSDirInfo(ipdir,fn,fdp,&buf,volid)); /* fill in */ + } + fdp->fdp_flg = 0; /* otherwise a file */ +#ifdef SHORT_NAMES + /* The PC asks for this information and wasn't implemented */ + /* should we be doing this for directories as well as files ? */ + if (fdp->fdp_fbitmap & FP_PDIR) + fdp->fdp_pdirid = ItoEdirid(ipdir,volid); +#endif SHORT_NAMES + return(OSFileInfo(ipdir,fn,fdp,&buf,path)); /* fill in */ +} + + +export OSErr +OSDirInfo(ipdir,fn,fdp,buf,volid) +IDirP ipdir; +char *fn; +FileDirParm *fdp; +struct stat *buf; +int volid; +{ + IDirP idirid; + dword ItoEAccess(); + char path[MAXPATHLEN]; + dirFinderInfo *dfi; + word bm; + int nchild; + extern int sessvers; + + fdp->fdp_dbitmap &= DP_AUFS_VALID; /* truncate to findable */ + if (sessvers == AFPVersion1DOT1) /* AFP1.1 ignores PRODOS */ + fdp->fdp_dbitmap &= ~DP_PDOS; + bm = fdp->fdp_dbitmap; + + if (DBDIR) + printf("OSDirInfo on %s bm=%x\n",fn,bm); + + if (bm & DP_ATTR) /* skip attr if not requested */ + OSGetAttr(ipdir,fn,&fdp->fdp_attr); + + if (bm & (DP_FINFO|DP_PDOS)) { /* skip finfo if not requested */ + OSGetFNDR(ipdir,fn,fdp->fdp_finfo); + dfi = (dirFinderInfo *)fdp->fdp_finfo; + OSfname(path, ipdir, fn, F_DATA); + strcat(path, "/Icon:0d"); + if (access(path, R_OK) == 0) + dfi->frFlags |= htons(FNDR_fHasCustomIcon); /* has custom ICON */ + else + dfi->frFlags &= htons(~FNDR_fHasCustomIcon); /* no custom ICON */ + } + + if (bm & DP_PDOS) /* generate some ProDOS info. */ + mapFNDR2PDOS(fdp); + + fdp->fdp_parms.dp_parms.dp_ownerid = ItoEID(buf->st_uid); + fdp->fdp_parms.dp_parms.dp_groupid = ItoEID(buf->st_gid); + fdp->fdp_parms.dp_parms.dp_accright = + ItoEAccess(buf->st_mode,buf->st_uid,buf->st_gid); + + idirid = Idndirid(ipdir,fn); /* must validate the entry now */ + if (idirid == NILDIR) + return(aeAccessDenied); + InitDIDVol(idirid, volid); +#ifdef notdef + /* should be done already - so don't do it here! */ + if ((idirid->flags & DID_VALID) == 0) /* info valid? */ + OSValidateDIDDirInfo(idirid); /* valid it then */ +#endif + if (bm & DP_CHILD) { + nchild = OSEnumCount(idirid); + if (nchild < 0) /* error if less than zero */ + nchild = 0; /* probably no read... set to 0 */ + fdp->fdp_parms.dp_parms.dp_nchild = nchild; + } + fdp->fdp_parms.dp_parms.dp_dirid = ItoEdirid(idirid,volid); + return(noErr); +} + +/* + * validates a did by making sure: + * - idirid points to a directory + * - limits number of symbolic links we will follow in any given path! + * o note: overlapping volumes may cause us to follow too many + * symbolic links, but things should eventually catch up. + * also checks if the .resource and .finderinfo directories exists + * (must not be symbolic links) + * double check that parent exists +*/ +export void +OSValidateDIDDirInfo(idirid) +IDirP idirid; +{ + char path[MAXPATHLEN]; + char p_ath[MAXPATHLEN]; + struct stat stb; + IDirP pdir; + int i; + + OSfname(p_ath,idirid,"",F_DATA); +#ifndef STAT_CACHE + i = stat(p_ath, &stb); +#else STAT_CACHE + i = OSstat(p_ath, &stb); +#endif STAT_CACHE +#ifdef NOCASEMATCH + if(i != 0) { + noCaseFind(p_ath); +#ifndef STAT_CACHE + i = stat(p_ath, &stb); +#else STAT_CACHE + i = OSstat(p_ath, &stb); +#endif STAT_CACHE + } +#endif NOCASEMATCH + if (i == 0) { + if (S_ISDIR(stb.st_mode)) + idirid->flags |= DID_DATA; +#ifndef NOLSTAT +#ifndef STAT_CACHE + if (lstat(p_ath, &stb) != 0) { /* shouldn't fail! */ + idirid->flags = DID_VALID; + return; + } + if ((pdir = Ipdirid(idirid)) != NILDIR) { /* get parent */ + /* get count of symlinks of parent */ + i = ((pdir->flags & DID_SYMLINKS) >> DID_SYMLINKS_SHIFT); + if ((stb.st_mode & S_IFMT) == S_IFLNK) { + i++; /* bump up count */ + if (i > DID_MAXSYMLINKS) { + idirid->flags = DID_VALID; /* toss it, too many links down */ + return; + } + } + /* really shouldn't need to mask it - means we are overinc'ed */ + idirid->flags |= ((i << DID_SYMLINKS_SHIFT) & DID_SYMLINKS); + } +#endif STAT_CACHE + /* don't follow symbolic links here! */ +#ifdef STAT_CACHE + if (idirid->flags & DID_DATA) { /* Dan */ +#endif STAT_CACHE + strcpy(path,p_ath); + toFinderInfo(path,""); +#ifndef STAT_CACHE + if (lstat(path, &stb) == 0) + if (S_ISDIR(stb.st_mode)) +#else STAT_CACHE + if (OSfinderinfo(p_ath)) +#endif STAT_CACHE + idirid->flags |= DID_FINDERINFO; + strcpy(path,p_ath); + toResFork(path,""); +#ifndef STAT_CACHE + if (lstat(path, &stb) == 0) + if (S_ISDIR(stb.st_mode)) +#else STAT_CACHE + if (OSresourcedir(p_ath)) +#endif STAT_CACHE + idirid->flags |= DID_RESOURCE; +#ifdef STAT_CACHE + } +#endif STAT_CACHE +#else NOLSTAT + /* no symolic links then */ + strcpy(path,p_ath); + toFinderInfo(path,""); +#ifndef STAT_CACHE + if (stat(path, &stb) == 0) + if (S_ISDIR(stb.st_mode)) +#else STAT_CACHE + if (OSfinderinfo(p_ath) +#endif STAT_CACHE + idirid->flags |= DID_FINDERINFO; + strcpy(path,p_ath); + toResFork(path,""); +#ifndef STAT_CACHE + if (stat(path, &stb) == 0) + if (S_ISDIR(stb.st_mode)) +#else STAT_CACHE + if (OSresourcedir(p_ath) +#endif STAT_CACHE + idirid->flags |= DID_RESOURCE; +#endif NOLSTAT + } + idirid->flags |= DID_VALID; +} + +export OSErr +OSFileInfo(ipdir,fn,fdp,buf,rpath) +IDirP ipdir; +char *fn,*rpath; +FileDirParm *fdp; +struct stat *buf; +{ + struct stat stb; + word bm; + extern int sessvers; + + fdp->fdp_fbitmap &= FP_AUFS_VALID; /* truncate to aufs supported */ + if (sessvers == AFPVersion1DOT1) /* AFP1.1 ignores PRODOS */ + fdp->fdp_dbitmap &= ~FP_PDOS; + bm = fdp->fdp_fbitmap; /* fetch file bitmap */ + + if (DBDIR) + printf("OSFileInfo on %s bm=%x\n",fn,bm); + + if (bm & FP_ATTR) /* skip attr if not requested */ + OSGetAttr(ipdir,fn,&fdp->fdp_attr); + + if (bm & (FP_FINFO|FP_PDOS)) /* skip finfo if not requested */ + OSGetFNDR(ipdir,fn,fdp->fdp_finfo); + + if (bm & FP_PDOS) /* generate some ProDOS info. */ + mapFNDR2PDOS(fdp); + +/* don't have volid available here ... +#ifdef SHORT_NAMES + if (fdp->fdp_fbitmap & FP_PDIR) + fdp->fdp_pdirid = ItoEdirid(ipdir,volid); +#endif SHORT_NAMES +*/ + + fdp->fdp_parms.fp_parms.fp_fileno = buf->st_ino; + fdp->fdp_parms.fp_parms.fp_dflen = buf->st_size; + fdp->fdp_parms.fp_parms.fp_rflen = 0; + +#ifndef STAT_CACHE + if (bm & FP_RFLEN) { +#else STAT_CACHE + if ((bm & FP_RFLEN) && ipdir != NULL && (ipdir->flags&DID_RESOURCE)) {/*Dan*/ +#endif STAT_CACHE + toResFork(rpath,fn); /* convert to rsrc name */ + if (stat(rpath,&stb) != 0) /* to figure size of resource fork */ + return(noErr); + if (DBFIL) + printf("OSFileInfo: %s size is %d\n", rpath, (int)stb.st_size); + fdp->fdp_parms.fp_parms.fp_rflen = stb.st_size; + } + return(noErr); +} + +/* + * simply check to see if a particular file exists + * +*/ +export OSErr +OSFileExists(ipdir, fn, type) +IDirP ipdir; +char *fn; +int type; +{ + char path[MAXPATHLEN]; + struct stat stb; +#ifdef NOCASEMATCH + register int i; +#endif NOCASEMATCH + + OSfname(path, ipdir, fn, type); +#ifdef NOCASEMATCH + if((i = unix_stat(path, &stb)) != noErr) { + noCaseFind(path); + i = unix_stat(path, &stb); + } + return(i); +#else NOCASEMATCH + return(unix_stat(path, &stb)); +#endif NOCASEMATCH +} + +export OSErr +OSSetFileDirParms(ipdir,idir,fn,fdp) +IDirP ipdir; +IDirP idir; /* directory id if dir */ +char *fn; +FileDirParm *fdp; +{ + char path[MAXPATHLEN]; + struct stat stb; + int err; + + OSfname(path,ipdir,fn,F_DATA); /* unix file name */ + if ((err = unix_stat(path,&stb)) != noErr) { +#ifdef NOCASEMATCH + noCaseFind(path); + if ((err = unix_stat(path,&stb)) != noErr) { +#endif NOCASEMATCH + /* can't find it !!! */ + if (idir) + Idrdirid(ipdir, idir); /* remove from tree */ + return(err); +#ifdef NOCASEMATCH + } +#endif NOCASEMATCH + } + + if (S_ISDIR(stb.st_mode)) /* if a directory */ + return(OSSetDirParms(ipdir,fn,fdp)); /* then set dir parms... */ + else /* else set file parms */ + return(OSSetFileParms(ipdir,fn,fdp)); +} + +export OSErr +OSSetFileParms(ipdir,fn,fdp) +IDirP ipdir; +char *fn; +FileDirParm *fdp; +{ + word bm = fdp->fdp_fbitmap; + +#ifdef USE_MAC_DATES + if (bm & (FP_FINFO|FP_ATTR|FP_PDOS|FP_CDATE|FP_MDATE)) +#else USE_MAC_DATES + if (bm & (FP_FINFO|FP_ATTR|FP_PDOS)) +#endif USE_MAC_DATES + OSSetFA(ipdir,fn,fdp->fdp_fbitmap,fdp); + + return(noErr); +} + +/* + * OSSetDirParms + * + */ + +OSErr +OSSetDirParms(ipdir,fn,fdp) +IDirP ipdir; +char *fn; +FileDirParm *fdp; +{ + u_short EtoIAccess(); + char p_ath[MAXPATHLEN]; + char path[MAXPATHLEN]; + int flags, err; + int own, grp; /* owner and group ids */ + DirParm *dp = &fdp->fdp_parms.dp_parms; +#ifdef NETWORKTRASH + struct stat buf; +#endif NETWORKTRASH + +#ifdef USE_MAC_DATES + if (fdp->fdp_dbitmap & (DP_FINFO|DP_ATTR|DP_PDOS|DP_CDATE|DP_MDATE)) +#else USE_MAC_DATES + if (fdp->fdp_dbitmap & (DP_FINFO|DP_ATTR|DP_PDOS)) +#endif USE_MAC_DATES + OSSetFA(ipdir,fn,fdp->fdp_dbitmap,fdp); + + grp = own = -1; + if (fdp->fdp_dbitmap & DP_CRTID) + own = EtoIID(dp->dp_ownerid); + if (fdp->fdp_dbitmap & DP_GRPID) + grp = EtoIID(dp->dp_groupid); + + flags = ipdir->flags; /* should use to prevent overworking */ + if (own != -1 || grp != -1) { + /* error recovery? do all just in case */ + OSfname(p_ath,ipdir,fn,F_DATA); + /* change owner/group for fn */ + if ((err = unix_chown(p_ath,own,grp)) != noErr) { +#ifdef NOCASEMATCH + noCaseFind(p_ath); + if ((err = unix_chown(p_ath,own,grp)) != noErr) +#endif NOCASEMATCH + return(err); + } + /* change owner/group for fn/.resource */ + strcpy(path,p_ath); strcat(path,RFDIR); + if ((err = unix_chown(path,own,grp)) != noErr && err != aeObjectNotFound) + return(err); + + /* change owner/group for fn/.finderinfo */ + strcpy(path,p_ath); strcat(path,FIDIR); + if ((err = unix_chown(path,own,grp)) != noErr && err != aeObjectNotFound) + return(err); + + /* change owner/group for ../.resource/fn */ + OSfname(path,ipdir,fn,F_RSRC); + if ((err = unix_chown(path,own,grp)) != noErr && err != aeObjectNotFound) { +#ifdef NOCASEMATCH + noCaseFind(path); + if ((err = unix_chown(path,own,grp)) != noErr && err != aeObjectNotFound) +#endif NOCASEMATCH + return(err); + } + + /* change owner/group for ../.finderinfo/fn */ + OSfname(path,ipdir,fn,F_FNDR); + if ((err = unix_chown(path,own,grp)) != noErr && err != aeObjectNotFound) { +#ifdef NOCASEMATCH + noCaseFind(path); + if ((err = unix_chown(path,own,grp)) != noErr && err != aeObjectNotFound) +#endif NOCASEMATCH + return(err); + } + EModified(ipdir); + } + + if (fdp->fdp_dbitmap & DP_ACCES) { + u_short acc, accd; /* file, directory mode */ + acc = accd = EtoIAccess(dp->dp_accright); +#ifdef NETWORKTRASH + /* make the Network Trash Folder the same */ + /* access mode as the parent directory */ + if (*fn == 'N') { /* if first letter is 'N' */ + if (strcmp(fn, "Network Trash Folder") == 0 +#ifndef STAT_CACHE + && stat(pathstr(ipdir),&buf) == 0 /* and stat() OK */ +#else STAT_CACHE + && OSstat(pathstr(ipdir),&buf) == 0 /* and stat() OK */ +#endif STAT_CACHE + ) + /* parent directory mode */ + acc = accd = buf.st_mode; + } +#endif NETWORKTRASH +#ifdef USEDIRSETGID + if (grp != usrgid) + accd |= I_SETGID; +#endif USEDIRSETGID + + /* change mode for fn */ + if ((err = os_chmod(ipdir,fn,accd,F_DATA)) != noErr) + return(err); + + /* change all file protections, owner & group in fn */ + os_change_all(ipdir, fn, acc, own, grp, F_DATA); + os_change_all(ipdir, fn, acc, own, grp, F_RSRC); + os_change_all(ipdir, fn, acc, own, grp, F_FNDR); + + OSfname(p_ath,ipdir,fn,F_DATA); + /* change mode for fn/.resource */ + strcpy(path,p_ath); strcat(path,RFDIR); + if (( err = unix_chmod(path,accd)) != noErr && err != aeObjectNotFound) { +#ifdef NOCASEMATCH + noCaseFind(path); + if (( err = unix_chmod(path,accd)) != noErr && err != aeObjectNotFound) +#endif NOCASEMATCH + return(err); + } + + /* change mode for fn/.finderinfo */ + strcpy(path,p_ath); strcat(path,FIDIR); + if (( err = unix_chmod(path,accd)) != noErr && err != aeObjectNotFound) { +#ifdef NOCASEMATCH + noCaseFind(path); + if (( err = unix_chmod(path,accd)) != noErr && err != aeObjectNotFound) +#endif NOCASEMATCH + return(err); + } + + /* change mode for ../.resource/fn */ + if ((err = os_chmod(ipdir,fn,acc,F_RSRC)) != noErr && + err != aeObjectNotFound) + return(err); + + /* change mode for ../.finderinfo/fn */ + if ((err = os_chmod(ipdir,fn,acc,F_FNDR)) != noErr && + err != aeObjectNotFound) + return(err); + } + return(noErr); +} + + +/* + * Set fork length specified the file handle + * - careful about len - should be at least a signed double word + * on mac (4 bytes) +*/ +export OSErr +OSSetForklen(fd, len) +int fd; +int len; +{ + int err; + if (DBOSI) + printf("OSSetForklen: truncating file on file descriptor %d to %d\n", + fd,len); + if (ftruncate(fd, len) < 0) { + err = errno; + if (DBOSI) + printf("OSSetForklen: error on truncate %s\n",syserr()); + return(ItoEErr(err)); + } + return(noErr); +} + +/* + * OSErr os_chmod(IDirP idir, u_short mode, int typ) + * + * Directory id (idir), and type (typ) specify a directory name. + * Internal access bits are mode. + * + * Change the protection of the directory to eacc. + * + */ +private OSErr +os_chmod(idir,fn,mode,typ) +IDirP idir; +char *fn; +u_short mode; +int typ; +{ + char path[MAXPATHLEN]; + int err; + + OSfname(path,idir,fn,typ); /* convert unix name */ + if (DBOSI) + printf("os_chmod: setting %o for %s\n",mode,path); + + err = unix_chmod(path,mode); /* and set for the directory */ +#ifdef NOCASEMATCH + if (err != noErr) { + noCaseFind(path); + if((err = unix_chmod(path,mode)) != noErr) + return(err); + } +#else NOCASEMATCH + if (err != noErr) + return(err); +#endif NOCASEMATCH + + EModified(idir); + return(noErr); +} + +/* + * Change file protection, owner & group for all files in directory + * + * Have to do because: + * unix has file protection and AFP does not, change the protection + * of each file in the directory as well. + * + * Do not change the protection of directories contained within + * the directory... + * +*/ +private void +os_change_all(idir,fn,mode,own,grp,typ) +IDirP idir; +char *fn; +u_short mode; +int own, grp, typ; +{ + char path[MAXPATHLEN]; + char p_ath[MAXPATHLEN]; + struct stat stb; +#ifdef USEDIRENT + struct dirent *dp, *readdir(); +#else USEDIRENT + struct direct *dp, *readdir(); +#endif USEDIRENT + DIR *dirp; + int pl,err; + + OSfname(path,idir,fn,F_DATA); /* convert unix name */ + pl = strlen(path); + switch (typ) { + case F_DATA: + break; + case F_RSRC: + strcpy(path+pl, RFDIR); + break; + case F_FNDR: + strcpy(path+pl, FIDIR); + break; + } + if (DBOSI) + printf("os_change_all: setting %o for %s\n",mode,path); + + dirp = opendir(path); + if (dirp == NULL) { +#ifdef NOCASEMATCH + noCaseFind(path); + if((dirp = opendir(path)) == NULL) { +#endif NOCASEMATCH + if (DBOSI) + printf("os_change_all: opendir failed on %s\n",path); + return; +#ifdef NOCASEMATCH + } +#endif NOCASEMATCH + } + + pl = strlen(path); /* length of the path */ + path[pl++] = '/'; /* add component terminator */ + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + strcpy(path+pl,dp->d_name); /* create file name */ + if (stat(path,&stb) != 0) { /* get the file status */ + if (DBOSI) + printf("os_change_all: stat failed for %s\n",path); + continue; /* some error... */ + } + if (S_ISDIR(stb.st_mode)) + continue; + if (typ == F_FNDR) { + OSfname(p_ath,idir,fn,F_DATA); + strcat(p_ath,"/"); strcat(p_ath,dp->d_name); + if (stat(p_ath,&stb) == 0) { + if (S_ISDIR(stb.st_mode)) + continue; + } + } + /* ignore errors */ + unix_chmod(path,mode); /* set the mode for this file */ + unix_chown(path,own,grp); /* set owner and group for file */ + } + closedir(dirp); /* close the directory */ +} + + +private u_short +EtoIPriv(emode,eshift,ishift) +dword emode; +int eshift,ishift; +{ + u_short i = 0; + + emode = (emode >> eshift); + if (emode & E_RD) i = I_RD | I_EX; + if (emode & E_WR) i |= I_WR | I_EX; +/* if (emode & E_SR) i |= I_EX; */ + return(i << ishift); +} + + +/* + * Is the current user in the group gid? + * +*/ +private int +OSInGroup(gid) +int gid; +{ + int i; + int gtgid; + + /* if ngroups gets very large, do a binary search */ + for (i=0; i < ngroups; i++) { + gtgid = groups[i]; + if (gtgid == gid) + return(TRUE); + if (gtgid > gid) /* limiting case */ + return(FALSE); + } + return(FALSE); +} + +/* + * Get groups that a user is valid in + * + * Sort the array for later use + * +*/ +dogetgroups() +{ + int ng; + int i,k,idxofmin; + int t; + + if ((ng = getgroups(NGROUPS, groups)) < 0) { + return(-1); + } + if (ng == 0) /* not in any groups? */ + return(0); + + /* sort groups array */ + /* we assume n is very small, else possibly replace with quicker sort */ + /* n**2 performance (comparisons), but no more than n swaps */ + /* the array tends to be mostly sorted with the first element out */ + /* of place. Don't use std quicksort -- under this assumption */ + /* it will give very poor performance on average (at much higher overhead) */ + /* NOTE: we could really speed this up if we knew that the */ + /* array was sorted after the first "n" items, but with N so */ + /* small (usually less than 32), it's not a big deal */ + for (i = 0 ; i < ng; i++) { + for ( idxofmin = i, k = i + 1; k < ng; k++) /* find (i+1)'th min */ + if (groups[k] < groups[idxofmin]) + idxofmin = k; + if (i != idxofmin) { + t = groups[idxofmin]; + groups[idxofmin] = groups[i]; + groups[i] = t; + } + } + return(ng); +} + +private u_short +EtoIAccess(emode) +dword emode; +{ + u_short imode; + + imode = (EtoIPriv(emode,ES_OWNER,IS_OWNER) | + EtoIPriv(emode,ES_GROUP,IS_GROUP) | + EtoIPriv(emode,ES_WORLD,IS_WORLD)); + + if (DBOSI) + printf("EtoIAccess: E=0x%x, I=o%o\n",emode,imode); + + return(imode); +} + +private dword +ItoEPriv(imode,ishift,eshift) +u_short imode; +int ishift,eshift; +{ + dword e = 0; + + imode = (imode >> ishift); + if (imode & I_RD) e = E_RD | E_SR; + if (imode & I_WR) e |= E_WR; +/* if (imode & I_EX) e |= E_SR; */ + return(e << eshift); +} + +private dword +ItoEAccess(imode,uid,gid) +u_short imode; +int uid; +int gid; +{ + dword e = 0; + + /* set user rights summary byte */ + + if (usruid == 0) /* are we running as root? */ + e |= ((E_RD|E_WR|E_SR)< */ + e |= E_IOWN; /* must set owner bit */ + + /* set owner, group and world bytes */ + + e |= (ItoEPriv(imode,IS_OWNER,ES_OWNER) | /* other access */ + ItoEPriv(imode,IS_GROUP,ES_GROUP) | + ItoEPriv(imode,IS_WORLD,ES_WORLD)); + + if (DBOSI) + printf("ItoEAccess: I=o%o, E=0x%x\n",imode,e); + + return(e); +} + +export OSErr +OSAccess(idir,fn,mode) +IDirP idir; +char *fn; +int mode; +{ + int imode = 0; + int err; + char path[MAXPATHLEN]; + + if (mode & OFK_MWR) + imode |= W_OK; + if (mode & OFK_MRD) + imode |= R_OK; + + OSfname(path,idir,fn,F_DATA); /* create unix style filename */ + err = access(path,imode); + if (err == 0) + return(noErr); +#ifdef NOCASEMATCH + noCaseFind(path); + if(access(path,imode) == 0) + return(noErr); +#endif NOCASEMATCH + switch (errno) { + case ENOTDIR: + return(aeDirNotFound); + case ENOENT: + return(aeObjectNotFound); + case EACCES: + return(aeAccessDenied); + } + return(aeAccessDenied); +} + +/* + * scale the AUFS volume size information to comply + * with the upper limit of Macintosh file systems + * (currently 2 Gigabytes) + * + * To avoid problems with "on disk" sizes being + * calculated incorrectly (ie: 2,546.9MB on disk for + * 8,903 bytes used) we seem to need an allocation + * block size smaller than 32k, so we arbitrarily + * choose 31k and apply the formula from IM vol IV, + * page 241 + * + * abSize = (1 + ((volSize/512)/64k)) * 512 + * + * giving a volume size of 2046820352 bytes (1,952MB) + * + */ + +#ifndef MAXMACVOLSIZE +#define MAXMACVOLSIZE 2046820352 +#endif MAXMACVOLSIZE + +void +scaleVolSize(v) +VolPtr v; +{ + float scale; + + if (v->v_size >= MAXMACVOLSIZE) { + scale = (MAXMACVOLSIZE >> 16)/(float)(v->v_size >> 16); + v->v_free = scale * v->v_free; + v->v_size = MAXMACVOLSIZE; + } + + return; +} + +/* + * set the extended volume size + * parameters defined by AFP 2.2 + * + * size, free and blocks are 32-bit numbers. + * We want to end up with a 64-bit number in + * network-byte-order. + * + * For the moment we note that block-sizes + * are usually a simple power of two so we + * just bit-shift the original numbers. + * + */ + +void +extendedVolSize(v, size, free, blk) +VolPtr v; +dword size, free, blk; +{ + int off; + int i, j; + int shift; + + bzero((char *)v->v_esize, sizeof(v->v_esize)); + bzero((char *)v->v_efree, sizeof(v->v_efree)); + + switch (blk) { + case 1: + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + case 128: + off = 0; + break; + case 256: + case 512: + case 1024: + case 2048: + case 4096: + case 8192: + case 16384: + case 32768: + off = 1; + break; + case 65536: + case 131072: + case 262144: + off = 2; + break; + default: + /* set some arbitrary number */ + v->v_esize[4] = 0x80; + v->v_efree[4] = 0x40; + return; + break; + } + + /* + * initialize the array in network byte + * order. If the multiplier is 1, 256 or + * 65536 there is nothing else to do. + * + */ + v->v_esize[7-off] = size & 0xff; + v->v_esize[6-off] = (size >> 8) & 0xff; + v->v_esize[5-off] = (size >> 16) & 0xff; + v->v_esize[4-off] = (size >> 24) & 0xff; + + v->v_efree[7-off] = free & 0xff; + v->v_efree[6-off] = (free >> 8) & 0xff; + v->v_efree[5-off] = (free >> 16) & 0xff; + v->v_efree[4-off] = (free >> 24) & 0xff; + + if (blk == 1 || blk == 256 || blk == 65536) + return; + + /* + * now bit shift each group of bytes + * + */ + shift = (blk < 256) ? blk : ((blk < 65536) ? (blk/256) : (blk/65536)); + + for (i = 1; i < 20; i++) { + for (j = 0 ; j < 8; j++) { + v->v_esize[j] <<= 1; + v->v_efree[j] <<= 1; + if (j < 7 && (v->v_esize[j+1] & 0x80)) + v->v_esize[j] |= 1; + if (j < 7 && (v->v_efree[j+1] & 0x80)) + v->v_efree[j] |= 1; + } + if (shift == (0x0001 << i)) + break; + } + + return; +} + +/* + * OSErr OSVolInfo(VolPtr v) + * + * Update volume information for volume pointed to by v. + * Returns error if path does not exist. + * + * Update: + * + * v_attr Read only flag (V_RONLY) + * v_cdate creation date (of v_path) + * v_mdate modification date (of v_path) + * v_size size of volume in bytes (32 bits ##) + * v_free free bytes on volume (32 bits ##) + * + * ## expect trouble if the volume size exceeds 4 Gigabytes. + * + */ + +export OSErr +OSVolInfo(path,v, bitmap) +char *path; +VolPtr v; +word bitmap; /* bitmap of info needed */ +{ + struct stat buf; +#if defined(USEQUOTA) || defined(USEBSDQUOTA) + struct dqblk dqblk; +#endif USEQUOTA +#ifdef USEUSTAT + struct ustat ubuf; +#endif USEUSTAT +#ifdef USESTATFS +# ifdef SOLARIS + struct statvfs fsbuf; +# else SOLARIS + struct statfs fsbuf; +# endif SOLARIS +#endif USESTATFS + time_t sometime; + void extendedVolSize(); + void scaleVolSize(); + + if (stat(path,&buf) != 0) /* directory exists? */ + return(aeObjectNotFound); /* no... */ + if (!(S_ISDIR(buf.st_mode))) /* check for directory */ + return(aeParamErr); /* not a directory! */ + + if (bitmap & VP_CDATE) { + /* pick out the earliest date for mac creation time */ + sometime = (buf.st_mtime > buf.st_ctime) ? buf.st_ctime : buf.st_mtime; + v->v_cdate = sometime > buf.st_atime ? buf.st_atime : sometime; + } + if (bitmap & VP_MDATE) { + /* pick the later of status change and modification for */ + /* mac modified */ + v->v_mdate = (buf.st_mtime < buf.st_ctime) ? buf.st_ctime : buf.st_mtime; + } +#ifdef notdef + /* had it as v->v_mdate -- probably the reason we ifdef'ed it out */ + if (bitmap & VP_BDATE) + v->v_bdate = 0; +#endif + + if (bitmap & VP_ATTR) { +#ifdef notdef + /* don't really want this - causes problems when you have access */ + /* futher down the tree - should only come up locked iff the */ + /* tree is write locked (no (easy) way to tell because of symbolic */ + /* links and mount points) */ + if (access(v->v_path,W_OK) != 0) /* ok to write into directory? */ + v->v_attr |= V_RONLY; /* no, set read-only */ + else +#endif + v->v_attr &= ~V_RONLY; /* clear read-only */ + } + + if ((bitmap & (VP_FREE|VP_SIZE|VP_EFREE|VP_ESIZE)) == 0) + return(noErr); /* naught else to do */ + + /* All the following is good and fine unless: (a) the volume */ + /* has symlinks off the volume or (b) there are mounted file systems */ + /* under the mac volume. In those cases, returning this information */ + /* could be damaging because some "good" programs base what they */ + /* can do on the volume information --- but people really like this */ + /* information!!! so we take the trade off (even though it is one */ + /* of the most system dependent parts of aufs) */ + + /* don't know how to calculate disk free and size without being su */ + /* (in the general case) */ + + /* hard coded values of 512 for quotas and 1024 for ustat are bad */ + /* where are the "real" numbers defined? */ + + /* careful on the ordering - these must go last and if you can possibly */ + /* define more than one then you must define in order you wish */ +#ifdef USEQUOTA +#if defined(gould) + if (gquota(Q_GETDLIM, usruid, buf.st_dev, &dqblk) == 0 && +#elif defined(encore) + if (equota(Q_GETDLIM, usruid, buf.st_dev, &dqblk) == 0 && +#elif defined(SOLARIS) + if (solaris_quota(Q_GETDLIM, usruid, path, &dqblk) == 0 && +#else /* gould || encore || SOLARIS */ + if (quota(Q_GETDLIM, usruid, buf.st_dev, &dqblk) == 0 && +#endif /* gould || encore || SOLARIS */ + dqblk.dqb_bhardlimit != 0) { /* make sure not unlimited */ + v->v_size = dqblk.dqb_bhardlimit*512; + v->v_free = (dqblk.dqb_bhardlimit-dqblk.dqb_curblocks)*512; + extendedVolSize(v, dqblk.dqb_bhardlimit, + dqblk.dqb_bhardlimit-dqblk.dqb_curblocks, 512); + scaleVolSize(v); + return(noErr); + } +#endif USEQUOTA +#ifdef USEBSDQUOTA + { + char *fsname; + private char *find_mount_spec(); + if ((fsname = find_mount_spec(buf.st_dev)) == NULL) { + errno = EPERM; + return(-1); + } + if (quotactl(fsname, QCMD(Q_GETQUOTA,USRQUOTA), usruid, &dqblk) == 0 && + dqblk.dqb_bhardlimit != 0) { + v->v_size = dqblk.dqb_bhardlimit*512; + v->v_free = (dqblk.dqb_bhardlimit-dqblk.dqb_curblocks)*512; + extendedVolSize(v, dqblk.dqb_bhardlimit, + dqblk.dqb_bhardlimit-dqblk.dqb_curblocks, 512); + scaleVolSize(v); + return(noErr); + } + } +#endif USEBSDQUOTA +#ifdef USEGETMNT + if (ultrix_volinfo(path, &buf, v) == noErr) + return(noErr); +#endif USEGETMNT +#ifdef USEUSTAT + if (ustat(buf.st_dev, &ubuf) >= 0) { + if (VP_SIZE & bitmap) { + /* should do something better here */ + v->v_size = ubuf.f_tfree*1024; + } + v->v_free = ubuf.f_tfree*1024; + extendedVolSize(v, ubuf.f_tfree, ubuf.f_tfree, 1024); + scaleVolSize(v); + return(noErr); + } +#endif USEUSTAT +#ifdef USESTATFS +#ifdef SOLARIS + if (statvfs(path, &fsbuf) >= 0) { + v->v_size = fsbuf.f_frsize * fsbuf.f_blocks; + /* limiting factor: cannot report on overutilization of a volume */ + v->v_free = (fsbuf.f_bavail < 0) ? 0 : fsbuf.f_frsize * fsbuf.f_bavail; + extendedVolSize(v, fsbuf.f_blocks, fsbuf.f_bavail, fsbuf.f_frsize); + scaleVolSize(v); + return(noErr); + } +#else /* SOLARIS */ +# if defined(sgi) || defined(apollo) + if (statfs(path, &fsbuf, sizeof(fsbuf), 0) >= 0) { + v->v_size = fsbuf.f_bsize * fsbuf.f_blocks; + /* limiting factor: cannot report on overutilization of a volume */ + v->v_free = (fsbuf.f_bfree < 0) ? 0 : fsbuf.f_bsize * fsbuf.f_bfree; + extendedVolSize(v, fsbuf.f_blocks, fsbuf.f_bfree, fsbuf.f_bsize); + scaleVolSize(v); + return(noErr); + } +# else /* sgi || apollo */ + if (statfs(path, &fsbuf) >= 0) { +#if (defined(__386BSD__) && !defined(__NetBSD__)) || defined(__osf__) + /* + * on 386/BSD, the block size is in f_fsize + * and f_bsize is the optimum transfer size + * + */ + v->v_size = fsbuf.f_fsize * fsbuf.f_blocks; + /* limiting factor: cannot report on overutilization of a volume */ + v->v_free = (fsbuf.f_bavail < 0) ? 0 : fsbuf.f_fsize * fsbuf.f_bavail; + extendedVolSize(v, fsbuf.f_blocks, fsbuf.f_bavail, fsbuf.f_fsize); +#else /* __386BSD__ || __osf__ */ + v->v_size = fsbuf.f_bsize * fsbuf.f_blocks; + /* limiting factor: cannot report on overutilization of a volume */ + v->v_free = (fsbuf.f_bavail < 0) ? 0 : fsbuf.f_bsize * fsbuf.f_bavail; + extendedVolSize(v, fsbuf.f_blocks, fsbuf.f_bavail, fsbuf.f_bsize); +#endif /* __386BSD__ || __osf__ */ + scaleVolSize(v); + return(noErr); + } +# endif /* sgi || apollo */ +#endif /* SOLARIS */ +#endif USESTATFS +#ifdef SIZESERVER + getvolsize(path, &v->v_size, &v->v_free); +#else SIZESERVER + v->v_size = 0x1000000; /* some random number */ + v->v_free = 0x1000000; /* same random number */ +#endif SIZESERVER + extendedVolSize(v, 0x1000000, 0x1000000, 512); + scaleVolSize(v); + return(noErr); /* all ok */ +} + +#ifdef SIZESERVER + +#ifndef SIZESERVER_PATH +#define SIZESERVER_PATH "/usr/local/cap/sizeserver" +#endif SIZESERVER_PATH + +static jmp_buf gotpipe; + +private void +getvolsize(path, ntot, nfree) +char *path; +sdword *ntot, *nfree; +{ + register int i; + int mask, socket[2]; + struct volsize vs; + static int server = -1, server1, servmask; + static struct timeval servtimeout = {0, 500000L}; + + if(setjmp(gotpipe)) { + if(server >= 0) + close(server); + server = -1; +unknown: + *ntot = 0x1000000; + *nfree = 0x1000000; + return; + } + if(server < 0) { + register int pid; + int catchsigpipe(); + + if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket) < 0) + goto unknown; + if((pid = fork()) < 0) { + close(socket[0]); + close(socket[1]); + goto unknown; + } + if(pid == 0) { /* the child */ + close(socket[0]); + if(socket[1] != 0) { + dup2(socket[1], 0); + close(socket[1]); + } + execl(SIZESERVER_PATH, "sizeserver", 0); + _exit(1); + } + close(socket[1]); + server = socket[0]; + server1 = server + 1; + servmask = 1 << server; + signal(SIGPIPE, catchsigpipe); + } + for(i = 3 ; ; ) { + if(i-- <= 0) + goto unknown; + lseek(server, 0L, 2); + write(server, path, strlen(path) + 1); + mask = servmask; + if(select(server1, &mask, NULL, NULL, &servtimeout) < 1) + goto unknown; + if(read(server, (char *)&vs, sizeof(vs)) == sizeof(vs)) + break; + } + *ntot = vs.total; + *nfree = vs.free; +} + +catchsigpipe() +{ + longjmp(gotpipe, 1); +} +#endif SIZESERVER + +#ifdef USEGETMNT +/* get info on path using buf when there is ambiguity (ultrix 2.0 or before) */ +/* fill in info on v */ +private OSErr +ultrix_volinfo(path,buf,v) +char *path; +struct stat *buf; +VolPtr v; +{ + int context = 0; + int i, num; + u_int vfree; + /* multiple buffers are wasteful when using Ultrix 2.2, but code is */ + /* good enough */ + struct fs_data buffer[NUMGETMNTBUF]; + struct fs_data *bp; + int nbytes = sizeof(struct fs_data)*NUMGETMNTBUF; + void extendedVolSize(); + void scaleVolSize(); + + if (!oldgetmnt) { + /* Ultrix 2.2 */ + /* use nostat_one -- we don't want to hang on nfs */ + /* return none if error or (0) - not found */ + nbytes = sizeof(struct fs_data); + if ((num=getmnt(&context, buffer, nbytes, NOSTAT_ONE, path)) <= 0) + return(-1); + bp = buffer; + } else { + while ((num=getmnt(&context, buffer, nbytes)) > 0) { + for (i = 0, bp = buffer; i < num; i++, bp++) + if (buf->st_dev == bp->fd_req.dev) + goto found; + /* context is set to zero if last call to getmnt returned */ + /* all file systems */ + if (context == 0) + return(-1); + } + /* should never reach here, means getmnt returned an error */ + return(-1); /* nothing found */ + } +found: + /* "overflow" space if any - looks used */ + v->v_size = bp->fd_req.btot * 1024; + /* bfreen must be "good" in that it cannot go below 0 when */ + /* out of space -- it is unsigned! */ + v->v_free = ((getuid() == 0) ? bp->fd_req.bfree : bp->fd_req.bfreen) * 1024; + extendedVolSize(v, bp->fd_req.btot, + (getuid() == 0) ? bp->fd_req.bfree : bp->fd_req.bfreen, 1024); + scaleVolSize(v); + return(noErr); +} +#endif /* GETMNT */ + +#if defined(USEQUOTA) && defined(SOLARIS) +int +solaris_quota(cmd, uid, path, dq) +int cmd, uid; +char *path; +struct dqblk *dq; +{ + int fd, res; + struct quotctl qctl; + + switch (cmd) { + case Q_GETQUOTA: /* let it be "read-only" */ + break; + default: + errno = EINVAL; + return(-1); + } + + if ((fd = open(path, O_RDONLY)) < 0) + return(-1); + + qctl.op = cmd; + qctl.uid = uid; + qctl.addr = (caddr_t)dq; + res = ioctl(fd, Q_QUOTACTL, &qctl); + close(fd); + + return(res); +} +#endif /* USEQUOTA && SOLARIS */ + +#if defined(USESUNQUOTA) || defined(USEBSDQUOTA) + +#ifndef MAXUFSMOUNTED +# ifdef NMOUNT +# define MAXUFSMOUNTED NMOUNT /* NMOUNT in param.h */ +# else NMOUNT +# define MAXUFSMOUNTED 32 /* arb. value */ +# endif NMOUNT +#endif MAXUFSMOUNTED + +private struct mount_points { + char *mp_fsname; /* name of "block" device */ + dev_t mp_dev; /* device number */ +} mount_points[MAXUFSMOUNTED]; + +private int num_mount_points = -1; + +private struct mount_points * +find_mount_spec_intable(dev) +dev_t dev; +{ + int i; + struct mount_points *mp; + + for (i = 0, mp = mount_points; i < num_mount_points; i++, mp++) { + if (dev == mp->mp_dev) + return(mp); + } + return(NULL); +} + +#ifdef USEBSDQUOTA +/* + * find the block special device... + * try updating mount point table if possible + * + * returns name if it can, null o.w. + * + */ +private char * +find_mount_spec(dev) +{ + struct mount_points *mp; + struct fstab *mtent; + struct stat sbuf; + char *strdup(); + + if ((mp = find_mount_spec_intable(dev)) != NULL) /* try once */ + return(mp->mp_fsname); + + if (num_mount_points < MAXUFSMOUNTED) { + /* check to see if mounted */ + if (setfsent() == 0) { + if (DBOSI) + logit(0,"setfsent failed!"); + return(NULL); + } + mp = NULL; + while ((mtent = getfsent()) != NULL) { + if (stat(mtent->fs_file, &sbuf) < 0) + continue; + if (dev != sbuf.st_dev) + continue; + mp = mount_points+num_mount_points; + num_mount_points++; + mp->mp_fsname = strdup(mtent->fs_file); + mp->mp_dev = sbuf.st_dev; + break; + } + endfsent(); + } else { + /* table would overflow, try rebuilding */ + if (build_mount_table() < 0) /* try rebuilding table */ + return(NULL); + mp = find_mount_spec_intable(dev); + } + + if (mp) + return(mp->mp_fsname); + + if (DBOSI) + logit(0,"Cannot find file system on device (%d,%d)\n", + major(dev), minor(dev)); + + return(NULL); /* total failure */ +} + +/* + * build a table of the various mounted file systems + * using getmntent. We want to use /etc/mtab, but + * if we can't, then we use /etc/fstab to get some + * information anyway if this is the first time around (to + * prevent constant rereads since /etc/fstab is pretty constant) + * + */ +int +build_mount_table() +{ + struct stat sbuf; + struct fstab *mtent; + struct mount_points *mp; + char *strdup(); + + setfsent(); + + /* free old info */ + if (num_mount_points) { + for (mp = mount_points ; num_mount_points > 0; num_mount_points--, mp++) + if (mp->mp_fsname) + free(mp->mp_fsname); + } + num_mount_points = 0; /* paranoia */ + + mp = mount_points; + while ((mtent = getfsent()) != NULL) { + if (stat(mtent->fs_file, &sbuf) < 0) + continue; + mp->mp_fsname = strdup(mtent->fs_file); + mp->mp_dev = sbuf.st_dev; + num_mount_points++, mp++; + if (num_mount_points > MAXUFSMOUNTED) { + logit(0,"Grrr.. too many mounted file systems for build_mount_table"); + break; + } + } + endfsent(); + + if (num_mount_points == 0 && DBOSI) + logit(0,"No mount points can be found"); + + return(0); +} + +#else USEBSDQUOTA + +/* + * find the block special device... + * try updating mount point table if possible + * + * returns name if it can, null o.w. + * + */ +private char * +find_mount_spec(dev) +{ + struct mount_points *mp; + struct mntent *mtent; + struct stat sbuf; + FILE *mt; + char *strdup(); + + if ((mp = find_mount_spec_intable(dev)) != NULL) /* try once */ + return(mp->mp_fsname); + + if (num_mount_points < MAXUFSMOUNTED) { + /* check to see if mounted */ + if ((mt = setmntent("/etc/mtab", "r")) == NULL) { + if (DBOSI) + logit(0,"/etc/mtab is read protected or nonexistant"); + return(NULL); + } + mp = NULL; + while ((mtent = getmntent(mt)) != NULL) { + if (stat(mtent->mnt_fsname, &sbuf) < 0) + continue; + if (dev != sbuf.st_rdev) + continue; + mp = mount_points+num_mount_points; + num_mount_points++; + mp->mp_fsname = strdup(mtent->mnt_fsname); + mp->mp_dev = sbuf.st_rdev; + break; + } + endmntent(mt); + } else { + /* table would overflow, try rebuilding */ + if (build_mount_table() < 0) /* try rebuilding table */ + return(NULL); + mp = find_mount_spec_intable(dev); + } + if (mp) + return(mp->mp_fsname); + + if (DBOSI) + logit(0,"Cannot find file system on device (%d,%d)\n", + major(dev), minor(dev)); + + return(NULL); /* total failure */ +} + +/* + * build a table of the various mounted file systems + * using getmntent. We want to use /etc/mtab, but + * if we can't, then we use /etc/fstab to get some + * information anyway if this is the first time around (to + * prevent constant rereads since /etc/fstab is pretty constant) + * + */ +int +build_mount_table() +{ + FILE *mt; + struct stat sbuf; + struct mntent *mtent; + struct mount_points *mp; + char *strdup(); + + if ((mt = setmntent("/etc/mtab", "r")) == NULL) { + if (DBOSI) + logit(0,"/etc/mtab is read protected or nonexistant"); + if (num_mount_points != 0) + return(-1); + if ((mt = setmntent("/etc/fstab", "r")) == NULL) { + if (DBOSI) + logit(0,"/etc/fstab is read protected or nonexistant"); + return(-1); + } + } + + /* free old info */ + if (num_mount_points) { + for (mp = mount_points ; num_mount_points > 0; num_mount_points--, mp++) + if (mp->mp_fsname) + free(mp->mp_fsname); + } + num_mount_points = 0; /* paranoia */ + + mp = mount_points; + while ((mtent = getmntent(mt)) != NULL) { + if (stat(mtent->mnt_fsname, &sbuf) < 0) + continue; + mp->mp_fsname = strdup(mtent->mnt_fsname); + mp->mp_dev = sbuf.st_rdev; + num_mount_points++, mp++; + if (num_mount_points > MAXUFSMOUNTED) { + logit(0,"Grrr.. too many mounted file systems for build_mount_table"); + break; + } + } + endmntent(mt); + + if (num_mount_points == 0 && DBOSI) + logit(0,"No mount points can be found"); + + return(0); +} + +/* + * SunOS doesn't use the Melbourne quota system - it has its own + * private one that uses quotactl instead of quota. We emulate + * quota here... + * + */ +int +#ifdef gould +gquota(cmd, uid, arg, addr) +#else gould +#ifdef encore +equota(cmd, uid, arg, addr) +#else encore +quota(cmd, uid, arg, addr) +#endif encore +#endif gould +int cmd, uid, arg; +caddr_t addr; +{ + char *fsname; + private char *find_mount_spec(); + + switch (cmd) { + case Q_QUOTAON: + case Q_QUOTAOFF: + case Q_SETQUOTA: + case Q_GETQUOTA: + case Q_SETQLIM: + case Q_SYNC: + break; + default: + errno = EINVAL; + return(-1); + } + + if ((fsname = find_mount_spec(arg)) == NULL) { + errno = EPERM; + return(-1); + } +#ifdef gould + /* oh dear, whose idea was this ? */ + return(quota(cmd, fsname, uid, addr)); +#else gould + return(quotactl(cmd, fsname, uid, addr)); +#endif gould +} +#endif USEBSDQUOTA +#endif USESUNQUOTA || USEBSDQUOTA + +export OSErr +OSCopyFile(spdir,sfile,dpdir,dfile) +IDirP spdir,dpdir; /* source and destination parents */ +char *sfile,*dfile; /* source and destination file names */ +{ + char s_path[MAXPATHLEN]; + char d_path[MAXPATHLEN]; + char spath[MAXPATHLEN]; + char dpath[MAXPATHLEN]; + struct stat stb; + int mo; + int err; + + OSfname(s_path,spdir,sfile,F_DATA); /* create unix style name for data */ + OSfname(d_path,dpdir,dfile,F_DATA); /* same for destination */ +#ifdef NOCASEMATCH + noCaseMatch(s_path); + noCaseMatch(d_path); +#endif NOCASEMATCH + + if (DBOSI) + printf("OSCopyFile: %s -> %s\n",s_path,d_path); + + err = unix_stat(d_path,&stb); /* see if destination exists... */ + if (err == noErr) /* yes... it does */ + return(aeObjectExists); /* return error... */ + + /* get info on parent of destination */ + if ((err = unix_stat(pathstr(dpdir), &stb)) != noErr) + return(err); + mo = filemode(stb.st_mode, stb.st_uid, stb.st_gid); + err = os_copy(s_path,d_path, mo); + + if (err != noErr && DBOSI) + printf("OSCopyFile: DATA copy failed %s\n",afperr(err)); + + if (err != noErr) + return(err); + + strcpy(spath,s_path); + toResFork(spath,sfile); /* create unix style name for rsrc */ + strcpy(dpath,d_path); + toResFork(dpath,dfile); /* same for destination */ + err = os_copy(spath,dpath,mo); /* do the copy... */ + /* allow object not found */ + if (err != noErr && err != aeObjectNotFound) { /* if failure.... */ + if (DBOSI) + printf("OSCopyFile: RSRC copy failed %s\n",afperr(err)); + (void) os_delete(dpdir,dfile,F_DATA); /* cleanup dest files */ + return(err); + } + + strcpy(spath,s_path); + toFinderInfo(spath,sfile); /* create unix style name for fndr */ + strcpy(dpath,d_path); + toFinderInfo(dpath,dfile); /* same for destination */ + err = os_copy(spath,dpath,mo); /* do the copy... */ + /* allow object not found */ + if (err != noErr && err != aeObjectNotFound) { + if (DBOSI) + printf("OSCopyFile: FNDR copy failed %s\n",afperr(err)); + (void) os_delete(dpdir,dfile,F_DATA); /* cleanup dest files */ + (void) os_delete(dpdir,dfile,F_RSRC); /* .... */ + return(err); + } + OSSetMacFileName(dpdir, dfile); + + FModified(dpdir, dfile); /* mark as modified */ +#ifdef notdef + EModified(dpdir); /* destination dir is modified */ +#endif + return(noErr); +} + +/* + * OSErr os_copy(char *from, char *to, mo) + * + * Copy the file from, to the file to. If "to" already exists then + * overwrite. File is created with mode "mo". + * + * Should probably lock the file! + * + */ + +private OSErr +os_copy(from, to, mo) +char *from,*to; +{ + int sfd,dfd,err; + char iobuf[IOBSIZE]; + struct stat sstb; + struct stat dstb; + int i; + + if ((err = unix_stat(from,&sstb)) != noErr) + return(err); + + if (S_ISDIR(sstb.st_mode)) { /* dirs not allowed... */ + printf("os_copy: source is directory\n"); + return(aeObjectTypeErr); + } + + if ((err=unix_open(from,0,&sfd)) != noErr) /* open source file */ + return(err); + +#ifdef APPLICATION_MANAGER + { + extern int fdplist[NOFILE]; + extern struct flist *applist; + + if (applist != NULL && fdplist[sfd] == 1) { + /* we want Finder copy protection */ + (void) unix_close(sfd); + return(aeAccessDenied); + } + } +#endif APPLICATION_MANAGER + + err = unix_stat(to,&dstb); /* check on destination */ + if (err == noErr) { /* file is there */ + if (sstb.st_dev == dstb.st_dev && sstb.st_ino == dstb.st_ino) { + if (DBOSI) + printf("os_copy: cannot copy to self\n"); + unix_close(sfd); + return(aeParamErr); + } + } /* else ignore error from stat */ + + err = unix_createo(to,TRUE,mo,&dfd); + if (err != noErr) { + printf("os_copy; create failed\n"); + (void) unix_close(sfd); + if (err == aeObjectNotFound) /* no destination? */ + err = aeParamErr; /* then return this */ + return(err); + } + + /* copy loop */ + for (i=0;;i++) { + register int len; + + len = read(sfd,iobuf,IOBSIZE); + if (len == 0) + break; + + if (len < 0) { + printf("os_copy: error during read\n"); + (void) unix_close(sfd); + (void) unix_close(dfd); + return(aeParamErr); /* disk error */ + } + + if (write(dfd,iobuf,len) != len) { + err = errno; /* preserve error code */ + if (DBOSI) + printf("os_copy: error on write %s\n",syserr()); + (void) unix_close(sfd); + (void) unix_close(dfd); + return(ItoEErr(err)); + } + if (i % 5) + abSleep(0, TRUE); + } + (void) unix_close(sfd); + (void) unix_close(dfd); + return(noErr); +} + +export OSErr +OSCreateFile(pdir,file,delf) +IDirP pdir; +char *file; +int delf; /* if want to delete existing file */ +{ + char p_ath[MAXPATHLEN]; + char path[MAXPATHLEN]; + int err,derr,rerr,cerr,mo; + struct stat stb; + + OSfname(p_ath,pdir,file,F_DATA); /* create data file name */ +#ifdef NOCASEMATCH + noCaseMatch(p_ath); +#endif NOCASEMATCH + + if (DBOSI) + printf("OSCreateFile: creating %s with %s\n",p_ath, + (delf) ? "OverWrite" : "No OverWrite"); + + err = unix_stat(pathstr(pdir),&stb); + if (err != noErr) + return(err); + mo = filemode(stb.st_mode, stb.st_uid, stb.st_gid); + + /* should never get aeObjectExists if delf was true */ + derr = unix_create(p_ath,delf,mo); /* using user delete flag */ + if (derr != noErr && derr != aeObjectExists) { + if (DBOSI) + printf("OSCreateFile: DATA fork create failed\n"); + /* previously under a conditional on delf, but not necessary */ + /* anymore because we don't get here if the object was already there */ + cerr = unix_unlink(p_ath); /* clean up... */ + if (cerr != noErr && DBOSI) + printf("OSCreateFile: cleanup failed\n"); + return(derr); + } + + strcpy(path,p_ath); + toResFork(path,file); /* try creating resource fork */ + rerr = unix_create(path,delf,mo); /* ... */ + if (rerr != noErr && rerr != aeObjectExists && rerr != aeObjectNotFound) { + if (DBOSI) + printf("OSCreateFile: RSRC create failed\n"); + /* previously under a conditional on delf, but not necessary */ + /* anymore because we don't get here if the object was already there */ + cerr = unix_unlink(path); /* clean up... */ + if (cerr != noErr && DBOSI) + printf("OSCreateFile: cleanup failed\n"); + /* should we clean up data fork? */ + return(rerr); + } + + strcpy(path,p_ath); + toFinderInfo(path,file); /* create finder fork */ + err = unix_create(path,delf,mo); + /* ignore error here - exactly what should be done? */ + + /* at this point, each had better be: aeObjectExists or noErr */ + if (rerr == aeObjectExists || derr == aeObjectExists) + return(aeObjectExists); + EModified(pdir); + return(noErr); +} + + +export OSErr +OSOpenFork(pdir,file,mode,typ,fhdl) +IDirP pdir; /* parent directory */ +char *file; +word mode; +int typ,*fhdl; +{ + register int i; + char path[MAXPATHLEN]; + char *ms; + int mo; + word attr; + extern int sessvers; +#ifdef DENYREADWRITE + int getAccessDenyMode(); + int setAccessDenyMode(); + int accessConflict(); + int cadm; +#endif DENYREADWRITE + + /* new for AFP2.0 */ + OSGetAttr(pdir,file,&attr); + if ((mode & OFK_MWR) && (attr & FPA_WRI)) + return((sessvers == AFPVersion1DOT1) ? aeAccessDenied : aeObjectLocked); + + OSfname(path,pdir,file,typ); /* expand name */ + + if ((mode & ~(OFK_MRD|OFK_MWR)) != 0) + if (DBOSI) + printf("OSOpenFork: open mode bits are octal %o\n",mode); + + if ((mode & (OFK_MRD|OFK_MWR)) == (OFK_MRD|OFK_MWR)) { + ms = "Read/Write"; + mo = O_RDWR; + } else if (mode & OFK_MWR) { + ms = "Write"; + mo = O_WRONLY; + } else if (mode & OFK_MRD) { + ms = "Read"; + mo = O_RDONLY; + } + +#ifdef DENYREADWRITE + if (mo == O_WRONLY) + mo = O_RDWR; +#endif DENYREADWRITE + + /* This is a special case hack for use with System 7.0 */ + if (*file == 'T') { /* improve performance a little */ + if (strcmp(file, "Trash Can Usage Map") == 0) { + ms = "Read/Write"; + mo = O_RDWR; + } + } + + if (DBOSI) + printf("OSOpenFork: Opening %s for %s\n",path,ms); + +#ifdef NOCASEMATCH + if ((i = unix_open(path,mo,fhdl)) != noErr) { + noCaseFind(path); + i = unix_open(path,mo,fhdl); + } +#else NOCASEMATCH + i = unix_open(path,mo,fhdl); +#endif NOCASEMATCH + +#ifdef DENYREADWRITE + if (*fhdl >= 0) { + if ((cadm = getAccessDenyMode(path, *fhdl)) >= 0) { + if (accessConflict(cadm, mode)) { + unix_close(*fhdl); + return(aeDenyConflict); + } + } + setAccessDenyMode(path, *fhdl, mode); + } +#endif DENYREADWRITE + + if (i == noErr) { + attr |= ((typ == F_DATA) ? FPA_DAO : FPA_RAO); + OSSetAttr(pdir, file, attr); + } + + return(i); +} + +private char *guestname = NULL; + +export boolean +setguestid(nam) +char *nam; +{ + struct passwd *p; + if ((p = getpwnam(nam)) == NULL) { + logit(0,"Guest id %s NOT IN PASSWORD FILE",nam); + logit(0,"Guest id %s NOT IN PASSWORD FILE",nam); + return(FALSE); + } + if (p->pw_uid == 0) { + logit(0,"Guest id %s is a root id! NOT ALLOWED!", nam); + logit(0,"Guest id %s is a root id! NOT ALLOWED!", nam); + logit(0,"Guest id %s is a root id! NOT ALLOWED!", nam); + return(FALSE); + } + if (p->pw_gid == 0) { + logit(0,"Guest id %s is in group 0. BE WARNED!", nam); + logit(0,"Guest id %s is in group 0. BE WARNED!", nam); + logit(0,"Guest id %s is in group 0. BE WARNED!", nam); + } + logit(0,"Guest id is %s, uid %d, primary gid %d",nam, p->pw_uid, p->pw_gid); + guestname = nam; + return(TRUE); +} + +private boolean apasswdfile = FALSE; + +export boolean +setpasswdfile(pw) +char *pw; +{ + + if (desinit(0) < 0) { + logit(0,"error: no des routines, so no aufs password file used"); + return(FALSE); + } + apasswdfile = init_aufs_passwd(pw); + return(apasswdfile); +} + +/* + * check if user exists + * + * IE: check if user in file specified with -P option + * (not fatal if no file specified for DISTRIB_PASSWDS) + * + */ + +export OSErr +OSLoginRand(nam) +char *nam; +{ + OSErr err = aeParamErr; + +#ifdef DISTRIB_PASSWDS + char *cp, line[80]; + FILE *fp, *fopen(); + extern char *distribpassfile; + if (distribpassfile == NULL) + return(noErr); + if ((fp = fopen(distribpassfile, "r")) == NULL) + return(noErr); + err = aeUserNotAuth; + while (fgets(line, sizeof(line), fp) != NULL) { + if (line[0] == '#') + continue; + for (cp = line; *cp != '\0'; cp++) { + if (*cp == ' ' || *cp == '\t' || *cp == '\n') { + *cp = '\0'; + break; + } + } + if (strcmp(nam, line) == 0) { + err = noErr; + break; + } + } + fclose(fp); +#else /* DISTRIB_PASSWDS */ + if (is_aufs_user(nam)) + return(noErr); +#endif /* DISTRIB_PASSWDS */ + + return(err); +} + +#ifdef PERMISSIVE_USER_NAME +/* + * allow the specified user name to be + * from the gcos field of the passwd file + * IE: Chooser names don't have to be login names + */ +static struct passwd * +getpwgnam(nam) +unsigned char *nam; +{ + char *ptm; + char nom[40]; + char nomi[200]; + struct passwd *pw; + int match, i, j; + + /* map to lower case, translate some special characters */ + + for (i = 0 ; nam[i] ; i++) { + switch (nam[i]) { + case 0x8d: + nom[i] = 'c'; + break; + case 0x8e: + case 0x8f: + case 0x90: + case 0x91: + nom[i] = 'e'; + break; + case 0x92: + case 0x93: + case 0x94: + case 0x95: + nom[i] = 'i'; + break; + case 0x96: + nom[i] = 'n'; + break; + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + nom[i] = 'o'; + break; + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + nom[i] = 'u'; + break; + default: + if (isupper(nam[i])) + nom[i] = tolower(nam[i]); + else + nom[i] = nam[i]; + break; + } + } + nom[i] = '\0'; + setpwent(); + while ((pw = getpwent()) != 0) { + ptm = pw->pw_gecos; + for (i = 0 ; ptm && *ptm && (*ptm != ','); ptm++, i++) { + if (isupper(*ptm)) + nomi[i] = tolower(*ptm); + else + nomi[i] = *ptm; + } + nomi[i] = '\0'; + for (match=i=j=0 ; ((nom[j] != 0) && (nomi[i] != 0)) ; ) { + if (nomi[i] == nom[j]) { + if (match == 0) + match = i+1; + j++; + i++; + } else { + if (match != 0) { + i = match; + match = 0; + j = 0; + } else + i++; + } + } + if (nom[j] == '\0') { /* found it */ + endpwent(); + return(pw); + } + } + endpwent(); + return(NILPWD); +} +#endif PERMISSIVE_USER_NAME + +#ifdef DISTRIB_PASSWDS +struct afppass *afp = NULL; +#endif /* DISTRIB_PASSWDS */ + +/* + * validate user 'nam' using User Authentication Method 'uam' + * + */ + +export OSErr +OSLogin(nam,pwd,pwdother,uam) +char *nam,*pwd; +byte *pwdother; +int uam; +{ + struct passwd *p; + boolean safedebug; + byte encrypted[8]; /* 64 bits */ + byte passkey[8]; /* password is 8 bytes max */ + char *pass; + char *crypt(); +#ifdef ULTRIX_SECURITY + char *ultrix_crypt(); + char *crypted_password; +#endif ULTRIX_SECURITY +#ifdef DIGITAL_UNIX_SECURITY + char *bigcrypt(); + struct pr_passwd *pr; +#endif DIGITAL_UNIX_SECURITY +#ifdef LWSRV_AUFS_SECURITY + extern char *userlogindir; + int namlen; +#endif LWSRV_AUFS_SECURITY +#ifdef SHADOW_PASSWD + struct spwd *sp; + int pw_check; +#endif SHADOW_PASSWD +#ifdef RUTGERS + extern char *ru_crypt(); +#endif RUTGERS +#ifdef DISTRIB_PASSWDS + OSErr err; + void afpdp_encr(); + int afpdp_init(), afpdp_writ(); + struct afppass afpp, *afpdp_read(); +#endif /* DISTRIB_PASSWDS */ + extern int nousrvol; + + safedebug = (DBOSI || (getuid() != 0 && geteuid() != 0)); + + logit(0,"Login requested for %s (we are %srunning as root)", + (uam == UAM_ANON) ? "" : nam, + (getuid() == 0 || geteuid() == 0) ? "" : "not "); + +#ifdef LWSRV_AUFS_SECURITY + bin=malloc(strlen(nam)+1); + strcpy(bin,nam); + + if ((tempbin = rindex(nam,':')) != NULL) + *tempbin='\0'; +#endif LWSRV_AUFS_SECURITY + + guestlogin = 0; + + switch (uam) { + case UAM_RANDNUM: +#ifndef DISTRIB_PASSWDS + /* + * 'Randnum Exchange' UAM using lookaside password file + * + */ + if (!apasswdfile) + return(aeBadUAM); + if ((pass = user_aufs_passwd(nam)) == NULL) { + logit(0, "Login: entry %s not found in password file", nam); + return(aeUserNotAuth); + } + bzero(passkey,sizeof(passkey)); /* make sure zero */ + strncpy((char *)passkey, pass, 8); +#ifdef SUNOS4_FASTDES + des_setparity(passkey); + /* copy the data to be encrypted */ + bcopy(pwdother, encrypted, sizeof(encrypted)); + ecb_crypt(passkey,encrypted,sizeof(encrypted),DES_ENCRYPT|DES_SW); +#else SUNOS4_FASTDES + dessetkey(passkey); + /* copy the data to be encrypted */ + bcopy(pwdother, encrypted, sizeof(encrypted)); + endes(encrypted); +#endif SUNOS4_FASTDES + if (bcmp(encrypted, pwd, 8) != 0) { + logit(0, "Login: encryption failed for user %s", nam); + return(aeUserNotAuth); + } + if ((p = aufs_unix_user(nam)) == NULL) { + logit(0, "Login: no UNIX user %s", nam); + return(aeUserNotAuth); + } + usrgid = p->pw_gid; + usruid = p->pw_uid; + usrnam = (char *)malloc(strlen(p->pw_name)+1); + strcpy(usrnam,p->pw_name); + usrdir = (char *)malloc(strlen(p->pw_dir)+1); + strcpy(usrdir,p->pw_dir); + break; +#else /* DISTRIB_PASSWDS */ + case UAM_2WAYRAND: + /* + * 'Randnum Exchange' or '2-Way Randnum exchange' + * UAM using distributed passwords (in ~/.afppass) + * + */ + err = aeUserNotAuth; + if ((p = (struct passwd *)getpwnam(nam)) != NILPWD) { + if (afpdp_init(AFP_DISTPW_FILE) >= 0) { + if ((afp = afpdp_read(nam, p->pw_uid, p->pw_dir)) != NULL) { + bcopy(pwdother, encrypted, sizeof(encrypted)); + if (uam == UAM_RANDNUM) { + desinit(0); + dessetkey(afp->afp_password); + endes(encrypted); + desdone(); + } else /* use key-shifted DES code */ + afpdp_encr(encrypted, afp->afp_password, NULL); + /* compare encrytpted passwords */ + if (bcmp(encrypted, pwd, 8) == 0) + err = noErr; + /* enforce & count failed login attempts */ + if (afp->afp_numattempt >= afp->afp_maxattempt + && afp->afp_maxattempt > 0) + err = aeParamErr; + if (err != noErr) { + if (afp->afp_numattempt < 255) + afp->afp_numattempt++; + } else + afp->afp_numattempt = 0; + /* update user password file (using copy of structure) */ + bcopy((char *)afp, (char *)&afpp, sizeof(struct afppass)); + (void)afpdp_writ(nam, p->pw_uid, p->pw_dir, &afpp); + } + } + } + if (err != noErr) { + logit(0, "Login failed for %s (%s)", nam, afperr(err)); + return(err); + } + /* save details */ + usrgid = p->pw_gid; + usruid = p->pw_uid; + usrnam = (char *)malloc(strlen(p->pw_name)+1); + strcpy(usrnam,p->pw_name); + usrdir = (char *)malloc(strlen(p->pw_dir)+1); + strcpy(usrdir,p->pw_dir); + break; +#endif /* DISTRIB_PASSWDS */ + case UAM_ANON: + /* + * 'No User Authent' UAM + * + */ + if (guestname == NULL) + return(aeParamErr); + p = (struct passwd *)getpwnam(guestname); + if (p == NILPWD) { + logit(0, "Login: guest user not valid %s", guestname); + return(aeParamErr); /* unknown user */ + } + usrgid = p->pw_gid; + usruid = p->pw_uid; + usrnam = (char *)malloc(strlen(guestname)+1); + strcpy(usrnam,guestname); + guestlogin = 1; + usrdir = NULL; + break; + case UAM_CLEAR: + /* + * 'Cleartxt Passwrd' UAM + * + */ + if (!apasswdfile) { + p = (struct passwd *)getpwnam(nam); /* user name */ + if (p == NILPWD) { + logit(0, "Login: user name %s NOT found in password file", nam); +#ifdef PERMISSIVE_USER_NAME + if ((p = (struct passwd *)getpwgnam(nam)) != NILPWD) /* gcos name */ + logit(0, "Login: mapping \"%s\" to login name %s", nam, p->pw_name); +#endif PERMISSIVE_USER_NAME + } else { + logit(0, "Login: user %s found, real name is %s", nam, p->pw_gecos); + } + if (p == NILPWD) { + logit(0, "Login: Unknown user %s", nam); + return(aeParamErr); /* unknown user */ + } + if (strlen(pwd) <= 0) { + logit(0, "Login: NULL password access denied for %s", nam); + return(aeUserNotAuth); /* null user passwords not allowed */ + } +#ifdef SHADOW_PASSWD + if (shadow_flag) { + sp = (struct spwd *)getspnam(p->pw_name); /* get shadow info */ + if (sp == NILSPWD) { + logit(0, "Login: user %s NOT found in shadow file", p->pw_name); + return(aeParamErr); /* unknown user */ + } else { + logit(0, "Login: user %s found in shadow password file", p->pw_name); + } + endspent(); + } +#else SHADOW_PASSWD + /* cope with some adjunct password file schemes */ + if (strlen(p->pw_passwd) <= 0 || strlen(pwd) <= 0) { + logit(0, "Login: NULL password access denied for %s", nam); + return(aeUserNotAuth); + } +#endif SHADOW_PASSWD +#ifdef ULTRIX_SECURITY + /* avoid evaluation order problem */ + crypted_password = ultrix_crypt(pwd, p); + if (strcmp(crypted_password, p->pw_passwd) != 0) { +#else ULTRIX_SECURITY +# ifdef DIGITAL_UNIX_SECURITY + pr = getprpwnam(nam); + if (pr == NULL || strcmp(bigcrypt(pwd, pr->ufld.fd_encrypt), + pr->ufld.fd_encrypt) != 0) { +# else DIGITAL_UNIX_SECURITY +# ifndef SHADOW_PASSWD +# ifdef RUTGERS + if (strcmp(ru_crypt(pwd,p->pw_passwd,p->pw_uid,p->pw_name), + p->pw_passwd) != 0) { +# else RUTGERS + if (strcmp(crypt(pwd,p->pw_passwd),p->pw_passwd) != 0) { +# endif RUTGERS +# else SHADOW_PASSWD + pw_check = (shadow_flag) ? +# ifdef RUTGERS + strcmp(ru_crypt(pwd,sp->sp_pwdp,p->pw_uid,p->pw_name),sp->sp_pwdp) : + strcmp(ru_crypt(pwd,p->pw_passwd,p->pw_uid,p->pw_name),p->pw_passwd); +# else RUTGERS + strcmp(crypt(pwd,sp->sp_pwdp),sp->sp_pwdp) : + strcmp(crypt(pwd,p->pw_passwd),p->pw_passwd); +# endif RUTGERS + if (pw_check) { +# endif SHADOW_PASSWD +# endif DIGITAL_UNIX_SECURITY +#endif ULTRIX_SECURITY + logit(0, "Login: Incorrect password for user %s", nam); + if (!safedebug) + return(aeUserNotAuth); + } + } else { + if ((p = aufs_unix_user(nam)) == NULL) + return(aeUserNotAuth); + if ((pass = user_aufs_passwd(nam)) == NULL) + return(aeUserNotAuth); + if (strcmp(pass,pwd) != 0) + return(aeUserNotAuth); + } + usrgid = p->pw_gid; + usruid = p->pw_uid; + usrnam = (char *)malloc(strlen(p->pw_name)+1); + strcpy(usrnam,p->pw_name); + usrdir = (char *)malloc(strlen(p->pw_dir)+1); + strcpy(usrdir,p->pw_dir); + break; + } + +#ifdef ADMIN_GRP + if (uam != UAM_ANON) { + struct group *grps; + if ((grps = getgrnam(ADMIN_GRP)) != NULL) { + while (*(grps->gr_mem) != NULL) { + if (strcmp(p->pw_name, *grps->gr_mem) == 0) { + logit(0, "User %s has admin privs, logging in as superuser.", + p->pw_name); + usrgid = grps->gr_gid; + usruid = 0; + break; + } + *(grps->gr_mem)++; + } + } + } +#endif /* ADMIN_GRP */ + +#ifdef LOG_WTMP + /* + * write a 'wtmp' entry for user name, address & time (then we + * write a null entry to terminate, since wtmp is often set to + * mode 0644 and we're not running as root when we disconnect). + * + * This is unduly complex for the end result + * + */ +#ifdef LOG_WTMPX +# ifdef WTMPX_FILE +# undef WTMP_FILE +# define WTMP_FILE WTMPX_FILE +# define ut_time ut_xtime +# define utmp utmpx +# endif /* WTMPX_FILE */ +#endif /* LOG_WTMPX */ + { extern AddrBlock addr; + struct utmp ut; + int fd; + + bzero((char *)&ut, sizeof(struct utmp)); + +#if defined(WTMP_FILE) && defined(USER_PROCESS) +# define utusername ut.ut_user + ut.ut_type = USER_PROCESS; + ut.ut_id[0] = 'a'; + ut.ut_id[1] = 'f'; + ut.ut_id[2] = 'p'; + ut.ut_id[3] = '\0'; +#else /* WTMP_FILE && USER_PROCESS */ +# define utusername ut.ut_name +# ifdef _PATH_WTMP +# define WTMP_FILE _PATH_WTMP +# else /* _PATH_WTMP */ +# define WTMP_FILE "/var/adm/wtmp" +# endif /* _PATH_WTMP */ +#endif /* WTMP_FILE && USER_PROCESS */ + +#ifdef LOG_WTMP_FILE +#undef WTMP_FILE +#define WTMP_FILE LOG_WTMP_FILE +#endif /* LOG_WTMP_FILE */ + + (void)sprintf(ut.ut_host, "%d.%d.%d", + ntohs(addr.net), addr.node, addr.skt); + (void)strncpy(ut.ut_line, "aufs", sizeof(ut.ut_line)); + (void)strncpy(utusername, usrnam, sizeof(utusername)); + ut.ut_time = time((time_t *)0); +#ifdef LOG_WTMPX + (void)updwtmpx(WTMP_FILE, &ut); + ut.ut_host[0] = '\0'; /* null hostname */ + utusername[0] = '\0'; /* null username */ + (void)updwtmpx(WTMP_FILE, &ut); +#else /* LOG_WTMPX */ + if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) >= 0) { + (void)write(fd, (char *)&ut, sizeof(struct utmp)); + ut.ut_host[0] = '\0'; /* null hostname */ + utusername[0] = '\0'; /* null username */ + (void)write(fd, (char *)&ut, sizeof(struct utmp)); + (void)close(fd); + } +#endif /* LOG_WTMPX */ + } +#endif /* LOG_WTMP */ + +#ifdef LWSRV_AUFS_SECURITY + /* budd... */ + if( userlogindir != NULL ) { /* need to save user logins? */ + extern AddrBlock addr; /* is this valid now?? seems to be! */ + char fname[ 100 ]; + FILE *f; + +#ifdef HIDE_LWSEC_FILE + if (hideLWSec(fname, userlogindir, usruid, usrgid, addr) < 0) { + logit(0, "OSLogin: error in hideLWSec() for %s", fname); + return(aeMiscErr); + } +#else HIDE_LWSEC_FILE + /* create file before setuid call so we can write in directory. */ + make_userlogin( fname, userlogindir, addr ); +#endif HIDE_LWSEC_FILE + + if( (f = fopen( fname, "w" )) != NULL ) { /* sigh. leaves race. */ + logit(0, "writing user %s into auth-file for %s", usrnam, bin); + fprintf( f, "%s\n", usrnam ); /* perhaps write temp */ + fclose( f ); /* and rename? */ + /* sigh. fchown and fchmod are BSDisms */ + chmod( fname, 0644 ); /* make owned by user so they */ + chown( fname, usruid, -1 ); /* can truncate it on exit!! */ + } /* fopen ok */ + else + logit(0,"Login: could not create %s: %s", fname, syserr() ); + } /* userlogindir and not guest */ + /* ...budd */ +#endif LWSRV_AUFS_SECURITY + + if (!safedebug && setgid(usrgid) != 0) { + logit(0,"Login: setgid failed for %s because %s",nam,syserr()); + return(aeUserNotAuth); + } +#ifdef RUTGERS + if ((getuid() == 0 || geteuid() == 0) && ru_initgroups(usrnam, usrgid) < 0) +#else RUTGERS + if ((getuid() == 0 || geteuid() == 0) && initgroups(usrnam, usrgid) < 0) +#endif RUTGERS + logit(0,"OSLogin: initgroups failed for %s!: reason: %s",syserr(),usrnam); + if ((ngroups = dogetgroups()) < 0) { + logit(0,"OSLogin: getgroups failed for %s!: reason: %s",syserr(), usrnam); + ngroups = 0; + } + + if (!safedebug && setuid(usruid) != 0) { + logit(0,"Login: setuid failed for %s because %s",nam,syserr()); + return(aeUserNotAuth); /* or something */ + } + + logit(0,"Login: user %s, home directory %s", + usrnam, usrdir == NULL ? "none" : usrdir); + + if ((usrdir != NULL) && (nousrvol != TRUE)) + VInit(usrnam,usrdir); /* initialize volume stuff */ + +#ifdef USR_FILE_TYPES + { + char uftpath[MAXPATHLEN]; + extern char *uftfilename; + + uft_init(); /* initialize */ + + /* ~user UFT file */ + if (usrdir != NULL) { + sprintf(uftpath, "%s/%s", usrdir, TYPFILE); + if (access(uftpath, R_OK) == 0) { + read_uft(uftpath); /* ~/afpfile */ + } else { + sprintf(uftpath, "%s/%s", usrdir, TYPFILE1); + if (access(uftpath, R_OK) == 0) + read_uft(uftpath); /* ~/.afpfile */ + } + } + /* global UFT file */ + if (uftfilename != NULL) + if (access(uftfilename, R_OK) == 0) + read_uft(uftfilename); + } +#endif USR_FILE_TYPES + + return(noErr); +} + +/* + * change user password. + * + */ + +OSChangePassword(nam, pwdold, pwdnew, uam) +char *nam; +byte *pwdold; +byte *pwdnew; +int uam; +{ + switch (uam) { + case UAM_CLEAR: + case UAM_2WAYRAND: + return(aeBadUAM); + break; + case UAM_RANDNUM: +#ifdef DISTRIB_PASSWDS + { struct passwd *p; + struct afppass *afpdp_read(); + int afpdp_writ(), afpdp_pwex(); + void afpdp_decr(), afpdp_upex(); + + /* must be an existing UNIX user */ + if ((p = (struct passwd *)getpwnam(nam)) == NILPWD) + return(aeUserNotAuth); + /* grab user password details */ + if ((afp = afpdp_read(nam, p->pw_uid, p->pw_dir)) == NULL) + return(aeUserNotAuth); + /* enforce different passwords */ + if (bcmp(pwdold, pwdnew, 8) == 0) + return(aePwdSameErr); + desinit(0); + /* use current password to decrypt new */ + dessetkey(afp->afp_password); + dedes(pwdnew); + /* use new password to decrypt old */ + dessetkey(pwdnew); + dedes(pwdold); + desdone(); + /* old password OK ? */ + if (strncmp((char *)afp->afp_password, (char *)pwdold, + strlen((char *)afp->afp_password)) != 0) + return(aeUserNotAuth); + /* enforce password length control */ + if (strlen((char *)pwdnew) < afp->afp_minmpwlen) + return(aePwdTooShort); + /* global expiry date, user changes not allowed */ + if (afpdp_pwex(afp) < 0) + return(aeAccessDenied); + /* update structure & write */ + afpdp_upex(afp); + afp->afp_numattempt = 0; + bcopy((char *)pwdnew, (char *)afp->afp_password, 8); + if (afpdp_writ(nam, p->pw_uid, p->pw_dir, afp) < 0) + return(aeUserNotAuth); + return(noErr); + } +#else /* DISTRIB_PASSWDS */ + return(aeBadUAM); +#endif /* DISTRIB_PASSWDS */ + break; + } + + return(aeBadUAM); +} + +/* + * OSErr OSExchangeFiles(IDirP apdir, char *afile, IDirP apdir, char *bfile) + * + * OSExchangeFiles swaps the data and resource forks but not the finder info + * of two files. + * + * Inputs: + * apdir parent directory id of one file. + * afile name of second file. + * bpdir parent directory id of second file. + * bfile name of second file. + * + * Outputs: + * OSErr Function result. + * + * Error recovery during renaming process is problematic ... + * + */ + +export OSErr +OSExchangeFiles(apdir,afile,bpdir,bfile) +IDirP apdir,bpdir; /* parent dirs */ +char *afile, *bfile; /* file names */ +{ + char a_path[MAXPATHLEN], b_path[MAXPATHLEN], t_path[MAXPATHLEN]; + char apath[MAXPATHLEN], bpath[MAXPATHLEN], tpath[MAXPATHLEN]; + char *temp = ".tXXX"; + int err, cerr, amo, bmo; + extern int sessvers; + struct stat stb; + word attr; + + /* + * either file rename-inhibited ? + * + */ + OSGetAttr(apdir, afile, &attr); + if (attr & FPA_RNI) + return((sessvers == AFPVersion1DOT1) ? aeAccessDenied : aeObjectLocked); + + OSGetAttr(bpdir, bfile, &attr); + if (attr & FPA_RNI) + return((sessvers == AFPVersion1DOT1) ? aeAccessDenied : aeObjectLocked); + + OSfname(a_path, apdir, afile, F_DATA); /* build A data file name */ + OSfname(b_path, bpdir, bfile, F_DATA); /* same for B file */ + OSfname(t_path, apdir, temp, F_DATA); /* same for tmp file */ + +#ifdef NOCASEMATCH + noCaseMatch(a_path); + noCaseMatch(b_path); +#endif NOCASEMATCH + + if (DBOSI) + printf("OSExchangeFiles A=%s, B=%s\n", a_path, b_path); + + /* + * can't exchange a file with itself + * + */ + if (strcmp(a_path, b_path) == 0) + return(aeSameObjectErr); + + /* + * Not allowed if one doesn't have a resource directory + * and the other does. + * + */ + if ((apdir->flags & DID_RESOURCE) != (bpdir->flags & DID_RESOURCE)) + return(aeParamErr); + + /* + * get info on existing files so we can set them back afterwards + * + */ + if ((err = unix_stat(a_path, &stb)) != noErr) + return(err); + amo = filemode(stb.st_mode, stb.st_uid, stb.st_gid); + + if ((err = unix_stat(b_path, &stb)) != noErr) + return(err); + bmo = filemode(stb.st_mode, stb.st_uid, stb.st_gid); + + /* + * build resource file names + * + */ + if (apdir->flags & DID_RESOURCE) { + strcpy(apath, a_path); + strcpy(bpath, b_path); + strcpy(tpath, t_path); + toResFork(apath, afile); + toResFork(bpath, bfile); + toResFork(tpath, temp); + } + + /* + * First: Rename the A data and resource forks as a temporary. + * + */ + err = unix_rename(a_path, t_path); + if (err != noErr) /* if an error on data files */ + return(err); /* then give up */ + + if (apdir->flags & DID_RESOURCE) { + err = unix_rename(apath, tpath); + /* allow non-existant resource */ + if (err != noErr && err != aeObjectNotFound) { /* error on rename? */ + if (DBOSI) + printf("os_rename: failed %s for %s -> %s\n", + afperr(err), apath, tpath); + cerr = unix_rename(t_path, a_path); /* rename data back to original */ + if (cerr != noErr && DBOSI) + printf("os_rename: cleanup failed\n"); + unix_chmod(b_path, bmo); /* file:try to reset protection */ + return(err); + } + } + + /* + * Second: Rename the B file as A. + * + */ + err = unix_rename(b_path, a_path); + if (err != noErr) { + /* put A back as it was */ + unix_rename(t_path, a_path); + unix_rename(tpath, apath); + return(err); + } + + if (apdir->flags & DID_RESOURCE) { + err = unix_rename(bpath, apath); + /* allow non-existant resource */ + if (err != noErr && err != aeObjectNotFound) { /* error on rename? */ + if (DBOSI) + printf("os_rename: failed %s for %s -> %s\n", + afperr(err), bpath, apath); + cerr = unix_rename(a_path, b_path); /* rename data back to original */ + if (cerr != noErr && DBOSI) + printf("os_rename: cleanup failed\n"); + unix_chmod(b_path, bmo); /* file:try to reset protection */ + /* put A back as it was */ + unix_rename(t_path, a_path); + unix_rename(tpath, apath); + return(err); + } + } + + /* + * Third: Rename the T file as B. + * + */ + err = unix_rename(t_path, b_path); + if (err != noErr) { + /* put B back as it was */ + unix_rename(a_path, b_path); + unix_rename(apath, bpath); + /* put A back as it was */ + unix_rename(t_path, a_path); + unix_rename(tpath, apath); + return(err); + } + + if (apdir->flags & DID_RESOURCE) { + err = unix_rename(tpath, bpath); + /* allow non-existant resource */ + if (err != noErr && err != aeObjectNotFound) { /* error on rename? */ + if (DBOSI) + printf("os_rename: failed %s for %s -> %s\n", + afperr(err), tpath, bpath); + cerr = unix_rename(b_path, t_path); /* rename data back to original */ + if (cerr != noErr && DBOSI) + printf("os_rename: cleanup failed\n"); + unix_chmod(t_path, bmo); /* file:try to reset protection */ + /* put B back as it was */ + unix_rename(a_path, b_path); + unix_rename(apath, bpath); + /* put A back as it was */ + unix_rename(t_path, a_path); + unix_rename(tpath, apath); + return(err); + } + } + + FModified(apdir, afile); /* does an emodified */ + FModified(bpdir, bfile); /* does an emodified */ + + return(noErr); +} + +export word +OSRandom() +{ + static time_t t = 0; + + if (t == 0) { + time(&t); +#ifdef USERAND + srand(t); +#else + srandom(t); +#endif + } +#ifdef USERAND + return((word) rand()); +#else + return((word) random()); +#endif +} + +sdword +CurTime() +{ + return(time(0)); +} + +/* + * char *tilde(char *s) + * + * Expands a path starting with tilde, the same as the shell. + * Returns the expanded path. + * + */ +export char * +tilde(s) +char *s; +{ + static char path[MAXPATHLEN]; + char *sp,*logdir(),*l; + + if (*s != '~') /* start with tilde? */ + return(s); /* no, return original */ + s++; /* skip over tilde */ + if (*s == '\0') /* if nothing more, return */ + return(usrdir); + + if (*s == '/') { /* case of ~/ */ + strcpy(path,usrdir); /* use user's dir */ + strcat(path,s); /* and then the remainder */ + return(path); /* return that */ + } + + if ((sp = index(s,'/')) == NULL) /* check for slash */ + return(logdir(s)); /* return ~john expanded */ + + *sp = '\0'; /* otherwise tie off ~bill/mac */ + if ((l = logdir(s)) == NULL) /* does the user exist? */ + return NULL; + strcpy(path,l); /* copy in the expanded ~bill */ + *sp = '/'; /* ... put back slash */ + strcat(path,sp); /* append the remainder */ + return(path); /* and return it */ +} + +export char * +logdir(user) +char *user; +{ + struct passwd *p; + + if (usrnam != NULL && strcmp(user,usrnam) == 0) + return(usrdir); /* already know logged in user dir */ + + p = (struct passwd *) getpwnam(user); + if (p != NILPWD) + return(p->pw_dir); + return(NULL); +} + +private OSErr +unix_rmdir(path) +char *path; +{ + if (DBUNX) + printf("unix_rmdir: path=%s\n",path); + + if (rmdir(path) == 0) /* and try to delete it */ + return(noErr); + + if (DBUNX) + printf("unix_rmdir: failed %s\n",syserr()); + + return(ItoEErr(errno)); +} + +private OSErr +unix_unlink(path) +char *path; +{ + if (DBUNX) + printf("unix_unlink: path=%s\n",path); + + if (unlink(path) == 0) /* remove the file */ + return(noErr); /* no error */ + + if (DBUNX) + printf("unix_unlink: failed %s\n",syserr()); + + return(ItoEErr(errno)); +} + +private OSErr +unix_rename(from,to) +char *from,*to; +{ + if (DBUNX) + printf("unix_rename: from %s to %s\n",from,to); + +#ifdef aux + if (strcmp(from, to) == 0) + return(noErr); +#endif aux + if (rename(from,to) == 0) + return(noErr); + +#ifdef XDEV_RENAME + return(xdev_rename(from,to)); +#else XDEV_RENAME + if (DBUNX) + printf("unix_rename: failed %s\n",syserr()); + return(ItoEErr(errno)); +#endif XDEV_RENAME +} + +#ifdef XDEV_RENAME +private OSErr +xdev_rename(from,to) +char *from, *to; +{ + struct stat fstb, tstb; + int err, mode, ffd, tfd; + + if (DBUNX) + printf("xdev_rename: from %s to %s\n",from,to); + + if ((err = unix_stat(from,&fstb)) != noErr) + return(ItoEErr(errno)); + + /* if "from" is a directory, recursively copy it */ + if (S_ISDIR(fstb.st_mode)) { +#ifdef USEDIRENT + struct dirent *dinfp, *readdir(); +#else USEDIRENT + struct direct *dinfp, *readdir(); +#endif USEDIRENT + char fname[MAXPATHLEN]; + char tname[MAXPATHLEN]; + char *fend, *tend; + DIR *dptr; + + if (DBUNX) + printf("xdev_rename: copying directory ...\n"); + + /* Create a destination directory with same owner, group */ + if ((err = unix_mkdir(to,fstb.st_mode)) != noErr) + return(err); + if ((err = unix_chown(to,fstb.st_uid,fstb.st_gid)) != noErr) + return(err); + + /* Read each item in the "from" dir and recurse to move it */ + if ((dptr = opendir(from)) == NULL) + return(ItoEErr(errno)); + + fend = fname + strlen(strcpy(fname,from)); + tend = tname + strlen(strcpy(tname,to)); + *fend++ = '/'; + *tend++ = '/'; + + for (dinfp = readdir(dptr); dinfp != NULL; dinfp = readdir(dptr)) { + if (*dinfp->d_name == '.') { + int namlen; +#if defined(USEDIRENT) && !defined(SOLARIS) + namlen = dinfp->d_namlen; +#else /* USEDIRENT */ + namlen = strlen(dinfp->d_name); +#endif /* USEDIRENT */ + if ((namlen == 1) || + ((namlen == 2) && (*(dinfp->d_name+1) == '.'))) + continue; + } + *fend = *tend = '\0'; + strcat(fname,dinfp->d_name); + strcat(tname,dinfp->d_name); + if ((err = xdev_rename(fname,tname)) != noErr) { + if (DBUNX) + printf("xdev_rename: copy failed %s\n",syserr()); + closedir(dptr); + return(err); + } + } + closedir(dptr); + + /* Finally, remove the directory */ + return(unix_rmdir(from)); + } else { + if ((err = os_copy(from,to,fstb.st_mode)) != noErr) + return(ItoEErr(err)); + + /* Remove the copied file */ + return(unix_unlink(from)); + } +} +#endif XDEV_RENAME + +private OSErr +unix_open(path,mode,fd) +char *path; +int mode; +int *fd; +{ + + *fd = open(path,mode); + +#ifdef APPLICATION_MANAGER + { + int lockn, protect; + extern int fdplist[NOFILE]; + extern struct flist *applist; + + /* don't check if we aren't read-only or open failed */ + if (applist != NULL && *fd >=0 && mode == O_RDONLY) { + if (wantLock(path, &lockn, &protect) == 0) { + if (enforceLock(*fd, lockn) == 0) { + if (DBUNX) + printf("unix_open: open refused for %s (O > %d)\n", path, lockn); + close(*fd); + return(aeLockErr); + } + if (protect == 1) /* protect from copying */ + fdplist[*fd] = 1; + } + } + } +#endif APPLICATION_MANAGER + + if (DBUNX) + printf("unix_open: fd=%d, mode=%d, path=%s\n",*fd,mode,path); + + if ((*fd) > 0) + return(noErr); + + if (DBUNX) + printf("unix_open: failed %s\n",syserr()); + + return(ItoEErr(errno)); +} + +private OSErr +unix_close(fd) +int fd; +{ + if (DBUNX) + printf("unix_close: fd=%d\n",fd); + +#ifdef APPLICATION_MANAGER + { + extern int fdplist[NOFILE]; + fdplist[fd] = -1; + } +#endif APPLICATION_MANAGER + +#ifdef DENYREADWRITE + { + struct accessMode *p, *q; + + p = accessMQueue; + q = (struct accessMode *)NULL; + + while (p != (struct accessMode *)NULL) { + if (p->fd == fd) { /* delete from Q */ + if (q == (struct accessMode *)NULL) + accessMQueue = p->next; + else + q->next = p->next; + free((char *)p); + break; + } + q = p; + p = p->next; + } + } +#endif DENYREADWRITE + + if (close(fd) == 0) + return(noErr); + + if (DBUNX) + printf("unix_close: failed %s\n",syserr()); + + return(ItoEErr(errno)); /* this would be a problem */ +} + +private OSErr +unix_mkdir(path,prot) +char *path; +int prot; /* protection */ +{ + if (DBUNX) + printf("unix_mkdir: path = %s\n",path); + + if (mkdir(path,prot) == 0) + return(noErr); + + if (DBUNX) + printf("unix_mkdir: failed %s\n",syserr()); + + return(ItoEErr(errno)); +} + +/* + * OSErr unix_create(char *path, int delf, int mode) + * + * Create a file. + * + */ + +private OSErr +unix_create(path,delf,mode) +char *path; +int delf; +int mode; +{ + int fd,flg; + + if (DBUNX) + printf("unix_create: delf=%d, mode=o%o, path=%s\n",delf,mode,path); + + flg = (delf) ? (O_CREAT | O_TRUNC) : (O_CREAT | O_EXCL); + + if ((fd = open(path,flg,mode)) != -1) { + (void) close(fd); + return(noErr); + } + + if (DBUNX) + printf("unix_create: failed %s\n",syserr()); + + return(ItoEErr(errno)); +} + +/* + * OSErr unix_createo(char *path, int delf, int mode, int *fd) + * + * Create a file and return the open file handle. + * + */ + +private OSErr +unix_createo(path,delf,mode,fd) +char *path; +int delf; +int mode; +int *fd; +{ + int flg; + + if (DBUNX) + printf("unix_createo: delf=%d, path=%s\n",delf,path); + + flg = (delf) ? (O_CREAT | O_TRUNC) : (O_CREAT | O_EXCL); + flg |= O_RDWR; + + if ((*fd = open(path,flg,mode)) != -1) + return(noErr); + + if (DBUNX) + printf("unix_createo: failed %s\n",syserr()); + + return(ItoEErr(errno)); +} + +#ifdef NOCHGRPEXEC +#ifndef USECHOWN +#define USECHOWN +#endif USECHOWN +#endif NOCHGRPEXEC + +private OSErr +unix_chown(path,own,grp) +char *path; +int own,grp; +{ + char gid[20]; /* big enough */ + int pid, npid; + WSTATUS status; +#ifndef USECHOWN + struct stat stb; + OSErr err; +#endif USECHOWN + + if (DBOSI) + printf("unix_chown: Attempting chown %s to owner %d, group %d\n", + path,own,grp); +#ifndef USECHOWN + if (usruid != 0) { /* not root, then do it hard way */ + if (grp < 0) { + if (DBOSI) + printf("unix_chown: skipping owner and group for %s\n",path); + return(noErr); + } + if (DBOSI) + printf("unix_chown: skipping owner, chgrp %s to group %d\n",path,grp); + if ((err = unix_stat(path, &stb)) != noErr) + return(err); + if (stb.st_gid == grp) /* naught to do */ + return(noErr); + sprintf(gid, "%d",grp); +#ifdef NOVFORK + if ((pid=fork()) == 0) { +#else NOVFORK + if ((pid=vfork()) == 0) { +#endif NOVFORK + execl("/bin/chgrp","chgrp",gid,path, 0); + _exit(1); /* no false eofs */ + } + while ((npid = wait(&status)) != pid) + /* NULL */; + /* right half of status is non-zero if */ + /* (a) stopped (&0xff == 0177) */ + /* or */ + /* (b) signaled (0x7f != 0) */ + /* (c) coredumped (0x80 != 0) */ +#if defined(WIFSTOPPED) && defined(WIFSIGNALED) && defined(W_COREDUMP) + if (WIFSTOPPED(status) || WIFSIGNALED(status) || (W_COREDUMP(status) != 0)) + return(aeAccessDenied); /* oh well */ +#else /* WIFSTOPPED && WIFSIGNALED && W_COREDUMP */ + if ((status & 0xff) != 0) + return(aeAccessDenied); /* oh well */ +#endif/* WIFSTOPPED && WIFSIGNALED && W_COREDUMP */ + /* retcode is leftmost 8 bits */ +#ifdef W_RETCODE + if (W_RETCODE(status) != 0) + return(aeAccessDenied); /* oh well */ +#else /* W_RETCODE */ + if ((status>>8) != 0) + return(aeAccessDenied); /* oh well */ +#endif /* W_RETCODE */ + return(noErr); + } +#endif USECHOWN +#ifdef NOCHGRPEXEC + if (usruid != 0) { /* not root, ignore user */ + if (grp < 0) { + if (DBOSI) + printf("unix_chown: skipping owner and group for %s\n",path); + return(noErr); + } + own = -1; + if (DBOSI) + printf("unix_chown: skipping owner, chgrp %s to group %d\n",path,grp); + } +#endif NOCHGRPEXEC + /* root can do what it pleases, so can any user on sysv */ + if (chown(path, own, grp) < 0) + return(ItoEErr(errno)); + return(noErr); +} + + +private OSErr +unix_chmod(path,mode) +char *path; +u_short mode; +{ + if (DBUNX) + printf("unix_chmod: mode=o%o path=%s\n",mode,path); + + if (chmod(path,(int) mode) == 0) + return(noErr); + + if (DBUNX) + printf("unix_chmod: failed %s\n",syserr()); + + return(ItoEErr(errno)); +} + +private OSErr +unix_stat(path,stb) +char *path; +struct stat *stb; +{ + if (DBUNX) + printf("unix_stat: path=%s\n",path); + + if (stat(path,stb) == 0) + return(noErr); + + if (DBUNX) + printf("unix_stat: failed %s\n",syserr()); + + return(ItoEErr(errno)); +} + +/* + * figure out the mode a file should have based on the uid, gid, and mode + * of its parents. Mainly for drop folders. + * + * really shouldn't have to do this -- instead change the owner + * of the file -- however: (a) bsd doesn't allow and (b) must do after + * all file operations because we don't have handle -- mucho work -- + * if we could. + * +*/ +private int +filemode(mode, uid, gid) +int mode, uid, gid; +{ + int mo = mode & 0777; /* convert st_mode to mode */ + if (uid != usruid) { + /* check for conditions that would mean drop folder for us */ + /* (but, don't accept a drop folder on basis of group that is */ + /* world viewable even though it really is a drop folder for us) */ + if ((mo & 04) == 0 && (mo & 040) == 0 && OSInGroup(gid)) + mo |= 0666; /* let everyone else read/write */ + /* We need to do this because the poor person who get's the file */ + /* can't do anything with it otherwise */ + } + return(mo); +} + +private char * +syserr() +{ +#if !(defined(__FreeBSD__) || defined(__NetBSD__)) + extern char *sys_errlist[]; +#endif + extern int sys_nerr; + static char buf[50]; + int serrno; + + serrno = errno; + if (serrno < 0 || serrno > sys_nerr) { + sprintf(buf,"Unknown error %d",serrno); + return(buf); + } + return(sys_errlist[serrno]); +} + +private OSErr +ItoEErr(num) +int num; +{ + extern int sessvers; /* AFP protocol version */ + + switch (num) { + case EPERM: /* Not owner */ + return(aeAccessDenied); + case ENOENT: /* No such file or directory */ + return(aeObjectNotFound); + case EACCES: /* Permission denied */ + return(aeAccessDenied); + case EEXIST: /* File exists */ + return(aeObjectExists); + case ENOTDIR: /* Not a directory */ + return(aeDirNotFound); + case EISDIR: /* Is a directory */ + return(aeObjectTypeErr); + case ENFILE: /* File table overflow */ + return(aeDiskFull); + case EMFILE: /* Too many files open */ + return(aeTooManyFilesOpen); + case ETXTBSY: /* Text file busy */ + return(aeFileBusy); + case ENOSPC: /* No space left on device */ + return(aeDiskFull); + case EROFS: /* read only file system */ + if (sessvers == AFPVersion1DOT1) + return(aeAccessDenied); + else + return(aeVolumeLocked); +#ifndef AIX +# ifdef ENOTEMPTY + case ENOTEMPTY: + return(aeDirNotEmpty); +# endif ENOTEMPTY +#endif AIX +#ifdef EDQUOT + case EDQUOT: + return(aeDiskFull); +#endif EDQUOT + default: + if (DBUNX) + printf("ItoEErr: Unknown unix error code %d\n",errno); + return(aeMiscErr); + } +} + +#ifdef ULTRIX_SECURITY +char * +ultrix_crypt(pwd, pw) +char *pwd; +struct passwd *pw; +{ + extern char *crypt(), *crypt16(); + extern AUTHORIZATION *getauthuid(); + AUTHORIZATION *au; + struct svcinfo *si; + char *passwd; + + /* + * the asterisk means that the real encrypted password + * is in the auth file. But we really should check to + * see if the security level is either SEC_UPGRADE or + * SEC_ENHANCED and the password is an asterisk because + * the security level could be BSD and someone put an + * asterisk in to turn an account off, but if that's the + * case the right thing will happen here anyways (i.e., + * nothing encrypts to a single asterisk so the test will + * fail). + */ + if (strcmp(pw->pw_passwd, "*") == 0) { + si = getsvc(); + if ((si->svcauth.seclevel == SEC_UPGRADE) || + (si->svcauth.seclevel == SEC_ENHANCED)) { + /* + * if they aren't in the auth file return + * the empty string. this can't match since + * we've already thrown out empty passwords. + */ + if ((au = getauthuid(pw->pw_uid)) == NULL) + return(""); + pw->pw_passwd = au->a_password; + } + return(crypt16(pwd, pw->pw_passwd)); + } + return(crypt(pwd, pw->pw_passwd)); +} +#endif ULTRIX_SECURITY +#ifdef APPLICATION_MANAGER + +/* + * Enforce control on the number of file opens (or Applications + * run) by checking our 'Application List' and attempting to apply + * a single byte-range read lock on the file resource fork at byte N. + * Can also specify no Finder copying with 'P' flag on number. + * + * djh@munnari.OZ.AU + * September, 1991 + * + */ + +int +wantLock(file, num, protect) +char *file; +int *num, *protect; +{ + int cmpval; + struct flist *applp; + extern struct flist *applist; + + applp = applist; + while (applp != NULL) { + /* check the SORTED list, return 0 if found */ + if ((cmpval = strcmp(file, applp->filename)) <= 0) { + *num = applp->incarnations; + *protect = applp->protected; + return(cmpval); + } + applp = applp->next; + } + return(1); +} + +int +enforceLock(fd, maxm) +int fd, maxm; +{ + int i; + struct flock flck; + + for (i = 1; i <= maxm; i++) { + flck.l_type = F_WRLCK; + flck.l_whence = 0; /* SEEK_SET */ + flck.l_start = i+4; + flck.l_len = 1; + + if (fcntl(fd, F_GETLK, &flck) == -1) + return(-1); /* not supported ? */ + + if (flck.l_type == F_RDLCK) + continue; + + if (flck.l_type == F_UNLCK) { + flck.l_type = F_RDLCK; + flck.l_whence = 0; /* SEEK_SET */ + flck.l_start = i+4; + flck.l_len = 1; + if (fcntl(fd, F_SETLK, &flck) == -1) + return(-1); /* not supported ? */ + + return(1); /* success */ + } + } + return(0); /* no locks left */ +} +#endif APPLICATION_MANAGER +#ifdef HIDE_LWSEC_FILE +/* + * int HideLWSec(char *fname, char *userlogindir, int usruid, int + * usrgid, AddrBlock addr ) + * + * Add additional security to LW security flag file when using + * LWSRV_AUFS_SECURITY. Only relevant if both HIDE_LWSEC_FILE + * and LWSRV_AUFS_SECURITY defined in m4.features. + * Original flag file in world read/writeable directory + * permitted links and "borrowing" laserWriters from others, thus + * circumventing laser page charges. + * This creates a directory with user id ownership and the flag + * file is placed in this directory. + * + */ + +int +hideLWSec(fname, userlogindir, usruid, usrgid, addr) +char *fname, *userlogindir; +int usruid, usrgid; +AddrBlock addr; +{ + char protecteddir[MAXPATHLEN], flagfile[MAXPATHLEN]; + struct stat dbuf; + DIR *locdirp; + + (void) strcpy(protecteddir, userlogindir); + make_userlogin(fname, protecteddir, addr); + (void) strcpy(protecteddir, fname); + fname[0] = '\0'; + make_userlogin(fname, protecteddir, addr); /* create flag file */ + + if (stat(protecteddir, &dbuf) == 0) { + /* dir found and stat sucessful, we need to zap dir */ + if (stat(fname, &dbuf) == 0) + if (S_ISREG(dbuf.st_mode)) + if (unlink(fname) < 0 ) + logit(0, "hideLWSec: errno=%d unlinking %s\n", errno, fname); + if (rmdir(protecteddir ) < 0 ) { + logit(0, "hideLWSec: errno=%d Can't zap %s\n", errno, fname); + return(-1); + } + } else /* error occured in stat, but not no entry */ + if (errno != ENOENT ) { + logit(0, "hideLWSec: stat errno= %d for %s\n", errno, fname); + return(-1); + } + if (mkdir(protecteddir, 0700) < 0) { + logit(0, "hideLWSec: unable to create %s,errno=%d\n", fname, errno); + return(-1); + } else { + chown(protecteddir, usruid, usrgid); + } + return(0); +} +#endif HIDE_LWSEC_FILE +#ifdef DENYREADWRITE +/* + * Implement full "access modes" and "deny modes" as + * specified in Inside AppleTalk 2nd Ed. page 13-35. + * + * Reserve four single byte locks at the beginning of + * each fork (ie: byte range locks are offset by four + * from their protected data. This is OK since locks + * are maintained by kernel tables and are independent + * of the data and the actual file length). + * + * byte 0 = access mode read bit + * byte 1 = access mode write bit + * byte 2 = deny mode read bit + * byte 3 = deny mode write bit + * + * DENYREADWRITE uses fcntl(2) advisory F_RDLCKs. F_GETLK + * returns F_UNLCK if the requesting process holds the + * specified lock. Thus, there is no way for a process to + * determine if it is still holding a specific lock. For + * this reason we maintain a separate queue of access modes + * and file names for files opened by this process. + * + */ + +int +getAccessDenyMode(path, fd) +char *path; +int fd; +{ + int i; + int mode = 0; + int mask = 0x01; + struct flock flck; + struct accessMode *p = accessMQueue; + + for (i = 1; i <= 4; i++) { + flck.l_type = F_WRLCK; + flck.l_whence = 0; /* SEEK_SET */ + flck.l_start = i; + flck.l_len = 1; + if (fcntl(fd, F_GETLK, &flck) != -1) + if (flck.l_type != F_UNLCK) + mode |= mask; + mask <<= 1; + } + + while (p != (struct accessMode *)NULL) { + if (strcmp(p->path, path) == 0) + mode |= p->mode; + p = p->next; + } + + return(((mode & 0x0c) << 2) | (mode & 0x03)); +} + +/* + * set access and deny modes on fd for 'path' + * + */ + +int +setAccessDenyMode(path, fd, mode) +char *path; +int fd, mode; +{ + int i; + int mask = 0x01; + struct flock flck; + struct accessMode *p; + + mode = ((mode & 0x30) >> 2) | (mode & 0x03); + + for (i = 1; i <= 4; i++) { + if (mode & mask) { + flck.l_type = F_RDLCK; + flck.l_whence = 0; /* SEEK_SET */ + flck.l_start = i; + flck.l_len = 1; + if (fcntl(fd, F_SETLK, &flck) == -1) + mode &= ~mask; + } + mask <<= 1; + } + + if ((p = (struct accessMode *)malloc(sizeof(struct accessMode))) != NULL) { + strcpy(p->path, path); + p->fd = fd; + p->mode = mode; + p->next = accessMQueue; + accessMQueue = p; + } + + return(((mode & 0x0c) << 2) | (mode & 0x03)); +} + +/* + * Inside AppleTalk, 2nd Ed. + * Table 13-1: Synchronization rules + * + */ + +int accessTable[16] = { + 0x0000, 0xf0f0, 0xff00, 0xfff0, + 0xaaaa, 0xfafa, 0xffaa, 0xfffa, + 0xcccc, 0xfcfc, 0xffcc, 0xfffc, + 0xeeee, 0xfefe, 0xffee, 0xfffe +}; + +/* + * check current file access/deny + * mode against requested mode + * + */ + +int +accessConflict(cadm, mode) +int cadm, mode; +{ + cadm = ((cadm & 0x30) >> 2) | (cadm & 0x03); + mode = ((mode & 0x30) >> 2) | (mode & 0x03); + + return((accessTable[cadm] >> mode) & 0x01); +} +#endif DENYREADWRITE diff --git a/applications/aufs/afposenum.c b/applications/aufs/afposenum.c new file mode 100644 index 0000000..91e8f71 --- /dev/null +++ b/applications/aufs/afposenum.c @@ -0,0 +1,990 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:49:40 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afposenum.c,v 2.15 1996/06/18 10:49:40 djh Rel djh $ + * $Revision: 2.15 $ + * + */ + +/* + * afposenum.c - Appletalk Filing Protocol OS enumeration. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * + */ + +#include +#include + +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif /* _TYPES */ + +#include +#include +#include +#include + +#ifdef USESTRINGDOTH +#include +#else USESTRINGDOTH +#include +#endif USESTRINGDOTH +#ifdef SHORT_NAMES +#include +#endif SHORT_NAMES +#ifdef NOCASEMATCH +#include +#include +#endif NOCASEMATCH +#ifdef USEDIRENT +# include +#else /* USEDIRENT */ +# ifdef xenix5 +# include +# else xenix5 +# include +# endif xenix5 +#endif /* USEDIRENT */ +#ifdef SOLARIS +# include +#endif /* SOLARIS */ +#ifdef linux +# include +#endif /* linux */ +#ifdef DISTRIB_PASSWDS +#include +#endif /* DISTRIB_PASSWDS */ + +#include "afps.h" +#include "afpdt.h" +#include "afpgc.h" + +#ifdef SHORT_NAMES +typedef struct { + char s_name[MAXSFLEN+1]; +}s_entry; + +typedef struct { /* Enumeration cache entry */ + IDirP ece_dirid; /* enumerated directory id */ + int ece_lru; + int ece_lock; /* do not purge lock */ + int ece_cnt; /* count of entries */ +#ifdef USEDIRENT + struct dirent **ece_nlst; /* direct info */ +#else USEDIRENT + struct direct **ece_nlst; /* direct info */ +#endif USEDIRENT + s_entry * s_namearray; + time_t ece_ctime; /* last directory write dt */ + int ece_mmode; /* last directory mode */ + int ece_mcnt; /* last internal modify count */ + int ece_idx; /* our index in the cache */ +} EnumCE; +#else SHORT_NAMES +typedef struct { /* Enumeration cache entry */ + IDirP ece_dirid; /* enumerated directory id */ + int ece_lru; + int ece_lock; /* do not purge lock */ + int ece_cnt; /* count of entries */ +#ifdef USEDIRENT + struct dirent **ece_nlst; /* direct info */ +#else USEDIRENT + struct direct **ece_nlst; /* direct info */ +#endif USEDIRENT + time_t ece_ctime; /* last directory write dt */ + int ece_mmode; /* last directory mode */ + int ece_mcnt; /* last internal modify count */ + int ece_idx; /* our index in the cache */ +} EnumCE; +#endif SHORT_NAMES + +#define NILECE ((EnumCE *) 0) + +private EnumCE *EnumCache[NECSIZE]; +private int EC_Clock; /* lru clock */ + +#ifdef USEDIRENT +#define NILNLST ((struct dirent **) 0) +#else USEDIRENT +#define NILNLST ((struct direct **) 0) +#endif USEDIRENT + +private EnumCE *EC_Min(),*EC_Find(),*EC_Fetch(),*EC_Load(); +private boolean EC_Valid(); +private void EC_Free(); +private iselect(); +#ifdef SHORT_NAMES +private name_toupper(), convert(),add_ast(); +private int mysort(); +#endif SHORT_NAMES + +#ifdef NOCASEMATCH +private noCaseDir(); +private searchDirectory(); +#endif NOCASEMATCH + +/* + * int iselect(struct direct *d) + * + * See if this directory entry should be included in our enumeration. + * Special files are skipped ("." , ".." ".finderinfo", etc) and + * a length check is made to see if the name is too long for AFP. + * + */ + +private +iselect(d) +#ifdef USEDIRENT +struct dirent *d; +#else USEDIRENT +struct direct *d; +#endif USEDIRENT +{ + if (d == NULL) + return(FALSE); + if (d->d_name == NULL) + return(FALSE); + if (ENameLen(d->d_name) > MAXLFLEN) /* external name too long? */ + return(FALSE); /* sorry this name is too long */ + if (d->d_name[0] != '.') /* all special dirs begin with dot */ + return(TRUE); + if (d->d_name[1] == '\0') /* check for dot */ + return(FALSE); + if (d->d_name[1] == '.' && /* check for dot dot */ + d->d_name[2] == '\0') + return(FALSE); + if (strcmp(d->d_name,RFDIRFN) == 0) /* resource fork directory? */ + return(FALSE); /* yes... don't include */ + if (strcmp(d->d_name,FIDIRFN) == 0) /* finder info directory? */ + return(FALSE); /* yes... don't include */ + if (strcmp(d->d_name,DESKTOP_ICON) == 0) /* check for desktop files */ + return(FALSE); + if (strcmp(d->d_name,DESKTOP_APPL) == 0) + return(FALSE); +#ifdef DISTRIB_PASSWDS + if (strcmp(d->d_name,AFP_DISTPW_USER) == 0) /* local password file */ + return(FALSE); +#endif /* DISTRIB_PASSWDS */ + return(TRUE); +} + +/* + * void ECacheInit() + * + * Initialize the cache table. + * + */ + +void +ECacheInit() +{ + int i; + + if (DBENU) + printf("ECacheInit: Cache size is %d\n",NECSIZE); + + EC_Clock = 1; /* init clock */ + for (i=0; i < NECSIZE; i++) { + EnumCache[i] = (EnumCE *) malloc(sizeof(EnumCE)); + EnumCache[i]->ece_dirid = NILDIR; + EnumCache[i]->ece_idx = i; + } +} + +private EnumCE * +EC_Min() +{ + int i,mi = -1; + int mlru = EC_Clock+1; + + for (i=0; i < NECSIZE; i++) { + if (EnumCache[i]->ece_dirid == NILDIR) + return(EnumCache[i]); /* found unused slot */ + if (!EnumCache[i]->ece_lock && /* if not locked and... */ + EnumCache[i]->ece_lru < mlru) { /* is older than current */ + mlru = EnumCache[i]->ece_lru; /* remember new clock */ + mi = i; /* and new index */ + } + } + + if (mi == -1) /* all locked? big problem */ + logit(0,"EC_Min: no entry found\n");/* at least we will know */ + + return(EnumCache[mi]); +} + +/* + * void EC_Free(EnumCE *ec) + * + * Free storage associate with a cache entry and set the entry to be + * unused by making ece_dirid = NILDIR. + * + */ + +private void +EC_Free(ec) +EnumCE *ec; +{ + int i; + + if (ec->ece_dirid == NILDIR) /* unused entry? */ + return; /* yes, just return */ + + if (DBENU) + printf("EC_Free: releasing %s\n",pathstr(ec->ece_dirid)); + + for (i=0; i < ec->ece_cnt; i++) + free((char *) ec->ece_nlst[i]); + + free((char *) ec->ece_nlst); + (ec->ece_dirid)->eceidx = NOECIDX; /* not in cache */ + ec->ece_dirid = NILDIR; /* indicate free entry */ +} + +/* + * boolean EC_Valid(EnumCE *ec, struct stat *stb) + * + * Check if the cache entry is valid by comparing information in + * the cache entry with information provided from the stat. + * + * If the cache entry is empty, the internal modification counters + * are different, or the directory modification time_t differs from + * the time_t the entry was created, then return FALSE. + * + */ + +private boolean +EC_Valid(ec,stb) +EnumCE *ec; +struct stat *stb; +{ + IDirP dirid = ec->ece_dirid; + + if (ec->ece_dirid == NILDIR) /* no entry? */ + return(FALSE); /* should not happen */ + + /* See if the directory has been modified and needs to be reread */ + /* Check both the internal modified counter and the os modify times. */ + /* The internal mod counts are to prevent a race condition with the */ + /* file's mod times not getting set/read correctly */ + + if (ec->ece_dirid->modified != ec->ece_mcnt) { /* modified by us? */ + if (DBENU) + printf("EC_Valid: internal modify invalidates %s\n",pathstr(dirid)); + return(FALSE); /* yes... */ + } + + if ((stb->st_ctime != ec->ece_ctime) || (stb->st_mode != ec->ece_mmode)) { + if (DBENU) + printf("EC_Valid: external modify invalidates %s\n",pathstr(dirid)); + return(FALSE); + } + return(TRUE); /* not modified */ +} + +/* + * EnumCE * EC_Find(IDirP dirid) + * + * Given a dirid return the cached Enum entry or NILECE. + * + */ + +private EnumCE * +EC_Find(dirid) +IDirP dirid; +{ + int cidx = dirid->eceidx; /* cache index */ + + if (cidx == NOECIDX) /* check dir for cache entry */ + return(NILECE); /* no cache entry */ + EnumCache[cidx]->ece_lru = EC_Clock++; /* this is a reference */ + return(EnumCache[cidx]); /* return the cache */ +} + +/* + * EnumCE * EC_Fetch(IDirP dirid) + * + * Get the EnumCE entry for this dirid. If the entry is in the + * cache then validate it by calling EC_Valid. If not valid + * (changed) then reload the information. If the directory info + * is not in the cache then recycle the min cache entry and load + * a new entry. + * + * Returns NILECE on failure, ece on success. + * + */ + +private EnumCE * +EC_Fetch(dirid) +IDirP dirid; +{ + EnumCE *ec; + struct stat stb; + + if (DBENU) + printf("EC_Fetch: request for %s\n",pathstr(dirid)); + + ec = EC_Find(dirid); /* see if entry is cached */ + if (ec != NILECE && ec->ece_lock) /* found and locked? */ + return(ec); /* then being enumerated, return */ + +#ifndef STAT_CACHE + if (stat(pathstr(dirid),&stb) != 0) { /* else do a stat for checks */ +#else STAT_CACHE + if(OSstat(pathstr(dirid),&stb)!= 0) { /* else do a stat for checks */ +#endif STAT_CACHE + Idrdirid(Ipdirid(dirid),dirid); /* unlink from directory tree */ + return(NILECE); /* nothing there... */ + } + + if (ec != NILECE) { /* find anything? */ + if (EC_Valid(ec,&stb)) /* yes... do validity check */ + return(ec); /* seems ok, return it */ + } else + ec = EC_Min(); /* else get a current min */ + + EC_Free(ec); /* free entry or min */ + + return(EC_Load(dirid,ec,&stb)); /* load dir and return */ +} + +#ifdef SHORT_NAMES + +/* + * table to map Mac character set represented with :aa into something + * "approximately similar" for MSDOS, any bad characters map to '#' + * (valid character list from "MSDOS User's Guide"). + * + * this is horrible!, but the results look a little nicer on the PC + * + * Modified 91/12/14 by Paul Buddington to improve translation based + * on MSDOS 5.0 User's Guide + * + */ + +char mapMacChar[] = { +'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', +'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', +'-', '!', '#', '#', '$', '%', '&', '\'','(', ')', '#', '#', '#', '-', '#', '#', +'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', '{', '#', '}', '~', '#', +'A', 'A', 'C', 'E', 'N', 'O', 'U', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', +'E', 'E', 'I', 'I', 'I', 'I', 'N', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', +'#', '#', 'C', '#', '#', '#', '#', 'B', 'R', 'C', 'T', '#', '#', '#', 'A', 'O', +'#', '#', '#', '#', '#', 'U', '#', '#', '#', '#', '#', 'A', '#', '#', 'A', 'O', +'#', '#', '#', '#', 'F', '#', '#', '#', '#', '#', '#', 'A', 'A', 'O', 'O', 'O', +'#', '#', '#', '#', '#', '#', '#', '#', 'Y', '#', '#', '#', '#', '#', '#', '#', +'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', +'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#' +}; + +/* + * Map a long name into a short, uppercase DOS compatible name. + * Nothing specifies exactly how to do this, so try and make it + * resemble the behaviour of a Mac AppleShare server. + */ + +private +name_toupper(nlen,name,s_name) +int nlen; +char *name, *s_name; +{ + int c; + int period = 0; + int altered = 0; + int i=0,j=0,k=0; + private byte hex2nibble(); + char sname[MAXSFLEN+4]; /* slop */ + + if (DBENU) + printf("nlen %d, MAXSFLEN %d name %s \n",nlen,MAXSFLEN,name); + + while (name[i] != '\0' && i < nlen && j < MAXSFLEN && period < 5) { + /* if the name starts with a . ignore for now, mark altered */ + if ((i == 0) && (name[i] == '.')) { + altered++; + i++; + /* when you hit the first period, save it and count it */ + } else if ((period == 0) && (name[i] == '.')) { + sname[j++] = '.'; + period++; + i++; + /* any weird characters, just say no !!!! */ + /* if we hit another period, skip over it */ + } else if ((name[i] == '*') || (name[i] == '+') || + (name[i] == '|') || (name[i] == '?') || + (name[i] == '<') || (name[i] == '>') || + (name[i] == '[') || (name[i] == ']') || + (name[i] == '"') || (name[i] == '=') || + (name[i] == '`') || (name[i] == '.') || + (name[i] == '/') || (name[i] == '\\')|| + (name[i] == ';') || (name[i] == ',')) { + altered++; + i++; + /* if we see a space, ignore it, but don't mark as altered */ + } else if (name[i] == ' ') { + i++; + /* check for aufs :aa special chars, map down into sim. ascii char */ + } else if (name[i] == ':' && i < (nlen-2)) { + c = hex2nibble(name[++i]); + c = c << 4; + c |= hex2nibble(name[++i]); + if ((sname[j] = mapMacChar[c & 0xff]) != '#') + j++; + altered++; + i++; + /* otherwise, just convert to upper */ + } else { + if (isalpha(name[i]) && !(isupper(name[i]))) + sname[j++] = toupper(name[i]); + else + sname[j++] = name[i]; + i++; + } + if (period) + period++; /* keep counting to get 3 more chars for DOS suffix */ + } + sname[j] = '\0'; + /* more characters to go ? */ + if (name[i] != '\0') + altered++; + j = k = 0; + /* copy to s_name, mark start of name with ! if altered */ + while(sname[j] != '\0' && j < MAXSFLEN && k < MAXSFLEN) { + if (altered && k == 0) + s_name[k++] = '!'; + if (!period && k == 8) + s_name[k++] = '.'; + s_name[k++] = sname[j++]; + } + s_name[k] = '\0'; +} + +private byte +hex2nibble(a) +char a; +{ + if (a >= '0' && a <= '9') + return(a - '0'); + if (a >= 'a' && a <= 'f') + return(a - 'a' + 10); + if (a >= 'A' && a <= 'F') + return(a - 'A' + 10); + return(0); +} + +private add_ast (ec,pos) +EnumCE *ec; +int pos; +{ + int i = 1; + char c,c1; + + c = ec->s_namearray[pos].s_name[0]; + ec->s_namearray[pos].s_name[0] = '*'; + while ((c != '\0') && (is_namearray[pos].s_name[i] = '.'; + c1 = ec->s_namearray[pos].s_name[i+1]; + ec->s_namearray[pos].s_name[i+1] = c; + i+=2; + } else { + c1 = ec->s_namearray[pos].s_name[i]; + ec->s_namearray[pos].s_name[i] = c; + i++; + } + c = c1; + } + ec->s_namearray[pos].s_name[i] = '\0'; +} + +private +convert(ec) +EnumCE *ec; +{ + int ast,i; + + ec->s_namearray = (s_entry*)malloc (ec->ece_cnt*sizeof(s_entry)); + ast = FALSE; + for (i = 0; iece_cnt; i++) { +#ifdef USEDIRENT + name_toupper(strlen(ec->ece_nlst[i]->d_name),ec->ece_nlst[i]->d_name, +#else /* USEDIRENT */ + name_toupper(ec->ece_nlst[i]->d_namlen, ec->ece_nlst[i]->d_name, +#endif /* USEDIRENT */ + ec->s_namearray[i].s_name); + if (i>0) { + if ((strcmp(ec->s_namearray[i].s_name, + ec->s_namearray[i-1].s_name)) == 0){ + add_ast(ec,i-1); + ast = TRUE; + } else if (ast) { + add_ast(ec,i-1); + ast = FALSE; + } + } + } + if (ast) + add_ast(ec,(ec->ece_cnt)-1); +} +private int +mysort(x,y) +#ifdef USEDIRENT +struct dirent **x,**y; +#else USEDIRENT +struct direct **x,**y; +#endif USEDIRENT +{ + char tempx[MAXNAMLEN],tempy[MAXNAMLEN]; + int i = 0; + +#ifdef USEDIRENT + while (i < strlen((*x)->d_name)) { +#else /* USEDIRENT */ + while (i < (*x)->d_namlen) { +#endif /* USEDIRENT */ + tempx[i] = toupper((*x)->d_name[i]); + i++; + } + tempx[i] = '\0'; + i = 0; +#ifdef USEDIRENT + while (i < strlen((*y)->d_name)) { +#else /* USEDIRENT */ + while (i < (*y)->d_namlen) { +#endif /* USEDIRENT */ + tempy[i] = toupper((*y)->d_name[i]); + i++; + } + tempy[i] = '\0'; + return(strcmp(tempx,tempy)); +} +#endif SHORT_NAMES +/* + * EnumCE *EC_Load(IDirP dirid, EnumCE *ec, struct stat *stb) + * + * Read the directory dirid into ec, filling in information from + * the stat structure stb. + * + * Return ec on success, or NILECE if failure. + * + */ + +private EnumCE * +EC_Load(dirid,ec,stb) +IDirP dirid; +EnumCE *ec; +struct stat *stb; +{ + char *path = pathstr(dirid); +#ifdef STAT_CACHE + extern char *cwd_path(); +#endif STAT_CACHE + int i; + + if (DBENU) + printf("EC_Load: adding %s\n",path); +#ifdef STAT_CACHE + path = cwd_path(path); +#endif STAT_CACHE + +#ifdef SHORT_NAMES + ec->ece_cnt = scandir(path,&ec->ece_nlst,iselect,mysort); +#else SHORT_NAMES + ec->ece_cnt = scandir(path,&ec->ece_nlst,iselect,0); +#endif SHORT_NAMES + if (ec->ece_cnt < 0) { + if (DBENU) + printf("EC_Load: scandir failed for %s\n",path); + return(NILECE); + } +#ifdef SHORT_NAMES + convert(ec); +#endif SHORT_NAMES + ec->ece_ctime = stb->st_ctime; /* copy last modify time */ + ec->ece_mmode = stb->st_mode; /* copy the last mode */ + ec->ece_dirid = dirid; /* copy directory id */ + ec->ece_mcnt = dirid->modified; /* copy modify count */ + ec->ece_lru = EC_Clock++; /* set LRU from clock */ + dirid->eceidx = ec->ece_idx; /* add new entry */ + return(ec); /* and return table */ +} + +/* + * char * OSEnumGet(dirid,idx) + * + * Fetch the idx'th file/dir name from an enumerated directory. + * Index starts at 1. + * + * You must have first called OSEnumIni() to initialize and + * discover the maximun count of entries in the directory. + * + */ + +char * +OSEnumGet(dirid,idx) +IDirP dirid; +int idx; /* ranges from 1..count */ +{ + EnumCE *ec; + + ec = EC_Find(dirid); /* find enum entry given dirid */ + if (ec == NILECE) { /* was locked could not be purged */ + logit(0,"OSEnumGet: bad enum!\n"); + return(""); + } + + if (idx > ec->ece_cnt) { /* check for bad idx */ + logit(0,"OSEnumGet: Bad idx!\n"); + return(""); + } + + if (!ec->ece_lock) /* check for bad lock */ + logit(0,"OSEnumGet: Handle not locked!\n"); + + if (DBENU) + printf("OSEnumGet: %d:%s\n",idx,ec->ece_nlst[idx-1]->d_name); + + return(ec->ece_nlst[idx-1]->d_name); +} + +/* + * int OSEnumInit(IDirP idir) + * + * Call OSEnumInit to initialize enumeration information. This + * procedure must be called before OSEnumGet which returns the + * nth entry for a directory. + * + * OSEnumDone must be called when finished to release enumeration + * information for a directory. + * + * Returns the count of entries in the directory or a negative error + * code. + * + */ + +int +OSEnumInit(idir) +IDirP idir; +{ + EnumCE *ec; + + if (DBENU) + printf("OSEnumInit: %s\n",pathstr(idir)); + +#ifdef STAT_CACHE + OScd(pathstr(idir)); +#endif STAT_CACHE + + OSValFICacheForEnum(idir); /* make sure fi cache for directory is valid */ + ec = EC_Fetch(idir); /* get directory info */ + if (ec == NILECE) /* access problem? */ + return(aeAccessDenied); /* yes... */ + + if (ec->ece_lock == TRUE) + logit(0,"OSEnumInit: directory already locked!\n"); + + ec->ece_lock = TRUE; /* entry is locked, no purge */ + return(ec->ece_cnt); /* return count */ +} + +/* + * void OSEnumDone(dirid) + * + * Call this routine to say that enumeration is no longer active for + * a directory. + * + * OSEnumDone unlocks the directory entry and allows it to be removed + * from the cache. + * + */ + +void +OSEnumDone(dirid) +IDirP dirid; +{ + EnumCE *ec; + + ec = EC_Find(dirid); /* get cached entry */ + if (ec == NILECE) /* should not happen */ + logit(0,"OSEnumDone: no cache for dirid\n"); + else + ec->ece_lock = FALSE; /* unlock */ +} + +/* + * int OSEnumCount(dirid) + * + * Return the count of entries in a directory. + * + */ + +int OSEnumCount(dirid) +IDirP dirid; +{ + EnumCE *ec; + + ec = EC_Fetch(dirid); /* get directory info */ + if (ec == NILECE) /* access problem? */ + return(aeAccessDenied); /* yes... */ + + if (DBENU) + printf("OSEnumCount: %s = %d\n",pathstr(ec->ece_dirid),ec->ece_cnt); + + return(ec->ece_cnt); /* return the count */ +} + +OSfname(r,idir,fn,typ) +IDirP idir; +register char *fn,*r; +register int typ; +{ + register char *p; + + for (p = pathstr(idir); *p != '\0';) /* copy the directory */ + *r++ = *p++; + + if (typ == F_RSRC || typ == F_FNDR) /* append directory names */ + for (p = ((typ == F_RSRC) ? RFDIR : FIDIR); *p != '\0'; ) + *r++ = *p++; + + if (*fn != '\0') { /* add slash */ + *r++ = '/'; /* if not null file */ + while (*fn != '\0') + *r++ = *fn++; + } + *r = '\0'; +} + +toResFork(str, fn) +register char *str; +char *fn; +{ + register char *fp, *tp; + if(*fn) { /* a real file */ + if(fp = rindex(str, '/')) /* skip over last slash */ + fp++; + else + fp = str; + str = fp; + fp = str + strlen(str); + tp = fp + DIRRFLEN; + *tp = 0; + while(fp > str) /* move filename, leaving space for .resource */ + *--tp = *--fp; + fp = DIRRF; + while(*fp) + *str++ = *fp++; + } else /* a directory */ + strcat(str,RFDIR); /* just append .resource */ +} + +toFinderInfo(str, fn) +register char *str; +char *fn; +{ + register char *fp, *tp; + + if(*fn) { /* a real file */ + if(fp = rindex(str,'/')) /* skip over last slash */ + fp++; + else + fp = str; + str = fp; + fp = str + strlen(str); + tp = fp + DIRFILEN; + *tp = 0; + while(fp > str) /* move filename, leaving space for .finderinfo */ + *--tp = *--fp; + fp = DIRFI; + while(*fp) + *str++ = *fp++; + } else /* a directory */ + strcat(str,FIDIR); /* just append .finderinfo */ +} + +#ifdef NOCASEMATCH +/* + * searchDirectory(dir, name) + * Do a case insensitive search for name in dir, and write the true name + * of the file in name. + */ + +private +searchDirectory(dir, name) +char *dir, *name; +{ + register char *fp, *tp; + register DIR *dp; +#ifdef USEDIRENT + register struct dirent *d; +#else USEDIRENT + register struct direct *d; +#endif USEDIRENT + register int len; + char lname[BUFSIZ], dname[BUFSIZ]; + + if((dp = opendir(dir)) == NULL) + return(0); + len = 0; + for(fp = name, tp = lname ; *fp ; fp++) { + *tp++ = isupper(*fp) ? tolower(*fp) : *fp; + len++; + } + *tp = 0; + while(d = readdir(dp)) { +#ifdef USEDIRENT + if (strlen(d->d_name) != len) +#else /* USEDIRENT */ + if (d->d_namlen != len) +#endif /* USEDIRENT */ + continue; + for(fp = d->d_name, tp = dname ; *fp ; fp++) + *tp++ = isupper(*fp) ? tolower(*fp) : *fp; + *tp = 0; + if(strcmp(dname, lname) == 0) { + strcpy(name, d->d_name); + closedir(dp); + return(1); + } + } + closedir(dp); + return(0); +} + +/* + * noCaseDir(path) + * Recursively verify the components of path. + */ + +private +noCaseDir(path) +register char *path; +{ + register char *last; + register int status; + + if(access(path, F_OK) >= 0) + return(1); + if(last = rindex(path, '/')) { + if(last == path) + return(searchDirectory("/", last + 1)); + else { + *last++ = 0; + status = 0; + if(noCaseDir(path)) + status = searchDirectory(path, last); + *--last = '/'; + return(status); + } + } + return(searchDirectory(".", path)); +} + +/* + * noCaseFind(path) + * noCaseFind() calls noCaseDir() and searchDirectory() recursively to + * take path (case insensitive) and converts it to (case sensitive) newpath + * corresponding to the true Unix filename. This is mainly to fix + * HyperCard doing funny things to stack names. + */ + +void +noCaseFind(path) +register char *path; +{ + register char *last; + + if(last = rindex(path, '/')) { + if(last == path) + searchDirectory("/", last + 1); + else { + *last++ = 0; + if(noCaseDir(path)) + searchDirectory(path, last); + *--last = '/'; + } + } else + searchDirectory(".", path); +} + +/* + * noCaseMatch(path) + * noCaseMatch() tests path first and will call noCaseFind() is path + * doesn't exist. + */ + +void +noCaseMatch(path) +register char *path; +{ + if(access(path, F_OK) >= 0) + return; + noCaseFind(path); +} +#endif NOCASEMATCH + +#ifdef SHORT_NAMES +private int +searchn (name,ec,type) +char *name; +EnumCE *ec; +{ + int i = 0; + + if (ec == NILECE) /* hmmmm ... */ + return(-1); + + if (type != 1) + while ((iece_cnt) + && (strcmp (name, ec->ece_nlst[i]->d_name) != 0)) { + i++; + } + else + while ((iece_cnt) + && (strcmp (name, ec->s_namearray[i].s_name) != 0)) { + i++; + } + if (i == ec->ece_cnt) + return(-1); + else + return(i); +} + +char* +Get_name(dirid,name,type,outname) +IDirP dirid; +char *name,*outname; +int type; +{ + EnumCE *ec; + int i, idx; + ec = EC_Fetch(dirid); + if (ec == NILECE) + idx = -1; + else + idx = searchn(name,ec,type); + if (idx == -1) { + if (strcmp (name, dirid->name) == 0) /*then it is a directory*/ + printf("wasn't found and it is a directory\n"); + if (DBENU) + printf("name wasn't found!!!!!Index == -1\n"); + sprintf(outname,"%s",name); + } + else if (type != 1) /*return longname*/ + sprintf(outname,"%s",ec->s_namearray[idx].s_name); + else /* return short name */ + sprintf(outname,"%s",ec->ece_nlst[idx]->d_name); +} +#endif SHORT_NAMES diff --git a/applications/aufs/afposfi.c b/applications/aufs/afposfi.c new file mode 100644 index 0000000..0d68dfd --- /dev/null +++ b/applications/aufs/afposfi.c @@ -0,0 +1,907 @@ +/* + * $Author: djh $ $Date: 1995/06/12 06:58:18 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afposfi.c,v 2.7 1995/06/12 06:58:18 djh Rel djh $ + * $Revision: 2.7 $ +*/ + +/* + * afposfi.c - Appletalk Filing Protocol OS FNDR File Interface. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * Jan 1988 CCKim New finderinfo format + * December 1990 djh tidy up for AFP 2.0 + * + */ + +/* PATCH: PC.aufs/afposfi.c.diffs, djh@munnari.OZ.AU, 15/11/90 */ +/* PATCH: Dan@lth.se/stat.cache.patches, djh@munnari.OZ.AU, 16/11/90 */ +/* PATCH: Moy@Berkeley/afposfi.c.diff, djh@munnari.OZ.AU, 17/11/90 */ + + +#include +#include +extern int errno; +#include +#ifndef _TYPES +# include +#endif +#include +#include +#include /* for htons, htonl */ +#include +#include +#include +#include "afps.h" +#include "afpgc.h" +#ifdef NEEDFCNTLDOTH +# include +#endif +#include "afpudb.h" + +typedef struct FCacheEnt { + struct FCacheEnt *next; /* link for free queue */ + IDirP fe_pdir; /* directory */ + int fe_okay; /* last internal modify count */ + char *fe_fnam; /* file */ + long fe_hash; /* hash value of fe_fnam */ + /* should we use ctime instead perhaps? */ + time_t fe_mtime; /* last modify time: file system */ + time_t fe_vtime; /* last time validated */ + FileInfo fe_fi; /* file info */ +} FCacheEnt; + +export GCHandle *fich; /* handle on cache */ +private FCacheEnt *fce, *lfce; /* recycle purge-load */ +private FCacheEnt *getfe; /* last os_getfi */ + +private int fc_valid(); +private int fc_compare(); +private char *fc_load(); +private void WriteFA(); +private void fc_purge(); +private void fc_flush(); +private void fc_flush_start(); +private void fc_flush_end(); +private FileInfo *os_getfi(); +private fc_readent(); + +#define FICacheSize 128 +/* Each cache entry has a lifetime that it goes through before it must */ +/* be revalidated before reuse: this prevents cache entries from getting */ +/* "stale". */ +/* The revalidation consists of seeing if the disk copy has changed */ +#define FI_VALID_TIME (60*5) /* 5 minutes */ + +InitOSFI() +{ + fich = GCNew(FICacheSize,fc_valid,fc_compare,fc_load,fc_purge,fc_flush); + lfce = fce = (FCacheEnt *) 0; + getfe = (FCacheEnt *) 0; +} + + + +/* + * Our flush doesn't flush modified entries since our cache is + * write through, but attempts instead to check validity of items in + * our cache + * + * can bracket with fc_flush_start and fc_flush_end to reduce # of + * stats. This depends on the .finderinfo directory modify time + * getting changed when a finderinfo entry is modified. WriteFA + * guarantees this as best it can (can you say hack?) + * + * +*/ +private time_t fcf_val_time = 0; /* validate time */ +private IDirP fcf_val_dir = NILDIR; /* directory val time is for */ + +private void +fc_flush_start(pdir) +IDirP pdir; +{ + char path[MAXPATHLEN]; + struct stat stb; + + OSfname(path, pdir, "", F_FNDR); /* get directory */ + if (stat(path, &stb) < 0) { +#ifdef NOCASEMATCH + noCaseFind(path); + if (stat(path, &stb) < 0) { + fcf_val_dir = NILDIR; + fcf_val_time = 0; + return; /* nothing else we can do */ + } +#else NOCASEMATCH + fcf_val_dir = NILDIR; + fcf_val_time = 0; + return; /* nothing else we can do */ +#endif NOCASEMATCH + } + fcf_val_dir = pdir; + fcf_val_time = stb.st_mtime; /* remember */ +} + +private void +fc_flush_end() +{ + fcf_val_time = 0; /* mark none */ + fcf_val_dir = NILDIR; +} + +private void +fc_flush(fe, pdir) +FCacheEnt *fe; +IDirP pdir; +{ + char path[MAXPATHLEN]; + struct stat stb; + + /* if passed directory isn't nildir (flush all), then the file */ + /* will only be check if the particular item has the same directory */ + if (pdir != NILDIR && (fe->fe_pdir != pdir)) + return; + if ((fe->fe_pdir->flags & DID_FINDERINFO)) { /* always okay */ + OSfname(path, fe->fe_pdir, fe->fe_fnam, F_FNDR); + /* if we have a .finderinfo directory update time */ + /* and it is more recent than cache entry, then check the */ + /* cache entry */ + /* also add a check against vtime. Makes it much more useful */ + /* since it is more than likely that fcf_val_time is later */ + /* than the mtime for the majority of the files */ + if (fcf_val_dir == fe->fe_pdir && + (fcf_val_time > fe->fe_mtime) && + (fcf_val_time > fe->fe_vtime)) { +#ifdef NOCASEMATCH + if (stat(path, &stb) < 0) { + noCaseFind(path); + if (stat(path, &stb) < 0) + return; /* nothing else we can do */ + } +#else NOCASEMATCH + if (stat(path, &stb) < 0) + return; /* nothing else we can do */ +#endif NOCASEMATCH + if (stb.st_mtime != fe->fe_mtime) /* reload entry */ + fe->fe_okay = FALSE; /* make entry as bad */ + } + } + time(&fe->fe_vtime); /* mark last validate time */ +} + +private +fc_valid(fe) +FCacheEnt *fe; +{ + char path[MAXPATHLEN]; + struct stat stb; + time_t mtime; + + if (!fe->fe_okay) /* if not okay, don't */ + return(fe->fe_okay); /* bother with checks */ + time(&mtime); + if (fe->fe_vtime + FI_VALID_TIME < mtime) { + fe->fe_vtime = mtime; /* mark as new validate time */ + if ((fe->fe_pdir->flags & DID_FINDERINFO) == 0) /* always okay */ + return(fe->fe_okay); + OSfname(path, fe->fe_pdir, fe->fe_fnam, F_FNDR); +#ifdef NOCASEMATCH + if (stat(path, &stb) < 0) { + noCaseFind(path); + if (stat(path, &stb) < 0) + return(fe->fe_okay); /* nothing else we can do */ + } +#else NOCASEMATCH + if (stat(path, &stb) < 0) + return(fe->fe_okay); /* nothing else we can do */ +#endif NOCASEMATCH + if (stb.st_mtime != fe->fe_mtime) /* reload entry */ + return(FALSE); /* bad entry */ + } + return(fe->fe_okay); +} + +private void +fc_purge(fe) +FCacheEnt *fe; +{ + if (DBOSI) + printf("fc_purge: %s\n",fe->fe_fnam); + + if (fe == getfe) /* purging last get? */ + getfe = (FCacheEnt *) 0; /* yes... then zero */ + + free(fe->fe_fnam); /* always free the name */ + fe->fe_fnam = (char *) 0; /* and zero... */ + fe->fe_hash = 0; /* clear hash */ + fe->next = NULL; /* trash it here since want in both */ + if (fce == (FCacheEnt *) 0) /* check for recycled entry */ + lfce = fce = fe; /* if none then save */ + else { + lfce->next = fe; /* put at end of free list */ + lfce = fe; /* remember the end */ +#ifdef notdef + free(fe); /* and the struct itself */ +#endif + } +} + +private +fc_compare(fe,key) +FCacheEnt *fe,*key; +{ + if (fe->fe_pdir != key->fe_pdir) + return(FALSE); + if (fe->fe_hash != key->fe_hash) + return(FALSE); + return(strcmp(fe->fe_fnam,key->fe_fnam) == 0); +} + +private char * +fc_load(key) +FCacheEnt *key; +{ + FCacheEnt *fe; + + if (DBOSI) + printf("fc_load: %s\n",key->fe_fnam); + + if (fce != (FCacheEnt *) 0) { /* recycled fc avail? */ + fe = fce; /* yes... use that */ + fce = fce->next; + } else /* else allocate */ + fe = (FCacheEnt *) malloc(sizeof(FCacheEnt)); + + fe->fe_pdir = key->fe_pdir; + fe->fe_fnam = (char *) malloc(strlen(key->fe_fnam)+1); + fe->fe_okay = TRUE; + fe->fe_mtime = 0; + time(&fe->fe_vtime); /* validate time stamp */ + strcpy(fe->fe_fnam,key->fe_fnam); + fe->fe_hash = key->fe_hash; + fc_readent(fe); + return((char *) fe); +} + + + +private +fc_readent(fe) +FCacheEnt *fe; +{ + struct stat stb; + char path[MAXPATHLEN]; + int fd, ft, len, err; + FileInfo *fi = &fe->fe_fi; + FinderInfo *fndr; + extern struct ufinderdb uf[]; + extern int sessvers; + word newattr; +#ifdef USR_FILE_TYPES + extern struct uft uft[]; +#endif USR_FILE_TYPES + + bzero((char *) fi,sizeof(FileInfo)); /* make sure clear before */ + if (fe->fe_pdir->flags & DID_FINDERINFO) { + OSfname(path,fe->fe_pdir,fe->fe_fnam,F_FNDR); + if (DBOSI) + printf("fc_readent: reading %s\n",path); + +#ifndef STAT_CACHE + fd = open(path,O_RDONLY); +#else STAT_CACHE + fd = cwd_open(path,O_RDONLY); +#endif STAT_CACHE +#ifdef NOCASEMATCH + if (fd < 0) { + noCaseFind(path); +#ifndef STAT_CACHE + fd = open(path,O_RDONLY); +#else STAT_CACHE + fd = cwd_open(path,O_RDONLY); +#endif STAT_CACHE + } +#endif NOCASEMATCH + if (fd >= 0) { + OSLockFileforRead(fd); + err = fstat(fd, &stb); + len = read(fd,(char *) fi,sizeof(FileInfo)); + OSUnlockFile(fd); + if (len < FI_BASELENGTH) { + close(fd); + if (len == 0) /* length zero means creat'd */ + goto dummy; + if (DBOSI) + printf("fc_readent: finderinfo too short\n"); + goto nofileinfo; + } + if (fi->fi_magic1 != FI_MAGIC1) { + OldFileInfo *ofi = (OldFileInfo *)fi; + + bcopy(ofi->fi_comnt, fi->fi_comnt, fi->fi_magic1); + fi->fi_comln = fi->fi_magic1; + newattr = (sessvers >= AFPVersion2DOT0) ? (FPA_RNI|FPA_DEI) : 0; + if (ofi->fi_attr & FI_ATTR_SETCLEAR) + fi->fi_attr = ofi->fi_attr & + (FI_ATTR_READONLY|FI_ATTR_MUSER|FI_ATTR_INVISIBLE|newattr); + else + fi->fi_attr = 0; + fi->fi_magic1 = FI_MAGIC1; + fi->fi_magic = FI_MAGIC; + fi->fi_version = FI_VERSION; + fi->fi_bitmap = FI_BM_MACINTOSHFILENAME; +#ifdef SHORT_NAMES + ItoEName(fe->fe_fnam, fi->fi_macfilename); + ItoEName_Short(fe->fe_pdir,fe->fe_fnam,fi->fi_shortfilename); +#else SHORT_NAMES + ItoEName(fe->fe_fnam, fi->fi_macfilename); +#endif SHORT_NAMES + /* make sure we update it */ + WriteFA(fe->fe_pdir, fe->fe_fnam, fi); + } else { + if (fi->fi_magic != FI_MAGIC || fi->fi_version != FI_VERSION) { + if (DBOSI) + printf("fc_readent: fileinfo check fail\n"); + close(fd); + goto nofileinfo; + } + fi->fi_attr = ntohs(fi->fi_attr); + if (err == 0) /* stat okay */ + fe->fe_mtime = stb.st_mtime; /* remember mtime */ + } + if (close(fd) != 0) + printf("fc_readent: close error"); + return(noErr); + } + + /* Open failed for .finderinfo file. Use defaults finfo or zero if dir */ + + if (DBOSI) + printf("fc_readent: Open failed for %s\n",path); + } + +nofileinfo: + + /* convert name to internal name */ + OSfname(path,fe->fe_pdir,fe->fe_fnam,F_DATA); /* create plain name */ +#ifdef NOCASEMATCH +#ifndef STAT_CACHE + if (stat(path,&stb) != 0) { /* check if it exists */ +#else STAT_CACHE + if (OSstat(path,&stb) != 0) { /* check if it exists */ +#endif STAT_CACHE + noCaseFind(path); +#endif NOCASEMATCH +#ifndef STAT_CACHE + if (stat(path,&stb) != 0) /* check if it exists */ +#else STAT_CACHE + if (OSstat(path,&stb) != 0) /* check if it exists */ +#endif STAT_CACHE + return(aeObjectNotFound); /* no... */ +#ifdef NOCASEMATCH + } +#endif NOCASEMATCH + if (S_ISDIR(stb.st_mode)) { + fi->fi_comln = 0; + } else { + ft = os_getunixtype(path, &stb); + fndr = (FinderInfo *)fi->fi_fndr; /* get pointer to finder info */ +#ifdef USR_FILE_TYPES + if (ft >= FNDR_UFT) { + ft -= FNDR_UFT; /* ft is now the index into the UFT table */ + bcopy(uft[ft].uft_ftype, fndr->file.fdType, sizeof(uft[ft].uft_ftype)); + bcopy(uft[ft].uft_creat, fndr->file.fdCreator, sizeof(uft[ft].uft_creat)); + bcopy(uft[ft].uft_comment, fi->fi_comnt, uft[ft].uft_commentlen); + fi->fi_comln = uft[ft].uft_commentlen; + } else { +#endif USR_FILE_TYPES + bcopy(uf[ft].ufd_ftype, fndr->file.fdType, sizeof(fndr->file.fdType)); + bcopy(uf[ft].ufd_creat,fndr->file.fdCreator,sizeof(fndr->file.fdCreator)); + bcopy(uf[ft].ufd_comment, fi->fi_comnt, uf[ft].ufd_commentlen); + fi->fi_comln = uf[ft].ufd_commentlen; +#ifdef USR_FILE_TYPES + } +#endif USR_FILE_TYPES + } + fi->fi_attr = DEFATTR; /* set default attributes */ +dummy: + fi->fi_magic1 = FI_MAGIC1; + fi->fi_version = FI_VERSION; + fi->fi_magic = 0; /* mark as "default" entry */ + fi->fi_bitmap = FI_BM_MACINTOSHFILENAME; +#ifdef SHORT_NAMES + ItoEName(fe->fe_fnam, fi->fi_macfilename); + ItoEName_Short(fe->fe_pdir,fe->fe_fnam, fi->fi_shortfilename); +#else SHORT_NAMES + ItoEName(fe->fe_fnam, fi->fi_macfilename); +#endif SHORT_NAMES + return(noErr); +} + +private void +WriteFA(ipdir,fn,fi) +IDirP ipdir; +char *fn; +FileInfo *fi; +{ + char path[MAXPATHLEN]; + int fd; + int needu = 0; + + if ((ipdir->flags & DID_FINDERINFO) == 0) { + if (DBOSI) + printf("WriteFA skipped, no finderinfo directory\n"); + return; + } + OSfname(path,ipdir,fn,F_FNDR); /* convert to internal name */ + + if (DBOSI) + printf("WriteFA: writing %s\n",path); + + needu++; + fd = open(path,O_WRONLY); +#ifdef NOCASEMATCH + if(fd < 0) { + noCaseFind(path); + fd = open(path,O_WRONLY); + } +#endif NOCASEMATCH + if (fd < 0) { /* open for write */ + if (errno != ENOENT) { + printf("WriteFA: error opening %s errno=%d\n",path,errno); + return; + } + if ((fd = open(path,O_WRONLY|O_CREAT,0666)) < 0) { + if (DBOSI) + printf("fc_flush: create failed for %s %d\n",path,errno); + return; + } + if (DBOSI) + printf("fc_flush: created %s\n",path); + } else needu++; + fi->fi_attr = htons(fi->fi_attr); /* swab!!!@ */ + fi->fi_magic = FI_MAGIC; + OSLockFileforWrite(fd); + (void)write(fd,(char *) fi,sizeof(FileInfo)); /* write stuff */ + OSUnlockFile(fd); + if (close(fd) != 0) + printf("WriteFA: close error"); + fi->fi_attr = htons(fi->fi_attr); /* then swab back!!!@ */ + /* horrible hack, but can't use utimes, because we must be owner then */ + if (needu) { /* update directory modified time */ + OSfname(path,ipdir,FIDIRFN,F_FNDR); /* pick bad name */ + if ((fd = open(path, O_WRONLY|O_CREAT,0666)) < 0) + return; + close(fd); + unlink(path); + } +/* EModified(fe->fe_pdir); /* and mark directory as modified */ +} + +private FileInfo * +os_getfi(pdir,fn) +IDirP pdir; +char *fn; +{ + FCacheEnt key; + register long hash; + register char *p; + + key.fe_pdir = pdir; + p = key.fe_fnam = fn; + + hash = 0; + while (*p) { + hash <<= 2; + hash += *p++; + } + key.fe_hash = hash; + + /* do the "quick" check first */ + if (getfe == 0 || !fc_compare(getfe,&key)) { + /* nope, either find in cache or load from disk */ + getfe = (FCacheEnt *)GCLocate(fich,(char *)&key); + } + return(&getfe->fe_fi); /* and return the FileInfo */ +} + +FModified(pdir, fn) +IDirP pdir; +char *fn; +{ + FCacheEnt key; + register long hash; + register char *p; + int idx; + + key.fe_pdir = pdir; + p = key.fe_fnam = fn; + + hash = 0; + while (*p) { + hash <<= 2; + hash += *p++; + } + key.fe_hash = hash; + + EModified(pdir); /* make parent directory as modified */ + if (getfe == 0 || !fc_compare(getfe,&key)) { + /* no kept entry */ + if (!GCScan(fich, &key, &idx)) /* not in cache, already flushed */ + return; + getfe = (FCacheEnt *)GCidx2ent(fich, idx); + + } + if (DBOSI) + printf("Invalidating file cache entry %s/%s\n",pathstr(pdir),fn); + getfe->fe_okay = FALSE; /* invalidate entry */ +} + +/* + * OSValidateFICacheforEnum(directory) + * + * validate a file cache for enumerate + * + * make fc_flush only stat if .finderinfo directory modification time + * has changed -- writefa guarantees us that + * +*/ +OSValFICacheForEnum(pdir) +IDirP pdir; +{ + fc_flush_start(pdir); + GCFlush(fich, pdir); /* make sure valid */ + fc_flush_end(); +} + +/* + * OSSetFA(IDirP pdir, char *fn, word bm, FileDirParm *fdp) + * + * Set finder and attr for a file specified by ancestor directory + * (pdir) and file name (fn). The bitmap in bm specifies which + * FP_FINFO (DP_FINFO) or FP_ATTR (DP_ATTR) are to be set. + * + * Update to handle AFP 2.0 as defined by "Inside AppleTalk", 2nd Ed, p13-21 + * djh@munnari.OZ.AU, 06/12/90 + * + */ +OSSetFA(pdir,fn,bm,fdp) +IDirP pdir; +char *fn; +word bm; +FileDirParm *fdp; +{ + FileInfo *fi; + word attr,newattr; + extern int sessvers; + + fi = os_getfi(pdir,fn); + if (bm & FP_ATTR) { + attr = fdp->fdp_attr; + /* limit: allowed alter some bits only & protocol vers. dependant */ + newattr = (sessvers >= AFPVersion2DOT0) ? (FPA_RNI|FPA_DEI) : 0; + attr &= (FI_ATTR_READONLY|FI_ATTR_MUSER|FI_ATTR_INVISIBLE|newattr); + if (sessvers == AFPVersion1DOT1 && (attr & FI_ATTR_READONLY)) + attr |= (FPA_RNI|FPA_DEI); + if (fdp->fdp_attr & FI_ATTR_SETCLEAR) + fi->fi_attr |= attr; + else + fi->fi_attr &= ~attr; + } + +#ifdef USE_MAC_DATES + if (bm & FP_CDATE) { + fi->fi_datemagic = FI_MAGIC; + fi->fi_datevalid |= FI_CDATE; + bcopy(&fdp->fdp_cdate,fi->fi_ctime,sizeof(fi->fi_ctime)); + } + if (bm & FP_MDATE) { + time_t when; + time(&when); + fi->fi_datemagic = FI_MAGIC; + fi->fi_datevalid |= FI_MDATE; + bcopy(&when,fi->fi_utime,sizeof(fi->fi_utime)); + bcopy(&fdp->fdp_mdate,fi->fi_mtime,sizeof(fi->fi_mtime)); + } +#endif USE_MAC_DATES + + if (bm & FP_FINFO) { + bcopy(fdp->fdp_finfo,fi->fi_fndr,FINFOLEN); + if (!(bm & FP_PDOS)) /* setting finder info BUT NOT PRODOS */ + mapFNDR2PDOS(fdp); /* derive suitable File and Aux types */ + } + + if (bm & FP_PDOS) + if (!(bm & FP_FINFO)) /* setting PRODOS info BUT NOT finder */ + mapPDOS2FNDR(fdp,fi); /* derive suitable fdCreator & fdType */ + +#ifdef USE_MAC_DATES + if (bm & (FP_ATTR|FP_FINFO|FP_PDOS|FP_CDATE|FP_MDATE)) +#else USE_MAC_DATES + if (bm & (FP_ATTR|FP_FINFO|FP_PDOS)) +#endif USE_MAC_DATES + WriteFA(pdir,fn,fi); + return(noErr); +} + +OSGetAttr(pdir,fn,attr) +IDirP pdir; +char *fn; +word *attr; +{ + FileInfo *fi; + extern int sessvers; + + fi = os_getfi(pdir,fn); + *attr = fi->fi_attr; + if (sessvers == AFPVersion1DOT1) { + if (*attr & (FPA_RNI|FPA_DEI)) + *attr |= FI_ATTR_READONLY; + *attr &= FPA_MASK1; /* give only AFP 1.1 Attributes */ + } + return(noErr); +} + +OSSetAttr(pdir, fn, attr) +IDirP pdir; +char *fn; +word attr; +{ + FileInfo *fi; + + fi = os_getfi(pdir, fn); + fi->fi_attr = attr; + WriteFA(pdir, fn, fi); + return(noErr); +} + +OSGetFNDR(pdir,fn,finfo) +IDirP pdir; +char *fn; +byte *finfo; +{ + FileInfo *fi; + + fi = os_getfi(pdir,fn); + bcopy(fi->fi_fndr,finfo,FINFOLEN); + return(noErr); +} + +OSSetComment(pdir,fn,cs,cl) +IDirP pdir; +char *fn; +byte *cs; +byte cl; +{ + FileInfo *fi; + + fi = os_getfi(pdir,fn); + bcopy(cs,fi->fi_comnt,cl); + fi->fi_comln = cl; + WriteFA(pdir,fn,fi); + return(noErr); +} + +OSGetComment(pdir,fn,cs,cl) +IDirP pdir; +char *fn; +byte *cs; +byte *cl; +{ + FileInfo *fi; + + fi = os_getfi(pdir,fn); + *cl = fi->fi_comln; + if (*cl == 0) + *cs = 0; + bcopy(fi->fi_comnt,cs,*cl); + return(noErr); +} + +#ifdef USE_MAC_DATES +OSGetCDate(pdir,fn,cdate) +IDirP pdir; +char *fn; +time_t *cdate; +{ + FileInfo *fi; + + fi = os_getfi(pdir,fn); + if (fi->fi_datemagic != FI_MAGIC || (!(fi->fi_datevalid & FI_CDATE))) + return(-1); + bcopy(fi->fi_ctime,cdate,sizeof(fi->fi_ctime)); + return(noErr); +} + +OSGetMDate(pdir,fn,mdate,udate) +IDirP pdir; +char *fn; +time_t *mdate; +time_t *udate; +{ + FileInfo *fi; + + fi = os_getfi(pdir,fn); + if (fi->fi_datemagic != FI_MAGIC || (!(fi->fi_datevalid & FI_MDATE))) + return(-1); + bcopy(fi->fi_mtime,mdate,sizeof(fi->fi_mtime)); + bcopy(fi->fi_utime,udate,sizeof(fi->fi_utime)); + return(noErr); +} +#endif USE_MAC_DATES + +/* + * Establish the mac file name after a os_move + * +*/ +OSSetMacFileName(pdir, fn) +IDirP pdir; +char *fn; +{ + FileInfo *fi; + + fi = os_getfi(pdir,fn); +#ifdef SHORT_NAMES + ItoEName(fn, fi->fi_macfilename); + ItoEName_Short(pdir,fn, fi->fi_shortfilename); +#else SHORT_NAMES + ItoEName(fn, fi->fi_macfilename); +#endif SHORT_NAMES + WriteFA(pdir, fn, fi); + return(noErr); +} + +/* + * mapPDOS2FNDR + * + * convert ProDOS File Type and Aux Type to Finder Info + * (even to have to contemplate doing this is disgusting) + * + */ + +#define CTLEN 4 + +mapPDOS2FNDR(fdp,fi) +FileDirParm *fdp; +FileInfo *fi; +{ + FinderInfo *fndr; + byte prodos_at[4]; + byte prodos_ft[2]; + + if (fdp->fdp_flg == FDP_DIRFLG) /* directory, do nothing! */ + return; + + bcopy(&fdp->fdp_prodos_aux, prodos_at, sizeof(prodos_at)); + bcopy(&fdp->fdp_prodos_ft, prodos_ft, sizeof(prodos_ft)); + fndr = (FinderInfo *) fi->fi_fndr; + + switch (prodos_ft[0]) { + case 0x00: + bcopy("BINA", fndr->file.fdType, CTLEN); + break; + case 0xb3: + bcopy("PS16", fndr->file.fdType, CTLEN); + break; + case 0xff: + bcopy("PSYS", fndr->file.fdType, CTLEN); + break; + case 0x04: + if (prodos_at[0] == 0x00 && prodos_at[1] == 0x00) { + bcopy("TEXT", fndr->file.fdType, CTLEN); + break; + } /* else fall thro' */ + default: + /* some fdTypes will be unprintable */ + fndr->file.fdType[0] = 'p'; + fndr->file.fdType[1] = prodos_ft[0]; /* file type */ + fndr->file.fdType[2] = prodos_at[1]; /* high byte */ + fndr->file.fdType[3] = prodos_at[0]; /* low byte */ + break; + } + bcopy("pdos", fndr->file.fdCreator, CTLEN); +} + +/* + * mapFNDR2PDOS + * + * convert Finder Info to ProDOS File Type and Aux Type + * (even to have to contemplate doing this is disgusting) + * + */ + +mapFNDR2PDOS(fdp) +FileDirParm *fdp; +{ + FinderInfo *fndr; + byte prodos_at[4]; + byte prodos_ft[2]; + byte hex2num(); + + fndr = (FinderInfo *) fdp->fdp_finfo; + + if (fdp->fdp_flg == FDP_DIRFLG) { + /* set specific ProDOS directory information */ + prodos_ft[0] = 0x0f; + prodos_ft[1] = 0x00; + prodos_at[0] = 0x00; + prodos_at[1] = 0x02; + prodos_at[2] = 0x00; + prodos_at[3] = 0x00; + bcopy(prodos_ft, &fdp->fdp_prodos_ft, sizeof(prodos_ft)); + bcopy(prodos_at, &fdp->fdp_prodos_aux, sizeof(prodos_at)); + return; + } + if (bcmp("TEXT", fndr->file.fdType, CTLEN) == 0) { + prodos_ft[0] = 0x04; + prodos_ft[1] = 0x00; + bzero(&fdp->fdp_prodos_aux, sizeof(prodos_at)); + bcopy(prodos_ft, &fdp->fdp_prodos_ft, sizeof(prodos_ft)); + return; + } + if (bcmp("pdos", fndr->file.fdCreator, CTLEN) == 0) { + if (bcmp("PSYS", fndr->file.fdType, CTLEN) == 0) { + prodos_ft[0] = 0xff; + prodos_ft[1] = 0x00; + bcopy(prodos_ft, &fdp->fdp_prodos_ft, sizeof(prodos_ft)); + return; + } + if (bcmp("PS16", fndr->file.fdType, CTLEN) == 0) { + prodos_ft[0] = 0xb3; + prodos_ft[1] = 0x00; + bcopy(prodos_ft, &fdp->fdp_prodos_ft, sizeof(prodos_ft)); + return; + } + if (bcmp("BINA", fndr->file.fdType, CTLEN) == 0) { + prodos_ft[0] = 0x00; + prodos_ft[1] = 0x00; + bcopy(prodos_ft, &fdp->fdp_prodos_ft, sizeof(prodos_ft)); + return; + } + if (fndr->file.fdType[0] == 'p') { /* bizarre */ + prodos_ft[0] = fndr->file.fdType[1]; + prodos_ft[1] = 0x00; + prodos_at[0] = fndr->file.fdType[3]; /* low byte */ + prodos_at[1] = fndr->file.fdType[2]; /* high byte */ + prodos_at[2] = 0x00; + prodos_at[3] = 0x00; + bcopy(prodos_ft, &fdp->fdp_prodos_ft, sizeof(prodos_ft)); + bcopy(prodos_at, &fdp->fdp_prodos_aux, sizeof(prodos_at)); + return; + } + if (fndr->file.fdType[2] == ' ' && fndr->file.fdType[3] == ' ') { + short num; /* much more bizarre */ + num = hex2num(fndr->file.fdType[0]) << 4; + num |= hex2num(fndr->file.fdType[1]) & 0xf; + prodos_ft[0] = (byte) num & 0xff; + prodos_ft[1] = 0x00; + bcopy(prodos_ft, &fdp->fdp_prodos_ft, sizeof(prodos_ft)); + return; + } + } + /* otherwise */ + prodos_ft[0] = 0x00; + prodos_ft[1] = 0x00; + bzero(&fdp->fdp_prodos_aux, sizeof(prodos_at)); + bcopy(prodos_ft, &fdp->fdp_prodos_ft, sizeof(prodos_ft)); +} + +byte +hex2num(ch) +char ch; +{ + if (ch >= '0' && ch <= '9') + return(ch - '0'); + if (ch >= 'a' && ch <= 'f') + return(ch - 'a' + 10); + if (ch >= 'A' && ch <= 'F') + return(ch - 'A' + 10); + return(0); +} diff --git a/applications/aufs/afposncs.c b/applications/aufs/afposncs.c new file mode 100644 index 0000000..dc96828 --- /dev/null +++ b/applications/aufs/afposncs.c @@ -0,0 +1,817 @@ +/* + * $Author: djh $ $Date: 1996/03/11 12:34:09 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afposncs.c,v 2.9 1996/03/11 12:34:09 djh Rel djh $ + * $Revision: 2.9 $ +*/ + +/* + * afposncs.c - Appletalk Filing Protocol OS Normalizing for Character Sets + * + * + * afposncs contains the logic to normalize between an OS character + * set and a Macintosh character set. It decides when a translation + * is necessary (based on file extentions and file types) and does + * the actual translation. It is particularly useful for normalizing + * between unix files on machines that support an international + * character set and the macintosh character set. + * Note: logic to map between EOL chars was moved here as well. + * + * + * The intent is to provide some "transparency". Translation is done + * based on a the ncs_transtable which defines which file + * type/creators get translated. In addition, it possible to specify + * that files with particular suffixes (and file type/creators) get + * translated. + * + * Caveat: much more massive changes must be made to allow for mapping + * from a single character to multiple (or vice versa). + * + * Jan 1988 Dan Sahlin Transliteration tables for .ascii, .swe, + * .fin and .latin1 + * Feb 1988 Charlie C. Kim Rewrite all but tables. + * + */ + +#include +#include +#include +#include +#include "afposncs.h" +#include "afps.h" +#ifdef USR_FILE_TYPES +#include "afpudb.h" +#endif USR_FILE_TYPES + +/* to_ tables are to unix from mac */ +/* from_ tables are from unix to mac */ + +/* ASCII: only a CR-LF mapping is performed */ +/* These are actually dummy table since it faster to compare than */ +/* index and store for the simple translation involved */ +private byte to_ascii[1]; /* mac to unix: dummy table */ +private byte from_ascii[1]; /* unix to mac: dummy table */ +#ifdef SHORT_NAMES +private byte pc_to_ascii[1]; +private byte pc_from_ascii[1]; +#endif SHORT_NAMES +#ifdef USR_FILE_TYPES +#ifdef ISO_TRANSLATE +private byte iso[1]; +#endif ISO_TRANSLATE +private byte raw[1]; +private byte text[1]; +private byte ascii[1]; +extern struct uft uft[]; +#endif USR_FILE_TYPES +#ifdef ISO_TRANSLATE +extern u_char ISO2Mac[]; +extern u_char Mac2ISO[]; +#endif ISO_TRANSLATE + +#ifdef notdef +private byte to_ascii[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, '\n', 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 +}; + +private byte from_ascii[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, '\r', 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 +}; +#endif notdef + +#ifdef FULL_NCS_SUPPORT +/* + * SWEdish standard D47, national characters {}|[]\ are mapped, + * characters with bit 8 set are untouched +*/ +private byte to_swe[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, '\n', 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, + '[' , ']' , 0x82, 0x83, 0x84, '\\', 0x86, 0x87, /*!*/ + 0x88, 0x89, '{' , 0x8b, '}' , 0x8d, 0x8e, 0x8f, /*!*/ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, '|' , 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 +}; + +private byte from_swe[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, '\r', 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, 0x80, 0x85, 0x81, 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, 0x8a, 0x9a, 0x8c, 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 +}; + +/* + * FINnish/Swedish standard D47, national characters {}|[]\~`@^ are mapped, + * characters with bit 8 set are untouched +*/ +byte to_fin[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, '\n', 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, + '[' , ']' , 0x82, 0x40, 0x84, '\\', 0x5e, 0x87, /*!*/ + 0x88, 0x89, '{' , 0x8b, '}' , 0x8d, 0x60, 0x8f, /*!*/ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, '|' , 0x9b, 0x9c, 0x9d, 0x9e, 0x7e, /*!*/ + 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 +}; + +private byte from_fin[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, '\r', 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, + 0x83, 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, 0x80, 0x85, 0x81, 0x86, 0x5f, /*!*/ + 0x8e, 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, 0x8a, 0x9a, 0x8c, 0x9f, 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 +}; + +/* + * ISO 8859 Latin1, 8-bit code, + * an attempt is made to map all characters into something similar + * but some characters without a similar character in latin1 + * are mapped into SUB (0x1a). + * Since it is not possible to map one character into two, no correct + * mapping CR <-> CRLF can be done here. + * This table has not been tested agains other software using latin1, + * so there might be some errors in the table. +*/ +private byte to_latin1[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, '\n', 0x0e, 0x0f, /*!*//*? really CRLF */ + 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, + 0xc4, 0xc5, 0xc7, 0xc9, 0xd1, 0xd6, 0xdc, 0xe1, /* ! */ + 0xe0, 0xe2, 0xe4, 0xe3, 0xe5, 0xe7, 0xe9, 0xe8, /* ! */ + 0xea, 0xeb, 0xed, 0xec, 0xee, 0xef, 0xf1, 0xf3, /* ! */ + 0xf2, 0xf4, 0xf6, 0xf5, 0xfa, 0xf9, 0xfb, 0xfc, /* ! */ + 0x1a, 0xb0, 0xa2, 0xa3, 0xa7, 0xb7, 0xb6, 0xdf, /* ! */ + 0xae, 0xa9, 0x1a, 0xb4, 0xa8, 0x1a, 0xc6, 0xd8, /* ! */ + 0x1a, 0xb1, 0x1a, 0x1a, 0xa5, 0xb5, 0x1a, 0x1a, /* ! */ + 0x1a, 0x1a, 0x1a, 0xaa, 0xba, 0x1a, 0xe6, 0xf8, /* ! */ + 0xbf, 0xa1, 0xac, 0x1a, 0x1a, 0x1a, 0x1a, 0xab, /* ! */ + 0xbb, 0x1a, 0xa0, 0xc0, 0xc3, 0xd5, 0x1a, 0x1a, /* ! */ + 0x2d, 0x2d, 0x22, 0x22, 0x60, 0x27, 0xf7, 0x1a, /* ! */ + 0xff, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, /* ! */ + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, /* ! */ + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, /* ! */ + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, /* ! */ + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a /* ! */ +}; +/* + * if no similar character is found 0xff is returned + * 0x1a is also defined as 0xff +*/ +private byte from_latin1[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, '\r', 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*!*/ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0xff, 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, + 0xca, 0xc1, 0xa2, 0xa3, 0xff, 0xb4, 0x7c, 0xa4, + 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xff, 0xa8, 0xff, + 0xa1, 0xb1, 0xff, 0xff, 0x27, 0xb5, 0xa6, 0xa5, + 0x2c, 0xff, 0xbc, 0xc8, 0xff, 0xff, 0xff, 0xc0, + 0xcb, 'A' , 'A' , 0xcc, 0x80, 0x81, 0xae, 0x82, + 'E' , 0x83, 'E' , 'E' , 'I' , 'I' , 'I' , 'I' , + 'D' , 0x84, 'O' , 'O' , 'O' , 0xcd, 0x85, 0xff, + 0xaf, 'U' , 'U' , 'U' , 0x86, 'Y' , 0xff, 0xa7, + 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, + 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, + 0xff, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, + 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 'y' , 0xff, 0xd8 +}; + +#endif /* FULL_NCS_SUPPORT */ + +#define NCS_entry(name, suf, conj, creator, type, totable, fromtable) \ + { name, suf, sizeof(suf)-1, conj, (byte *) creator, sizeof(creator)-1, \ + (byte *) type, sizeof(type)-1, totable, fromtable } + +/* + * NCS_entry: + * suffix to map on + * conj: AND for suffix and creator/type, OR for suffix or creator/type + * creator/type: macintosh file creator/type + * totable - translate to unix from macintosh + * fromtable - translate from unix to macintosh + * + * NULL entries for suffix or file type/creator always match + * Table is searched sequentially so put the more specific items near the + * start. + * +*/ +NCS_TransTable ncs_transtables[] = { +#ifdef FULL_NCS_SUPPORT + /* Swedish standard D47 translation */ + NCS_entry("Swedish D47",".swe",NCS_AND,NULL,NULL, to_swe, from_swe), + /* Swedish-Finnish standard E47 translation */ + NCS_entry("Swedish-Finnish E47",".fin",NCS_AND,NULL, NULL, to_fin, from_fin), + /* ISO 8859-1 latin1 */ + NCS_entry("ISO 8859-1 Latin 1", ".latin1", NCS_AND, NULL, NULL, + to_latin1, from_latin1), +#endif FULL_NCS_SUPPORT + +#if defined (ISO_TRANSLATE) & defined (ISO_FILE_TRANS) +#ifdef NCS_ALL_TEXT + /* ISO 8859-1 latin1 translation for all TEXT files */ + NCS_entry("ISO 8859-1 Latin 1", NULL, NCS_AND, NULL, "TEXT", + Mac2ISO, ISO2Mac), +#else NCS_ALL_TEXT + /* ISO 8859-1 latin1 translation for unix/TEXT files */ + NCS_entry("ISO 8859-1 Latin 1", NULL, NCS_AND, "unix", "TEXT", + Mac2ISO, ISO2Mac), +#endif NCS_ALL_TEXT +#endif ISO_TRANSLATE & ISO_FILE_TRANS + +#ifdef NCS_ALL_TEXT + /* ASCII translation for all TEXT files */ + NCS_entry("ascii", NULL, NCS_AND, NULL, "TEXT", + to_ascii, from_ascii), +#else NCS_ALL_TEXT + /* ASCII translation for unix/TEXT files */ + NCS_entry("ascii", NULL, NCS_AND, "unix", "TEXT", + to_ascii, from_ascii), +#endif NCS_ALL_TEXT + +#ifdef SHORT_NAMES + NCS_entry("pc-ascii", NULL, NCS_AND, "unix", "TEXT", + pc_to_ascii, pc_from_ascii), +#endif SHORT_NAMES + {NULL} +}; + +#define NCS_TRANSTABLELEN ((sizeof(ncs_transtables)/sizeof(NCS_TransTable))-1) + + +/* + * find the translation table and return index. return -1 o.w. +*/ +int +#ifdef SHORT_NAMES +ncs_istrans(finderinfo, file, ptype) +byte ptype; +#else SHORT_NAMES +ncs_istrans(finderinfo, file) +#endif SHORT_NAMES +fileFinderInfo *finderinfo; +char *file; +{ + int i, idx; + register NCS_TransTable *tt; + int suffixmatch, finderinfomatch; + +#ifdef USR_FILE_TYPES + /* check for file suffix */ + if ((i = uft_match(file)) >= 0) { + /* must also be of the correct creator & type */ + if (bcmp(uft[i].uft_creat, finderinfo->fdCreator, 4) == 0 + && bcmp(uft[i].uft_ftype, finderinfo->fdType, 4) == 0) + return(i+FNDR_UFT); + } + /* suffix check failed, so check table for file creator and type */ + if ((i = uft_match_finfo(finderinfo->fdCreator, finderinfo->fdType)) >= 0) + return(i+FNDR_UFT); +#endif USR_FILE_TYPES + + for (idx = 0, tt = ncs_transtables; idx < NCS_TRANSTABLELEN; tt++, idx++) { + suffixmatch = (tt->ncs_suffix == NULL) || + (file && (i = strlen(file)) >= tt->ncs_suffix_len && + strcmp(tt->ncs_suffix, file+i-tt->ncs_suffix_len)==0); + if (suffixmatch) { + if (tt->ncs_conj == NCS_OR) + return(idx); + } else { + if (tt->ncs_conj == NCS_AND) + continue; + } + /* see if file type and file creator match (except if null, then */ + /* ignored). Partial matches are acceptable -- depends on what */ + /* is specified */ + finderinfomatch = FALSE; + if (tt->ncs_file_creator == NULL || + bcmp(tt->ncs_file_creator, finderinfo->fdCreator, tt->ncs_fc_len) == 0) + if (tt->ncs_file_type == NULL || + bcmp(tt->ncs_file_type, finderinfo->fdType, tt->ncs_ft_len) == 0) + finderinfomatch = TRUE; +#ifdef SHORT_NAMES +printf("suffixmatch %d, finderinfomatch %d, ptype 0x%x\n",suffixmatch,finderinfomatch,ptype); +#endif SHORT_NAMES + switch (tt->ncs_conj) { + case NCS_OR: +#ifdef SHORT_NAMES + if ((suffixmatch || finderinfomatch) && (ptype == 0x01)) + return(idx+1); +/* so,,,, this is bad, and will only work when there + are 2 entries in the trans table. */ +#endif SHORT_NAMES + if (suffixmatch || finderinfomatch) + return(idx); + break; + case NCS_AND: +#ifdef SHORT_NAMES + if (suffixmatch && finderinfomatch && (ptype == 0x01)) { +printf("returning idx+1 = %d\n",idx+1); + return(idx+1); + } +#endif SHORT_NAMES + if (suffixmatch && finderinfomatch) + return(idx); + break; + } + } + return(-1); /* no match */ +} + +/* + * return the name of a translation table given its index + * +*/ +char * +ncs_tt_name(ttidx) +int ttidx; +{ +#ifdef USR_FILE_TYPES + if (ttidx >= FNDR_UFT) + return("user specified translation"); +#endif USR_FILE_TYPES + if (ttidx < 0 || ttidx >= NCS_TRANSTABLELEN) + return("no translation"); + return(ncs_transtables[ttidx].ncs_trans_name); +} + +ncs_translate(which, ttidx, buf, cnt) +int which; +int ttidx; +byte *buf; +register int cnt; +{ + register byte *bp; + register byte *tt; + register byte tc, fc; +#ifdef USR_FILE_TYPES + int texttrans = 0; +#endif USR_FILE_TYPES + + if (ttidx < 0 || ttidx >= NCS_TRANSTABLELEN) +#ifdef USR_FILE_TYPES + if (ttidx < FNDR_UFT) +#endif USR_FILE_TYPES + return; + + switch (which) { + case NCS_TRANS_UNIXTOMAC: +#ifdef USR_FILE_TYPES + if (ttidx >= FNDR_UFT) { + tt = uft[ttidx-FNDR_UFT].uft_xlate; + if (tt == ascii || tt == text) + tt = from_ascii; + } else +#endif USR_FILE_TYPES + tt = ncs_transtables[ttidx].ncs_fromtable; + break; + case NCS_TRANS_MACTOUNIX: +#ifdef USR_FILE_TYPES + if (ttidx >= FNDR_UFT) { + tt = uft[ttidx-FNDR_UFT].uft_xlate; + if (tt == text) + texttrans = 1; + if (tt == ascii || tt == text) + tt = to_ascii; + } else +#endif USR_FILE_TYPES + tt = ncs_transtables[ttidx].ncs_totable; + break; + default: + return; + } +#ifdef USR_FILE_TYPES + if (tt == raw) /* no translation */ + return; +#endif USR_FILE_TYPES + bp = buf; + if (tt == to_ascii || tt == from_ascii) { + /* by pass for to_ascii, and from_ascii to speed things up */ + if (tt == to_ascii) + fc = ENEWLINE, tc = INEWLINE; + else + fc = INEWLINE, tc = ENEWLINE; + while (cnt--) { +#ifdef USR_FILE_TYPES + if (texttrans) { + switch (*bp) { + case ELDQUOTE: + case ERDQUOTE: + *bp++ = IDQUOTE; + continue; + break; + case ELSQUOTE: + case ERSQUOTE: + *bp++ = ISQUOTE; + continue; + break; + } + } +#endif USR_FILE_TYPES +#ifndef NONLXLATE + if (*bp == fc) + *bp = tc; +#endif NONLXLATE + bp++; + } +#ifdef SHORT_NAMES + } else if ((tt == pc_to_ascii) || (tt == pc_from_ascii)) { + while (cnt--) + bp++; +#endif SHORT_NAMES +#if defined (USR_FILE_TYPES) & defined (ISO_TRANSLATE) + } else if (tt == iso) { + while (cnt--) { + switch (which) { + case NCS_TRANS_UNIXTOMAC: + *bp = (*bp == INEWLINE) ? ENEWLINE : + (*bp & 0x80) ? ISO2Mac[*bp & 0x7f] : *bp; + break; + case NCS_TRANS_MACTOUNIX: + *bp = (*bp == ENEWLINE) ? INEWLINE : + (*bp & 0x80) ? Mac2ISO[*bp & 0x7f] : *bp; + break; + } + bp++; + } +#endif USR_FILE_TYPES & ISO_TRANSLATE +#if defined (ISO_TRANSLATE) & defined (ISO_FILE_TRANS) + } else if ((tt == ISO2Mac) || (tt == Mac2ISO)) { + while (cnt--) { + switch (which) { + case NCS_TRANS_UNIXTOMAC: + *bp = (*bp == INEWLINE) ? ENEWLINE : + (*bp & 0x80) ? ISO2Mac[*bp & 0x7f] : *bp; + break; + case NCS_TRANS_MACTOUNIX: + *bp = (*bp == ENEWLINE) ? INEWLINE : + (*bp & 0x80) ? Mac2ISO[*bp & 0x7f] : *bp; + break; + } + bp++; + } +#endif ISO_TRANSLATE & ISO_FILE_TRANS + } else { + /* standard */ + while (cnt--) { + *bp = tt[*bp]; + bp++; + } + } +} + +#define strval(x) ((x) == NULL ? "" : (x)) + +ncs_table_dump() +{ + register NCS_TransTable *tt; + int idx; + + for (idx = 0, tt = ncs_transtables; idx < NCS_TRANSTABLELEN; tt++, idx++) { + logit(0," %s: xlate on: suffix %s %s file type %s, creator %s", + tt->ncs_trans_name, + strval(tt->ncs_suffix), + (tt->ncs_conj == NCS_AND ? "and" : "or"), + strval((char *)tt->ncs_file_type), + strval((char *)tt->ncs_file_creator)); + } +} + +#ifdef USR_FILE_TYPES +/* + * The file .afpfile (or afpfile) in the user's home directory and/or + * the file denoted by the -F option specifies the treatment to be applied + * to UNIX files with certain file suffixes, eg: .c .h .gif + * + * The file data may be copied unchanged ('raw'), have NL characters + * converted to CR and vice-versa ('ascii') or have Mac quote characters + * converted to UNIX quotes (but not vice-versa, also implies 'ascii'). + * If ISO_TRANSLATE is defined, also handle Mac <-> ISO translation ('iso'). + * + * Each file may also have the Macintosh file Creator and Type specified. + * For example, .c files can be represented as 'MPS '/'TEXT' files and be + * double-clicked to open the MPW editor. + * + * Different comments may also be provided for different file suffixes. + * EG: "This is a UNIX GIF file." for .gif + * + * Weird behaviour: + * MicroSoft Word writes the file back as type 'WDBN' and later + * changes it to type 'TEXT' (when saving as text). This defeats + * the user file type code. + * + * The MPW editor writes a file back as file-N which defeats the + * suffix test. To get around this, files are also checked for + * a listed file creator and type. If found, the specified translation + * method is used. + * + * djh@munnari.OZ.AU + * + */ + +int uftidx = 0; +char *lineptr; + +/* + * read the user specified file mappings + * + */ + +int +read_uft(file) +char *file; +{ + char string[256]; + char *p, line[256]; + FILE *fp, *fopen(); + + if ((fp = fopen(file, "r")) != NULL) { + while (fgets(line, sizeof(line), fp) != NULL) { + if (line[0] == '#' || line[0] == '\n') + continue; /* ignore comments, blank lines */ + if ((p = (char *)index(line, '\n')) != NULL) + *p = '\0'; /* kill line feeds from fgets() */ + lineptr = line; + /* file suffix, use "*" as wildcard */ + gettoken(uft[uftidx].uft_suffix, sizeof(uft[uftidx].uft_suffix)); + if (uft[uftidx].uft_suffix[0] == '*') + uft[uftidx].uft_suffix[0] = '\0'; + uft[uftidx].uft_suffixlen = strlen(uft[uftidx].uft_suffix); + /* translation requested */ + gettoken(string, sizeof(string)); + switch (string[0]) { + case 'R': case 'r': /* raw */ + uft[uftidx].uft_xlate = raw; + break; + case 'T': case 't': /* text */ + uft[uftidx].uft_xlate = text; + break; + case 'A': case 'a': /* ascii */ + uft[uftidx].uft_xlate = ascii; + break; +#ifdef ISO_TRANSLATE + case 'I': case 'i': /* ISO */ + uft[uftidx].uft_xlate = iso; + break; +#endif ISO_TRANSLATE + default: /* ignore */ + continue; + break; + } + /* file creator */ + gettoken(uft[uftidx].uft_creat, sizeof(uft[uftidx].uft_creat)); + if (uft[uftidx].uft_creat[0] == '\0') + bcopy(DEFFCREATOR,uft[uftidx].uft_creat,sizeof(uft[uftidx].uft_creat)); + /* file type */ + gettoken(uft[uftidx].uft_ftype, sizeof(uft[uftidx].uft_ftype)); + if (uft[uftidx].uft_ftype[0] == '\0') + bcopy(DEFFTYPE, uft[uftidx].uft_ftype, sizeof(uft[uftidx].uft_ftype)); + /* user comment */ + uft[uftidx].uft_comment = DEFCMNT; + gettoken(string, sizeof(string)); + if (string[0] != '\0') { + if ((p = (char *)malloc(strlen(string)+1)) != NULL) { + bcopy(string, p, strlen(string)+1); + uft[uftidx].uft_comment = p; + } + } + uft[uftidx].uft_commentlen = strlen(uft[uftidx].uft_comment); + uftidx++; + } + fclose(fp); + } + uft[uftidx].uft_suffixlen = -1; +} + +/* + * parse the user specified suffix/type mappings file + * + */ + +int +gettoken(str, strn) +char *str; +int strn; +{ + while (*lineptr == ' ' || *lineptr == '\t') + lineptr++; /* skip leading white space */ + if (*lineptr == '\'' || *lineptr == '"') { + lineptr++; /* skip initial quote */ + while (!(*lineptr == '\'' || *lineptr == '"')) { + if (strn-- > 0) + *str++ = *lineptr; + if (*lineptr == '\0') + return(0); + lineptr++; + } + lineptr++; /* skip trailing quote */ + if (strn > 0) /* terminate if space */ + *str = '\0'; + return(0); + } + while (*lineptr != '\0' && *lineptr != ' ' && *lineptr != '\t') { + if (strn-- > 0) + *str++ = *lineptr; + if (*lineptr == '\0') + return(0); + lineptr++; + } + if (strn > 0) /* terminate if space */ + *str = '\0'; +} +#endif USR_FILE_TYPES diff --git a/applications/aufs/afposncs.h b/applications/aufs/afposncs.h new file mode 100644 index 0000000..d124655 --- /dev/null +++ b/applications/aufs/afposncs.h @@ -0,0 +1,40 @@ +/* + * $Author: djh $ $Date: 91/02/15 21:08:58 $ + * $Header: afposncs.h,v 2.1 91/02/15 21:08:58 djh Rel $ + * $Revision: 2.1 $ +*/ + + +/* + * afposncs.h - Appletalk Filing Protocol OS Normalizing for Character Sets + * + * structures used to hold normalizing character set routines and declarations + * + * + * Feb 1988 Charlie C. Kim + * + */ + +typedef struct { /* trans tab */ + char *ncs_trans_name; /* Translation name */ + char *ncs_suffix; /* Suffix to translate on */ + int ncs_suffix_len; + int ncs_conj; /* conjunction (suffix creator/type */ + byte *ncs_file_creator; /* File creator to xlate on */ + int ncs_fc_len; + byte *ncs_file_type; /* File type to xlate on */ + int ncs_ft_len; + byte *ncs_totable; + byte *ncs_fromtable; +} NCS_TransTable; + +/* extern TransTab transtables[]; */ + +char *ncs_tt_name(); +int ncs_istrans(); + +#define NCS_TRANS_UNIXTOMAC 1 +#define NCS_TRANS_MACTOUNIX 2 + +#define NCS_AND 0 +#define NCS_OR 1 diff --git a/applications/aufs/afppasswd.c b/applications/aufs/afppasswd.c new file mode 100644 index 0000000..efea86d --- /dev/null +++ b/applications/aufs/afppasswd.c @@ -0,0 +1,167 @@ +/* + * $Author: djh $ $Date: 91/03/13 20:29:11 $ + * $Header: afppasswd.c,v 2.2 91/03/13 20:29:11 djh Exp $ + * $Revision: 2.2 $ +*/ + +/* + * afppasswd.c - Appletalk Filing Protocol - support for internal password file + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * June 1987 CCKim Created. + * + */ +#include +#include +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +static char *mypasswdfile = NULL; +static FILE *pfp = NULL; + +typedef struct { + char uname[9]; /* unix user name that name maps to */ + char passwd[9]; /* password to use */ + char name[40]; /* up 39 char user name */ +} aufspasswd; + +rewaufs() +{ + if (pfp == NULL) + if ((pfp=fopen(mypasswdfile, "r")) == NULL) + return(FALSE); + rewind(pfp); + return(TRUE); +} + +endaufspw() +{ + if (pfp != NULL) + fclose(pfp); +} + +static aufspasswd * +getaufspwent() +{ + static aufspasswd apd; + char buf[128]; + int cnt; + + while (fgets(buf, 128, pfp) != NULL) { + cnt = sscanf(buf, "USER: %s\tUNIXUSER: %s\tPASSWORD: %s", + apd.name, apd.uname, apd.passwd); + if (cnt != 3) + continue; + if (strlen(apd.name) > 39 || strlen(apd.uname) > 8 || + strlen(apd.passwd) > 8) { + logit(0,"Bad password file entry: %s", buf); + continue; + } + return(&apd); + } + return(NULL); +} + +static aufspasswd * +getaufspwnam(nam) +char *nam; +{ + static aufspasswd *apd; + + if (apd && strcmp(apd->name, nam) == 0) + return(apd); + if (!rewaufs()) + return(NULL); + while ((apd=getaufspwent()) != NULL) + /* should be case insensitive */ + if (strcmp(nam, apd->name) == 0 && getpwnam(apd->uname) != NULL) { + endaufspw(); + return(apd); + } + endaufspw(); + apd = NULL; + return(NULL); +} + +init_aufs_passwd(pw) +char *pw; +{ + struct stat buf; + +# ifndef NOLSTAT + if (lstat(pw, &buf) < 0) { + logit(0,"error: can't find aufs password file %s",pw); + return(FALSE); + } +# else + if (stat(pw, &buf) < 0) { + logit(0,"error: can't find aufs password file %s",pw); + return(FALSE); + } +#endif + if ((buf.st_mode & S_IFMT) != S_IFREG) { + logit(0,"error: aufs password file %s isn't a regular file", pw); + return(FALSE); + } + if (buf.st_uid != 0) { +#ifdef DEBUG + logit(0,"debug: aufs password file %s not owned by root", pw); +#else + logit(0,"error: aufs password file %s not owned by root", pw); + return(FALSE); +#endif + } + if (buf.st_mode & 002) { + logit(0,"error: aufs password file %s is writable by world",pw); + return(FALSE); + } + if (buf.st_mode & 020) + logit(0,"warning: aufs password file %s is writable by group",pw); + if (buf.st_mode & 004) + logit(0,"warning: aufs password file %s is readable by world",pw); + mypasswdfile = pw; + return(TRUE); +} + +int +is_aufs_user(nam) +char *nam; +{ + if (getaufspwnam(nam) == NULL) + return(FALSE); + return(TRUE); +} + + +char * +user_aufs_passwd(nam) +char *nam; +{ + aufspasswd *apd; + + if ((apd=getaufspwnam(nam)) == NULL) + return(NULL); + return(apd->passwd); +} + +struct passwd * +aufs_unix_user(nam) +char *nam; +{ + aufspasswd *apd; + + if ((apd=getaufspwnam(nam)) == NULL) + return(NULL); + return(getpwnam(apd->uname)); +} + diff --git a/applications/aufs/afppasswd.h b/applications/aufs/afppasswd.h new file mode 100644 index 0000000..9a305fb --- /dev/null +++ b/applications/aufs/afppasswd.h @@ -0,0 +1,24 @@ +/* + * $Author: djh $ $Date: 91/02/15 21:09:06 $ + * $Header: afppasswd.h,v 2.1 91/02/15 21:09:06 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * afppasswd.h - Appletalk Filing Protocol - support for internal password file + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * June 1987 CCKim Created. + * +*/ + +boolean init_aufs_passwd(); +boolean is_aufs_user(); +char *user_aufs_passwd(); +struct passwd *aufs_unix_user(); diff --git a/applications/aufs/afps.h b/applications/aufs/afps.h new file mode 100644 index 0000000..205cdd7 --- /dev/null +++ b/applications/aufs/afps.h @@ -0,0 +1,259 @@ +/* + * $Author: djh $ $Date: 1996/06/19 04:04:29 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afps.h,v 2.11 1996/06/19 04:04:29 djh Rel djh $ + * $Revision: 2.11 $ + * + */ + +/* + * afps.h - Appletalk Filing Protocol Common Server Definitions + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1987,1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * April 1987 Schilit Created. + * + */ + +extern int statflg; /* external stats flag */ +extern int afp_dbug; /* the debug flags */ + +#define uSEC 1000000 /* microseconds in a second */ +typedef OSErr (*PFE)(); /* function returning OSErr */ +/* volume bit map - limited to long by macros below */ +typedef long VolBitMap; /* longer than long is a bad idea?*/ + +/* All File System Calls */ + +OSErr FPByteRangeLock(), FPCloseVol(), FPCloseDir(), FPCloseFork(); +OSErr FPCopyFile(), FPCreateDir(), FPCreateFile(), FPDelete(); +OSErr FPEnumerate(), FPFlush(), FPFlushFork(), FPGetForkParms(); +OSErr FPGetSrvrInfo(), FPGetSrvrParms(), FPGetVolParms(), FPLogin(); +OSErr FPLoginCont(), FPLogout(), FPMapID(), FPMapName(); +OSErr FPMove(), FPOpenVol(), FPOpenDir(), FPOpenFork(); +OSErr FPRead(), FPRename(), FPSetDirParms(), FPSetFileParms(); +OSErr FPSetForkParms(), FPSetVolParms(), FPWrite(), FPGetFileDirParms(); +OSErr FPSetFileDirParms(), FPOpenDT(), FPCloseDT(), FPGetIcon(); +OSErr FPGetIconInfo(), FPAddAPPL(), FPRmvAPPL(), FPGetAPPL(); +OSErr FPAddComment(), FPRmvComment(), FPGetComment(), FPAddIcon(); +OSErr FPNop(); +/* afp2.0 */ +OSErr FPChgPasswd(), FPGetUserInfo(); +/* afp2.1 */ +OSErr FPExchangeFiles(), FPGetSrvrMsg(); +OSErr FPCreateID(), FPDeleteID(), FPResolveID(); + +/* abafpserver.c */ + +char *SrvrInfo(); +OSErr SrvrRegister(); + +#define F_RSRC 01 /* resource file */ +#define F_DATA 02 /* data file */ +#define F_FNDR 03 /* finder info file */ + +#define DBG_FILE 00001 /* debug file routines */ +#define DBG_FORK 00002 /* debug fork routines */ +#define DBG_SRVR 00004 /* debug server routines */ +#define DBG_VOLS 00010 /* debug volume routines */ +#define DBG_OSIN 00020 /* debug os interface routines */ +#define DBG_DIRS 00040 /* debug directory routine */ +#define DBG_DESK 00100 /* debug desktop routines */ +#define DBG_DEBG 00200 /* in debug */ +#define DBG_UNIX 00400 /* debug unix routines */ +#define DBG_ENUM 01000 /* debug directory enumerations */ + +#define DBG_ALL (DBG_FILE|DBG_FORK|DBG_SRVR|DBG_VOLS| \ + DBG_OSIN|DBG_DIRS|DBG_DESK|DBG_DEBG| \ + DBG_UNIX|DBG_ENUM) + + +#define DBOSI (afp_dbug & DBG_OSIN) +#define DBFRK (afp_dbug & DBG_FORK) +#define DBSRV (afp_dbug & DBG_SRVR) +#define DBDIR (afp_dbug & DBG_DIRS) +#define DBVOL (afp_dbug & DBG_VOLS) +#define DBFIL (afp_dbug & DBG_FILE) +#define DBDSK (afp_dbug & DBG_DESK) +#define DBDEB (afp_dbug & DBG_DEBG) +#define DBUNX (afp_dbug & DBG_UNIX) +#define DBENU (afp_dbug & DBG_ENUM) + +/* afpdid.c */ + +typedef struct idir { /* local directory info (internal) */ + char *name; /* the directory name */ + long hash; /* hash of the directory name */ + struct idir *next; /* ptr to next at same level */ + struct idir *subs; /* ptr to children */ + struct idir *pdir; /* ptr to parent */ + unsigned int modified; /* count of times modified */ + VolBitMap volbm; /* vols that contain this dir */ + sdword edirid; /* external dirid */ + int eceidx; /* index into enum cache */ + int flags; /* any flags */ +#define DID_RESOURCE 0x1 /* remember there is resource subdir */ +#define DID_FINDERINFO 0x2 /* remember there is finderinfo subdir */ +#define DID_VALID 0x4 /* flags are valid? (always) */ +#define DID_DATA 0x8 /* data - means dir. exists :-) */ +#define DID_SYMLINKS 0x30 /* number of symbolic links in list */ + /* allow up to 3 (0 means none, 1-3 in */ + /* 3 bit field */ +#define DID_MAXSYMLINKS 3 +#define DID_SYMLINKS_SHIFT 4 /* shift from right */ +} IDir, *IDirP; + +#define NILDIR ((IDirP) 0) /* a null directory pointer */ + +#define EROOTD 02 /* external ID of volumes root directory */ +#define EPROOTD 01 /* external ID of parent of root */ + +IDirP EtoIdirid(); /* external to internal translation */ +sdword ItoEdirid(); /* internal to external translation */ +IDirP Idirid(); /* return internal dirid given path */ +IDirP Idndirid(); /* return internal dirid given dirid, name */ +IDirP Ipdirid(); /* return parent of dirid given dirid */ +char *pathstr(); /* return path given dirid */ +void InitDID(); /* initialize directory id mechanism */ +OSErr EtoIfile(); /* convert file names */ +void Idmove(); /* rename a directory */ +byte *ItoEName(); /* unix to mac name */ +OSErr EtoIName(); /* mac to unix name */ +int ENameLen(); /* length of mac name (arg is unix name) */ +void EModified(); /* mark an entry as having been modified */ + +/* afpvols.c */ + + +#define VOLFILE "afpvols" /* name of the file containing volume info */ +#define VOLFILE1 ".afpvols" /* alternate afpvols file */ +#define MAXLLEN 200 /* max line length in VOLFILE file */ + +/* + * We use a bitmap to record information about groupings of volumes + * The way we did things we are limited to sizeof(long)*8 bits in size + * (well, if you have a scalar type larger than long then you are + * limited to the number of bits in that type). + * + * Note however, that the logic behind things is such that you should + * be able to recode the macros to arbritrary sizes in the future + * without any problem, but for now just say we got lazy (actually the + * real issue was that the code gets a little long the other way...) + * +*/ +#define MAXVOLS (sizeof(VolBitMap)*8) /* max number of volumes per user */ + +#define V_BITSET(item,bit) (item) |= (1<<(bit)) +#define V_BITTST(item,bit) ((item) & (1<<(bit))) +#define V_BITCLR(item,bit) (item) &= ~(1<<(bit)) +#define V_BITOR(res, i1, i2) (res) = ((i1) | (i2)) +#define V_ZERO(item) bzero(&(item), sizeof(VolBitMap)) +#define V_COPY(d,s) (d) = (s) + +void VInit(); /* Initialize user volume structure */ +IDirP VolRootD(); /* return root dirid for a volume */ +word ItoEVolid(); +int EtoIVolid(); + +/* afpdt.c */ + +sdword CurTime(); + +/* afpvols.c */ + +void VolModified(); + +/* afpcmd.c */ + +void PackWord(); +void PackDWord(); + +/* afpdt.c */ + +void InitIconCache(); + +/* afposenum.c */ + +#define NOECIDX (-1) + +#define NECSIZE 100 /* size of the enum cache */ + +void ECacheInit(); +char *OSEnumGet(); +int OSEnumInit(); +void OSEnumDone(); +#ifdef NOCASEMATCH +void noCaseFind(); +void noCaseMatch(); +#endif NOCASEMATCH + +/* Portable library functions */ + +#if (!(defined(AIX) || defined(hpux) || defined(SOLARIS))) +char *malloc(),*strcpy(),*strcat(),*realloc(); +#endif /* AIX || hpux || SOLARIS */ + +/* defined here so that enumerate can skip these entries when reading dirs */ + +#define DESKTOP_ICON ".IDeskTop" +#define DESKTOP_APPL ".ADeskTop" + +#define RFDIRFN ".resource" /* subdir holding resource forks */ +#define FIDIRFN ".finderinfo" /* subdir holding finder info */ + +#define RFDIR "/.resource" +#define FIDIR "/.finderinfo" + +#define DIRRF ".resource/" +#define DIRFI ".finderinfo/" +#define DIRRFLEN 10 +#define DIRFILEN 12 + +/* Finder info bits */ +#define DEFCMNT "This is a Unix\252 created file." +#define DEFCMNTZ (sizeof(DEFCMNT)-1) +#ifdef notdef +#ifndef DEFFNDR +# define DEFFNDR "TEXTunix" /* type, creator */ +#endif +#define DEFFNDRZ (sizeof(DEFFNDR)-1) /* size of above */ +#endif + +#define DEFATTR (0x00) /* get rid of "locked" bit */ +#define DEFFCREATOR "unix" +#define DEFFTYPE "TEXT" + +#define ENEWLINE '\r' /* external (mac) new line char */ +#define INEWLINE '\n' /* internal (unix) new line char */ + +#ifdef USR_FILE_TYPES +#define ELDQUOTE 0xd2 /* external (mac) left double quote */ +#define ERDQUOTE 0xd3 /* external (mac) right double quote */ +#define ELSQUOTE 0xd4 /* external (mac) left single quote */ +#define ERSQUOTE 0xd5 /* external (mac) right single quote */ +#define IDQUOTE '\"' /* internal (unix) double quote */ +#define ISQUOTE '\'' /* internal (unix) single quote */ +#define TYPFILE "afpfile" +#define TYPFILE1 ".afpfile" +#endif USR_FILE_TYPES + +/* used internally */ +#define AFPVersionUnknown 0 +#define AFPVersion1DOT0 100 +#define AFPVersion1DOT1 110 +#define AFPVersion2DOT0 200 +#define AFPVersion2DOT1 210 +#define AFPVersion2DOT2 220 + +#ifdef APPLICATION_MANAGER +struct flist { + char *filename; /* full pathname to resource fork */ + int protected; /* we don't want it to be copied */ + short incarnations; /* can be opened this many times */ + struct flist *next; /* this is a sorted linked list */ +}; +#endif APPLICATION_MANAGER diff --git a/applications/aufs/afpserver.c b/applications/aufs/afpserver.c new file mode 100644 index 0000000..ad1a945 --- /dev/null +++ b/applications/aufs/afpserver.c @@ -0,0 +1,1677 @@ +/* + * $Author: djh $ $Date: 1996/06/19 04:23:36 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpserver.c,v 2.17 1996/06/19 04:23:36 djh Rel djh $ + * $Revision: 2.17 $ + * + */ + +/* + * afpserver.c - Appletalk Filing Protocol Server Level Routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1987,1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * December 1990 djh tidy up for AFP 2.0 + * + */ + +/* + * Various server support routines: + * + * SrvrInfo() + * SrvrRegister() + * SrvrSetTrace() + * + * Non OS dependant support routines: + * + * FPLogin() + * FPLoginCont() + * FPLogout() + * FPGetSrvrParms() + * FPGetSrvrMsg() + * FPMapID() + * FPMapName() + * FPCreateID() + * + */ + +#include +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "afpntoh.h" +#include "afps.h" +#ifdef USETIMES +# include +#endif + +#ifdef DISTRIB_PASSWDS +#include +char *distribpassfile = NULL; +#endif /* DISTRIB_PASSWDS */ + +/* consistency */ +#ifdef USETIMES +# define NORUSAGE +#endif + +#ifdef DEBUG_AFP_CMD +extern FILE *dbg; +#endif /* DEBUG_AFP_CMD */ + +typedef struct { + int dsp_cmd; + PFE dsp_rtn; + char *dsp_name; + int dsp_flg; +#define DSPF_DMPIN 01 +#define DSPF_DMPOUT 02 +#define DSPF_DOESSPWRITE 0x4 /* needs more args.. */ +#define DSPF_DMPBOTH (DSPF_DMPIN | DSPF_DMPOUT) +#ifdef STAT_CACHE +#define DSPF_FSTAT 0x8 +#endif STAT_CACHE + int dsp_cnt; +#ifndef NORUSAGE + struct timeval dsp_utime; + struct timeval dsp_stime; +#endif +#ifdef USETIMES + time_t dsp_utime; + time_t dsp_stime; +#endif +} AFPDispEntry; + +private char *afpmtyp = "Unix"; + +#define AFPVERSZ (6*16) /* room for IndStr hold all versions */ +/* define 1 more than needed just in case */ +struct afpversionstruct { + char *version_name; + int version; +} afpversions[] = { + {"AFPVersion 1.0", AFPVersion1DOT0}, + {"AFPVersion 1.1", AFPVersion1DOT1}, + {"AFPVersion 2.0", AFPVersion2DOT0}, + {"AFPVersion 2.1", AFPVersion2DOT1}, + {"AFP2.2", AFPVersion2DOT2}, + {NULL, AFPVersionUnknown} +}; + +#define NUAM "No User Authent" +#define CUAM "Cleartxt passwrd" +#define RUAM "Randnum exchange" +#define TUAM "2-Way Randnum exchange" + +#define MAX_USE_UAM 5 /* maximum number at one time */ + +private int numuam = 0; +struct afpuamstruct { + char *uamname; + int uam; +}; +private struct afpuamstruct afpuams[MAX_USE_UAM]; + +private byte uamflags[] = { + 0, /* no flags for "No User Authent" */ + UAMP_USER|UAMP_PASS|UAMP_ZERO, /* user and password for cleartext */ + /* zero means put a zero to even */ + /* out packet between user & passwd */ + UAMP_USER, /* user only in randnum */ + UAMP_USER /* user only in 2-way rand */ +}; + +#define AFPUAMSZ (MAX_USE_UAM*17+1) /* room for IndStr */ + + +private AFPDispEntry *DispTab[AFPMaxCmd+1]; + +private AFPDispEntry Entries[] = { + {AFPByteRangeLock,FPByteRangeLock,"ByteRangeLock",0,0}, + {AFPChgPasswd, FPChgPasswd, "ChangePassword", 0, 0}, /* AFP2.0 */ + {AFPGetUserInfo, FPGetUserInfo, "GetUserInfo", 0, 0}, /* AFP2.0 */ + {AFPGetSrvrMsg, FPGetSrvrMsg, "GetSrvrMsg", 0, 0}, /* AFP2.1 */ +#ifdef FIXED_DIRIDS + {AFPCreateID, FPCreateID, "CreateID", 0, 0}, /* AFP2.1 */ + {AFPDeleteID, FPDeleteID, "DeleteID", 0, 0}, /* AFP2.1 */ + {AFPResolveID, FPResolveID, "ResolveID", 0, 0}, /* AFP2.1 */ +#endif /* FIXED_DIRIDS */ + {AFPCloseVol,FPCloseVol,"CloseVol",0,0}, + {AFPCloseDir,FPCloseDir,"CloseDir",0,0}, + {AFPCreateDir,FPCreateDir,"CreateDir",0,0}, + {AFPEnumerate,FPEnumerate,"Enumerate",0,0}, + {AFPGetForkParms,FPGetForkParms,"GetForkParms",0,0}, + {AFPGetSrvrParms,FPGetSrvrParms,"GetSrvrParms",0,0}, + {AFPGetVolParms,FPGetVolParms,"GetVolParms",0,0}, + {AFPLogin,FPLogin,"Login",0,0}, + {AFPLoginCont,FPLoginCont,"LoginCont",0,0}, + {AFPLogout,FPLogout,"Logout",0,0}, + {AFPMapID,FPMapID,"MapID",0,0}, + {AFPMapName,FPMapName,"MapName",0,0}, + {AFPOpenVol,FPOpenVol,"OpenVol",0,0}, + {AFPOpenDir,FPOpenDir,"OpenDir",0,0}, + {AFPRead,FPRead,"Read",0,0}, + {AFPGetFileDirParms,FPGetFileDirParms,"GetFileDirParms",0,0}, + {AFPOpenDT,FPOpenDT,"OpenDT",0,0}, + {AFPCloseDT,FPCloseDT,"CloseDT",0,0}, + {AFPGetIcon,FPGetIcon,"GetIcon",0,0}, + {AFPGetIconInfo,FPGetIconInfo,"GetIconInfo",0,0}, + {AFPAddAPPL,FPAddAPPL,"AddAPPL",0,0}, + {AFPRmvAPPL,FPRmvAPPL,"RmvAPPL",0,0}, + {AFPGetAPPL,FPGetAPPL,"GetAPPL",0,0}, + {AFPAddComment,FPAddComment,"AddComment",0,0}, + {AFPRmvComment,FPRmvComment,"RmvComment",0,0}, + {AFPGetComment,FPGetComment,"GetComment",0,0}, + {AFPAddIcon,FPAddIcon,"AddIcon",DSPF_DOESSPWRITE,0}, +#ifndef STAT_CACHE + {AFPCloseFork,FPCloseFork,"CloseFork",0,0}, + {AFPCopyFile,FPCopyFile,"CopyFile",0,0}, + {AFPCreateFile,FPCreateFile,"CreateFile",0,0}, + {AFPDelete,FPDelete,"Delete",0,0}, + {AFPFlush,FPFlush,"Flush",0,0}, + {AFPFlushFork,FPFlushFork,"FlushFork",0,0}, + {AFPMove,FPMove,"Move",0,0}, + {AFPOpenFork,FPOpenFork,"OpenFork",0,0}, + {AFPRename,FPRename,"Rename",0,0}, + {AFPSetDirParms,FPSetDirParms,"SetDirParms",0,0}, + {AFPSetFileParms,FPSetFileParms,"SetFileParms",0,0}, + {AFPSetForkParms,FPSetForkParms,"SetForkParms",0,0}, + {AFPSetVolParms,FPSetVolParms,"SetVolParms",0,0}, + {AFPWrite,FPWrite,"Write",DSPF_DOESSPWRITE,0}, + {AFPSetFileDirParms,FPSetFileDirParms,"SetFileDirParms",0,0}, + {AFPExchangeFiles,FPExchangeFiles,"ExchangeFiles",0,0} +#else STAT_CACHE + {AFPCloseFork,FPCloseFork,"CloseFork",DSPF_FSTAT,0}, + {AFPCopyFile,FPCopyFile,"CopyFile",DSPF_FSTAT,0}, + {AFPCreateFile,FPCreateFile,"CreateFile",DSPF_FSTAT,0}, + {AFPDelete,FPDelete,"Delete",DSPF_FSTAT,0}, + {AFPFlush,FPFlush,"Flush",DSPF_FSTAT,0}, + {AFPFlushFork,FPFlushFork,"FlushFork",DSPF_FSTAT,0}, + {AFPMove,FPMove,"Move",DSPF_FSTAT,0}, + {AFPOpenFork,FPOpenFork,"OpenFork",DSPF_FSTAT,0}, + {AFPRename,FPRename,"Rename",DSPF_FSTAT,0}, + {AFPSetDirParms,FPSetDirParms,"SetDirParms",DSPF_FSTAT,0}, + {AFPSetFileParms,FPSetFileParms,"SetFileParms",DSPF_FSTAT,0}, + {AFPSetForkParms,FPSetForkParms,"SetForkParms",DSPF_FSTAT,0}, + {AFPSetVolParms,FPSetVolParms,"SetVolParms",DSPF_FSTAT,0}, + {AFPWrite,FPWrite,"Write",DSPF_DOESSPWRITE|DSPF_FSTAT,0}, + {AFPSetFileDirParms,FPSetFileDirParms,"SetFileDirParms",DSPF_FSTAT,0}, + {AFPExchangeFiles,FPExchangeFiles,"ExchangeFiles",DSPF_FSTAT,0} +#endif STAT_CACHE +}; + +#define NumEntries (sizeof(Entries)/sizeof(AFPDispEntry)) + +private DumpBuf(), clockstart(), clockend(); + +private struct sig { + byte filler[8]; + struct timeval s_time; +} sig; + +#ifdef LOGIN_AUTH_PROG +extern char *srvrname; /* NBP registered name of server */ +extern char *login_auth_prog; /* name of authorization program */ +extern AddrBlock addr; /* AppleTalk address of client */ +char log_command[MAXPATHLEN]; /* buffer to build auth. command */ +int log_status; /* status return */ +#endif LOGIN_AUTH_PROG + +IniServer() /* ini disp entries */ +{ + int i; + + for (i=0; i < AFPMaxCmd; i++) /* clear all pointers */ + DispTab[i] = (AFPDispEntry *) 0; /* in dispatch table */ + + for (i=0; i < NumEntries; i++) { /* for each entry add into dispatch */ + DispTab[Entries[i].dsp_cmd] = &Entries[i]; +#ifndef NORUSAGE + Entries[i].dsp_utime.tv_sec = 0; + Entries[i].dsp_utime.tv_usec = 0; + Entries[i].dsp_stime.tv_sec = 0; + Entries[i].dsp_stime.tv_usec = 0; +#endif NORUSAGE +#ifdef USETIMES + Entries[i].dsp_utime = 0; + Entries[i].dsp_stime = 0; +#endif USETIMES + } + + InitOSFI(); /* init finder file info */ + ECacheInit(); /* init afposenum cache */ + InitIconCache(); /* init afpdt cache */ +#ifndef FIXED_DIRIDS + InitDID(); /* init directory stuff */ +#endif FIXED_DIRIDS +#ifdef STAT_CACHE + OSStatInit(); /* init stat cache */ +#endif STAT_CACHE + + /* init server signature */ + bzero((char *)&sig, sizeof(struct sig)); + gettimeofday(&sig.s_time, NULL); +} + +/* + * login states + * + */ +#define LOGGED_NY 0x00 /* not yet logged in */ +#define LOGGED_IN 0x01 /* authorised OK */ +#define LOGGED_PX 0x02 /* password expired */ + +private int loginstate = LOGGED_NY; + +/* + * command dispatcher + * + */ + +OSErr +SrvrDispatch(pkt,len,rsp,rlen,cno,reqref) +byte *pkt,*rsp; +int len,*rlen; +int cno; +ReqRefNumType reqref; +{ + AFPDispEntry *d; + int err; + byte *p; + byte cmd; +#ifdef DEBUG_AFP_CMD + time_t diff; +#endif /* DEBUG_AFP_CMD */ + + + *rlen = 0; + cmd = *pkt; + + d = DispTab[cmd]; + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + char *ctime(); + time_t now; + time(&now); + diff = clock(); + if (d == (AFPDispEntry *)0) + fprintf(dbg, "## UNKNOWN (%d) %s\n", cmd, ctime(&now)); + else + fprintf(dbg, "## %s (%d) %s", d->dsp_name, cmd, ctime(&now)); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (d == (AFPDispEntry *) 0) { + printf("SrvrDispatch: Unknown cmd=%d (%x)\n",cmd,cmd); + DumpBuf(pkt,len,"Unknown Cmd",cmd); + return(aeCallNotSupported); + } + + if (DBSRV) + printf("SrvrDispatch cmd=%s (%d 0x%x), len=%d\n", + d->dsp_name,cmd,cmd,len); + + + d->dsp_cnt++; /* increment counter */ + + if (d->dsp_flg & DSPF_DMPIN) + DumpBuf(pkt,len,d->dsp_name,cmd); + + if (statflg) + clockstart(); + + if (loginstate == 0 + && d->dsp_cmd != AFPLogin + && d->dsp_cmd != AFPLoginCont) { + logit(0,"SrvrDispatch: Security. Cmd before login %s\n",d->dsp_name); + err = aeMiscErr; + } else { + if (loginstate & LOGGED_PX + && d->dsp_cmd != AFPLogout + && d->dsp_cmd != AFPChgPasswd) { + logit(0,"SrvrDispatch: Security. Cmd before ChangePass %s\n",d->dsp_name); + err = aePwdExpired; + } else { + if (d->dsp_flg & DSPF_DOESSPWRITE) + err = (*d->dsp_rtn)(pkt,len,rsp,rlen,cno,reqref); + else + err = (*d->dsp_rtn)(pkt,len,rsp,rlen); + } + } + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "## %s returns %d (%s) [%4.2f mS]\n\n\n", d->dsp_name, + err, afperr(err), ((float)((float)clock()-(float)diff))/1000); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (statflg) + clockend(d); + +#ifdef STAT_CACHE + if (d->dsp_flg & DSPF_FSTAT) + OSflush_stat(); + else + OSstat_cache_update(); +#endif STAT_CACHE + + if (d->dsp_flg & DSPF_DMPOUT) + DumpBuf(rsp,*rlen,d->dsp_name,cmd); + + return(err); +} + +#define PERLINE 20 + +/* + * If you can understand this, then you are far better person than I... + * This code is convoluted unecessarily, but it really isn't important + * code, so... + * +*/ +private +DumpBuf(p,l,s,c) +byte *p; +int l; +char *s; +byte c; +{ + int ln,ll,left; + int lll; + + printf("DmpBuf for %s (%d 0x%x) length = %d\n",s,c,c,l); + if (l > 1000) + lll = 1000; + else + lll = l; + + for (ll = 0; ll < lll; ll += PERLINE) { + + printf(" %06d ",ll); + left = (lll-ll < PERLINE ? lll-ll : PERLINE); + for (ln=0; ln < left; ln++) + printf("%02x ",(unsigned int) p[ln]); + + printf("\n "); + for (ln=0; ln < left; ln++) + if ((p[ln] < 0200) && isprint(p[ln])) + printf("%2c ",p[ln]); + else + printf(" . "); + printf("\n"); + p += PERLINE; + } + if (lll < l) + printf(" ... too large to print ... "); +} + +/* + * Map a user name to a user ID, + * or a group name to a group ID. + * + */ + +/*ARGSUSED*/ +OSErr +FPMapName(p,l,r,rl) +byte *p,*r; +int *rl,l; +{ + char name[MAXPSTR]; + OSErr err; + sdword id; + MNPPtr mnp = (MNPPtr) p; + + cpyp2cstr(name,mnp->mpn_name); /* copy into c string */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_func(); + fprintf(dbg, "\tMapFn: %d\t", (int)mnp->mpn_fcn); + dbg_print_func((int)mnp->mpn_fcn, 1); + fprintf(dbg, "\tMapNm: \"%s\"\n", name); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + err = OSMapName(mnp->mpn_fcn,name,&id); + if (err != noErr) + return(err); + PackDWord(id,r); /* pack long format */ + *rl = 4; /* length of reply */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tMapID: %08x\n", id); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/* + * Map a user ID to a user name, + * or a group ID to a group name. + * + */ + +OSErr +FPMapID(p,l,r,rl) +byte *p,*r; /* packet and response */ +int *rl,l; /* response length */ +{ + char name[MAXPSTR]; + OSErr err; + MapIDPkt mip; + + ntohPackX(PsMapID,p,l,(byte *) &mip); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_func(); + fprintf(dbg, "\tMapFn: %d\t", (int)mip.mpi_fcn); + dbg_print_func((int)mip.mpi_fcn, 0); + fprintf(dbg, "\tMapID: %08x\n", mip.mpi_id); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + err = OSMapID(mip.mpi_fcn,name,mip.mpi_id); + if (err != noErr) + return(err); + cpyc2pstr(r,name); /* copy to pascal string for return */ + *rl = strlen(name)+1; /* return length of reply packet */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tMapNm: \"%s\"\n", name); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + + +/* + * Begin login code + * + * Login methods assume we are "forked" from main server!!! + * +*/ + +/* + * map a uam string to internal uam number + * +*/ +private int +mapuamstr(uamstr) +char *uamstr; +{ + int i; + + if (uamstr == NULL) + return(UAM_UNKNOWN); + for (i = 0; i < numuam; i++) + /* do it case insenstive */ + if (strcmpci(uamstr, afpuams[i].uamname) == 0) + return(afpuams[i].uam); + return(UAM_UNKNOWN); +} + +private int +mapverstr(verstr) +char *verstr; +{ + struct afpversionstruct *av; + + for (av = afpversions; av->version_name != NULL ; av++) + if (strcmpci(verstr, av->version_name) == 0) + return(av->version); + return(AFPVersionUnknown); +} + +export void +allowguestid(gn) +char *gn; +{ + if (!setguestid(gn)) + return; + afpuams[numuam].uamname = NUAM; + afpuams[numuam].uam = UAM_ANON; + numuam++; +} + +export void +allowcleartext() +{ + afpuams[numuam].uamname = CUAM; + afpuams[numuam].uam = UAM_CLEAR; + numuam++; +} + +export void +allowrandnum(pw) +char *pw; +{ + if (!setpasswdfile(pw)) + return; + afpuams[numuam].uamname = RUAM; + afpuams[numuam].uam = UAM_RANDNUM; + numuam++; +} + +#ifdef DISTRIB_PASSWDS +export void +allow2wayrand(pw) +char *pw; +{ + /* + * 2-Way implies Randnum for + * ChangePasswd function. + * + */ + distribpassfile = pw; + afpuams[numuam].uamname = RUAM; + afpuams[numuam].uam = UAM_RANDNUM; + numuam++; + afpuams[numuam].uamname = TUAM; + afpuams[numuam].uam = UAM_2WAYRAND; + numuam++; + + return; +} +#endif /* DISTRIB_PASSWDS */ + +/* + * following vars are used for randnum exchange + * + */ +private LoginReplyPkt rne = { /* initialize to minimize */ + 0x0, /* possiblity of bad transaction */ + 0xffff, /* when starting */ + {0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8} +}; +private int useruam; /* UAM_RANDNUM or UAM_2WAYRAND */ +private char username[50]; /* some reasonable length */ + +int sessvers = 0; /* keep protocol version */ + +/* + * establish an AFP session with the server + * + */ + +/*ARGSUSED*/ +OSErr +FPLogin(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + LoginPkt lg; + char upwd[MAXPASSWD+1]; + int uam, i; + OSErr err; + +#ifdef SHORT_NAMES +int temp; +#endif SHORT_NAMES + + if (loginstate) /* already logged in */ + return(aeMiscErr); + + (void) ntohPackX(PsLogin,p,l,(byte *) &lg); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tAFPVer: \"%s\"\n", (char *)lg.log_ver); + fprintf(dbg, "\tAFPUAM: \"%s\"\n", (char *)lg.log_uam); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + /* + * locate AFP version and UAM so we can unpack accordingly + * + */ + if ((sessvers = mapverstr(lg.log_ver)) == AFPVersionUnknown) { + logit(0,"Bad version %s", lg.log_ver); + return(aeBadVersNum); + } + if ((uam = mapuamstr(lg.log_uam)) == UAM_UNKNOWN) { + logit(0,"BAD UAM %s in connection attempt", lg.log_uam); + return(aeBadUAM); /* no... forget it */ + } + if (sessvers < AFPVersion2DOT1 && uam == UAM_2WAYRAND) + return(aeBadUAM); + + /* + * mactime <-> unix time is different for afp 1.1 + * this calldown is to afpcmd + * + */ + InitPackTime(sessvers == AFPVersion1DOT0); + +#ifdef SHORT_NAMES + temp = 3 + p[1] + p[p[1] +2]; + temp = temp + p[temp] + 1; + if ((p[temp] != '\0') && (uam == UAM_CLEAR)) /* doesn't need the zero*/ + (void) ntohPackXbitmap(PsLogin,p,l,(byte *) &lg,UAMP_USER|UAMP_PASS); + else + (void) ntohPackXbitmap(PsLogin,p,l,(byte *) &lg, uamflags[uam]); +#else SHORT_NAMES + (void) ntohPackXbitmap(PsLogin,p,l,(byte *) &lg, uamflags[uam]); +#endif SHORT_NAMES + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tUserNm: \"%s\"\n", (char *)lg.log_user); + if (uam == UAM_CLEAR) + fprintf(dbg, "\tUserPw: (%d bytes)\n", strlen(lg.log_passwd)); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + +#ifdef LOGIN_AUTH_PROG + if (login_auth_prog) { + sprintf(log_command, "%s \"%d\" \"%d\" \"%s\" \"%s\"", + login_auth_prog, ntohs(addr.net), addr.node, lg.log_user, srvrname); + if ((log_status = ((system(log_command) >> 8) & 0xff)) != 0) { + logit(0, "Login disallowed for %d %d (%s); authorization returned %d", + ntohs(addr.net), addr.node, lg.log_user, log_status); + return(aeParamErr); + } + } +#endif LOGIN_AUTH_PROG + + /* + * 'Randnum exchange' or '2-Way Randnum exchange' + * + */ + if (uam == UAM_RANDNUM || uam == UAM_2WAYRAND) { + if ((err = OSLoginRand(lg.log_user)) != noErr) + return(err); + for (i = 0; i < 8; i++) + rne.logr_randnum[i] = (byte)OSRandom(); + useruam = uam; + strcpy(username, lg.log_user); /* remember it */ + rne.logr_flag = UAMP_RAND | UAMP_INUM; + rne.logr_idnum = OSRandom(); /* some random number */ + *rl = htonPackXbitmap(PsLoginReply, (byte *)&rne, r, rne.logr_flag); +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tAFPID: %04x\n", rne.logr_idnum); + fprintf(dbg, "\tRandm: "); + for (i = 0; i < 8; i++) + fprintf(dbg, "%02x", rne.logr_randnum[i]); + fprintf(dbg, "\n"); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + return(aeAuthContinue); + } + + /* + * 'Cleartxt Passwrd' + * + */ + if (uamflags[uam] & UAMP_PASS) { + bcopy(lg.log_passwd,upwd,MAXPASSWD); /* copy the password */ + upwd[MAXPASSWD] = '\0'; /* make sure null terminated */ + } + + if (DBSRV) { + printf("Login ver=%s, uam=%s, user=%s, pwd=%s\n", + lg.log_ver,lg.log_uam, + ((uamflags[uam] & UAMP_USER) ? (char *)lg.log_user : ""), + ((uamflags[uam] & UAMP_PASS) ? upwd : "no passwd")); + } + + if ((err = OSLogin((char *)lg.log_user, upwd, NULL, uam)) == noErr) { + logit(0,"Using protocol '%s'", lg.log_ver); + loginstate = LOGGED_IN; + } + + return(err); +} + +/* + * continue login and user authentication process started by FPLogin + * + */ + +/*ARGSUSED*/ +OSErr +FPLoginCont(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + LoginContPkt lcp; + word bitmap; + OSErr err; +#ifdef DEBUG_AFP_CMD + int i; +#endif /* DEBUG_AFP_CMD */ + + /* + * get id number + * + */ + ntohPackXbitmap(PsLoginCont, p, l, &lcp, UAMP_INUM); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tAFPID: %04x\n", lcp.lgc_idno); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (lcp.lgc_idno != rne.logr_idnum) + return(aeParamErr); + + /* + * ID numbers match, so unpack encrypted passwd(s) + * + */ + bitmap = UAMP_INUM | UAMP_ENCR | ((useruam==UAM_2WAYRAND) ? UAMP_TWAY : 0); + ntohPackXbitmap(PsLoginCont, p, l, &lcp, bitmap); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tUserA: "); + for (i = 0; i < 8; i++) + fprintf(dbg, "%02x", lcp.lgc_encrypted[i]); + fprintf(dbg, "\n"); + if (useruam == UAM_2WAYRAND) { + if (l == 20) { + fprintf(dbg, "\tRandm: "); + for (i = 0; i < 8; i++) + fprintf(dbg, "%02x", lcp.lgc_wsencrypt[i]); + fprintf(dbg, "\n"); + } else + fprintf(dbg, "\t\n"); + } + } +#endif /* DEBUG_AFP_CMD */ + + if (DBSRV) + printf("Login Randnum exchange for %s\n",username ); + + err = OSLogin(username, lcp.lgc_encrypted, rne.logr_randnum, useruam); + + if (err == noErr) + loginstate = LOGGED_IN; + + if (useruam != UAM_2WAYRAND) + return(err); + +#ifdef DISTRIB_PASSWDS + { extern struct afppass *afp; + void afpdp_encr(); + int afpdp_pwex(); + + if (afp == NULL) + err = aeUserNotAuth; + + if (err != noErr) + return(err); + + /* encrypt in place */ + afpdp_encr(lcp.lgc_wsencrypt, afp->afp_password, NULL); + + *rl = htonPackXbitmap(PsLoginContR, (byte *)&lcp, r, UAMP_TWAY); + + /* password expired ? */ + if (afpdp_pwex(afp) != 0) + loginstate |= LOGGED_PX; + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tSrvrA: "); + for (i = 0; i < 8; i++) + fprintf(dbg, "%02x", lcp.lgc_wsencrypt[i]); + fprintf(dbg, "\n"); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + } +#endif /* DISTRIB_PASSWDS */ + + return(err); +} + +/* + * AFP2.0: change user password + * + */ + +OSErr +FPChgPasswd(p, l, r, rl) +byte *p, *r; +int l, *rl; +{ + ChgPasswdPkt cpp; + ChgPasswdReplyPkt cprp; + OSErr err; + int uam; + + (void)ntohPackX(PsChangePassword, p, l, (byte *)&cpp); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tChgUAM: \"%s\"\n", cpp.cp_uam); + fprintf(dbg, "\tChgUsr: \"%s\"\n", cpp.cp_user); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if ((uam = mapuamstr(cpp.cp_uam)) == UAM_UNKNOWN) { + logit(0,"BAD UAM %s in change password attempt", cpp.cp_uam); + return(aeBadUAM); + } + if (uam == UAM_ANON) + return(aeBadUAM); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + int i; + switch (uam) { + case UAM_CLEAR: + fprintf(dbg, "\tOldPwd: (%d bytes)\n", strlen(cpp.cp_oldpass)); + fprintf(dbg, "\tNewPwd: (%d bytes)\n", strlen(cpp.cp_newpass)); + break; + case UAM_RANDNUM: + fprintf(dbg, "\tOldPwd: "); + for (i = 0; i < 8; i++) + fprintf(dbg, "%02x", cpp.cp_oldpass[i]); + fprintf(dbg, "\n"); + fprintf(dbg, "\tNewPwd: "); + for (i = 0; i < 8; i++) + fprintf(dbg, "%02x", cpp.cp_newpass[i]); + fprintf(dbg, "\n"); + break; + } + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (DBSRV) + printf("ChangePassword uam=%s, user=%s\n", cpp.cp_uam, cpp.cp_user); + + err = OSChangePassword(cpp.cp_user, cpp.cp_oldpass, cpp.cp_newpass, uam); + +#ifdef DISTRIB_PASSWDS + if (err == noErr) + loginstate &= ~LOGGED_PX; +#endif /* DISTRIB_PASSWDS */ + + if (err != noErr) + logit(0, "ChangePassword failed for %s (%s)", cpp.cp_user, afperr(err)); + + return(err); +} + +/* + * AFP2.1: get srvr message + * + */ + +OSErr +FPGetSrvrMsg(p, l, r, rl) +byte *p, *r; +int l, *rl; +{ + SrvrMsgPkt smp; + SrvrMsgReplyPkt smrp; + OSErr err; + + (void)ntohPackX(PsGetSrvrMsg, p, l, (byte *)&smp); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tMsgTyp: %04x\t", smp.msg_typ); + fprintf(dbg, "(%s)\n", (smp.msg_typ == 0) ? "Login" : "Server"); + fprintf(dbg, "\tMsgBMp: %04x\n", smp.msg_bitmap); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (sessvers < AFPVersion2DOT1) + return(aeCallNotSupported); + if (smp.msg_bitmap != 0x01) + return(aeBitMapErr); + smrp.msgr_typ = smp.msg_typ; + smrp.msgr_bitmap = smp.msg_bitmap; + smrp.msgr_data[0] = '\0'; + err = OSGetSrvrMsg(smrp.msgr_typ, smrp.msgr_data); + if (err != noErr) + return(err); + *rl = htonPackX(PsGetSrvrMsgReply, (byte *)&smrp, r); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_name(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tMsgTyp: %04x\t", smrp.msgr_typ); + fprintf(dbg, "(%s)\n", (smrp.msgr_typ == 0) ? "Login" : "Server"); + fprintf(dbg, "\tMsgBMp: %04x\n", smrp.msgr_bitmap); + fprintf(dbg, "\tMsgStr: %s\n", smrp.msgr_data); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +/* + * AFP2.0: get user information + * + */ + +FPGetUserInfo(p, l, r, rl) +byte *p, *r; +int l, *rl; +{ + GetUserInfoPkt guip; + GetUserInfoReplyPkt guirp; + OSErr err; + + (void)ntohPackX(PsGetUserInfo, p, l, (byte *)&guip); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tGUFlg: %02x\t", guip.gui_flag); + fprintf(dbg, "%s\n", (guip.gui_flag & 0x01) ? "(ThisUser)" : ""); + fprintf(dbg, "\tUsrID: %08x\t", guip.gui_userid); + fprintf(dbg, "%s\n", (guip.gui_flag & 0x01) ? "(Invalid)" : ""); + fprintf(dbg, "\tBtMap: %04x\n", guip.gui_bitmap); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + guirp.guir_bitmap = guip.gui_bitmap; + err=OSGetUserInfo((guip.gui_flag & GUI_THIS_USER)!=0,guip.gui_userid,&guirp); + if (err != noErr) + return(err); + *rl=htonPackXbitmap(PsGetUserInfoReply,(byte *)&guirp,r,guirp.guir_bitmap); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tBtMap: %04x\n", guirp.guir_bitmap); + if (guirp.guir_bitmap & 0x0001) + fprintf(dbg, "\tUsrID: %08x\n", guirp.guir_userid); + if (guirp.guir_bitmap & 0x0002) + fprintf(dbg, "\tGrpID: %08x\n", guirp.guir_pgroup); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); +} + +#ifdef notdef +int IndStrPack(d,s) +byte *d; +char *s[]; +{ + IniIndStr(d); /* init indexed string */ + for (; *s != NULL; s++) /* copy entries from array */ + AddIndStr(*s,d); /* adding each array element */ + return(IndStrLen(d)); /* return length */ +} +#endif + +#define MAXNADSIZ 8 /* tag #2, IP addr & port */ +#define NUMNAD 5 /* no more than 5 IP addr/host */ + +int +GetSrvrInfo(r,sname,icon,iconsize) +byte *r; +char *sname; +byte icon[]; +int iconsize; +{ + byte *q; + int i,len; + extern int nopwdsave; + GetSrvrInfoReplyPkt sr; + OPTRType avo,uamo,vicono,sigo,nado; + byte avobuf[AFPVERSZ],uamobuf[AFPUAMSZ]; + byte nadbuf[MAXNADSIZ*NUMNAD+1]; + extern u_short asip_port; + extern u_int asip_addr; + extern int asip_enable; + struct hostent *he; + char hostname[128]; + + /* + * set server capabilities + * + */ + sr.sr_flags = SupportsFPCopyFile; +#ifdef DISTRIB_PASSWDS + sr.sr_flags |= SupportsChgPwd; +#endif DISTRIB_PASSWDS + sr.sr_flags |= SupportsServerMsgs; + sr.sr_flags |= SupportsServerSig; + if (asip_enable) + sr.sr_flags |= SupportsTCPIP; + if (nopwdsave) + sr.sr_flags |= DontAllowSavePwd; + + /* + * set Server Name & Machine Type + * + */ + strcpy(sr.sr_machtype,afpmtyp); + cpyc2pstr(sr.sr_servername,sname); + + /* + * set Volume Icon & Mask + * + */ + vicono.optr_loc = icon; + vicono.optr_len = iconsize; + sr.sr_vicono = (char *) &vicono; + + /* + * set AFP Versions + * + */ + IniIndStr(avobuf); + for (i = 0; afpversions[i].version_name != NULL; i++) + AddIndStr(afpversions[i].version_name, avobuf); + avo.optr_len = IndStrLen(avobuf); + avo.optr_loc = avobuf; + sr.sr_avo = (byte *) &avo; + + /* + * set UAMs + * + */ + IniIndStr(uamobuf); + for (i=0 ; i < numuam; i++) + AddIndStr(afpuams[i].uamname, uamobuf); + uamo.optr_len = IndStrLen(uamobuf); + uamo.optr_loc = uamobuf; + sr.sr_uamo = (byte *) &uamo; + + /* + * set server signature + * + */ + sigo.optr_len = 16; + sigo.optr_loc = (byte *)&sig; + sr.sr_sigo = (byte *)&sigo; + + /* + * set network address(es) + * use single bound address (-B ) + * or all known addresses for this machine + * + */ + q = nadbuf+1; + nadbuf[0] = 0; + if (asip_enable) { + if (asip_addr) { + nadbuf[0] = 1; + q[2] = (asip_addr >> 24) & 0xff; + q[3] = (asip_addr >> 16) & 0xff; + q[4] = (asip_addr >> 8) & 0xff; + q[5] = (asip_addr & 0xff); + if (asip_port == ASIP_PORT) { + q[0] = 0x06; /* len */ + q[1] = 0x01; /* tag */ + q += 6; + } else { + q[0] = 0x08; /* len */ + q[1] = 0x02; /* tag */ + q[6] = asip_port >> 8; + q[7] = asip_port & 0xff; + q += 8; + } + } else { /* list all known addresses */ + if (gethostname(hostname, sizeof(hostname)) == 0) { + if ((he = gethostbyname(hostname)) != NULL) { + for (i = 0; he->h_addr_list[i] && i < NUMNAD; i++) { + bcopy(he->h_addr_list[i], q+2, 4); /* copy IP */ + if (asip_port == ASIP_PORT) { + q[0] = 0x06; /* len */ + q[1] = 0x01; /* tag */ + q += 6; + } else { + q[0] = 0x08; /* len */ + q[1] = 0x02; /* tag */ + q[6] = asip_port >> 8; + q[7] = asip_port & 0xff; + q += 8; + } + } + nadbuf[0] = i; + } + } + } + } + nado.optr_len = q-nadbuf; + nado.optr_loc = nadbuf; + sr.sr_naddro = (byte *)&nado; + + /* + * pack data for sending + * + */ + len = htonPackX(ProtoSRP,(byte *) &sr,r); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_info(); + time_t now; + time(&now); + fprintf(dbg, "## FPGetSrvrInfo (15) %s", ctime(&now)); + fprintf(dbg, " Return Parameters:\n"); + dbg_print_info(r, len); + fprintf(dbg, "## FPGetSrvrInfo returns %d (%s)\n\n\n", 0, afperr(0)); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(len); +} + +PrtSrvrInfo(pkt,pktl) +byte *pkt; +int pktl; +{ + GetSrvrInfoReplyPkt sr; + char name[MAXSNAM+1]; + + ntohPackX(ProtoSRP,pkt,pktl,(byte *) &sr); /* unpack from net */ + cpyp2cstr(name,sr.sr_servername); + + printf("PrtSrvrInfo Packet length is %d\n",pktl); + printf(" Machine Type = %s\n",sr.sr_machtype); + printf(" Server Name = %s\n",name); + printf(" Flags = %d\n",sr.sr_flags); + printf(" AFP Versions Supported:\n"); + PrtIndStr(sr.sr_avo); + printf(" User Authentication Methods:\n"); + PrtIndStr(sr.sr_uamo); + printf(" Icon %s present\n",(sr.sr_vicono == 0) ? "is not" : "is"); +} + +/*ARGSUSED*/ +OSErr +FPLogout(p,l,r,rl) +char *p,*r; +int l,*rl; +{ + printf("FPLogout\n"); + return(noErr); +} + +/* + * OSErr FPGetSrvrParms(byte *p,byte *r,int *rl) + * + * This call is used to retrieve server-level parameters (for now only + * the volume names) + * + */ + +/*ARGSUSED*/ +OSErr +FPGetSrvrParms(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + extern PackEntry ProtoGSPRP[]; + GetSrvrParmsReplyPkt gpr; + byte *up; + int cnt; + + gpr.gspr_time = CurTime(); + gpr.gspr_nvols = VCount(); + cnt = htonPackX(ProtoGSPRP, (byte *)&gpr, r); + cnt += VMakeVList(r+cnt); /* add vols, return count */ + *rl = cnt; /* pkt length */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + byte *q = r + 5; + void dbg_print_name(); + fprintf(dbg, "\tSTime: %s", ctime((time_t *)&gpr.gspr_time)); + fprintf(dbg, "\tNVols: %02x\n", gpr.gspr_nvols); + while (q < (r + (*rl))) { + fprintf(dbg, "\tVFlag: %02x (", (int)(*q)); + fprintf(dbg, "%s", (*q & 0x80) ? "HasPasswd" : ""); + fprintf(dbg, "%s",((*q & 0x81) == 0x81) ? ", " : ""); + fprintf(dbg, "%s", (*q & 0x01) ? "HasConfig" : ""); + fprintf(dbg, ")\n"); + dbg_print_name("\tVName:", q+1); + q += (*(q+1) + 2); + } + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); /* no error */ +} + +/* + * void SrvrSetTrace(char *tstr) + * + * Set trace of command packets on input, output, or both. Tstr + * is a string containing items of the form: + * + * [I|O|B]CmdName [I|O|B]CmdName ... + * + * The first character of an item specifies if you want the packet + * displayed on input, output, or both input and output. The rest + * of the item is the name of the command, Enumerate or AddAPPL for + * example. + * + */ + +int SetPktTrace(tstr) +char *tstr; +{ + int flg,i; + char cmdbuf[40],*cp,*opt,c; + + while (*tstr != '\0') { + while (*tstr == ' ' || *tstr == '\t') + tstr++; + + if (*tstr == '\0') + break; + + switch (c = *tstr++) { + case 'I': case 'i': + flg = DSPF_DMPIN; opt = "input"; break; + case 'O': case 'o': + flg = DSPF_DMPOUT; opt = "output"; break; + case 'B': case 'b': + flg = DSPF_DMPBOTH; opt = "input and output"; break; + default: + fprintf(stderr,"SetPktTrace: I, O, or B required. %c unknown\n",c); + return(FALSE); + } + + cp = cmdbuf; + while (*tstr != ' ' && *tstr != '\t' && *tstr != '\0') + *cp++ = *tstr++; + *cp++ = '\0'; + for (i=0; i < NumEntries; i++) + if (strcmpci(cmdbuf,Entries[i].dsp_name) == 0) { + fprintf(stderr,"SetPktTrace: Tracing %s on %s\n", + Entries[i].dsp_name,opt); + Entries[i].dsp_flg = flg; + break; + } + if (i >= NumEntries) { + fprintf(stderr,"SetPktTrace: Unknown command %s\n",cmdbuf); + return(FALSE); + } + } + return(TRUE); +} + +#ifndef NORUSAGE +private +acctime(b,a,s) +struct timeval *b,*a,*s; +{ + s->tv_sec += a->tv_sec - b->tv_sec; + s->tv_usec += a->tv_usec - b->tv_usec; + if (s->tv_usec < 0) { + s->tv_sec--; + s->tv_usec += uSEC; + } + if (s->tv_sec > uSEC) { + s->tv_sec++; + s->tv_usec -= uSEC; + } +} + +private +printt(s,tv) +char *s; +struct timeval *tv; +{ + printf("%3d.%06d %s ",tv->tv_sec,tv->tv_usec,s); +} + +private struct rusage before; + +private +clockstart() +{ + getrusage(RUSAGE_SELF,&before); +} + +private +clockend(d) +AFPDispEntry *d; +{ + struct rusage after; + + getrusage(RUSAGE_SELF,&after); + + acctime(&before.ru_utime,&after.ru_utime,&d->dsp_utime); + acctime(&before.ru_stime,&after.ru_stime,&d->dsp_stime); +} +#endif + +#ifdef USETIMES +private struct tms before; + +private +clockstart() +{ + (void)times(&before); +} + +private +clockend(d) +AFPDispEntry *d; +{ + struct tms after; + + (void)times(&after); + + d->dsp_utime += after.tms_utime - before.tms_utime; + d->dsp_stime += after.tms_stime - before.tms_stime; + d->dsp_utime += after.tms_cutime - before.tms_cutime; + d->dsp_stime += after.tms_cstime - before.tms_cstime; +} + +#ifndef HZ /* in case not defined... */ +# define HZ 60 +#endif +private +printt(s,tv) +char *s; +time_t *tv; +{ + float pval; + pval = ((float)*tv)/((float)HZ); + printf("%.06f %s ",pval,s); +} + +#endif + +SrvrPrintStats() +{ + int i; + + printf("\nServer Statistics:\n"); + for (i=0; i < NumEntries; i++) + if (Entries[i].dsp_cnt != 0) { + printf(" %20s Count=%5d ",Entries[i].dsp_name, + Entries[i].dsp_cnt); + printt("Usr",&Entries[i].dsp_utime); + printt("Sys",&Entries[i].dsp_stime); + printf("\n"); + } +} + +#ifdef DEBUG_AFP_CMD +/* + * print the uid/gid Name/ID function + * + */ + +void +dbg_print_func(func, typ) +int func; +int typ; +{ + if (dbg != NULL) { + if (typ == 0 && (!(func == 1 || func == 2))) { + fprintf(dbg, "\n", func); + return; + } + if (typ == 1 && (!(func == 3 || func == 4))) { + fprintf(dbg, "\n", func); + return; + } + switch (func) { + case 1: + fprintf(dbg, "(ID to Usrname)\n"); + break; + case 2: + fprintf(dbg, "(ID to Grpname)\n"); + break; + case 3: + fprintf(dbg, "(Usrname to ID)\n"); + break; + case 4: + fprintf(dbg, "(Grpname to ID)\n"); + break; + default: + fprintf(dbg, "(\n)"); + break; + } + } + + return; +} + +/* + * dump server information + * + */ + +#define get2(s) (u_short)(((s)[0]<<8)|((s)[1])) +#define get4(s) (u_int)(((s)[0]<<24)|((s)[1]<<16)|((s)[2]<<8)|((s)[3])) + +void +dbg_print_info(r, rl) +byte *r; +int rl; +{ + int i; + byte *q = r; + u_short srvrflags; + short machoff, afpoff, uamoff; + short vicnoff, sigoff, nadoff; + void dbg_print_name(); + void dbg_print_sflg(); + void dbg_print_icon(); + + machoff = get2(q); q += 2; + fprintf(dbg, "\tMchOff: %d\n", machoff); + afpoff = get2(q); q += 2; + fprintf(dbg, "\tAFPOff: %d\n", afpoff); + uamoff = get2(q); q += 2; + fprintf(dbg, "\tUAMOff: %d\n", uamoff); + vicnoff = get2(q); q += 2; + fprintf(dbg, "\tICNOff: %d\n", vicnoff); + srvrflags = get2(q); q += 2; + fprintf(dbg, "\tVolFlg: %04x\t", srvrflags); + dbg_print_sflg(srvrflags); + dbg_print_name("\tSrvrNm:", q); + q += ((*q)+1); + if ((u_long)q & 0x01) + q++; /* even */ + sigoff = get2(q); q += 2; + fprintf(dbg, "\tSIGOff: %d\n", sigoff); + nadoff = get2(q); q += 2; + fprintf(dbg, "\tNADOff: %d\n", nadoff); + if (machoff != 0) + dbg_print_name("\tMchTyp:", r+machoff); + if (afpoff != 0) { + q = r + afpoff; + fprintf(dbg, "\tVerCnt: %d\n", (int)(*q)); + for (i = *q++; i > 0; i--) { + dbg_print_name("\tAFPVer:", q); + q += ((*q) + 1); + } + } else + fprintf(dbg, "\t\n"); + if (uamoff != 0) { + q = r + uamoff; + fprintf(dbg, "\tUAMCnt: %d\n", (int)(*q)); + for (i = *q++; i > 0; i--) { + dbg_print_name("\tUAMStr:", q); + q += ((*q) + 1); + } + } else + fprintf(dbg, "\t\n"); + if (vicnoff != 0) { + fprintf(dbg, "\tVolICN:\n"); + dbg_print_icon((r+vicnoff), 256); + fprintf(dbg, "\n"); + } else + fprintf(dbg, "\t\n"); + if (sigoff != 0) { + q = r + sigoff; + fprintf(dbg, "\tSrvSIG: "); + for (i = 0; i < 16; i++) + fprintf(dbg, "%02x ", *(q+i)); + fprintf(dbg, "\n"); + } else + fprintf(dbg, "\t\n"); + if (nadoff != 0) { + q = r + nadoff; + fprintf(dbg, "\tNADCnt: %d\n", (int)(*q)); + for (i = *q++; i > 0; i--) { + fprintf(dbg, "\tAFPNAD: len %d tag %d ", q[0], q[1]); + switch (q[1]) { + case 0x01: + fprintf(dbg, "IP %d.%d.%d.%d\n", + q[2], q[3], q[4], q[5]); + break; + case 0x02: + fprintf(dbg, "IP %d.%d.%d.%d Port %d\n", + q[2], q[3], q[4], q[5], (q[6] << 8) | q[7]); + break; + case 0x03: + fprintf(dbg, "DDP net %d.%d node %d skt %d\n", + q[2], q[3], q[4], q[5]); + break; + default: + fprintf(dbg, "\n"); + break; + } + q += q[0]; + } + } else + fprintf(dbg, "\t\n"); + + return; +} + +/* + * print a pascal string + * + */ + +void +dbg_print_name(str, nam) +char *str; +byte *nam; +{ + int i; + + if (dbg != NULL) { + fprintf(dbg, "%s \"", str); + for (i = 0; i < (int)*nam; i++) + fprintf(dbg, "%c", *(nam+i+1)); + fprintf(dbg, "\"\n"); + } + + return; +} + +/* + * print server flags + * + */ + +void +dbg_print_sflg(bmap) +u_short bmap; +{ + int i, j; + + if (dbg != NULL) { + fprintf(dbg, "("); + for (i = 0, j = 0; i < 16; i++) { + if (bmap & (0x0001 << i)) { + bmap &= ~(0x0001 << i); + switch (i) { + case 0: + fprintf(dbg, "CopyFile"); + j++; + break; + case 1: + fprintf(dbg, "ChngPass"); + j++; + break; + case 2: + fprintf(dbg, "DontSavePass"); + j++; + break; + case 3: + fprintf(dbg, "SrvrMesg"); + j++; + break; + case 4: + fprintf(dbg, "SrvrSig"); + j++; + break; + case 5: + fprintf(dbg, "TCP/IP"); + j++; + break; + case 6: + fprintf(dbg, "SrvrNotf"); + j++; + break; + case 15: + fprintf(dbg, "MGetReqs"); + j++; + break; + default: + fprintf(dbg, "", i); + j++; + break; + } + if (bmap) + fprintf(dbg, ", "); + if (bmap && (j % 4) == 0) + fprintf(dbg, "\n\t\t\t"); + } + } + fprintf(dbg, ")\n"); + } + + return; +} +#endif /* DEBUG_AFP_CMD */ diff --git a/applications/aufs/afpspd.c b/applications/aufs/afpspd.c new file mode 100644 index 0000000..6a90014 --- /dev/null +++ b/applications/aufs/afpspd.c @@ -0,0 +1,410 @@ +/* + Speedup routines + + Author: Dan Oscarsson (Dan@dna.lth.se) +*/ + +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif +#include +#include +#include +#include +#include +#include "afps.h" + +#ifdef STAT_CACHE + +struct statobj { + char * NAME; + struct stat STAT; + int FN,RS; + time_t VAL_TIME; +}; + +#define VAL_OK 1 +#define VAL_NO 0 +#define VAL_INVALID 2 + +#define MAX_LEVEL 20 + +private struct statobj STATS[MAX_LEVEL+1]; + +private time_t CUR_TIME; +private struct timeval *Sched_Now = NULL; /* pointer to scheduler clock */ + + +#define EXP_TIME 120; + +private char CUR_DIR_STR[1024]; +private char * CUR_DIR = NULL; +private int CUR_DIR_LEN; +private char CUR_STAT_STR[1024]; +private int CUR_STAT_LEVEL = 0; + +/* + * The following is used as a sentinel in a statobj to mark invalid + * entries. It is stored in the st_nlink field in the STAT substructure. + * + */ + +#define BAD_LINK 0x7fff + +OSStatInit() +{ + register int P; + + for (P = 0; P <= MAX_LEVEL; P++) { + STATS[P].NAME = NULL; + STATS[P].STAT.st_nlink = BAD_LINK; + STATS[P].FN = VAL_INVALID; + STATS[P].RS = VAL_INVALID; + } + + getschedulerclock(&Sched_Now); + time(&CUR_TIME); /* since scheduler has not run yet */ +} + +OScd(path) + char * path; +{ + if (CUR_DIR != NULL && strlen(path) == CUR_DIR_LEN-1 && strncmp(path,CUR_DIR,CUR_DIR_LEN-1) == 0) + return; + if (chdir(path) < 0) + return; + CUR_DIR = CUR_DIR_STR; + strcpy(CUR_DIR,path); + strcat(CUR_DIR,"/"); + CUR_DIR_LEN = strlen(CUR_DIR); + if (DBOSI) + printf("OScd=%s\n",CUR_DIR); +} + +cwd_stat(path,buf) + char * path; + struct stat *buf; +{ + if (CUR_DIR != NULL && strncmp(path,CUR_DIR,CUR_DIR_LEN) == 0) { + if (DBOSI) + printf("OScwd_stat=%s\n",path+CUR_DIR_LEN); + return(stat(path+CUR_DIR_LEN,buf)); + } + if (DBOSI) + printf("OScwd_stat=%s\n",path); + return(stat(path,buf)); +} + +cwd_open(path,flags,mode) + char * path; + int flags,mode; +{ + if (CUR_DIR != NULL && strncmp(path,CUR_DIR,CUR_DIR_LEN) == 0) { + if (DBOSI) + printf("OScwd_open=%s\n",path+CUR_DIR_LEN); + return(open(path+CUR_DIR_LEN,flags,mode)); + } + if (DBOSI) + printf("OScwd_open=%s\n",path); + return(open(path,flags,mode)); +} + +char * cwd_path(path) + char * path; +{ + if (CUR_DIR != NULL && strncmp(path,CUR_DIR,CUR_DIR_LEN) == 0) { + if (DBOSI) + printf("OScwd_path=%s\n",path+CUR_DIR_LEN); + return path+CUR_DIR_LEN; + } + if (DBOSI) + printf("OScwd_path=%s\n",path); + return path; +} + +private release_stats(K) + int K; +{ + register int P; + + if (DBOSI) + printf("release_stats=%d of %d\n",K,CUR_STAT_LEVEL); + + for (P = K; P <= CUR_STAT_LEVEL; P++) { + STATS[P].STAT.st_nlink = BAD_LINK; + STATS[P].FN = VAL_INVALID; + STATS[P].RS = VAL_INVALID; + STATS[P].NAME = NULL; + } + if (K < CUR_STAT_LEVEL) + CUR_STAT_LEVEL = K; +} + +private int EXPIRE_REQ; + +private expire_stats() +{ + register int K; + + if (!EXPIRE_REQ) + return; + EXPIRE_REQ = 0; + for (K = 0; K <= CUR_STAT_LEVEL; K++) { + if (STATS[K].STAT.st_nlink == BAD_LINK) + continue; + if (STATS[K].VAL_TIME <= CUR_TIME) { + if (DBOSI) + printf("expired=%d(%s) t=%d\n", + K, STATS[K].NAME, + STATS[K].VAL_TIME - CUR_TIME); + STATS[K].STAT.st_nlink = BAD_LINK; + STATS[K].FN = VAL_INVALID; + STATS[K].RS = VAL_INVALID; + STATS[K].NAME = NULL; + } + } +} + +private struct statobj * locate_statobj(path) + char * path; +{ + register char *NEW, *OLD; + register char *TMP; + register int K; + char *REPLACE; + + if (DBOSI) + printf("locate_statobj: path '%s'\n", path); + + /* get the path and make sure it is an absolute one */ + NEW = path; + if (*NEW++ != '/') + return(NULL); + + /* get rid of old information */ + expire_stats(); + + /* record what we are replacing */ + REPLACE = CUR_STAT_STR; + + /* loop thru each path component and check against current */ + for(K=0;;) { + /* strip leading slashes */ + while (*NEW == '/') + NEW++; + + /* handle root */ + if (K == 0) { + if (*NEW == '\0') { + if (DBOSI) + printf("locate_statobj: root\n"); + STATS[0].NAME = ""; + return(&STATS[0]); + } + /* something more */ + K++; + } + + /* do we have info for this level? */ + if (K > CUR_STAT_LEVEL) + break; /* nope */ + + /* do we have a name for this level */ + if ((OLD = STATS[K].NAME) == NULL) + break; /* nope */ + + /* record buffer name */ + REPLACE = OLD; + + /* compare name */ + TMP = NEW; + while (*OLD && *OLD == *TMP) { + OLD++; + TMP++; + } + + /* did we end cleanly? */ + if (*OLD != '\0') + break; /* nope */ + + /* did NEW path end? */ + if (*TMP == '\0') { + /* yes */ + if (DBOSI) + printf("locate_statobj: found %d: '%s'\n", + K,STATS[K].NAME); + return(&STATS[K]); + } + + /* did NEW path end on slash? */ + if (*TMP != '/') + break; /* nope */ + + /* about to loop, see if too many levels */ + if (++K >= MAX_LEVEL) { + if (DBOSI) + printf("locate_statobj: too many '%s'\n", + path); + return(NULL); + } + + /* loop again */ + REPLACE = ++OLD; + NEW = TMP; + } + + /* add components */ + release_stats(K); + + OLD = REPLACE; + for (;;) { + /* strip leading slashes */ + while (*NEW == '/') + NEW++; + + /* make sure we have something */ + if (*NEW == '\0') { + /* must be trailing slash */ + if (DBOSI) + printf("locate_statobj: trailing / in '%s'\n", + path); + return(NULL); + } + + /* record and copy component */ + STATS[K].NAME = OLD; + + while (*NEW && *NEW != '/') + *OLD++ = *NEW++; + *OLD++ = '\0'; + + /* record new stat level */ + CUR_STAT_LEVEL = K; + + /* are we done? */ + if (*NEW == '\0') { + /* yep */ + if (DBOSI) + printf("locate_statobj: return %d: '%s'\n", + K,STATS[K].NAME); + return(&STATS[K]); + } + + /* about to loop, see if too many levels */ + if (++K >= MAX_LEVEL) { + if (DBOSI) + printf("locate_statobj: too many (add) '%s'\n", + path); + return(NULL); + } + + /* loop again */ + } + + /* NEVER REACHED */ +} + +OSstat(path,buf) + char * path; + struct stat *buf; +{ + struct statobj * CE; + + if (DBOSI) + printf("OSstat=%s\n",path); + + CE = locate_statobj(path); + if (CE != NULL) { + if (CE->STAT.st_nlink != BAD_LINK) { + if (DBOSI) + printf("OSstat=cache path\n"); + bcopy(&CE->STAT,buf,sizeof(struct stat)); + return 0; + } + if (cwd_stat(path,buf) == 0) { + if (DBOSI) + printf("OSstat=caching path\n"); + bcopy(buf,&CE->STAT,sizeof(struct stat)); + CE->VAL_TIME = CUR_TIME+EXP_TIME; + return 0; + } + if (DBOSI) + printf("OSstat=no cache\n"); + return -1; + } + if (DBOSI) + printf("OSstat=not cached\n"); + return cwd_stat(path,buf); +} + +OSfinderinfo(path) + char * path; +{ + char pp[MAXPATHLEN]; + struct stat S; + struct statobj * CE; + + CE = locate_statobj(path); + if (CE != NULL) { + if (CE->FN != VAL_INVALID) + return CE->FN; + strcpy(pp,path); + toFinderInfo(pp,""); + if (cwd_stat(pp,&S) < 0 || !S_ISDIR(S.st_mode)) + CE->FN = VAL_NO; + else + CE->FN = VAL_OK; + return CE->FN; + } + strcpy(pp,path); + toFinderInfo(pp,""); + if (cwd_stat(pp,&S) < 0 || !S_ISDIR(S.st_mode)) + return 0; + else + return 1; +} + +OSresourcedir(path) + char * path; +{ + char pp[MAXPATHLEN]; + struct stat S; + struct statobj * CE; + + CE = locate_statobj(path); + if (CE != NULL) { + if (CE->RS != VAL_INVALID) + return CE->RS; + strcpy(pp,path); + toResFork(pp,""); + if (cwd_stat(pp,&S) < 0 || !S_ISDIR(S.st_mode)) + CE->RS = VAL_NO; + else + CE->RS = VAL_OK; + return CE->RS; + } + strcpy(pp,path); + toResFork(pp,""); + if (cwd_stat(pp,&S) < 0 || !S_ISDIR(S.st_mode)) + return 0; + else + return 1; +} + +OSflush_stat() +{ + CUR_TIME = Sched_Now->tv_sec; + if (DBOSI) + printf("OSflush_stat\n"); + release_stats(0); +} + +OSstat_cache_update() +{ + CUR_TIME = Sched_Now->tv_sec; + EXPIRE_REQ = 1; +} + +#endif STAT_CACHE diff --git a/applications/aufs/afpudb.c b/applications/aufs/afpudb.c new file mode 100644 index 0000000..7cbc4de --- /dev/null +++ b/applications/aufs/afpudb.c @@ -0,0 +1,391 @@ +/* + * $Author: djh $ $Date: 1993/08/04 15:23:33 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpudb.c,v 2.3 1993/08/04 15:23:33 djh Rel djh $ + * $Revision: 2.3 $ +*/ + +/* + * afpudb.c - Appletalk Filing Protocol Unix Finder Information + * database. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * August 1987 CCKim Created. + * + */ + +/* PATCH: XENIX:file.3, djh@munnari.OZ.AU, 20/11/90 */ + +#include +#include +#ifndef _TYPES +# include +#endif +#include +#include +#include +#include "afpudb.h" +#ifdef NEEDFCNTLDOTH +# include +#endif + +#ifdef bsd +# define BSDSYSTEM +#endif + +#ifdef bsd4_2 +# define BSDSYSTEM +#endif + +/* + * Deal with unix file types + * +*/ + +/* This is the "file" icon - generic for unix files */ +private byte unix_text[] = { + 0x0f,0xff,0xfc,0x00, /* XXXXXXXXXXXXXXXXXX */ + 0x08,0x00,0x06,0x00, /* X XX */ + 0x08,0x00,0x05,0x00, /* X X X */ + 0x08,0x00,0x04,0x80, /* X X X */ + 0x08,0x00,0x04,0x40, /* X X X */ + 0x08,0x0c,0x04,0x20, /* X XX X X */ + 0x09,0xf8,0x87,0xf0, /* X XXXXXX X XXXXXXX */ + 0x0b,0xf9,0xc0,0x10, /* X XXXXXXX XXX X */ + 0x08,0x9b,0xe0,0x10, /* X X XX XXXXX X */ + 0x09,0x29,0x84,0x50, /* X X X X XX X X X */ + 0x0b,0x29,0x84,0x50, /* X XX X X XX X X X */ + 0x0b,0x29,0x85,0x50, /* X XX X X XX X X X X */ + 0x0b,0x69,0x85,0x50, /* X XX XX X XX X X X X */ + 0x0b,0x69,0x85,0x50, /* X XX XX X XX X X X X */ + 0x0b,0x49,0x85,0x50, /* X XX X X XX X X X X */ + 0x0b,0x89,0x85,0x50, /* X XXX X XX X X X X */ + 0x09,0x8b,0xa5,0x10, /* X XX X XXX X X X X */ + 0x09,0xfe,0xc5,0x50, /* X XXXXXXXX XX X X X X */ + 0x08,0x7c,0xc0,0x10, /* X XXXXX XX X */ + 0x08,0x00,0x00,0x10, /* X X */ + 0x08,0x40,0x44,0x50, /* X X X X X X */ + 0x09,0x54,0x45,0x50, /* X X X X X X X X X X */ + 0x09,0x55,0x55,0x50, /* X X X X X X X X X X X X */ + 0x09,0x51,0x55,0x50, /* X X X X X X X X X X X */ + 0x09,0x55,0x55,0x50, /* X X X X X X X X X X X X */ + 0x09,0x55,0x55,0x50, /* X X X X X X X X X X X X */ + 0x09,0x15,0x14,0x50, /* X X X X X X X X X */ + 0x09,0x55,0x54,0x10, /* X X X X X X X X X X */ + 0x09,0x41,0x50,0x10, /* X X X X X X X */ + 0x08,0x00,0x00,0x10, /* X X */ + 0x08,0x00,0x00,0x10, /* X X */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + /* mask */ + 0x0f,0xff,0xfc,0x00, /* XXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xfe,0x00, /* XXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0x00, /* XXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0x80, /* XXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xc0, /* XXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xe0, /* XXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0 /* XXXXXXXXXXXXXXXXXXXXXXXX */ +}; + +#ifdef SMART_UNIX_FINDERINFO +private byte unix_framemaker[] = { + 0x0F,0xFF,0xFE,0x00, /* XXXXXXXXXXXXXXXXXXX */ + 0x08,0x00,0x03,0x00, /* X XX */ + 0x0B,0xFF,0xFE,0x80, /* X XXXXXXXXXXXXXXXXX X */ + 0x0B,0xFF,0xFE,0x40, /* X XXXXXXXXXXXXXXXXX X */ + 0x0B,0xFF,0xFE,0x20, /* X XXXXXXXXXXXXXXXXX X */ + 0x08,0x00,0x02,0x10, /* X X X */ + 0x08,0x40,0x03,0xFA, /* X X XXXXXXX X */ + 0x38,0xA0,0x00,0x08, /* XXX X X X */ + 0x09,0x53,0xF7,0x8F, /* X X X X XXXXXXXXX X XXXX */ + 0xF9,0xB0,0x00,0x0B, /* XXXXX XX XX X XX */ + 0x19,0x53,0x7F,0x4B, /* XX X X X XX XXXXXXX X X XX */ + 0x18,0xA0,0x00,0x08, /* XX X X X */ + 0x18,0x40,0x00,0x0E, /* XX X XXX */ + 0x88,0x07,0xDF,0x48, /* X X XXXXX XXXXX X X */ + 0x28,0x00,0x00,0x0B, /* X X X XX */ + 0x09,0xBE,0xDF,0xCC, /* X XX XXXXX XX XXXXXXX XX */ + 0xA8,0x00,0x00,0x0E, /* X X X XXX */ + 0x49,0xEE,0xFB,0xC8, /* X X XXXX XXX XXXXX XXXX X */ + 0x08,0x00,0x00,0x08, /* X X */ + 0x09,0xDF,0x3D,0xC8, /* X XXX XXXXX XXXX XXX X */ + 0x08,0x00,0x00,0x08, /* X X */ + 0x08,0x00,0x03,0xC8, /* X XXXX X */ + 0x09,0xFA,0xEB,0xC8, /* X XXXXXX X XXX X XXXX X */ + 0x08,0x00,0x03,0xC9, /* X XXXX X X */ + 0x09,0xDF,0x5B,0xCD, /* X XXX XXXXX X XX XXXX XX X */ + 0x48,0x00,0x00,0x0B, /* X X X XX */ + 0x08,0x00,0x00,0x08, /* X X */ + 0x7B,0xFF,0xFF,0xEB, /* XXXX XXXXXXXXXXXXXXXXXXXXX X XX */ + 0x0B,0xFF,0xFF,0xE8, /* X XXXXXXXXXXXXXXXXXXXXX X */ + 0x7B,0xFF,0xFF,0xEE, /* XXXX XXXXXXXXXXXXXXXXXXXXX XXX */ + 0xC8,0x00,0x00,0x08, /* XX X X */ + 0x0F,0xFF,0xFF,0xF8, /* XXXXXXXXXXXXXXXXXXXXXXXXX */ + /* mask */ + 0x0f,0xff,0xfc,0x00, /* XXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xfe,0x00, /* XXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0x00, /* XXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0x80, /* XXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xc0, /* XXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xe0, /* XXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x0f,0xff,0xff,0xf0 /* XXXXXXXXXXXXXXXXXXXXXXXX */ +}; + +/* other icons */ +#endif SMART_UNIX_FINDERINFO + +#define FNDR_NOFNDR 0 +#define FNDR_DEF 1 +#define FNDR_DEVICE 2 +#define FNDR_SOCKET 3 +#ifdef SMART_UNIX_FINDERINFO +#define FNDR_BIN 4 +#define FNDR_UPGM 5 +#define FNDR_UOBJ 6 +#define FNDR_ARCHIVE 7 +#define FNDR_CPIO 8 +#define FNDR_LOCKED 9 +#define FNDR_FRAME 10 +#endif SMART_UNIX_FINDERINFO + +#define deffinfo(C1,C2,C3,C4,c1,c2,c3,c4,com,icon) {{(C1),(C2),(C3),(C4)}, \ + {(c1),(c2),(c3),(c4)}, \ + (com), sizeof(com)-1, \ + (icon), sizeof((icon))} + +struct ufinderdb uf[] = { + {{0, 0, 0, 0}, {0, 0, 0, 0}, "", 0, NULL, 0}, + deffinfo('u','n','i','x','T','E','X','T', + "This is a Unix\252 created file", unix_text), + deffinfo('u','n','i','x','D','E','V',' ', + "This is a Unix\252 device", NULL), + deffinfo('u','n','i','x','S','K','T',' ', + "This is a Unix\252 socket", NULL), +#ifdef SMART_UNIX_FINDERINFO + deffinfo('u','n','i','x','D','A','T','A', + "This is a Unix\252 binary file", NULL), + deffinfo('u','n','i','x','P','G','R','M', + "This is a Unix\252 program", NULL), + deffinfo('u','n','i','x','O','B','J',' ', + "This is a Unix\252 object file", NULL), + deffinfo('u','n','i','x','A','R',' ',' ', + "This is a Unix\252 archive file", NULL), + deffinfo('u','n','i','x','C','P','I','O', + "This is a Unix\252 cpio file", NULL), + deffinfo('u','n','i','x','L','C','K','D', + "This file is not readable", NULL), + deffinfo('F','r','a','m','F','A','S','L', + "This is a Unix\252 FrameMaker file", unix_framemaker) +#endif SMART_UNIX_FINDERINFO +}; + +export int uf_len = sizeof(uf)/sizeof(struct ufinderdb); + +#ifdef USR_FILE_TYPES +struct uft uft[NUMUFT]; +#endif USR_FILE_TYPES + +int +os_getunixtype(path, stb) +char *path; +struct stat *stb; +{ +#ifdef SMART_UNIX_FINDERINFO + char buf[BUFSIZ]; + int fd, k, l; +#endif SMART_UNIX_FINDERINFO + int i; + + if ((stb->st_mode & S_IFMT) == S_IFDIR) /* a directory? */ + return(FNDR_NOFNDR); + + switch (stb->st_mode & S_IFMT) { + case S_IFCHR: + case S_IFBLK: + /* super wanky thing to do would be to return an type */ + /* based upon the device type */ + return(FNDR_DEVICE); + break; +#ifdef S_IFSOCK + case S_IFSOCK: + return(FNDR_SOCKET); + break; +#endif S_IFSOCK + } + +#ifdef SMART_UNIX_FINDERINFO + if ((fd=open(path,O_RDONLY)) < 0) + return(FNDR_DEF); + if ((i = read(fd, buf, BUFSIZ)) <= 0) { + (void)close(fd); /* ignore error here */ + return(FNDR_DEF); + } + (void)close(fd); /* ignore error here */ + switch (*(int *)buf) { +#ifdef BSDSYSTEM + case 0413: + case 0410: + case 0411: + case 0407: + return(FNDR_UPGM); + case 0177555: + case 0177545: + return(FNDR_ARCHIVE); +#endif BSDSYSTEM + case 070707: + return(FNDR_CPIO); + } + if (strncmp(buf, "\n", sizeof("!\n")-1) == 0) + return(FNDR_ARCHIVE); +#endif BSDSYSTEM +#ifndef BIN_FUZZ +# define BIN_FUZZ BUFSIZ/3 +#endif BIN_FUZZ + for (k = 0, l = 0; k < i; k++) + if (buf[k] & 0x80) + l++; + if (l > BIN_FUZZ) + return(FNDR_BIN); +#endif SMART_UNIX_FINDERINFO +#ifdef USR_FILE_TYPES + if ((i = uft_match(path)) >= 0) + return(i+FNDR_UFT); +#endif USR_FILE_TYPES + return(FNDR_DEF); +} + +int +os_issmartunixfi() +{ +#ifdef SMART_UNIX_FINDERINFO + return(1); +#else + return(0); +#endif +} + +#ifdef USR_FILE_TYPES +/* + * initialize the UFT structure + * + */ + +int +uft_init() +{ + uft[0].uft_suffixlen = -1; +} + +/* + * check the file name against the suffix list + * + */ + +int +uft_match(file) +char *file; +{ + int i = 0; + + while (uft[i].uft_suffixlen >= 0) { + if (suffix_match(file, &uft[i])) + return(i); + i++; + } + return(-1); +} + +int +suffix_match(file, uft) +char *file; +struct uft *uft; +{ + int i; + + if (uft->uft_suffixlen == 0) /* wildcard */ + return(1); + + if ((i = strlen(file)) >= uft->uft_suffixlen + && strcmp(uft->uft_suffix, file+i-uft->uft_suffixlen) == 0) + return(1); + + return(0); +} + +/* + * check the file creator and type against the list + * + */ + +int +uft_match_finfo(creat, ftype) +char *creat, *ftype; +{ + int i = 0; + + while (uft[i].uft_suffixlen >= 0) { + if (bcmp(creat, uft[i].uft_creat, 4) == 0 + && bcmp(ftype, uft[i].uft_ftype, 4) == 0) + return(i); + i++; + } + return(-1); +} +#endif USR_FILE_TYPES diff --git a/applications/aufs/afpudb.h b/applications/aufs/afpudb.h new file mode 100644 index 0000000..28c3a9e --- /dev/null +++ b/applications/aufs/afpudb.h @@ -0,0 +1,45 @@ +/* + * $Author: djh $ $Date: 1993/08/04 15:23:33 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpudb.h,v 2.3 1993/08/04 15:23:33 djh Rel djh $ + * $Revision: 2.3 $ +*/ + +/* + * afpudb.c - Appletalk Filing Protocol Unix Finder Information + * database. Header file. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * August 1987 CCKim Created. + * + */ + +struct ufinderdb { + byte ufd_creat[4]; + byte ufd_ftype[4]; + char *ufd_comment; + int ufd_commentlen; + byte *ufd_icon; + int ufd_iconsize; +}; + +int os_getunixtype(); + +#ifdef USR_FILE_TYPES +struct uft { + byte uft_suffix[16]; /* unix filename suffix, ie: .tar */ + short uft_suffixlen; + byte *uft_xlate; /* translation to perform ie: raw */ + byte uft_creat[4]; /* creator to appear as ie: 'TAR ' */ + byte uft_ftype[4]; /* macintosh file type ie: 'TEXT' */ + char *uft_comment; /* "This is a UNIX tar file" */ + short uft_commentlen; +}; +#define NUMUFT 50 +#define FNDR_UFT 100 +#endif USR_FILE_TYPES diff --git a/applications/aufs/afpvols b/applications/aufs/afpvols new file mode 100644 index 0000000..7614459 --- /dev/null +++ b/applications/aufs/afpvols @@ -0,0 +1,12 @@ +# +# This is Bill's Apple Filing Protocol Volume file +# +# Format: +# path:volume name[:password][:] +# + +/usr/src/local/mac/Special:Special Mac Files:: +/usr/src/local/mac:Macintosh Files: +~cck/mac:Chuck's Volume: + + diff --git a/applications/aufs/afpvols.c b/applications/aufs/afpvols.c new file mode 100644 index 0000000..1a800f4 --- /dev/null +++ b/applications/aufs/afpvols.c @@ -0,0 +1,1043 @@ +/* + * $Author: djh $ $Date: 1996/06/19 04:16:30 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpvols.c,v 2.18 1996/06/19 04:16:30 djh Rel djh $ + * $Revision: 2.18 $ + * + */ + +/* + * afpvols.c - Appletalk Filing Protocol Volume Routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1987,1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * + */ + +/* + * Non OS dependant support routines for: + * + * FPGetVolParms() + * FPSetVolParms() + * FPOpenVol() + * FPCloseVol() + * FPFlush() + * + */ + +#include +#include +#ifndef _TYPES +# include /* assume included by param.h */ +#endif _TYPES +#ifdef CREATE_AFPVOL +# include +#endif CREATE_AFPVOL +#include +#include +#include +#include "afps.h" +#include "afpntoh.h" +#include "afpvols.h" +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#ifdef DEBUG_AFP_CMD +extern FILE *dbg; +#endif /* DEBUG_AFP_CMD */ + +private VolPtr VolTbl[MAXVOLS]; /* table of VolEntry records */ +private int VolCnt = 0; /* number of volumes */ +private VolBitMap VolModBitMap = 0; /* bitmap of modified volumes */ + +private PackEntry VolPackR[] = { /* Volume Parms Reply */ + PACK(VolPtr,P_WORD,v_bitmap), /* bitmap specifies below items */ + PACK(VolPtr,P_BMAP,v_bitmap), /* bitmap specifies below items */ + PAKB(VolPtr,P_WORD,v_attr,VP_ATTR), /* attributes word */ + PAKB(VolPtr,P_WORD,v_sig,VP_SIG), /* signature word */ + PAKB(VolPtr,P_TIME,v_cdate,VP_CDATE), /* creation date */ + PAKB(VolPtr,P_TIME,v_mdate,VP_MDATE), /* modification date */ + PAKB(VolPtr,P_TIME,v_bdate,VP_BDATE), /* last back date */ + PAKB(VolPtr,P_WORD,v_volid,VP_VOLID), /* volume id */ + PAKB(VolPtr,P_DWRD,v_free,VP_FREE), /* free bytes */ + PAKB(VolPtr,P_DWRD,v_size,VP_SIZE), /* size in bytes */ + PKSB(VolPtr,P_OSTR,v_name,VP_NAME), /* name of volume */ + PKSB(VolPtr,P_BYTS,v_efree,VP_EFREE), /* extended free bytes */ + PKSB(VolPtr,P_BYTS,v_esize,VP_ESIZE), /* extended total bytes */ + PACKEND() +}; + +/* + * void VNew(char *path, char *name, char *pwd) + * + * Given a path, volume name, and password string, create a new volume + * in VolTbl. + * + */ + +void +VNew(path,name,pwd) +char *name; +char *path; +char *pwd; +{ + VolPtr vp; + char *malloc(); + extern int sessvers; + + if ((strlen(name) > MAXVLEN) || + (strlen(path) > MAXDLEN) || + (strlen(pwd) > MAXPLEN)) { + logit(0,"VNew: path, name or password too long on path = %s",path); + return; + } + + if (DBVOL) + printf("Adding vol '%s' on path '%s' pwd='%s'\n",name,path,pwd); + + /* create volume record */ + vp = (VolPtr) malloc(sizeof(VolEntry)); + strcpy(vp->v_name,name); /* copy the name */ + strcpy(vp->v_path,path); /* the path */ + strcpy(vp->v_pwd,pwd); /* and the password */ + vp->v_mounted = FALSE; /* not opened */ + vp->v_volid = VolCnt+1; /* will be volcnt+1 */ + vp->v_sig = VOL_FIXED_DIRID; /* signature word */ + vp->v_attr = 0; /* clear attributes */ + if (sessvers >= AFPVersion2DOT1) + vp->v_attr |= V_SUPPORTSFILEIDS; /* for ExchangeFiles call */ + + /* Now make sure entry is valid */ + if (OSVolInfo(path,vp,VP_ALL) != noErr) { /* get volume info */ + free((char *) vp); /* bad... release storage */ + printf("VNew: No volinfo for %s on path %s\n",name,path); + return; + } + + /* This is deferred because I'm not sure how to deallocate :-) */ + /* and isn't needed until we have validated the entry anyway */ + vp->v_rootd = Idirid(path); /* create directory handle */ + if (vp->v_rootd != NILDIR) /* avoid NULL handles */ + InitDIDVol(vp->v_rootd, VolCnt); /* initialize volume info for list */ + + /* Okay, stick it into the table */ + if (vp->v_rootd != NILDIR) /* avoid NULL handles */ + VolTbl[VolCnt++] = vp; /* set volume record */ + + /* check for color volume icon */ + if (!icon_exists(path)) + icon_create(path); + + return; +} + +private char * +spanspace(p) +char *p; +{ + while (*p == ' ' || *p == '\t' || *p == '\n') + p++; + return(p); +} + +/* + * char *vskip(char *p) + * + * vskip skips to the next field in a line containing volume information + * as read from the VOLFILE. The field seperator ":" or the end of line + * character is set to NULL in order to terminate the field. + * + */ + +private char * +vskip(p) +char *p; +{ + while (*p != '\0' && *p != ':' && *p != '\n') + p++; + if (*p != '\0') /* at end of string? */ + *p++ = '\0'; /* no tie off this field */ + return(p); /* and return pointer */ +} + +/* + * VRdVFile(FILE *fd) + * + * Reads a file containing volume information from the specified path. + * The file descriptor is closed after reading the file + * + * VRdVFile is intended to read VOLFILE from the user's home directory + * after FPLogin. The information in this file is stored in the VolTbl. + * + * Format of VOLFILE: + * path:volume name[:optional password][:] + * + * blank lines and lines starting with '#' are ignored. + * + */ + +VRdVFile(fd) +FILE *fd; +{ + char line[MAXLLEN+1]; + char *pathp,*namep,*pswdp,*p,*tilde(); + int origVolCnt; +#ifdef ISO_TRANSLATE + void cISO2Mac(); +#endif ISO_TRANSLATE + + origVolCnt = VolCnt; + while ((p = fgets(line,MAXLLEN,fd)) != NULL) { /* read lines */ + if (DBVOL) printf("VRdVFile: Parse : %s\n",p); + p = spanspace(p); /* span spaces */ + if (DBVOL) printf("VRdVFile: After spanspace : %s\n",p); + if (*p == '#' || *p == '\0') /* comment or blank line? */ + continue; /* yes, skip it */ + pathp = p; p = vskip(p); /* save ptr to start of path */ + if (DBVOL) printf("VRdVFile: pathp : %s\n",pathp); + namep = p; p = vskip(p); /* start of name */ + if (DBVOL) printf("VRdVFile: namep : %s\n",namep); +#ifdef ISO_TRANSLATE + cISO2Mac(namep); +#endif ISO_TRANSLATE + pswdp = p; p = vskip(p); /* save it */ + if (DBVOL) printf("VRdVFile: pswdp : %s\n",pswdp); + pathp = tilde(pathp); /* expand the path */ + if (pathp == NULL) /* non existent user */ + continue; /* skip it */ + if (DBVOL) printf("VRdVFile: pathp after tilde : %s\n",pathp); + VNew(pathp,namep,pswdp); /* add new entry */ + } + fclose(fd); /* close file */ + return(VolCnt != origVolCnt); /* return true if found any entries */ +} + +#ifdef REREAD_AFPVOLS +/* + * VRRdVFile(FILE *fd) + * + * VRRdVFile() is called after a USR1 signal to the aufs process. + * We delete contents of current VolTbl and call VRdVFile() again. + * + */ + +VRRdVFile(fd) +FILE *fd; +{ + int i; + + for (i = 0; i < VolCnt; i++) + free(VolTbl[i]); + + VolCnt = 0; + + return(VRdVFile(fd)); +} +#endif REREAD_AFPVOLS + +/* + * void VInit(char *usr,char *home) + * + * VInit adds entries to the table of volumes by reading the user's + * VOLFILE or VOLFILE1. If no VOLFILE exists on the path specified by + * "home" then a default entry entry of path=home and name=usr is + * setup. + * + */ + +void +VInit(usr,home) +char *usr; +char *home; +{ + char vfn[MAXDLEN+20]; /* afpvols file name */ + FILE *fd; + + if (home == NULL) /* no home, then nothing to do */ + return; + + strcpy(vfn,home); /* copy directory */ + strcat(vfn,"/"); /* terminator */ + strcat(vfn,VOLFILE); /* and then the volsfile name */ + + if ((fd = fopen(vfn, "r")) == NULL) { + strcpy(vfn,home); /* copy directory */ + strcat(vfn,"/"); /* terminator */ + strcat(vfn,VOLFILE1); /* and then the volsfile name */ + if ((fd = fopen(vfn, "r")) == NULL) { +#ifdef CREATE_AFPVOL + if (aufsDirSetup(home, usr) >= 0) + logit(0, "CREATE_AFPVOL: setting up %s for Mac access", home); +#else CREATE_AFPVOL + if (DBVOL) + printf("VRdVFile: no afpvols or .afpvols in %s\n",home); + VNew(home,usr,""); /* if none, then setup home dir */ +#endif CREATE_AFPVOL + return; + } + } + (void)VRdVFile(fd); /* read vols file from user */ + if (VolCnt == 0) + VNew(home, usr, ""); +} + +/* + * return count of volumes + * +*/ +int +VCount() +{ + return(VolCnt); +} + +/* + * int VMakeVList(VolParm *vp,byte *vpl) + * + * Store into vp a number of VolParm entries, one for each volume + * we know about. Return in vpl the length of the information. + * Return the count of volumes. + * + * Used by GetSrvrParms. + * + */ + +int +VMakeVList(r) +byte *r; /* ptr to place to store */ +{ + VolParm vpp; + int v, len; + extern PackEntry ProtoGSPRPvol[]; + + for (v=0,len=0; v < VolCnt; v++) { + if (*VolTbl[v]->v_pwd != '\0') + vpp.volp_flag = SRVRP_PASSWD; /* is password protected */ + else + vpp.volp_flag = 0; /* no flags */ + cpyc2pstr(vpp.volp_name,VolTbl[v]->v_name); + len += htonPackX(ProtoGSPRPvol, &vpp, r+len); + } + return(len); /* return size used */ +} + +/* + * OSErr FPGetVolParms(byte *p,int l,byte *r,int *rl); + * + * This call is used to retrieve paramters for a particular volume. + * The volume is specified by its Volume ID as returned from the + * FPOpenVol call. + * + * + */ + +OSErr +FPGetVolParms(p,l,r,rl) +byte *p,*r; +int *rl,l; +{ + GetVolParmsPkt gvp; /* cast to packet type */ + int ivol,err; + VolPtr vp; + + ntohPackX(PsGetVolParms,p,l,(byte *) &gvp); /* decode packet */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_vmap(); + fprintf(dbg, "\tVolID: %04x\n", gvp.gvp_volid); + fprintf(dbg, "\tBtMap: %04x\t", gvp.gvp_bitmap); + dbg_print_vmap(gvp.gvp_bitmap); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + ivol = EtoIVolid(gvp.gvp_volid); /* pick up volume id */ + if (ivol < 0) + return(ivol); /* unknown volume */ + if (!VolTbl[ivol]->v_mounted) /* must have called FPOpenVol first */ + return(aeParamErr); /* not opened */ + vp = VolTbl[ivol]; /* ptr to volume */ + err = OSVolInfo(pathstr(vp->v_rootd),vp,gvp.gvp_bitmap); /* get vol info */ + + if (V_BITTST(VolModBitMap,ivol)) { /* modified since last call? */ + V_BITCLR(VolModBitMap,ivol); /* yes... clear the flag */ + vp->v_mdate = CurTime(); /* set modify time */ + } + + if (err != noErr) + return(err); + vp->v_bitmap = gvp.gvp_bitmap; + *rl = htonPackX(VolPackR,(byte *) vp,r); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_vmap(); + void dbg_print_vprm(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tBtMap: %04x\t", vp->v_bitmap); + dbg_print_vmap(vp->v_bitmap); + dbg_print_vprm(vp->v_bitmap, r+2, (*rl)-2); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); /* all ok */ +} + +/* + * OSErr FPSetVolParms(byte *p,byte *r,int *rl) [NOOP] + * + * This call is used to set the parameters for a particular volume. The + * volume is specified by its VolumeID as returned from the FPOpenVol + * call. + * + * In AFP Version 1.0 only the Backup Date field may be set. Under + * Unix this is a noop. + * + */ + +/*ARGSUSED*/ +OSErr +FPSetVolParms(p,l,r,rl) +byte *p,*r; +int *rl; +{ + SetVolParmsPkt svp; + int ivol; + + ntohPackX(PsSetVolParms,p,l,(byte *) &svp); /* unpack */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_vmap(); + void dbg_print_date(); + fprintf(dbg, "\tVolID: %04x\n", svp.svp_volid); + fprintf(dbg, "\tBtMap: %04x\t", svp.svp_bitmap); + dbg_print_vmap(svp.svp_bitmap); + dbg_print_date(svp.svp_backdata); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + ivol = EtoIVolid(svp.svp_volid); + if (ivol < 0) + return(aeParamErr); /* unknown volume */ + + if ((svp.svp_bitmap & ~VP_BDATE) != 0) + return(aeBitMapErr); /* trying to set unknown parms */ + + if (svp.svp_bitmap & VP_BDATE) /* want to set backup date? */ + VolTbl[ivol]->v_bdate = svp.svp_backdata; /* yes... */ + + return(noErr); /* return ok... */ +} + +/* + * OSErr FPOpenVol(byte *p, byte *r, int *rl) + * + * This call is used to "mount" a volume. It must be called before any + * other call can be made to access objects on the volume. + * + */ + +OSErr +FPOpenVol(p,l,r,rl) +byte *p; +int l; +byte *r; +int *rl; +{ + OpenVolPkt ovl; + char pwd[MAXPASSWD+1]; /* null terminated password */ + int v; + OSErr err; + VolPtr vp; + + ovl.ovl_pass[0] = '\0'; /* zero optional password */ + ntohPackX(PsOpenVol,p,l,(byte *) &ovl); /* decode packet */ + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_vmap(); + fprintf(dbg, "\tBtMap: %04x\t", ovl.ovl_bitmap); + dbg_print_vmap(ovl.ovl_bitmap); + fprintf(dbg, "\tVolNm: \"%s\"\n", ovl.ovl_name); + fprintf(dbg, "\tVolPw: \"%s\"\n", ovl.ovl_pass); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + strncpy(pwd,(char *) ovl.ovl_pass,MAXPASSWD); /* copy optional pwd */ + pwd[MAXPASSWD] = '\0'; /* tie off with a null */ + + if (DBVOL) + printf("FPOpenVol: name=%s, pwd=%s, bm=%d\n", + ovl.ovl_name,pwd,ovl.ovl_bitmap); + + for (v=0; v < VolCnt; v++) /* locate the volume */ + if (strcmp(VolTbl[v]->v_name,(char *) ovl.ovl_name) == 0) + break; + + if (v >= VolCnt) /* did we find a vol in the scan? */ + return(aeParamErr); /* no... unknown volume name */ + + vp = VolTbl[v]; /* dereference */ + if (*vp->v_pwd != '\0') /* password exists on volume? */ + if (strcmp(vp->v_pwd,pwd) != 0) /* yes, check for match */ + return(aeAccessDenied); /* not the same... return failure */ + + if (DBVOL) + printf("FPOpenVol: name=%s volid=%d\n", vp->v_name,vp->v_volid); + + vp->v_mounted = TRUE; /* now it is opened... */ + + /* update volume info */ + if ((err = OSVolInfo(vp->v_path,vp,VP_ALL)) != noErr) + return(err); + + vp->v_bitmap = ovl.ovl_bitmap; /* bitmap for packing result */ + + *rl = htonPackX(VolPackR,(byte *) vp,r); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + void dbg_print_vmap(); + void dbg_print_vprm(); + fprintf(dbg, " Return Parameters:\n"); + fprintf(dbg, "\tBtMap: %04x\t", vp->v_bitmap); + dbg_print_vmap(vp->v_bitmap); + dbg_print_vprm(vp->v_bitmap, r+2, (*rl)-2); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + return(noErr); /* return ok */ +} + + +/* + * OSErr FPCloseVol(byte *p,byte *r,int *rl) + * + * This call is used to "unmount" a volume. + * + */ + +/*ARGSUSED*/ +OSErr +FPCloseVol(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + CloseVolPkt cv; + int ivol; + + ntohPackX(PsCloseVol,p,l,(byte *) &cv); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tVolID: %04x\n", cv.cv_volid); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + ivol = EtoIVolid(cv.cv_volid); /* convert to internal format */ + if (ivol < 0) /* error code */ + return(ivol); + + if (DBVOL) + printf("FPCloseVol %d=%s\n",ivol,VolTbl[ivol]->v_name); + + if (!VolTbl[ivol]->v_mounted) /* was it mounted? */ + return(aeMiscErr); /* no... not mounted */ + + VolTbl[ivol]->v_mounted = FALSE; /* indicate no longer mounted */ + return(noErr); /* and return ok */ +} + + +/* + * OSErr FPFlush(byte *p,byte *r, int *rl) + * + * This call is used to flush to disk any data relating to the specified + * volume that has been modified by the user. + * + */ + +/*ARGSUSED*/ +OSErr +FPFlush(p,l,r,rl) +byte *p,*r; +int l,*rl; +{ + FlushPkt fls; + int ivol; + + ntohPackX(PsFlush,p,l,(byte *) &fls); + +#ifdef DEBUG_AFP_CMD + if (dbg != NULL) { + fprintf(dbg, "\tVolID: %04x\n", fls.fls_volid); + fflush(dbg); + } +#endif /* DEBUG_AFP_CMD */ + + if (DBVOL) + printf("FPFLush: ...\n"); + + ivol = EtoIVolid(fls.fls_volid); + if (ivol < 0) + return(ivol); + + return(OSFlush(ivol)); +} + +/* + * IDirP VolRootD(int volid) + * + * Return the internal directory pointer for this volumes root directory. + * Root directory is the volumes mount point, or initial path. + * + */ + +IDirP +VolRootD(volid) +int volid; +{ + if (volid > VolCnt) + return(NILDIR); + return(VolTbl[volid]->v_rootd); +} + +/* + * void VolModified(VolBitMap volbm) + * + * Indicate that the volumes specified in volbm have been modified. + * + */ + +void +VolModified(volbm) +VolBitMap volbm; +{ + V_BITOR(VolModBitMap, /* result */ + VolModBitMap,volbm); /* is OR of these two */ +} + + +char * +VolName(volid) +{ + if (volid > VolCnt) + return(""); + return(VolTbl[volid]->v_name); +} + +#ifdef SHORT_NAMES +char * +VolSName(volid) +{ + static char temp[9]; + + if (volid > VolCnt) + return(""); + strncpy(temp,VolTbl[volid]->v_name,8); + temp[8] = '\0'; + if (DBVOL) + printf("VolSname %s\n",temp); + return(temp); +} +#endif SHORT_NAMES + +word +ItoEVolid(iv) +int iv; +{ + return(VolTbl[iv]->v_volid); /* return volume id */ +} + +int +EtoIVolid(ev) +word ev; +{ + int iv; + for (iv=0; iv < VolCnt; iv++) + if (VolTbl[iv]->v_volid == ev) + return(iv); + if (DBVOL) + printf("EtoIVolid: Bad Volid %d\n",ev); + return(aeParamErr); +} + +#ifdef CREATE_AFPVOL +/* + * int aufsDirSetup(char *home, char *usr) + * + * Added by Heather Ebey, UC San Diego + * + * Sets up home directory of UNIX user needing aufs(1) for file sharing. + * Will create .afpvols and appropriate directory/subdirectories. + * + * CREATE_AFPVOL is defined as the name of the AUFS Mac volume + * + */ + +int +aufsDirSetup(home, usr) +char *home, *usr; +{ + char volfname[MAXDLEN+20]; + char dirname[MAXDLEN+20]; + char subdir[MAXDLEN+20]; + extern int errno; + FILE *fp; + + strcpy(volfname, home); + strcat(volfname, "/"); + strcat(volfname, VOLFILE1); /* .afpvols */ + if ((fp = fopen(volfname, "w")) == NULL) { + logit(0, "CREATE_AFPVOL: can't open %s for writing", volfname); + return(-1); + } +#ifdef CREAT_AFPVOL_NAM + { char *makeVolName(), *cp; + char host[64], name[128]; + gethostname(host, sizeof(host)); + host[sizeof(host)-1] = '\0'; + if ((cp = index(host, '.')) != NULL) + *cp = '\0'; /* remove domain name */ + sprintf(name, makeVolName(CREAT_AFPVOL_NAM,usr,host,CREATE_AFPVOL,home)); + if (fprintf(fp, "~%s/%s:%s\n", usr, CREATE_AFPVOL, name) < 0) { + logit(0, "CREATE_AFPVOL: error in fprintf()"); + (void)fclose(fp); + return(-1); + } + } +#else CREAT_AFPVOL_NAM + if (fprintf(fp, "~%s/%s:%s\n", usr, CREATE_AFPVOL, CREATE_AFPVOL) < 0) { + logit(0, "CREATE_AFPVOL: error in fprintf()"); + (void)fclose(fp); + return(-1); + } +#endif CREAT_AFPVOL_NAM + (void)fclose(fp); + sprintf(subdir, "%s/%s", home, FIDIRFN); + if (mkdir(subdir, 0700) < 0) { + if (errno != EEXIST) { + logit(0, "CREATE_AFPVOL: unable to create %s", subdir); + return(-1); + } + } + sprintf(subdir, "%s/%s", home, RFDIRFN); + if (mkdir(subdir, 0700) < 0) { + if (errno != EEXIST) { + logit(0, "CREATE_AFPVOL: unable to create %s", subdir); + return(-1); + } + } + sprintf(dirname, "%s/%s", home, CREATE_AFPVOL); + if (mkdir(dirname, 0700) < 0) { + if (errno != EEXIST) { + logit(0, "CREATE_AFPVOL: unable to create %s", dirname); + return(-1); + } + } + sprintf(subdir, "%s/%s", dirname, FIDIRFN); + if (mkdir(subdir, 0700) < 0) { + if (errno != EEXIST) { + logit(0, "CREATE_AFPVOL: unable to create %s", subdir); + return(-1); + } + } + sprintf(subdir, "%s/%s", dirname, RFDIRFN); + if (mkdir(subdir, 0700) < 0) { + if (errno != EEXIST) { + logit(0, "CREATE_AFPVOL: unable to create %s", subdir); + return(-1); + } + } + if ((fp = fopen(volfname, "r")) == NULL) { + logit(0, "CREATE_AFPVOL: can't open %s for reading", volfname); + return(-1); + } + (void)VRdVFile(fp); /* read vols file from user */ + return(0); +} + +#ifdef CREAT_AFPVOL_NAM +/* + * build a Mac volume name out of specified args in fmt + * %U username + * %H hostname + * %V volumename + * %D home directory + * + */ + +char * +makeVolName(fmt, user, host, volm, home) +char *fmt, *user, *host, *volm, *home; +{ + char *p, *q; + static char string[128]; + + p = fmt; + q = string; + string[0] = '\0'; + + while (*p != '\0') { + if (*p == '%' && *(p+1) != '\0') { + switch (*(p+1)) { + case 'U': + strcat(string, user); + q += strlen(user); + break; + case 'H': + strcat(string, host); + q += strlen(host); + break; + case 'V': + strcat(string, volm); + q += strlen(volm); + break; + case 'D': + strcat(string, home); + q += strlen(home); + break; + } + p += 2; + continue; + } + *q++ = *p++; + *q = '\0'; + } + + return(string); +} +#endif CREAT_AFPVOL_NAM +#endif CREATE_AFPVOL + +#ifdef DEBUG_AFP_CMD +/* + * print bitmap for Volume Parameters + * + */ + +void +dbg_print_vmap(bmap) +u_short bmap; +{ + int i, j; + + if (dbg != NULL) { + fprintf(dbg, "("); + for (i = 0, j = 0; i < 16; i++) { + if (bmap & (0x0001 << i)) { + bmap &= ~(0x0001 << i); + switch (i) { + case 0: + fprintf(dbg, "Attributes"); + j++; + break; + case 1: + fprintf(dbg, "Signature"); + j++; + break; + case 2: + fprintf(dbg, "Creat Date"); + j++; + break; + case 3: + fprintf(dbg, "Modif Date"); + j++; + break; + case 4: + fprintf(dbg, "Bakup Date"); + j++; + break; + case 5: + fprintf(dbg, "Volume ID"); + j++; + break; + case 6: + fprintf(dbg, "Bytes Free"); + j++; + break; + case 7: + fprintf(dbg, "Bytes Total"); + j++; + break; + case 8: + fprintf(dbg, "Volume Name"); + j++; + break; + default: + fprintf(dbg, "Unknwn Bit"); + j++; + break; + } + if (bmap) + fprintf(dbg, ", "); + if (bmap && (j % 4) == 0) + fprintf(dbg, "\n\t\t\t"); + } + } + fprintf(dbg, ")\n"); + } + + return; +} + +/* + * dump Volume parameters described by bitmap + * + */ + +#define get2(s) (u_short)(((s)[0]<<8)|((s)[1])) +#define get4(s) (u_int)(((s)[0]<<24)|((s)[1]<<16)|((s)[2]<<8)|((s)[3])) + +void +dbg_print_vprm(bmap, r, rl) +u_short bmap; +byte *r; +int rl; +{ + int i, j; + byte *p, *q; + short offset; + void dbg_print_date(); + void dbg_print_vatr(); + + p = r; /* parameters */ + + if (dbg != NULL) { + for (i = 0; i < 16 && rl > 0; i++) { + if (bmap & (0x0001 << i)) { + switch (i) { + case 0: + fprintf(dbg, "\tAttributes: "); + dbg_print_vatr(get2(r)); + rl -= 2; + r += 2; + break; + case 1: + fprintf(dbg, "\tVSignature: %04x\n", get2(r)); + rl -= 2; + r += 2; + break; + case 2: + fprintf(dbg, "\tCreat Date: "); + dbg_print_date(get4(r)); + rl -= 4; + r += 4; + break; + case 3: + fprintf(dbg, "\tModif Date: "); + dbg_print_date(get4(r)); + rl -= 4; + r += 4; + break; + case 4: + fprintf(dbg, "\tBakup Date: "); + dbg_print_date(get4(r)); + rl -= 4; + r += 4; + break; + case 5: + fprintf(dbg, "\t Volume ID: %04x\n", get2(r)); + rl -= 2; + r += 2; + break; + case 6: + fprintf(dbg, "\tBytes Free: %d\n", get4(r)); + rl -= 4; + r += 4; + break; + case 7: + fprintf(dbg, "\tBytes Totl: %d\n", get4(r)); + rl -= 4; + r += 4; + break; + case 8: + fprintf(dbg, "\tVolume Nam: \""); + offset = get2(r); + q = p + offset; + for (j = 0; j < (int)*q; j++) + fprintf(dbg, "%c", *(q+j+1)); + fprintf(dbg, "\"\n"); + rl -= 2; + r += 2; + break; + default: + fprintf(dbg, "\tUnknwn Bit: %d\n", i); + break; + } + } + } + } + + return; +} + +/* + * print volume attributes + * + */ + +void +dbg_print_vatr(attr) +u_short attr; +{ + int i, j; + + if (dbg != NULL) { + fprintf(dbg, "%04x (", attr); + for (i = 0, j = 0; i < 16; i++) { + if (attr & (0x0001 << i)) { + attr &= ~(0x0001 << i); + switch (i) { + case 0: + fprintf(dbg, "ReadOnlyVol"); + j++; + break; + case 1: + fprintf(dbg, "HasVolPaswd"); + j++; + break; + case 2: + fprintf(dbg, "SuppFileIDs"); + j++; + break; + case 3: + fprintf(dbg, "SuppCatSrch"); + j++; + break; + case 4: + fprintf(dbg, "SuppBlnkAcs"); + j++; + break; + default: + fprintf(dbg, "", i); + j++; + break; + } + if (attr) + fprintf(dbg, ", "); + if (attr && (j % 4) == 0) + fprintf(dbg, "\n\t\t\t"); + } + } + fprintf(dbg, ")\n"); + } + + return; +} +#endif /* DEBUG_AFP_CMD */ diff --git a/applications/aufs/afpvols.h b/applications/aufs/afpvols.h new file mode 100644 index 0000000..4c7e194 --- /dev/null +++ b/applications/aufs/afpvols.h @@ -0,0 +1,48 @@ +/* + * $Author: djh $ $Date: 1994/02/16 05:09:02 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/afpvols.h,v 2.3 1994/02/16 05:09:02 djh Rel djh $ + * $Revision: 2.3 $ +*/ + +/* + * afpvols.h - Appletalk Filing Protocol Volume definitions + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * + */ + + +typedef struct { /* Volume Entry */ + IDirP v_rootd; /* root directory (mount point) */ + word v_bitmap; /* for packing purposes only */ + char v_path[MAXDLEN]; /* path for this volume */ + word v_attr; /* (lsb) read-only flag */ +#define V_RONLY V_READONLY /* backward compat */ +#define V_READONLY 0x0001 /* read only flag */ +#define V_HASVOLUMEPASSWORD 0x0002 /* has a volume password */ +#define V_SUPPORTSFILEIDS 0x0004 /* supports file IDs */ +#define V_SUPPORTSCATSEARCH 0x0008 /* supports cat search */ +#define V_SUPPORTSBLNKACCESS 0x0010 /* supports blank access privs */ + char v_name[MAXVLEN]; /* advertised name */ + char v_pwd[MAXPLEN]; /* volume password (unused) */ + boolean v_mounted; /* mounted flag */ + word v_sig; /* volume signature */ + word v_volid; /* volume ID */ + sdword v_cdate; /* volume creation date */ + sdword v_mdate; /* volume modification date */ + sdword v_bdate; /* volume backup date */ + dword v_size; /* size of volume in bytes */ + dword v_free; /* free bytes on volume */ + byte v_esize[8]; /* size of volume in bytes */ + byte v_efree[8]; /* free bytes on volume */ +} VolEntry, *VolPtr; /* user volume table */ + +#define NILVOL ((VolPtr) 0) + diff --git a/applications/aufs/aufs.c b/applications/aufs/aufs.c new file mode 100644 index 0000000..0669c7e --- /dev/null +++ b/applications/aufs/aufs.c @@ -0,0 +1,1977 @@ +/* + * $Author: djh $ $Date: 1996/09/12 05:11:59 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/aufs.c,v 2.34 1996/09/12 05:11:59 djh Rel djh $ + * $Revision: 2.34 $ + * + */ + +/* + * aufs - UNIX AppleTalk Unix File Server + * + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * + */ + +char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +#include +#endif _TYPES +#include +#include +#include +#include +#include /* for htons, etc. */ +#include + +#include /* include appletalk definitions */ +#include +#include +#include "afps.h" /* server includes */ + +#ifdef NEEDFCNTLDOTH +#include +#endif NEEDFCNTLDOTH + +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#ifdef USEVPRINTF +# include +#endif USEVPRINTF + +#ifndef NOWAIT3 +# define DORUSAGE +#endif NOWAIT3 + +#ifdef aux +# ifdef DORUSAGE +# undef DORUSAGE +# endif DORUSAGE +#endif aux + +#ifdef NOPGRP +# ifdef xenix5 +# define killpg(pid,sig) kill(-(pid),sig) +# else xenix5 +# define NOSHUTDOWNCODE +# endif xenix5 +#endif NOPGRP + +#ifdef DEBUG_AFP_CMD +#ifndef DEBUG_AFP +#define DEBUG_AFP +#endif /* DEBUG_AFP */ +#endif /* DEBUG_AFP_CMD */ + +/* known attention codes */ +#define AFPSHUTDOWNTIME(x) (0x8000|((x)&0xfff)) +#define AFPSHUTDOWNCANCEL (0x8fff) +#define AFPSHUTDOWNNOW (0x8000) +#define AFPSERVERCRASH (0x4000) +#define AFPSERVERMESG (0x2000) +#define AFPDONTRECONN (0x1000) + +export int statflg = FALSE; /* default is not to show stats */ +export u_char *srvrname = NULL; /* NBP registered name */ +export u_char *srvrtype = (u_char *)AFSTYPE; /* NBP registered type */ +export char *messagefile = NULL; /* AFP2.1 GetSrvrMsg srvr msg filename */ +export char *motdfile = NULL; /* AFP2.1 GetSrvrMsg login msg filename */ +export char *dsiTCPIPFilter = NULL; /* AFP2.2 AppleShareIP address filter */ +export u_int asip_addr = INADDR_ANY; /* AFP2.2 AppleShare over TCP/IP */ +export u_short asip_port = ASIP_PORT; /* AFP2.2 AppleShare TCP/IP port */ +export int asip_enable = FALSE; /* AFP2.2 AppleShare TCP/IP default off */ + +private char *sysvolfile = NULL; /* system afpvols file */ +private char *passwdlookaside = NULL; /* local password file??? */ +private char *guestid = NULL; /* any guest ids? */ +private char *coredir = NULL; /* place to put coredumps */ + +#ifdef DEBUG_AFP +private char *debugAFPFile = NULL; /* AFP debug file name */ +#endif DEBUG_AFP +#ifdef ISO_TRANSLATE +private u_char isoSrvrName[80]; +private u_char isoSrvrType[80]; +#endif ISO_TRANSLATE +#ifdef REREAD_AFPVOLS +private int readafpvols = FALSE;/* set by USR1 signal */ +#endif REREAD_AFPVOLS +#ifdef LWSRV_AUFS_SECURITY +export char *userlogindir = NULL; /* budd -- name of userlogin file */ +#endif LWSRV_AUFS_SECURITY +#ifdef APPLICATION_MANAGER +export char *enforcelist = NULL;/* name of list for appln run control */ +#endif APPLICATION_MANAGER +#ifdef USR_FILE_TYPES +export char *uftfilename = NULL;/* file with suffix -> creat&type mappings */ +#endif USR_FILE_TYPES +#ifdef LOGIN_AUTH_PROG +export char *login_auth_prog = NULL; /* path to external login auth. prog */ +#endif LOGIN_AUTH_PROG +export int mcs, qs; /* maxcmdsize and quantum size */ +export int sqs = atpMaxNum; /* maximum send quantum */ +export int n_rrpkts = atpMaxNum; /* maximum send quantum to allow remote */ + /* (used in spwrtcontinue) */ +export int nousrvol = FALSE; /* no user home dir/vol flag */ +export int nopwdsave = FALSE; /* allow user to save password */ +#ifdef AUFS_README +export char *aufsreadme; /* path of readme file */ +export char *aufsreadmename; /* pointer into aufsreadme with just name */ +#endif AUFS_README + +#ifdef DEBUG_AFP +export FILE *dbg = NULL; +#endif /* DEBUG_AFP */ + +private EntityName srvr_entity_name; /* our entity name */ +private char zonetorun[34]; /* zone to run in if different than std. */ +private char logfile[MAXPATHLEN]; /* log file name if any */ +#ifdef PID_FILE +private char pid_file[MAXPATHLEN]; /* pid file name if any */ +#endif /* PID_FILE */ +private int parent_pid; /* pid of main server */ +private int mypid; /* pid of running process */ +private int nomoresessions = FALSE; /* set true of out of asp sessions */ +private int sesscount = 0; /* number of asp sessions active */ +private int maxsess = 10; +private int cno = -1; /* current connection */ +#ifndef NOSHUTDOWNCODE +private int minutes_to_shutdown = -1; +#endif NOSHUTDOWNCODE; +#ifdef AUFS_IDLE_TIMEOUT +private int idletime = 0; /* allowable no traffic period */ +private int timeidle = 0; /* have been idle for this time */ +private int guestonly = 1; /* default to guest idle timeouts only */ +#endif AUFS_IDLE_TIMEOUT + +/* + * CNO TO PID handling + * + * Must keep a table to session reference numbers (aka connection + * numbers) to pid mappings for the server since sessions are + * "half"-closed. This handling was previously in the asp protocol + * handler, but this doesn't make sense and there is much better + * control over things here + * + * internal strategy at this point is to record fork terminations + * and handling via garbage collector at a later point. maximum time + * to scan is around 5 seconds at this point. to minimize scan time + * for gc, the "dead" ones are pushed onto a stack. + * + * an alternative strategy is to have the signal handler write to an + * internal pipe watched by a fdlistener. if this is done the sleeps + * can be made much longer. carefully handled this will work very + * well, but it is a little ugly. (note: best way to do is probably + * to have the listener set a variable and the inferior termination + * handler write the pids out. the scan process would then pick up + * the pids from the pipe.) + * +*/ + +typedef struct cno_to_pid { + int state; /* back reference */ +#define CP_NOTINUSE 0x1 /* to mark not in use */ +#define CP_RUNNING 0x2 /* inferior is running? */ +#define CP_TIMEDOUT 0x4 /* tickle timeout on inferior */ + int pid; /* recorded pid */ + long timeval; /* time when pid died */ + WSTATUS status; +#ifdef DORUSAGE + struct rusage rusage; +#endif DORUSAGE +} CTP, *CTPP; + +private struct cno_to_pid *ctp_tab; +private int *ctp_stack; +private int ctp_stack_cnt; + +usage(name) +char *name; +{ + char *DBLevelOpts(); + + fprintf(stderr,"usage: %s [-d cap-flags] [-a afp-levels] ",name); +#ifdef LWSRV_AUFS_SECURITY + fprintf(stderr,"[-n name] [-t packets] [-s] [-V afpvols] [-X usrfile]\n"); +#else LWSRV_AUFS_SECURITY + fprintf(stderr,"[-n name] [-t packets] [-s] [-V afpvols]\n"); +#endif LWSRV_AUFS_SECURITY +#ifdef APPLICATION_MANAGER + fprintf(stderr,"[-A applistfile] "); +#endif APPLICATION_MANAGER +#ifdef AUFS_README + fprintf(stderr,"[-r readme_path] "); +#endif AUFS_README +#ifdef AUFS_IDLE_TIMEOUT + fprintf(stderr,"[-[i|I] idle_timeout] "); +#endif AUFS_IDLE_TIMEOUT +#ifdef USR_FILE_TYPES + fprintf(stderr,"[-F typelist] "); +#endif USR_FILE_TYPES +#ifdef LOGIN_AUTH_PROG + fprintf(stderr,"[-L authprog] "); +#endif LOGIN_AUTH_PROG + fprintf(stderr,"[-m login_message_file] [-M srvr_message_file] "); + fprintf(stderr,"[-p] [-u] "); + fprintf(stderr,"\n\t-d for CAP debugging flags:\n"); + fprintf(stderr,"\t l = lap, d = ddp, a = atp, n = nbp, p = pap,"); + fprintf(stderr,"i = ini, s = asp\n"); + fprintf(stderr,"\t-Ds[shct] for ASP debug limiting flags:\n"); + fprintf(stderr,"\t s = socket, h = handlers, c = call, t = tickle\n"); + fprintf(stderr,"\t-a for AFP debugging by level (or setenv AUFSDEBUG):\n"); + fprintf(stderr,"\t %s\n",DBLevelOpts()); + fprintf(stderr,"\t-t for packet traces (or setenv AUFSTRACE):\n"); + fprintf(stderr,"\t [I|O|B]CmdName\n"); + fprintf(stderr,"\t-n for setting the server's name\n"); + fprintf(stderr,"\t-s for statistics\n"); + fprintf(stderr,"\t-u do not show user home directory or vols\n"); + fprintf(stderr,"\t-p do not allow AFP client to save password\n"); + fprintf(stderr,"\t-V VolsFile for server wide afp volumes\n"); + fprintf(stderr,"\t-G to set guest id for logins\n"); + fprintf(stderr,"\t-P LookAsidePasswordFile for scrambled transactions\n"); + fprintf(stderr,"\t-T enable AFP connections via TCP/IP (default addr)\n"); + fprintf(stderr,"\t-B enable AFP over TCP/IP & set address\n"); + fprintf(stderr,"\t-f set the AFP over TCP-IP address filter\n"); + fprintf(stderr,"\t-U to allow sessions\n"); + fprintf(stderr,"\t-m|M specifies login or server message file\n"); +#ifndef STAT_CACHE + fprintf(stderr,"\t-c directory to specify a directory to coredump to\n"); +#endif STAT_CACHE + fprintf(stderr,"\t-l file to send logfile to other than .log\n"); +#ifdef PID_FILE + fprintf(stderr,"\t-w file to write pid to other than ./%s\n", PID_FILE); +#endif /* PID_FILE */ + fprintf(stderr,"\t-S limit responses to packets\n"); + fprintf(stderr,"\t-R limit remote to sending packets\n"); +#ifdef LWSRV_AUFS_SECURITY + fprintf(stderr,"\t-X datafile of logged in users\n"); /* budd */ +#endif LWSRV_AUFS_SECURITY +#ifdef APPLICATION_MANAGER + fprintf(stderr,"\t-A application run manager\n"); +#endif APPLICATION_MANAGER +#ifdef AUFS_README + fprintf(stderr,"\t-r readme file for new users\n"); +#endif AUFS_README +#ifdef USR_FILE_TYPES + fprintf(stderr,"\t-F to map from suffix to creator/translation\n"); +#endif USR_FILE_TYPES +#ifdef LOGIN_AUTH_PROG + fprintf(stderr,"\t-L to provide login authorization service\n"); +#endif LOGIN_AUTH_PROG +#ifdef DEBUG_AFP + fprintf(stderr, "\t-Z to dump parameters from AFP commands\n"); +#endif DEBUG_AFP + fprintf(stderr,"\nExample: %s -t 'bdelete irename' -a 'file fork dir' -s\n", + name); + exit(1); +} + +/* + * generate default name from host name + * +*/ +char * +afs_default_name() +{ + char hostname[255]; + static char afsname[255]; + char *ap; + + gethostname(hostname,(int) sizeof(hostname)); + if ((ap = index(hostname,'.')) != NULL) + *ap = '\0'; /* remove trailing domain name */ + sprintf(afsname,"%s Aufs",hostname); + return(afsname); +} + +doargs(argc,argv) +int argc; +char **argv; +{ + int c; + char *p; + u_char *parsename(); + extern char *optarg; + extern int optind; + extern boolean dochecksum; + static char optlist[100] = "a:B:d:f:D:n:N:t:kpsTuV:U:G:P:c:l:z:S:R:M:m:"; +#ifdef ISO_TRANSLATE + void cISO2Mac(); +#endif ISO_TRANSLATE + +#ifdef LWSRV_AUFS_SECURITY + strcat(optlist, "X:"); +#endif LWSRV_AUFS_SECURITY +#ifdef APPLICATION_MANAGER + strcat(optlist, "A:"); +#endif APPLICATION_MANAGER +#ifdef AUFS_README + strcat(optlist, "r:"); +#endif AUFS_README +#ifdef AUFS_IDLE_TIMEOUT + strcat(optlist, "i:I:"); +#endif AUFS_IDLE_TIMEOUT +#ifdef USR_FILE_TYPES + strcat(optlist, "F:"); +#endif USR_FILE_TYPES +#ifdef LOGIN_AUTH_PROG + strcat(optlist, "L:"); +#endif LOGIN_AUTH_PROG +#ifdef PID_FILE + pid_file[0] = '\0'; + strcat(optlist, "w:"); +#endif /* PID_FILE */ +#ifdef DEBUG_AFP + strcat(optlist, "Z:"); +#endif /* DEBUG_AFP */ + + while ((c = getopt(argc,argv,optlist)) != EOF) { + switch (c) { + case 'z': + strncpy(zonetorun, optarg, 32); + break; + case 'l': + strncpy(logfile, optarg, sizeof(logfile)-1); + break; + case 'c': + coredir = optarg; + break; + case 'k': + dochecksum = 0; /* no DDP checksum */ + break; + case 'p': + nopwdsave = TRUE; /* don't allow save password */ + break; + case 's': + statflg = TRUE; + break; + case 'u': + nousrvol = TRUE; /* don't show home dir */ + break; + case 'd': + dbugarg(optarg); /* '-d' is debug */ + break; + case 'D': + if (optarg[0] != 's') + usage(argv[0]); + aspdebug(optarg+1); /* call asp debug */ + break; + case 'a': + if (!SetDBLevel(optarg)) /* -a for afp debug */ + usage(argv[0]); + break; + case 'm': + motdfile = optarg; /* -m message file path */ + break; + case 'M': + messagefile = optarg; /* -M message file path */ + break; + case 'n': + case 'N': + srvrname = (u_char *)optarg; /* '-n' to set server name */ + srvrtype = parsename(srvrname); /* (optional "-n name:type") */ +#ifdef ISO_TRANSLATE + bcopy(srvrname, isoSrvrName, strlen(srvrname)+1); + cISO2Mac(srvrname); + bcopy(srvrtype, isoSrvrType, strlen(srvrtype)+1); + cISO2Mac(srvrtype); +#endif ISO_TRANSLATE + break; + case 't': + if (!SetPktTrace(optarg)) + usage(argv[0]); + break; + case 'f': /* AppleShareIP IP address filter */ + dsiTCPIPFilter = optarg; + break; + case 'B': /* Bind AppleShare TCP/IP address */ + if ((p = (char *)index(optarg, ':')) != NULL) { + asip_port = atoi(p+1); + *p = '\0'; + } + if ((asip_addr = (u_int)ntohl(inet_addr(optarg))) == -1) + asip_addr = INADDR_ANY; + if (p != NULL) + *p = ':'; + /* fall through */ + case 'T': /* Enable AppleShare TCP/IP */ + asip_enable = TRUE; + break; + case 'V': /* system afpvols file */ + sysvolfile = optarg; + break; + case 'U': + maxsess = atoi(optarg); + break; + case 'P': + passwdlookaside = optarg; + break; + case 'G': + guestid = optarg; + break; + case 'S': + sqs = atoi(optarg); + if (sqs <= 0) { + fprintf(stderr, "Must have at least one packet in a response, resetting to one\n"); + sqs = 1; + } + if (sqs > atpMaxNum) { + fprintf(stderr, "No more than %d packets allowed in a response\n", + atpMaxNum); + sqs = atpMaxNum; + } + break; + case 'R': + n_rrpkts = atoi(optarg); + if (n_rrpkts <= 0) { + fprintf(stderr, "Must have at least one packet in a response, resetting to one\n"); + n_rrpkts = 1; + } + if (n_rrpkts > atpMaxNum) { + fprintf(stderr, "No more than %d packets allowed in a response\n", + atpMaxNum); + n_rrpkts = atpMaxNum; + } + break; +#ifdef LWSRV_AUFS_SECURITY + case 'X': /* budd... */ + userlogindir = optarg; + break; /* ...budd */ +#endif LWSRV_AUFS_SECURITY +#ifdef APPLICATION_MANAGER + case 'A': + enforcelist = optarg; + break; +#endif APPLICATION_MANAGER +#ifdef AUFS_README + case 'r': + aufsreadme = optarg; + if (*aufsreadme != '/') { + fprintf(stderr, "The -r parameter must be a full path name\n"); + exit(1); + } + aufsreadmename = (char *)rindex(aufsreadme, '/') + 1; + break; +#endif AUFS_README +#ifdef AUFS_IDLE_TIMEOUT + case 'I': /* also users */ + guestonly = 0; + /* fall thro' */ + case 'i': /* guests only */ + idletime = atoi(optarg) * 6; + break; +#endif AUFS_IDLE_TIMEOUT +#ifdef USR_FILE_TYPES + case 'F': /* file suffix mapping */ + uftfilename = optarg; + break; +#endif USR_FILE_TYPES +#ifdef LOGIN_AUTH_PROG + case 'L': /* login authorization */ + login_auth_prog = optarg; + if (*login_auth_prog != '/') { + fprintf(stderr, "The -L parameter must be a full path name\n"); + exit(1); + } + break; +#endif LOGIN_AUTH_PROG +#ifdef PID_FILE + case 'w': /* pid file */ + strncpy(pid_file, optarg, sizeof(pid_file)-1); + break; +#endif /* PID_FILE */ +#ifdef DEBUG_AFP + case 'Z': /* command debug file */ + debugAFPFile = optarg; + break; +#endif /* DEBUG_AFP */ + default: + usage(argv[0]); + break; + } + } +} + +/* + * check name for optional ':' delimited type + * + */ + +u_char * +parsename(name) +u_char *name; +{ + u_char *cp; + + if ((cp = (u_char *)index((char *)name, ':')) != NULL) { + *cp++ = '\0'; /* NULL terminate name */ + return(cp); + } + return((u_char *)AFSTYPE); +} + +export AddrBlock addr; /* budd */ + +main(argc,argv) +int argc; +char **argv; +{ + int timedout(); + void inferiordone(), dienow(), dying(); +#ifdef REREAD_AFPVOLS + void setreadafpvols(); + void dorereadafpvols(); +#endif REREAD_AFPVOLS +#ifdef CLOSE_LOG_SIG + void closelog(); +#endif /* CLOSE_LOG_SIG */ +#ifndef NOSHUTDOWNCODE + void killnow(), killin5(), diein5(); + void msgnotify(), msgavail(); +#endif NOSHUTDOWNCODE; + int err,atpskt,slsref; + int comp,comp2; + byte *srvinfo; + int srvinfolen; + char *getenv(),*env; + int pid; + int mask; + byte *buf; /* [atpMaxData]; */ + byte *rspbuf; /* [atpMaxData*atpMaxNum]; */ + import byte *aufsicon; /* aufs icon */ + import int aufsiconsize; /* and its size */ + import char *aufs_versiondate; + import int aufs_version[]; + extern int errno; +#ifdef ISO_TRANSLATE + void cISO2Mac(); +#endif ISO_TRANSLATE + +#ifdef DIGITAL_UNIX_SECURITY + set_auth_parameters(argc, argv); +#endif DIGITAL_UNIX_SECURITY + + OSEnable(); /* enable OS dependent items */ + + IniServer(); + srvrname = (u_char *)afs_default_name(); /* set default server name */ +#ifdef ISO_TRANSLATE + bcopy(srvrname, isoSrvrName, strlen(srvrname)+1); + cISO2Mac(srvrname); + bcopy(AFSTYPE, isoSrvrType, strlen(AFSTYPE)+1); +#endif ISO_TRANSLATE + logfile[0] = '\0'; + zonetorun[0] = '\0'; + doargs(argc,argv); /* handle command line */ +#ifdef AUTHENTICATE +#ifdef ISO_TRANSLATE + initauthenticate(*argv, (char *)isoSrvrName); +#else ISO_TRANSLATE + initauthenticate(*argv, (char *)srvrname); +#endif ISO_TRANSLATE +#endif AUTHENTICATE + + env = getenv("AUFSTRACE"); /* See if user wants to */ + if (env != NULL) /* trace some packets */ + SetPktTrace(env); + env = getenv("AUFSDEBUG"); + if (env != NULL) + SetDBLevel(env); + + if (!DBDEB) { +#ifdef ISO_TRANSLATE + fprintf(stderr,"Apple Unix File Server (%s:%s@*) starting\n", + (char *)isoSrvrName, (char *)isoSrvrType); +#else ISO_TRANSLATE + fprintf(stderr,"Apple Unix File Server (%s:%s@*) starting\n", + (char *)srvrname, (char *)srvrtype); +#endif ISO_TRANSLATE + } + + /* here's the place to fork off */ + if (!DBDEB) { + if (fork()) + _exit(0); + { + int f; + for (f = 0; f < 10; f++) + (void) close(f); + } + (void) open("/", 0); +#ifndef NODUP2 + (void) dup2(0, 1); + (void) dup2(0, 2); +#else NODUP2 + (void)dup(0); /* for slot 1 */ + (void)dup(0); /* for slot 2 */ +#endif NODUP2 +#ifndef POSIX +#ifdef TIOCNOTTY + { + int t; + t = open("/dev/tty", 2); + if (t >= 0) { + ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } +#endif TIOCNOTTY +#ifdef linux + (void) setsid(); +#endif linux +#ifdef xenix5 + /* + * USG process groups: + * The fork guarantees that the child is not a process group leader. + * Then setpgrp() can work, whick loses the controllong tty. + * Note that we must be careful not to be the first to open any tty, + * or it will become our controlling tty. C'est la vie. + */ + setpgrp(); +#endif xenix5 +#else POSIX + (void) setsid(); +#endif POSIX + } + +#ifdef DEBUG_AFP + if (debugAFPFile != NULL) + if ((dbg = fopen(debugAFPFile, "w")) != NULL) + fprintf(dbg, "AUFS Debug Session\n"); +#endif /* DEBUG_AFP */ + +#ifdef FIXED_DIRIDS + InitDID(); /* init directory stuff */ +#endif FIXED_DIRIDS + + mypid = parent_pid = getpid(); /* remember who we are */ + +#ifdef PID_FILE + { FILE *pidfd; + if (pid_file[0] == '\0') + strncpy(pid_file, PID_FILE, sizeof(pid_file)-1); + if( (pidfd = fopen(pid_file, "w")) == NULL ) { + logit(0,"Can't open pid file %s", pid_file); + exit(-1); + } + fprintf(pidfd, "%d\n", mypid); + fclose(pidfd); + } +#endif /* PID_FILE */ + + if (logfile[0] == '\0') { +#ifdef ISO_TRANSLATE + sprintf(logfile, "%s.log", (char *)isoSrvrName); +#else ISO_TRANSLATE + sprintf(logfile, "%s.log", (char *)srvrname); +#endif ISO_TRANSLATE + } + logitfileis(logfile, "a+"); + logit(0,"****************************************"); +#ifdef ISO_TRANSLATE + logit(0,"Apple Unix File Server (%s:%s@*) starting", + (char *)isoSrvrName, (char *)isoSrvrType); +#else ISO_TRANSLATE + logit(0,"Apple Unix File Server (%s:%s@*) starting", + (char *)srvrname, (char *)srvrtype); +#endif ISO_TRANSLATE + logit(0,"Aufs version: %d(%d) as of %s",aufs_version[0], aufs_version[1], + aufs_versiondate); + if (sysvolfile != NULL) { /* if known system vols file */ + FILE *fd = fopen(sysvolfile, "r"); + + if (fd == NULL) + logit(0,"Aufs: no such system volumes file %s", sysvolfile); + else if (!VRdVFile(fd)) /* then try to read it now */ + logit(0,"Aufs: system volumes file %s bad",sysvolfile); + } + if (zonetorun[0] != '\0') + zoneset(zonetorun); + abInit(TRUE); /* initialize applebus */ + nbpInit(); /* initialize nbp */ + maxsess = aspInit(maxsess); /* init asp */ + logit(0,"%d sessions allowed",maxsess); + if ((ctp_tab = (CTPP)malloc(sizeof(struct cno_to_pid)*maxsess)) == NULL) { + logit(0,"couldn't malloc table for pid recording, fatal!"); + } + if ((ctp_stack = (int *)malloc(sizeof(int)*maxsess)) == NULL) { + logit(0,"couldn't malloc stack for pid recording, fatal!"); + } + if (asip_enable) + if (asip_addr != INADDR_ANY) + logit(0,"AFP over TCP/IP enabled (IP %08x Port %d)",asip_addr,asip_port); + else + logit(0,"AFP over TCP/IP enabled (IP INADDR_ANY Port %d)", asip_port); +#ifdef LWSRV_AUFS_SECURITY + if (userlogindir != NULL) { /* budd... */ + logit(0,"Aufs: user login database in %s", userlogindir); + } /* ...budd */ +#endif LWSRV_AUFS_SECURITY +#ifdef APPLICATION_MANAGER + if (enforcelist != NULL) { + logit(4,"aufs: application manager database in %s", enforcelist); + readAppList(enforcelist); + } +#endif APPLICATION_MANAGER +#ifdef AUFS_IDLE_TIMEOUT + if (idletime) + logit(0, "Idle timeout set to %d minutes for %s sessions", + idletime/6, (guestonly) ? "guest" : "all"); +#endif AUFS_IDLE_TIMEOUT +#ifdef LOGIN_AUTH_PROG + if (login_auth_prog) + logit(0, "Login authorization program: %s\n", login_auth_prog); +#endif LOGIN_AUTH_PROG + ctp_stack_cnt = 0; + { int i; + for (i = 0 ; i < maxsess; i++) { + ctp_tab[i].state = CP_NOTINUSE; + ctp_tab[i].pid = -1; + } + } + + tellaboutos(); /* tell about os stuff */ + + if (sqs < atpMaxNum) + logit(0,"maximum of %d packet%s will be sent on a response", sqs, + sqs > 1 ? "s" : ""); + sqs *= atpMaxData; + if (n_rrpkts < atpMaxNum) + logit(0,"remote limited to %d packet%s in a response", n_rrpkts, + n_rrpkts > 1 ? "s" : ""); + dsiGetParms(&mcs, &qs); + if (DBDEB) + printf("Command buffer size is %d, Quantum size is %d\n", mcs, qs); + buf = (byte *)malloc(mcs); + rspbuf = (byte *)malloc(qs); + if (buf == NULL || rspbuf == NULL) { + logit(0,"memory allocation failure!\n"); + exit(999); + } + + addr.net = addr.node = addr.skt = 0; /* use any */ + atpskt = 0; /* make sure we use dynamic skt */ + err = ATPOpenSocket(&addr,&atpskt); + if (err != noErr) { + logit(0,"ATPOpenSocket failed with error %d\n",err); + exit(0); + } + + GetMyAddr(&addr); /* set net and node */ + addr.skt = atpskt; + + if (SrvrRegister(atpskt,srvrname,srvrtype,"*", &srvr_entity_name) != noErr) { + logit(0,"SrvrRegister for %s:%s failed...", srvrname, srvrtype); + exit(2); + } + + /* + * set available User Authentication Methods + * + */ + if (guestid != NULL) + allowguestid(guestid); + allowcleartext(); +#ifdef DISTRIB_PASSWDS + allow2wayrand(passwdlookaside); +#else /* DISTRIB_PASSWDS */ + if (passwdlookaside != NULL) + allowrandnum(passwdlookaside); +#endif /* DISTRIB_PASSWDS */ + +#ifndef STAT_CACHE + if (coredir) + if (chdir(coredir) < 0) { + perror("chdir for coredumps"); + logit(0,"chdir to %s for coredumps failed",coredir); + } else + logit(0,"***CORE DUMPS WILL BE IN %s",coredir); +#endif STAT_CACHE + + /* Get server info once and stash away*/ + srvinfolen = GetSrvrInfo(rspbuf,srvrname,aufsicon,aufsiconsize); + srvinfo = (byte *) malloc(srvinfolen); + bcopy(rspbuf,srvinfo,srvinfolen); + + if (DBSRV) + PrtSrvrInfo(srvinfo,srvinfolen); + + /* Init asp */ + err = dsiInit(&addr,srvinfo,srvinfolen,&slsref); + if (err != noErr) { + logit(0,"dsiInit failed with code %d, fatal",err); + exit(0); + } + + logit(0,"Aufs Starting (%s)",srvrname); + if (sysvolfile) + logit(0,"System vols in '%s'",sysvolfile); + logit(0,"dsiInit Completed. Waiting for connection..."); + +#ifndef NOSHUTDOWNCODE +# ifndef NOPGRP + setpgrp(0, parent_pid); /* set our process group */ + /* (inherited) */ +# endif NOPGRP +#endif NOSHUTDOWNCODE; + +#ifndef DEBUGFORK + if (!DBDEB) +#endif + signal(SIGCHLD, inferiordone); +#ifndef NOSHUTDOWNCODE + signal(SIGHUP, killnow); /* kill -HUP -- Immediate shutdown */ + signal(SIGTERM, killin5); /* kill -TERM -- Timed (5 min) shutdown */ + signal(SIGURG, msgnotify); /* kill -URG -- A srvr message is available */ +#endif NOSHUTDOWNCODE; +#ifdef REREAD_AFPVOLS + signal(SIGUSR1, setreadafpvols); /* kill -USR1 -- force afpvols re-read */ +#endif REREAD_AFPVOLS +#ifdef CLOSE_LOG_SIG + signal(CLOSE_LOG_SIG, closelog); /* kill -USR2 -- force close/reopen log */ +#endif /* CLOSE_LOG_SIG */ + + do { + pid = -1; /* make sure zero at start */ + dsiGetSession(slsref,&cno,&comp); + if (comp > 0) + logit(0,"Waiting for session %d to activate", cno); + /* won't wait if we set comp above */ + while (comp > 0) { + abSleep(sectotick(5),TRUE); +#ifdef REREAD_AFPVOLS + if (readafpvols == TRUE) + dorereadafpvols(); +#endif REREAD_AFPVOLS + gcinferior(); + } + if (comp == NoMoreSessions) { + nomoresessions = TRUE; + logit(0,"Woops, no more sessions"); + while (nomoresessions) { + gcinferior(); + abSleep(sectotick(5), TRUE); + } + logit(0,"Okay, sessions should be available"); + continue; + } + if (comp != noErr) { + logit(0,"GetSession returned %d on server socket %d",comp,slsref); + continue; + } else { + sesscount++; + logit(0,"New session %d started on server socket %d, count %d", + cno,slsref,sesscount); + if ((err = dsiGetNetworkInfo(cno, &addr)) != noErr) { + if (err > 0) { /* AppleShareIP session */ + logit(0,"Session %d from [IP addr %d.%d.%d.%d, port %d]", + cno, addr.net>>8, addr.net&0xff, addr.node, addr.skt, err); + err = noErr; + } else + logit(0,"Get Network info failed with error %d", err); + } else { +#ifdef AUTHENTICATE + err = (authenticate(ntohs(addr.net), addr.node)) ? noErr : ~noErr; + logit(0,"%session %d from [Network %d.%d, node %d, socket %d]", + (err == noErr) ? "S" : "Authentication failed, s", +#else AUTHENTICATE + logit(0,"Session %d from [Network %d.%d, node %d, socket %d]", +#endif AUTHENTICATE + cno, + nkipnetnumber(addr.net), nkipsubnetnumber(addr.net), + addr.node, addr.skt); + } +#ifdef AUTHENTICATE + if(err != noErr) { + dsiCloseSession(cno, 1, 1, &comp2); + continue; + } +#endif AUTHENTICATE + } +#ifndef DEBUGFORK + if (!DBDEB) { +#endif +#ifdef NOSIGMASK + mask = 0; + sighold(SIGCHLD); + sighold(SIGHUP); +# ifndef NOSHUTDOWNCODE + sighold(SIGTERM); + sighold(SIGURG); +# endif NOSHUTDOWNCODE; +#else NOSIGMASK +# ifndef NOSHUTDOWNCODE + mask = sigblock(sigmask(SIGCHLD)|sigmask(SIGHUP) + |sigmask(SIGTERM)|sigmask(SIGURG)); +# else NOSHUTDOWNCODE; + mask = sigblock(sigmask(SIGCHLD)|sigmask(SIGHUP)); +# endif NOSHUTDOWNCODE; +#endif NOSIGMASK + /* fork on connection - only tickle from parent */ + if ((pid = dsiFork(cno, TRUE, FALSE)) < 0) { + logit(0,"dsiFork failed on session %d, last system error %d",cno,errno); + /* try to close, but don't worry too much */ + dsiCloseSession(cno, 1, 1, &comp2); +#ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGHUP); +# ifndef NOSHUTDOWNCODE + sigrelse(SIGTERM); + sigrelse(SIGURG); +# endif NOSHUTDOWNCODE; +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK + continue; + } + dsiTickleUserRoutine(cno, timedout, pid); + if (pid) { + logit(0,"pid %d starting for session %d",pid, cno); + addinferior(cno, pid); /* addinferior scans (phew) */ + } else { +#ifndef NOSHUTDOWNCODE + alarm(0); /* make sure off */ +#endif NOSHUTDOWNCODE; + nbpShutdown(); /* don't need this in inferior */ + signal(SIGCHLD, SIG_DFL); +#ifndef NOSHUTDOWNCODE + signal(SIGTERM, diein5); /* die in 5 minutes */ + signal(SIGURG, msgavail); /* message is available */ +#endif NOSHUTDOWNCODE; + signal(SIGHUP, dienow); /* superior signaled us to shutdown */ + mypid = getpid(); +#ifndef NOSHUTDOWNCODE +#ifdef REREAD_AFPVOLS + signal(SIGUSR1, SIG_DFL);/* ignore in inferior */ +#endif REREAD_AFPVOLS +# ifndef NOPGRP + setpgrp(0, parent_pid); /* set our process group */ +# endif NOPGRP + dying(); /* are we dying? if so, handle it*/ +#endif NOSHUTDOWNCODE; + } +#ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGHUP); +# ifndef NOSHUTDOWNCODE + sigrelse(SIGTERM); + sigrelse(SIGURG); +# endif NOSHUTDOWNCODE; +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK +#ifndef DEBUGFORK + } else pid = 0; +#endif + } while (pid != 0); /* pid = 0 implies inferior process */ + + if (pid == 0) + inferior_handler(buf, rspbuf); + + if (statflg) + SrvrPrintStats(); /* print stats */ + if (mypid == parent_pid) + if ((err = SrvrShutdown(&srvr_entity_name)) != noErr) + logit(0,"NBPRemove failed: code %d\n", err); + exit(0); +} + +#ifndef TREL_TIMEOUT +inferior_handler(buf, rspbuf) +byte *buf; +byte *rspbuf; +#else TREL_TIMEOUT +inferior_handler(buf1, rspbuf1) +byte *buf1; +byte *rspbuf1; +#endif TREL_TIMEOUT +{ + int mask; + OSErr err; +#ifndef TREL_TIMEOUT + int comp; + int type,rlen,rsplen; + ReqRefNumType reqref; +#else TREL_TIMEOUT + int comp1,comp2; + int type1,rlen1,rsplen1; + int type2,rlen2,rsplen2; + ReqRefNumType reqref1; + ReqRefNumType reqref2; + byte * buf2; + byte * rspbuf2; +#endif TREL_TIMEOUT + +#ifdef TREL_TIMEOUT + buf2 = (byte *)malloc(mcs); + rspbuf2 = (byte *)malloc(qs); + if (buf2 == NULL || rspbuf2 == NULL) { + logit(0,"memory allocation failure!\n"); + exit(999); + } + + comp1 = 0; + comp2 = 0; +#endif TREL_TIMEOUT + umask(0); /* file creates have explict modes */ + for (;;) { +#ifndef TREL_TIMEOUT + dsiGetRequest(cno,buf,mcs,&reqref,&type,&rlen,&comp); + while (comp > 0) { + abSleep(sectotick(60),TRUE); +#ifdef AUFS_IDLE_TIMEOUT + if (idletime) + (void) checkIdle(cno,buf,comp); +#endif AUFS_IDLE_TIMEOUT + } + if (comp == SessClosed || comp == ParamErr) { +#else TREL_TIMEOUT + if (comp1 > 0 && comp2 > 0) { + while (comp1 > 0 && comp2 > 0) { + abSleep(sectotick(60),TRUE); + } + if (DBSRV) + printf("done\n"); + } + + if (comp1 <= 0) { + dsiGetRequest(cno,buf1,mcs,&reqref1,&type1,&rlen1,&comp1); + while (comp1 > 0) { + abSleep(sectotick(60),TRUE); +#ifdef AUFS_IDLE_TIMEOUT + if (idletime) + (void) checkIdle(cno,buf1,comp1); +#endif AUFS_IDLE_TIMEOUT + } + if (comp1 == SessClosed || comp1 == ParamErr) { +#endif TREL_TIMEOUT + logit(0,"Session (%d) closed",cno); +#ifdef LWSRV_AUFS_SECURITY + clearuserlogin(); /* budd */ +#endif LWSRV_AUFS_SECURITY + return; + } +#ifndef TREL_TIMEOUT + if (comp < 0) { + logit(0,"dsiGetRequest failed %d",comp); +#else TREL_TIMEOUT + if (comp1 < 0) { + logit(0,"dsiGetRequest failed %d",comp1); +#endif TREL_TIMEOUT + continue; + } +#ifndef TREL_TIMEOUT + if (rlen == 0) +#else TREL_TIMEOUT + if (rlen1 == 0) +#endif TREL_TIMEOUT + continue; +#ifndef NOSHUTDOWNCODE + /* mask off potential race condition */ +# ifdef NOSIGMASK + sighold(SIGTERM); + sighold(SIGURG); + sighold(SIGALRM); +# else NOSIGMASK + mask = sigblock(sigmask(SIGTERM)|sigmask(SIGURG)|sigmask(SIGALRM)); +# endif NOSIGMASK +#endif NOSHUTDOWNCODE; +#ifndef TREL_TIMEOUT + switch (type) { +#else TREL_TIMEOUT + switch (type1) { +#endif TREL_TIMEOUT + case aspWrite: + case aspCommand: +#ifndef TREL_TIMEOUT + err = SrvrDispatch(buf,rlen,rspbuf,&rsplen,cno,reqref); +#else TREL_TIMEOUT + err = SrvrDispatch(buf1,rlen1,rspbuf1,&rsplen1,cno,reqref1); +#endif TREL_TIMEOUT + if (DBSRV) { +#ifndef TREL_TIMEOUT + printf("Sending reply len=%d err=%s ...\n",rsplen,afperr(err)); +#else TREL_TIMEOUT + printf("Sending reply len=%d err=%s ...\n",rsplen1,afperr(err)); +#endif TREL_TIMEOUT + fflush(stdout); /* force out */ + } +#ifndef TREL_TIMEOUT + if (type == aspWrite) + dsiWrtReply(cno,reqref,(dword) err,rspbuf,rsplen,&comp); +#else TREL_TIMEOUT + if (type1 == aspWrite) + dsiWrtReply(cno,reqref1,(dword) err,rspbuf1,rsplen1,&comp1); +#endif TREL_TIMEOUT + else +#ifndef TREL_TIMEOUT + dsiCmdReply(cno,reqref,(dword) err,rspbuf,rsplen,&comp); + while (comp > 0) { + abSleep(sectotick(60),TRUE); + } + if (DBSRV) + printf("done\n"); +#else TREL_TIMEOUT + dsiCmdReply(cno,reqref1,(dword) err,rspbuf1,rsplen1,&comp1); +#endif TREL_TIMEOUT + break; + case aspCloseSession: + logit(0,"Closing ASP Session..."); +#ifndef TREL_TIMEOUT + dsiCloseSession(cno,10,3,&comp); /* 5 times, .75 seconds */ + while (comp > 0) +#else TREL_TIMEOUT + dsiCloseSession(cno,10,3,&comp1); /* 5 times, .75 seconds */ + while (comp1 > 0) +#endif TREL_TIMEOUT + abSleep(1, TRUE); +#ifndef NOSHUTDOWNCODE +#ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGHUP); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK +#endif NOSHUTDOWNCODE; + return; + default: +#ifndef TREL_TIMEOUT + logit(0,"Unknown asp command type = %d",type); +#else TREL_TIMEOUT + logit(0,"Unknown asp command type = %d",type1); +#endif TREL_TIMEOUT + break; + } +#ifndef NOSHUTDOWNCODE +#ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGHUP); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK +#endif NOSHUTDOWNCODE; +#ifdef TREL_TIMEOUT + } else { /* comp2 */ + dsiGetRequest(cno,buf2,mcs,&reqref2,&type2,&rlen2,&comp2); + while (comp2 > 0) { + abSleep(sectotick(60),TRUE); +#ifdef AUFS_IDLE_TIMEOUT + if (idletime) + (void) checkIdle(cno,buf2,comp2); +#endif AUFS_IDLE_TIMEOUT + } + if (comp2 == SessClosed || comp2 == ParamErr) { + logit(0,"Session (%d) closed",cno); +#ifdef LWSRV_AUFS_SECURITY + clearuserlogin(); /* budd */ +#endif LWSRV_AUFS_SECURITY + return; + } + if (comp2 < 0) { + logit(0,"dsiGetRequest failed %d",comp2); + continue; + } + if (rlen2 == 0) + continue; +#ifndef NOSHUTDOWNCODE + /* mask off potential race condition */ +# ifdef NOSIGMASK + sighold(SIGTERM); + sighold(SIGURG); + sighold(SIGALRM); +# else NOSIGMASK + mask = sigblock(sigmask(SIGTERM)|sigmask(SIGURG)|sigmask(SIGALRM)); +# endif NOSIGMASK +#endif NOSHUTDOWNCODE; + switch (type2) { + case aspWrite: + case aspCommand: + err = SrvrDispatch(buf2,rlen2,rspbuf2,&rsplen2,cno,reqref2); + if (DBSRV) { + printf("Sending reply len=%d err=%s ...\n",rsplen2,afperr(err)); + fflush(stdout); /* force out */ + } + if (type2 == aspWrite) + dsiWrtReply(cno,reqref2,(dword) err,rspbuf2,rsplen2,&comp2); + else + dsiCmdReply(cno,reqref2,(dword) err,rspbuf2,rsplen2,&comp2); + break; + case aspCloseSession: + logit(0,"Closing ASP Session..."); + dsiCloseSession(cno,10,3,&comp2); /* 5 times, .75 seconds */ + while (comp2 > 0) + abSleep(1, TRUE); +#ifndef NOSHUTDOWNCODE +#ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGHUP); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK +#endif NOSHUTDOWNCODE; + return; + default: + logit(0,"Unknown asp command type = %d",type2); + break; + } +#ifndef NOSHUTDOWNCODE +#ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGHUP); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK +#endif NOSHUTDOWNCODE; + } +#endif TREL_TIMEOUT + } +} + +/* + * Deals with inferior process termination - just close off the + * "half closed" socket + * +*/ +void +inferiordone() +{ + WSTATUS status; +#ifdef DORUSAGE + struct rusage rusage; +# define RUSAGEVAR &rusage +#else DORUSAGE +# define RUSAGEVAR 0 /* may not be used, but.. */ +#endif DORUSAGE + int pid; + int i; + struct cno_to_pid *cp; + +#ifndef NOWAIT3 +# define DOWAIT while ((pid=wait3(&status, WNOHANG, RUSAGEVAR)) > 0) +#else NOWAIT3 +# define DOWAIT if ((pid=wait(&status)) > 0) +#endif NOWAIT3 + + DOWAIT { + /* remember for later */ + for (i = 0, cp = ctp_tab; i < maxsess; i++, cp++) { + if (cp->pid == pid && (cp->state & CP_NOTINUSE) == 0) { + /* one of alive, dead or timedout */ + /* if alive, move to dead */ + /* if dead, shouldn't be here */ + /* if timedout, then leave state */ + if ((cp->state & CP_RUNNING) == 0) + logit(0,"Internal error: pid %d has died twice according to wait",pid); + cp->state &= ~CP_RUNNING; /* not running anymore */ + (void)time(&cp->timeval); /* log time */ + bcopy(&status, &cp->status, sizeof(status)); /* copy status */ +#ifdef DORUSAGE + bcopy(&rusage, &cp->rusage, sizeof(rusage)); +#endif DORUSAGE + ctp_stack[ctp_stack_cnt++] = i; /* mark cno */ + logit(0,"Recorded terminated inferior Aufs PID %d", pid); + break; + } + } + if (i == maxsess) + logit(0,"Unknown terminating inferior pid %d ignored", pid); + } + signal(SIGCHLD, inferiordone); +} + +/* + * scan for dead inferiors and handle gracefully. Know we are never + * called from a "bad" context. e.g. we won't be called while in a + * "critical" section where data structures are being updated + * +*/ +gcinferior() +{ + char tmpbuf[30]; /* reasonable */ + int srn, comp; + struct cno_to_pid *cp; + int mask; + + if (ctp_stack_cnt <= 0) { /* stack of died pids empty? */ + if (ctp_stack_cnt < 0) + logit(0,"internal error: unsafe condition: ctp stack count less than zero"); + return; /* yes, just return */ + } + +#ifdef NOSIGMASK + sighold(SIGCHLD); +#else NOSIGMASK + mask = sigblock(sigmask(SIGCHLD)); +#endif NOSIGMASK + do { + srn = ctp_stack[--ctp_stack_cnt]; /* get pid */ + cp = &ctp_tab[srn]; /* pointer to info on died pid */ + if (cp->state & CP_NOTINUSE) /* nothing to do */ + continue; + /* fork termination has three cases: */ + /* [?,0177] - stopped */ + /* [?, 0] - exit status */ + /* [0, ?] - terminated due to signal */ + if (cp->state & CP_TIMEDOUT) { + logit(0,"process %d, session %d was terminated due to a timeout", + cp->pid, srn); + } else if (WIFSTOPPED(cp->status)) { + logit(0,"process %d, session %d was suspended! gads what is happening?", + cp->pid, srn); + } else if (WIFSIGNALED(cp->status)) { + dsiAttention(srn, AFPSHUTDOWNNOW, 1, -1, &comp); + while (comp > 0) {abSleep(30, TRUE);} /* ignore error */ + dsiCloseSession(srn, 3, 2, &comp); /* try 3 times every .5 seconds */ + while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */ + logit(0,"process %d, session %d was terminated on signal %d", + cp->pid, srn, W_TERMSIG(cp->status)); + if (W_COREDUMP(cp->status)) { + logit(0,"woops: we just dumped core on pid %d", cp->pid); + sprintf(tmpbuf, "core%d",cp->pid); + if (link("core", tmpbuf) == 0) { /* making copy */ + if (unlink("core") != 0) + logit(0,"woops: couldn't unlink core for pid %d", cp->pid); + logit(0,"core dump for pid %d is in %s",cp->pid, tmpbuf); + } else + logit(0,"core dump for pid %d is in core - plz be careful though!", + cp->pid); + } + } else { + logit(0,"process %d, session %d terminated with exit code %d", + cp->pid, srn, W_RETCODE(cp->status)); + } + dsiShutdown(srn); + nomoresessions = FALSE; /* if this was set, unset now */ +#ifdef DORUSAGE + logit(0,"%d messages out, %d in, CPU %.2f user %.2f system", + cp->rusage.ru_msgsnd,cp->rusage.ru_msgrcv, + ((float)cp->rusage.ru_utime.tv_sec)+ + ((float)cp->rusage.ru_utime.tv_usec)/1000000.0, + ((float)cp->rusage.ru_stime.tv_sec)+ + ((float)cp->rusage.ru_stime.tv_usec)/1000000.0); +#endif DORUSAGE + logit(0,"Process %d terminated", cp->pid); + cp->state = CP_NOTINUSE; + cp->pid = -1; /* make -1 to speed up sigchld handler */ + } while (ctp_stack_cnt); +#ifdef NOSIGMASK + sigrelse(SIGCHLD); +#else NOSIGMASK + sigsetmask(mask); /* restore mask */ +#endif NOSIGMASK +} + +/* + * + * record inferior pid for later handling + * + * must be called with sigchild and other signals,etc. that could + * affect ctp_tab off. + * + * +*/ +addinferior(cno, pid) +int cno; +int pid; +{ + struct cno_to_pid *cp; + + /* need to scan here in case something timed out */ + gcinferior(); + cp = &ctp_tab[cno]; /* get pointer to table entry */ + /* If was in table, just smash because we have a new pid on cno */ + /* should we scan table for duplicates? */ + cp->state = CP_RUNNING; + cp->pid = pid; +} + +/* + * Connection timed because remote didn't tickle us enough + * Kill inferior and shutdown half closed skt. + * + * Note: called from abSleep scheduler. + * +*/ +int +timedout(srn, pid) +int srn; +int pid; +{ + nomoresessions = FALSE; /* if this was set, unset now */ + logit(0,"Server timeout on session %d pid %d, not talking to remote anymore", + srn, pid); + /* shouldn't need to do anymore here since remote stopped talking */ + /* assume sigchild interlocked here */ + if (ctp_tab[srn].state & CP_RUNNING) + ctp_tab[srn].state |= CP_TIMEDOUT; + dsiShutdown(srn); /* ignore errors */ + kill(pid, SIGHUP); /* hangup inferior */ +} + +/* got a hup and are inferior of server */ +void +dienow() +{ + int comp; + + logit(0,"Superior told us to shutdown - probably tickle timeout"); + /* The following shouldn't really do anything since remote should be gone */ + /* be in case it really isn't, let's go through this rigamorle */ + /* Tell remote we are shutting down */ + dsiAttention(cno, AFPSHUTDOWNNOW, 1, -1, &comp); + while (comp > 0) {abSleep(30, TRUE);} /* ignore error */ + /* Try closing just in case */ + dsiCloseSession(cno, 3, 2, &comp); /* 3 times, .5 seconds */ + while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */ +#ifdef LWSRV_AUFS_SECURITY + clearuserlogin(); /* gtw: delete auth-file entry for dead users */ +#endif LWSRV_AUFS_SECURITY + exit(0); +} + +#ifndef NOSHUTDOWNCODE + +/* + * inferior handler: setup and handle: terminate in minutes_to_shutdown + * + */ +void +dying() +{ + int comp; + void dying(); + void dienow(); + + if (minutes_to_shutdown < 0) /* not in die mode */ + return; + if (!minutes_to_shutdown) + dienow(); + signal(SIGALRM, dying); + /* Tell remote we are shutting down */ + if (minutes_to_shutdown % 2) { /* all odd minutes */ + /* there is a potential race condition here */ + dsiAttention(cno, AFPSHUTDOWNTIME(minutes_to_shutdown), 1, -1, &comp); + while (comp > 0) {abSleep(30, TRUE);} /* ignore error */ + } + minutes_to_shutdown--; + alarm(60); +} + +/* + * inferior handler: setup and handle: terminate in 5 minutes + * + */ +void +diein5() +{ + void dying(); + + signal(SIGTERM, SIG_IGN); + if (minutes_to_shutdown >= 0) /* already in shutdown mode */ + return; + logit(0,"Superior told us to shutdown by time -- initiating 5 minute shutdown"); + minutes_to_shutdown = 5; + dying(); /* start */ +} + +/* + * inferior handler: advise client that a message is available + * + */ +void +msgavail() +{ + int comp; + + signal(SIGURG, SIG_IGN); + dsiAttention(cno, AFPSERVERMESG, 1, -1, &comp); + while (comp > 0) + abSleep(30, TRUE); + signal(SIGURG, msgavail); + return; +} + +/* + * Call when the master process receives a SIGHUP -- immediate shutdown + * + */ +void +killnow() +{ + OSErr err; + + signal (SIGHUP, SIG_IGN); + logit(0,"Parent received SIGHUP -- immediate shutdown"); +#ifdef linux + killlist(SIGHUP); +#else linux + killpg (parent_pid, SIGHUP); +#endif linux + if ((err = SrvrShutdown(&srvr_entity_name)) != noErr) + logit(0,"NBPRemove failed: code %d\n", err); + exit(0); +} + +/* + * Called with the master process receives a SIGTERM + */ +void +killin5() +{ + void killinn(); + +#ifdef REREAD_AFPVOLS + signal(SIGUSR1, SIG_IGN); +#endif REREAD_AFPVOLS + signal (SIGTERM, SIG_IGN); + logit(0,"Shutdown by time -- initiating 5 minute shutdown"); +#ifdef linux + killlist(SIGTERM); +#else linux + killpg (parent_pid, SIGTERM); +#endif linux + minutes_to_shutdown = 4; + /* in case children get blocked up */ + signal(SIGALRM, killinn); + alarm(68); /* a litte more than a minute */ +} + +void +killinn() +{ + void killinn(); + void killnow(); + + if (minutes_to_shutdown < 0) /* not in die mode */ + return; + if (!minutes_to_shutdown) + killnow(); +#ifdef linux + killlist(SIGTERM); +#else linux + killpg (parent_pid, SIGTERM); /* in case inferior was blocked up */ +#endif linux + signal(SIGALRM, killinn); + minutes_to_shutdown--; + alarm(60); +} + +/* + * called when the master process receives a SIGURG + * + */ +void +msgnotify() +{ + signal(SIGURG, SIG_IGN); + logit(0, "Server Message available -- notifying clients"); +#ifdef linux + killlist(SIGURG); +#else linux + killpg(parent_pid, SIGURG); +#endif linux + signal(SIGURG, msgnotify); + return; +} + +#ifdef linux +/* + * send specified signal to child processes + * (process groups appear to be broken) + * + */ +killlist(sig) +int sig; +{ + int i; + struct cno_to_pid *cp; + + for (i = 0, cp = ctp_tab; i < maxsess; i++, cp++) + if (cp->state == CP_RUNNING) + kill(cp->pid, sig); + return; +} +#endif /* linux */ +#endif /* NOSHUTDOWNCODE */ + +#ifdef REREAD_AFPVOLS +/* + * a SIGUSR1 sets the 'readafpvols' flag which causes + * dorereadafpvols() to be called next time through the loop + * + */ + +void +setreadafpvols() +{ + signal(SIGUSR1, SIG_IGN); + readafpvols = TRUE; + signal(SIGUSR1, setreadafpvols); +} + +void +dorereadafpvols() +{ + FILE *fd, *fopen(); + + readafpvols = FALSE; + if (sysvolfile != NULL) { + if ((fd = fopen(sysvolfile, "r")) == NULL) + logit(0, "aufs: system volumes file %s disappeared", sysvolfile); + else + if (!VRRdVFile(fd)) + logit(0, "aufs: bad format system volumes file %s", sysvolfile); + } +} +#endif REREAD_AFPVOLS + +#ifdef CLOSE_LOG_SIG +/* + * A SIGUSR2 requests that the log be closed and reopened. This is + * important if you wish to rotate your logs on a regular basis. + * + */ + +void +closelog() +{ + signal(CLOSE_LOG_SIG, SIG_IGN); + nologitfile(); + logitfileis(logfile, "a+"); + signal(CLOSE_LOG_SIG, closelog); + return; +} +#endif /* CLOSE_LOG_SIG */ + +#ifdef notdef +/* + * log an error message + */ +#ifndef USEVPRINTF +/* Bletch - gotta do it because pyramids don't work the other way */ +/* (using _doprnt and &args) and don't have vprintf */ +/* of course, there will be something that is just one arg larger :-) */ +/*VARARGS1*/ +logit(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +char *fmt; +#else /* USEVPRINTF */ +logit(va_alist) +va_dcl +#endif /* USEVPRINTF */ +{ + static FILE *fp = NULL; +#ifdef USEVPRINTF + register char *fmt; + va_list args; +#endif + + if (fp == NULL) + if (DBDEB) + fp = stderr; + else + if ((fp = fopen(logfile, "a+")) == NULL) + return; + + if (mypid != parent_pid) + fprintf(fp, "%05d: ", mypid); + else + fprintf(fp, "%05d* ",mypid); + tmprt(fp); +#ifdef USEVPRINTF + va_start(args); + fmt = va_arg(args, char *); + vfprintf(fp, fmt, args); + va_end(args); +#else /* USEVPRINTF */ + fprintf(fp, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); +#endif /* USEVPRINTF */ + putc('\n', fp); + fflush(fp); +} + +tmprt(fp) +FILE *fp; +{ + fprintf(fp, "%s", mytod(0L)); +} + +/* + * return tod in a static buffer, rotate among buffers to + * allow at least calls "uses" to be active at a time + * + * is currently 2 + * +*/ +char * +mytod(ptime) +long ptime; +{ + long tloc; + struct tm *tm, *localtime(); +#define NTODBUF 2 + char * buf; + static char buffers[NTODBUF][100]; + static int idx = 0; + + if (ptime == 0L) + (void)time(&tloc); + else + tloc = ptime; + tm = localtime(&tloc); + buf = buffers[idx]; + ++idx; /* move to next */ + idx %= NTODBUF; /* make sure in range */ + if (tm->tm_year > 99) + sprintf(buf, "%02d:%02d:%02d %02d/%02d/%04d ", + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_mon+1, tm->tm_mday, tm->tm_year+1900); + else + sprintf(buf, "%02d:%02d:%02d %02d/%02d/%02d ", + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_mon+1, tm->tm_mday, tm->tm_year); + return(buf); +} +#endif notdef + +#ifdef LWSRV_AUFS_SECURITY +/**************** budd... ****************/ +clearuserlogin() +{ +#ifdef HIDE_LWSEC_FILE + char protecteddir[MAXPATHLEN]; + char dir_fname[MAXPATHLEN]; +#endif HIDE_LWSEC_FILE + + if( userlogindir != NULL ) { + char fname[ 100 ]; + int fd; + +#ifdef HIDE_LWSEC_FILE + strcpy(protecteddir, userlogindir); + make_userlogin(fname, protecteddir, addr); + strcpy(protecteddir, fname); + strcpy(dir_fname, fname); + fname[0] = '\0'; + make_userlogin(fname, protecteddir, addr); + if (unlink(fname) < 0) { + logit(0, "clearuserlogin: unlink failed for %s", fname); + if ((fd = open(fname, O_WRONLY|O_TRUNC)) >= 0) + close(fd); + } else + if (rmdir(dir_fname) < 0) + logit(0, "clearuserlogin: rmdir failed for %s", dir_fname); +#else HIDE_LWSEC_FILE + make_userlogin( fname, userlogindir, addr ); + if( unlink( fname ) < 0 ) { + if( (fd = open( fname, O_WRONLY|O_TRUNC )) != -1 ) + close( fd ); + } /* unlink failed */ +#endif HIDE_LWSEC_FILE + } /* have userlogindir */ +} /* clearuserlogin */ + +/* duplicated in aufs.c and lwsrv.c sigh */ +make_userlogin( buf, dir, addrb ) + char *buf, *dir; + AddrBlock addrb; +{ + sprintf( buf, "%s/net%d.%dnode%d", + dir, + nkipnetnumber(addrb.net), nkipsubnetnumber(addrb.net), + addrb.node + ); +} /* make_userlogin */ +/**************** ...budd ****************/ +#endif LWSRV_AUFS_SECURITY +#ifdef APPLICATION_MANAGER +/* + * read the -A specified file for a list of pathnames + * and an indication of the maximum number of times that + * the file (resource fork) can be opened (colon separated). + * Build a sorted list so that searching can be more efficient. + * When the file is opened, read lock the next available byte, + * return 'aeLockErr' if no locks available. If the maximum number + * is specified with a trailing 'P', then the file is PROTECTED from + * Finder copying. + * + * djh@munnari.OZ.AU + * September, 1991 + * + */ + +struct flist *applist = NULL; /* head ptr for file list */ +int fdplist[NOFILE]; /* fd list for protections */ + +readAppList(file) +char *file; +{ + char *c; + int qty = 0; + int num, protect; + FILE *fp, *fopen(); + char linebuf[MAXPATHLEN*2]; + struct flist *newapplp, *p, *q; + + for (num = 0; num < NOFILE; num++) + fdplist[num] = -1; + + if ((fp = fopen(file, "r")) == NULL) { + logit(0, "readAppList(): cannot open %s for reading", file); + return; + } + while (fgets(linebuf, sizeof(linebuf), fp) != NULL) { + if (linebuf[0] == '#') + continue; + if ((c = (char *) rindex(linebuf, ':')) == NULL) { + logit(0, "readAppList(): bad format (line %d) in %s", qty+1, file); + fclose(fp); + return; + } + *c++ = '\0'; + if ((num = atoi(c)) <= 0) { + logit(0, "readAppList(): illegal value (%d) in %s", num, file); + fclose(fp); + return; + } + protect = ((char *)index(c, 'P') == NULL) ? 0 : 1; + if ((newapplp = (struct flist *)malloc(sizeof(struct flist))) == NULL) { + logit(0, "readAppList(): malloc(%d) failed", sizeof(struct flist)); + fclose(fp); + return; + } + if ((newapplp->filename = (char *)malloc(strlen(linebuf)+12)) == NULL) { + logit(0, "readAppList(): malloc(%d) failed", strlen(linebuf)+12); + fclose(fp); + return; + } + if ((c = (char *)rindex(linebuf,'/')) == NULL) + continue; + *c++ = '\0'; + strcpy(newapplp->filename, linebuf); + strcat(newapplp->filename, "/.resource/"); + strcat(newapplp->filename, c); + newapplp->incarnations = num; + newapplp->protected = protect; + newapplp->next = NULL; + qty++; + + /* start list if none */ + if (applist == NULL) { + applist = newapplp; + continue; + } + /* else insert in-order */ + q = NULL; + p = applist; + while (p != NULL) { + if (strcmp(p->filename, newapplp->filename) > 0) + break; + q = p; + p = p->next; + } + newapplp->next = p; + if (q == NULL) + applist = newapplp; + else + q->next = newapplp; + } + fclose(fp); + p = applist; + logit(0, "Read %d application restrictions from %s", qty, file); + while (p != NULL) { + logit(0, " %s, %d%s", p->filename, p->incarnations, + (p->protected) ? " (no copy)" : ""); + p = p->next; + } + return; +} +#endif APPLICATION_MANAGER +#ifdef AUFS_IDLE_TIMEOUT +/* + * If an idle time is set ('-i' for guests, '-I' for everyone) then + * check for number of minutes of no AFP activity. Give warnings + * at 5, 3 & 1 minute marks. If anything happens then we reset the + * timer and notify user that shutdown is aborted. + * + * djh@munnari.OZ.AU + * February, 1992 + * + */ +int +checkIdle(cno, buf, cmp) +int cno, cmp; +unsigned char *buf; +{ + int i, comp; + extern int guestlogin; + static int sentshutdown = 0; + + if (guestonly && !guestlogin) + return; + + if (cmp == 0 && *buf != 17) { /* periodic GetVolParms AFP call */ + if (sentshutdown) { + logit(0, "Session %d: Aborting Idle Timeout", cno); + dsiAttention(cno, AFPSHUTDOWNCANCEL, 1, -1, &comp); + while (comp > 0) { abSleep(30, TRUE); } /* ignore error */ + sentshutdown = 0; + } + timeidle = 0; + return; + } + + switch (cmp) { + case 0: /* noErr */ + timeidle++; + break; + case 1: /* abSleep() timeout */ + if (!sentshutdown) return; + timeidle += 6; + break; + default: /* some error */ + return; + break; + } + + if (timeidle < idletime) + return; + + switch ((i = timeidle-idletime)) { + case 0: /* shutdown in 5 min */ + case 12: /* shutdown in 3 min */ + case 24: /* shutdown in 1 min */ + logit(0, "Session %d: sending %d minute idle timeout warning",cno,5-i/6); + dsiAttention(cno, AFPSHUTDOWNTIME(5-i/6), 1, -1, &comp); + while (comp > 0) { abSleep(30, TRUE); } /* ignore error */ + sentshutdown++; + return; + break; + case 30: /* shutdown now */ + logit(0, "Session %d: Idle Timeout Shutdown", cno); + dsiAttention(cno, AFPSHUTDOWNNOW, 1, -1, &comp); + while (comp > 0) { abSleep(30, TRUE); } /* ignore error */ + dsiCloseSession(cno, 3, 2, &comp); /* 3 times, .5 seconds */ + while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */ +#ifdef LWSRV_AUFS_SECURITY + clearuserlogin(); /* gtw: delete auth-file entry for dead users */ +#endif LWSRV_AUFS_SECURITY + exit(0); + break; + } +} +#endif AUFS_IDLE_TIMEOUT diff --git a/applications/aufs/aufs_vers b/applications/aufs/aufs_vers new file mode 100755 index 0000000..feda587 --- /dev/null +++ b/applications/aufs/aufs_vers @@ -0,0 +1 @@ +Major 3 Minor 4 diff --git a/applications/aufs/aufs_vers.sh b/applications/aufs/aufs_vers.sh new file mode 100644 index 0000000..ad83601 --- /dev/null +++ b/applications/aufs/aufs_vers.sh @@ -0,0 +1,17 @@ +#!/bin/sh +echo "int aufs_version[2] = {" $2 "," $4 "};" > $6 +if [ -f /usr/local/fdate ]; then + echo "char *aufs_versiondate = \""`fdate "%L %d, 19%y"`"\";" >> $6 +else + echo "char *aufs_versiondate = \"" `date` "\";" >> $6 +fi +if [ $5 != "useold" ]; then + newver=`expr $4 + 1` + echo $1 $2 $3 $newver > nv$$ + mv nv$$ $5 +else + echo "No version updating" +fi + + + diff --git a/applications/aufs/aufscicon.c b/applications/aufs/aufscicon.c new file mode 100644 index 0000000..d254c9b --- /dev/null +++ b/applications/aufs/aufscicon.c @@ -0,0 +1,2389 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:49:40 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/aufsicon.c,v 2.9 1996/06/18 10:49:40 djh Rel djh $ + * $Revision: 2.9 $ + */ + +/* + * aufscicon.c - aufs color icon. + * + * Set up to display a color CAP volume ICON, and where + * possible represent the underlying hardware platform. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in + * the City of New York. + * + */ + +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif +#ifdef SOLARIS +# include +#endif /* SOLARIS */ +#ifdef linux +# include +#endif /* linux */ +#include +#include +#include +#include +#include +#ifdef NEEDFCNTLDOTH +#include +#endif /* NEEDFCNTLDOTH */ +#include "afps.h" + +/* + * The Icons in this file are intended for non-commercial CAP use, + * they are provided to visually link the CAP server and host type. + * Components of the images are copyright by the respective hardware + * manufacturers. + * + * BSD Daemon Copyright 1988 Marshall Kirk McKusick. All Rights Reserved. + * Penguin With Scarf Copyright 1996 David Hornsby. All Rights Reserved. + * Used with permisson. + * + * To enable the automatic host ICON selection, define USE_HOST_ICON in + * the m4.features file. + * + * The color_cap_icon[] array mirrors the resource fork of a ResEdit file + * that contains 'icl4', 'icl8', 'ICN#', 'ics#', 'ics4' & 'ics8' resources + * (resource IDs -16455). If you edit an Icon, or add resource names etc. + * ensure that the array length is accurate. + * + */ + +#define ICON_FNDR_WAS 286 +#define ICON_FNDR_LEN 300 +#define ICON_RSRC_LEN 2670 +#define ICON_NAME "Icon:0d" +#define INFO_MESSAGE1 "BSD Daemon Copyright 1988 Marshall Kirk McKusick - \ +All Rights Reserved.\r\rhttp://www.cs.mu.OZ.AU/appletalk/cap.html\r" +#define INFO_MESSAGE2 "http://www.cs.mu.OZ.AU/appletalk/cap.html\r" + +/* Automatic host/ICON selection */ + +#ifdef USE_HOST_ICON + +#ifdef __NetBSD__ +#define BSD_ICON 1 +#endif /* __NetBSD__ */ +#ifdef __386BSD__ +#define BSD_ICON 1 +#endif /* __386BSD__ */ +#ifdef __FreeBSD__ +#define BSD_ICON 1 +#endif /* __FreeBSD__ */ +#ifdef __bsdi__ +#define BSD_ICON 1 +#endif /* __bsdi__ */ + +#ifdef BSD_ICON +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* BSD2icon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x00,0x00,0x80,0x00,0xc0,0xab,0x00,0x08,0x66,0x08,0x30,0x3c, + 0xf8,0xc2,0x60,0x00,0x00,0x88,0x20,0x3c,0x00,0x00,0x80,0x00,0xc0,0xab, + 0x00,0x08,0x66,0x5a,0x20,0x3c,0x09,0x42,0x53,0x44,0x5f,0x50,0x69,0x63, + 0x6f,0x6e,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, + 0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x3c,0xdf,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x4e,0xba,0x0c,0x44,0x26,0x40,0x4f,0xef,0x00,0x14,0x20,0x3c,0x00,0x00, + 0x80,0x00,0xc0,0xab,0x00,0x08,0x66,0x06,0x30,0x3c,0xf8,0xc2,0x60,0x0e, + 0x20,0x6e,0x00,0x18,0x20,0x8b,0x20,0x6e,0x00,0x14,0x20,0x8a,0x70,0x00, + 0x4c,0xee,0x1c,0xe0,0xff,0xe2,0x4e,0x5e,0x4e,0x75,0x4e,0x56,0xff,0xee, + 0x48,0xe7,0x0f,0x18,0x28,0x2e,0x00,0x1c,0x2a,0x2e,0x00,0x18,0x20,0x6e, + 0x00,0x08,0x26,0x68,0x00,0x40,0x4a,0xae,0x00,0x14,0x67,0x14,0x00,0x84, + 0x00,0x00,0x80,0x00,0x2e,0x05,0x08,0x07,0x00,0x00,0x67,0x0a,0x20,0x07, + 0x52,0x87,0x60,0x04,0x7e,0x00,0x2a,0x07,0x2f,0x0b,0x2f,0x2e,0x00,0x0c, + 0x4e,0xba,0xf6,0x8e,0x2c,0x00,0x50,0x4f,0x66,0x7e,0x20,0x53,0x2c,0x10, + 0x20,0x4b,0x22,0x06,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0xd8,0xff,0x00,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0xff,0xd8,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0xff,0xd8,0xd8, + 0xff,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff, + 0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00, + 0xff,0xff,0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8, + 0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff, + 0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff, + 0xff,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff, + 0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00, + 0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xfc,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xd8,0xff,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00, + 0xfc,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xff,0x00,0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xff, + 0xff,0xff,0xd8,0xd8,0xff,0x00,0x00,0x00,0xfc,0xfc,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xfc,0xfc,0x00, + 0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0xfc,0xfc, + 0xfc,0x00,0x00,0xfc,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0xff,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff, + 0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xfc,0xff,0xd8,0xd8,0xd8,0xff, + 0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff, + 0xff,0xd8,0xff,0x00,0x00,0x00,0xff,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8, + 0xff,0xff,0xff,0xd8,0xff,0x00,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0x00, + 0x00,0x00,0xff,0xd8,0xff,0xd8,0xff,0xff,0xff,0xff,0xd8,0xff,0xff,0xd8, + 0xd8,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0xff,0x00,0x00,0xff, + 0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xff,0xff,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xc0,0xc0, + 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xfc,0xc0,0xc0,0xc0, + 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xff,0x00, + 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0xff,0xff,0xff,0xfd, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd, + 0xff,0xff,0xff,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xf9, + 0xfb,0xfb,0xfb,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb, + 0xf9,0xf9,0xf7,0xf7,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0xf6,0xf6,0xf9,0xf9,0xfb,0xfb,0xfb,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0x00,0x00, + 0x00,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00,0xff,0xff,0xf0,0x00, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x3f,0x0f,0x33,0x33, + 0x3f,0x0f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0xf3, + 0x3f,0xff,0x33,0xf3,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0x0f,0xff,0x33,0xf3,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0f,0x00,0xff,0x00,0xf3,0x3f,0x33,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xf0,0x00,0xf0,0x00,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0xff,0xf0,0x0f,0x3f,0x33,0x3f,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0xff,0xf0,0x0f,0x3f,0x33, + 0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0xff,0xf0,0x0f, + 0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0xf0, + 0x00,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0, + 0x00,0xf0,0x00,0xf3,0x3f,0x33,0x3f,0x00,0x0e,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0f,0xff,0xff,0xff,0x3f,0xf3,0x33,0x3f,0x00,0xe0,0x0e,0x00,0x00, + 0x00,0x00,0x00,0x00,0xf3,0x33,0xf3,0x33,0x33,0x33,0x3f,0x00,0xe0,0xe0, + 0x00,0x00,0x00,0x00,0x00,0x00,0xf3,0x33,0xf3,0x3f,0xff,0x33,0xf0,0x00, + 0xee,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0x33,0x33,0xf3,0x33, + 0xf0,0x0e,0xe0,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf3,0x33,0x3f, + 0x33,0x3f,0xee,0xe0,0x0e,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f, + 0xff,0xff,0xff,0xf3,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xf3,0x33,0x33,0xf3,0x33,0xf0,0x00,0xff,0xf0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0xf3,0x3f,0x33,0x33,0xf0,0x0f,0x00,0x0f,0x00,0x00, + 0xf0,0xff,0x00,0x00,0x0f,0x33,0x3f,0xef,0x33,0x3f,0xf0,0x0f,0x0f,0x0f, + 0x00,0x00,0xff,0xf3,0xf0,0x00,0xff,0x33,0x3f,0x33,0xff,0xf3,0xf0,0x0f, + 0x0f,0xf0,0x00,0x00,0xf3,0xf3,0xff,0xff,0x3f,0xf3,0x3f,0x3f,0x33,0x33, + 0xf0,0xf0,0x0f,0xff,0x00,0x00,0xf3,0xf3,0x33,0x33,0x33,0xff,0xf3,0x33, + 0x33,0x33,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0x77,0x77,0x77,0x77,0x77, + 0x77,0xe7,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0xf0,0x00,0xff,0xff,0xff, + 0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x0a,0xff,0xfa,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xff,0xfa,0x00,0x00, + 0x00,0x00,0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab,0xba,0xbb, + 0xaa,0xae,0xee,0xdd,0xcc,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb, + 0xa0,0xab,0xbb,0xbb,0xbb,0xcc,0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa, + 0xaa,0xaa,0x00,0x0a,0xaa,0xae,0xee,0xdd,0x00,0x00,0x01,0x00,0x00,0x0c, + 0x01,0x80,0x00,0x18,0xf8,0xc0,0x00,0x15,0x05,0x40,0x00,0x12,0x72,0x40, + 0x00,0x0d,0xc9,0x40,0x00,0x13,0x24,0xc0,0x00,0x22,0x14,0x40,0x00,0x3b, + 0x94,0x40,0x00,0x3b,0x94,0x40,0x00,0x3b,0x94,0x40,0x00,0x22,0x14,0x40, + 0x00,0x22,0x24,0x44,0x00,0x1f,0xd8,0x49,0x00,0x08,0x80,0x4a,0x00,0x08, + 0x9c,0x8c,0x00,0x07,0x08,0x99,0x00,0x02,0x11,0xe6,0x00,0x01,0xfe,0x80, + 0x00,0x02,0x08,0x8e,0x00,0x03,0x90,0x91,0x0b,0x04,0x71,0x95,0x0e,0x8c, + 0x4e,0x96,0x0a,0xf6,0x50,0xa7,0x0a,0x03,0x80,0xc0,0xff,0xff,0xff,0xff, + 0x40,0x02,0x00,0x02,0x3f,0xff,0xff,0xfc,0x00,0x08,0x1f,0x00,0x00,0x00, + 0x1f,0x00,0xff,0xff,0xe4,0xff,0x00,0x00,0x0a,0x00,0xff,0xff,0xf1,0xff, + 0x00,0x0c,0x01,0x80,0x00,0x18,0xf8,0xc0,0x00,0x1d,0xfd,0xc0,0x00,0x1f, + 0xff,0xc0,0x00,0x0f,0xff,0xc0,0x00,0x1f,0xff,0xc0,0x00,0x3f,0xff,0xc0, + 0x00,0x3f,0xff,0xc0,0x00,0x3f,0xff,0xc0,0x00,0x3f,0xff,0xc0,0x00,0x3f, + 0xff,0xc0,0x00,0x3f,0xff,0xc4,0x00,0x1f,0xff,0xc9,0x00,0x0f,0xff,0xca, + 0x00,0x0f,0xff,0x8c,0x00,0x07,0xff,0x99,0x00,0x03,0xff,0xe6,0x00,0x01, + 0xff,0x80,0x00,0x03,0xff,0x8e,0x00,0x03,0xff,0x91,0x0b,0x07,0xff,0x95, + 0x0f,0x8f,0xff,0x96,0x0f,0xff,0xff,0xa7,0x0f,0xff,0xff,0xc0,0xff,0xff, + 0xff,0xff,0x7f,0xff,0xff,0xfe,0x3f,0xff,0xff,0xfc,0x00,0x08,0x1f,0x00, + 0x00,0x00,0x1f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff, + 0xf1,0xff,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00, + 0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0x00,0x00,0xff,0xff,0x00,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xd8,0xd8,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xd8,0xd8,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xd8,0xff,0xff,0xff,0xd8, + 0xd8,0xd8,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0xd8,0xd8,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xd8,0x00, + 0xd8,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff,0x00,0x00, + 0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xff, + 0x00,0x00,0xff,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, + 0xc0,0xc0,0xc0,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x80, + 0x00,0x00,0x0f,0xf0,0x00,0xff,0x00,0x00,0x00,0x00,0x0f,0x00,0xff,0x03, + 0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x0f,0x0f, + 0x0f,0x33,0xf0,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x33,0xf0,0x00,0x00,0x00, + 0x03,0x33,0x33,0x33,0xf0,0x0f,0x00,0x00,0x03,0xff,0xf3,0x33,0xf0,0xf0, + 0x00,0x00,0x00,0x33,0x33,0x3f,0x00,0xff,0x00,0x00,0x00,0x0f,0xf3,0x3f, + 0xff,0x00,0x00,0x30,0x30,0x00,0xf3,0x33,0xf0,0x0f,0x00,0x3f,0x33,0x33, + 0x33,0xf3,0xff,0xff,0x00,0x03,0x33,0x3f,0x33,0x33,0x3f,0x00,0xf7,0x77, + 0x77,0x77,0x77,0x77,0x77,0x7f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xf0, + 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0xaa,0xaa,0x00,0x00,0x00,0x40,0x06,0x30,0x04,0xd0,0x07,0xf0,0x05,0x78, + 0x05,0x78,0x07,0xf9,0x07,0xfa,0x03,0xf3,0x01,0xfc,0x28,0xf9,0x29,0xff, + 0x1f,0xfc,0xff,0xff,0x7f,0xfe,0x00,0x30,0xff,0xff,0x06,0x30,0x04,0xf0, + 0x07,0xf0,0x07,0xf8,0x07,0xf8,0x07,0xf9,0x07,0xfa,0x03,0xf3,0x01,0xfc, + 0x28,0xf9,0x3f,0xff,0x1f,0xfc,0xff,0xff,0x7f,0xfe,0x00,0x30,0xff,0xff, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x03,0x71,0xee,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x38,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x34, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x38,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x23,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x03,0x71,0xf3,0x30,0xbf,0xb9,0xff,0xff,0x00,0x00,0x04,0x04, + 0x03,0x71,0xea,0xd8,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, + 0xf5,0xd4,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xef,0x78, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x08,0x10,0x03,0x70,0xf8,0xc0,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x08,0x94,0x03,0x71,0xf4,0xf0 +}; +#endif /* BSD_ICON */ + +#ifndef HOST_ICON +#ifdef sun +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* SUNicon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x08,0xc8, + 0x09,0x99,0x08,0xc8,0x09,0xca,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x07,0x53,0x55,0x4e,0x69,0x63,0x6f,0x6e, + 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00, + 0x00,0x00,0x00,0x81,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0x00,0x00,0x00,0x81,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x3b,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x00,0x00,0x00,0x53, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x01,0x02, + 0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04, + 0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02, + 0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04, + 0x00,0x07,0x07,0x43,0x6f,0x75,0x72,0x69,0x65,0x72,0x02,0x05,0x06,0x02, + 0x05,0x07,0x03,0x05,0x06,0x07,0x01,0x2d,0x04,0x42,0x6f,0x6c,0x64,0x07, + 0x4f,0x62,0x6c,0x69,0x71,0x75,0x65,0x00,0x00,0x00,0x01,0x00,0x00,0x02, + 0x1a,0xf6,0x00,0x02,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f, + 0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00, + 0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f, + 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, + 0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f, + 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f, + 0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f, + 0x7f,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00, + 0x7f,0x7f,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f, + 0x7f,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00, + 0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, + 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f, + 0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x7f,0x7f, + 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0xab, + 0xab,0xab,0xab,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x00, + 0x00,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f, + 0x7f,0x00,0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7f,0x7f,0x7f,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x7f,0x7f, + 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, + 0xab,0xab,0xab,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, + 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0xab,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x7f, + 0x7f,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00, + 0xab,0x2a,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00, + 0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, + 0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, + 0xab,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00, + 0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff, + 0x2a,0xff,0x2a,0x2a,0xab,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f, + 0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x7f,0x7f,0x7f,0x00, + 0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00, + 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00, + 0x00,0x00,0x00,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00, + 0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, + 0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x7f,0x7f, + 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd, + 0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb, + 0xfb,0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb, + 0xf9,0xf9,0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x55,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x55,0x50, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55, + 0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x50,0x55,0x50,0x55,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x50,0x55,0x50,0x55,0x50,0x55,0x50,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x55,0x50,0x55,0x50,0x55,0x50,0x50, + 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x55,0x05,0x55,0x05,0x55,0x05, + 0x55,0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x50,0x50,0x55,0x50, + 0x55,0x50,0x50,0x55,0x50,0x50,0x00,0x00,0x00,0x00,0x05,0x55,0x05,0x55, + 0x05,0x55,0x05,0x50,0x05,0x55,0x05,0x55,0x00,0x00,0x00,0x00,0x55,0x50, + 0x55,0x50,0x00,0x55,0x55,0x50,0x55,0x50,0x55,0x50,0x00,0x00,0x00,0x05, + 0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00, + 0x00,0x00,0x00,0x55,0x50,0x55,0x55,0x50,0x00,0x55,0x50,0x55,0x50,0x55, + 0x55,0x50,0x0e,0xee,0xe0,0x55,0x05,0x55,0x05,0x50,0x00,0x55,0x05,0x55, + 0x05,0x55,0x05,0x50,0xe7,0x77,0x7e,0x00,0x00,0x00,0x55,0x50,0x00,0x55, + 0x55,0x50,0x55,0x50,0x55,0x50,0xee,0xee,0xee,0xee,0xee,0xe0,0x55,0x05, + 0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0, + 0x50,0x55,0x55,0x50,0x00,0x55,0x50,0x55,0x50,0x00,0xe7,0x77,0xf7,0x77, + 0x77,0xe0,0x00,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0x00,0xe7,0x7f, + 0x77,0x77,0x77,0xe0,0x50,0x55,0x50,0x55,0x50,0x50,0x55,0x50,0x00,0x00, + 0xe7,0xf7,0x7f,0x7f,0x77,0xe0,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00, + 0x00,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x55,0x50,0x55,0x50,0x55,0x50, + 0x50,0x00,0x00,0x00,0xee,0xee,0xee,0xee,0xee,0xe0,0x05,0x55,0x05,0x55, + 0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0xad,0xa0,0x00,0x00,0x50,0x55, + 0x50,0x55,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x05, + 0x55,0x05,0x55,0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f, + 0x00,0x00,0x55,0x50,0x55,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x0f, + 0xff,0xff,0x00,0x00,0x05,0x55,0x05,0x50,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0a,0xda,0xda,0x00,0x00,0x00,0x55,0x55,0x50,0x00,0x00,0x00,0x00, + 0x00,0x00,0xde,0xad,0xda,0xdd,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0xaa,0xae,0xee,0xdd,0xcd,0xdd,0xa0,0xad,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd, + 0xdd,0xdd,0xdd,0xdd,0xdd,0xcc,0xde,0xaa,0x00,0x0a,0xaa,0xaa,0xaa,0xaa, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01,0xc0,0x00,0x00,0x03,0xe0,0x00,0x00,0x03,0x70,0x00, + 0x00,0x0b,0xb8,0x00,0x00,0x1d,0xdc,0x00,0x00,0x2e,0xee,0x00,0x00,0x77, + 0x77,0x00,0x00,0xbb,0xba,0x80,0x01,0xdd,0xdd,0xc0,0x03,0xae,0xeb,0xa0, + 0x07,0x77,0x67,0x70,0x0e,0xe3,0xee,0xe0,0x1d,0xdd,0xdd,0xdc,0x03,0xbe, + 0x3b,0xbe,0x7b,0x76,0x37,0x76,0x84,0x0e,0x3e,0xee,0xff,0xed,0xdd,0xdc, + 0x80,0x2b,0xe3,0xb8,0x88,0x23,0x77,0x70,0x90,0x2b,0xba,0xe0,0xa5,0x2d, + 0xdd,0xc0,0x80,0x2e,0xee,0x80,0xff,0xe7,0x77,0x00,0x0a,0x0b,0xba,0x00, + 0x1f,0x1d,0xdc,0x00,0x11,0x0e,0xe8,0x00,0x1f,0x07,0x60,0x00,0x15,0x03, + 0xe0,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x03,0xe0,0x00,0x00,0x07,0xf0,0x00,0x00,0x0b, + 0xf8,0x00,0x00,0x1f,0xfc,0x00,0x00,0x3f,0xfe,0x00,0x00,0x7f,0xff,0x00, + 0x00,0xff,0xff,0x80,0x01,0xff,0xff,0xc0,0x03,0xff,0xff,0xe0,0x07,0xff, + 0xff,0xf0,0x0f,0xff,0xef,0xf8,0x1f,0xf7,0xff,0xf4,0x3f,0xff,0xff,0xfe, + 0x03,0xff,0xff,0xfe,0x7b,0xff,0xff,0xfe,0xfc,0x0f,0xff,0xfe,0xff,0xef, + 0xff,0xfe,0xff,0xef,0xf7,0xfc,0xff,0xeb,0xff,0xf8,0xff,0xef,0xff,0xf0, + 0xff,0xef,0xff,0xe0,0xff,0xef,0xff,0xc0,0xff,0xef,0xff,0x80,0x0e,0x0f, + 0xff,0x00,0x1f,0x3f,0xfe,0x00,0x1f,0x1f,0xfc,0x00,0x1f,0x0f,0xe8,0x00, + 0x1f,0x07,0xf0,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, + 0xff,0xff,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f, + 0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00, + 0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, + 0x7f,0x7f,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, + 0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0xab,0xab,0xab,0x7f,0x7f,0x7f,0x7f,0x7f, + 0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0xab,0xab,0xab,0xab,0xab,0xab, + 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0xab,0x2a,0xff,0x2a, + 0x2a,0xab,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0xab,0xff, + 0xff,0xff,0x2a,0xab,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00, + 0xab,0xab,0xab,0xab,0xab,0xab,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00, + 0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, + 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x7f,0x7f,0x7f,0x7f, + 0x7f,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9,0x00,0x00,0x00,0x80, + 0x00,0x00,0x00,0x05,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x55,0x00, + 0x00,0x00,0x00,0x00,0x05,0x55,0x55,0x50,0x00,0x00,0x00,0x00,0x55,0x55, + 0x55,0x55,0x00,0x00,0x00,0x05,0x55,0x55,0x55,0x55,0x50,0x00,0x00,0x55, + 0x55,0x55,0x55,0x55,0x55,0x00,0x05,0x55,0x55,0x55,0x55,0x55,0x55,0x50, + 0xee,0xe5,0x55,0x55,0x05,0x55,0x55,0x55,0xee,0xee,0xee,0x55,0x55,0x55, + 0x55,0x55,0xe7,0xf7,0x7e,0x55,0x55,0x55,0x55,0x50,0xef,0xff,0x7e,0x55, + 0x55,0x55,0x55,0x00,0xee,0xee,0xee,0x55,0x55,0x55,0x50,0x00,0x0f,0xff, + 0x05,0x55,0x55,0x55,0x00,0x00,0x0f,0xff,0x00,0x55,0x55,0x50,0x00,0x00, + 0xed,0xdd,0xaa,0xaa,0xaa,0xaa,0xae,0xed,0xed,0xad,0xdd,0xdd,0xdd,0xdd, + 0xde,0xed,0x00,0x00,0x00,0x40,0x01,0x80,0x03,0xc0,0x07,0xe0,0x0f,0xf0, + 0x1f,0xf8,0x3f,0xfc,0x7f,0xfe,0xff,0x7f,0xff,0xff,0xa7,0xfe,0xf7,0xfc, + 0xff,0xf8,0x77,0xf0,0x73,0xe0,0xff,0xff,0xff,0xff,0x01,0xc0,0x03,0xe0, + 0x07,0xf0,0x0f,0xf8,0x1f,0xfc,0x3f,0xfe,0x7f,0xff,0xff,0xff,0xff,0xff, + 0xff,0xfe,0xff,0xfc,0xff,0xf8,0x7f,0xf0,0x7f,0xe0,0xff,0xff,0xff,0xff, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x03,0x71,0xee,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x38,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x34, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x38,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x23,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x03,0x71,0xf1,0xbc,0xbf,0xb9,0xff,0xff,0x00,0x00,0x04,0x04, + 0x03,0x71,0xf2,0xd4,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, + 0xf1,0x7c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf1,0x74, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x08,0x10,0x03,0x71,0xf5,0x4c,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x08,0x94,0x03,0x71,0xf4,0xac +}; +#endif /* sun */ +#endif /* HOST_ICON */ + +#ifndef HOST_ICON +#ifdef NeXT +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* NeXTicon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x6e,0x00,0x08,0x32,0x2e,0x00,0x12,0x60,0x06,0x12,0xc2,0x30, + 0x01,0x53,0x41,0x4a,0x41,0x66,0xf6,0x4e,0x5e,0x4e,0x75,0x4e,0x56,0xff, + 0xfc,0x48,0xe7,0x03,0x08,0x42,0x08,0x4e,0x45,0x58,0x54,0x69,0x63,0x6f, + 0x6e,0x58,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, + 0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x36,0xfb,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x6f,0x00,0x04,0x70,0x00,0xa0,0xac,0x20,0x5f,0x58,0x8f,0x60,0x0c,0x30, + 0x6f,0x00,0x04,0x70,0x01,0xa0,0xac,0x20,0x5f,0x54,0x8f,0x3e,0x80,0x4e, + 0xd0,0x48,0xe7,0x18,0x00,0x20,0x6f,0x00,0x0c,0x36,0x2f,0x00,0x10,0x38, + 0x2f,0x00,0x12,0x70,0x05,0xa0,0xac,0x4c,0xdf,0x00,0x18,0x20,0x5f,0x50, + 0x8f,0x60,0xde,0x48,0xe7,0x18,0x00,0x20,0x6f,0x00,0x10,0x30,0x10,0x20, + 0x6f,0x00,0x0c,0x31,0x40,0x00,0x04,0x36,0x2f,0x00,0x14,0x38,0x2f,0x00, + 0x16,0x70,0x02,0xa0,0xac,0x32,0x04,0x4c,0xdf,0x00,0x18,0x4a,0x40,0x66, + 0x06,0x20,0x6f,0x00,0x08,0x30,0x81,0x20,0x5f,0xde,0xfc,0x00,0x0c,0x60, + 0xa8,0x2f,0x03,0x20,0x6f,0x00,0x08,0x36,0x2f,0x00,0x0c,0x70,0x06,0xa0, + 0xac,0x26,0x1f,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0xf1,0xf0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x1f,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0xf1,0xff, + 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0x1f,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff, + 0xff,0xf1,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0xff,0xff,0x1f,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0f,0xff,0xff,0xf1,0xff,0xff,0xf1,0x11,0xff,0xff,0xf0,0x00,0x00, + 0x00,0x00,0x00,0xff,0xff,0xff,0x1f,0xff,0xff,0xf1,0x1f,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0xf1,0xff,0xff,0xff,0xf1,0xff,0x1f, + 0xff,0xff,0xf0,0x00,0x00,0x00,0xff,0xff,0xff,0x1f,0xff,0xff,0x1f,0xff, + 0x11,0xff,0x1f,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0xf1,0xff,0xff,0xff, + 0xf1,0xff,0xff,0xf1,0xff,0xff,0xff,0xf0,0x00,0x0f,0xff,0xff,0x1f,0xff, + 0xff,0x11,0x11,0x1f,0xff,0x1f,0x1f,0xff,0xff,0xff,0x0e,0xee,0xef,0xff, + 0xf1,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xf0,0xe7,0x77, + 0x7e,0xff,0xff,0x1f,0xff,0xff,0x1f,0xff,0x1f,0xff,0xff,0xff,0xff,0x00, + 0xee,0xee,0xee,0xee,0xee,0xe1,0xff,0xff,0xff,0xff,0x1f,0xff,0xff,0xff, + 0xf0,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x1f,0xff,0xff,0x11,0x11,0x1f, + 0xff,0xff,0x00,0x00,0xe7,0x77,0xf7,0x77,0x77,0xe0,0xf1,0xff,0xff,0xff, + 0x1f,0xff,0xff,0xf0,0x00,0x00,0xe7,0x7f,0x77,0x77,0x77,0xe0,0xff,0x1f, + 0xff,0xff,0x1f,0xff,0xff,0x00,0x00,0x00,0xe7,0xf7,0x7f,0x7f,0x77,0xe0, + 0xff,0xf1,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0xe7,0x77,0x77,0x77, + 0x77,0xe0,0xff,0xff,0x1f,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xee,0xee, + 0xee,0xee,0xee,0xe0,0xff,0xff,0xf1,0xff,0xff,0xf0,0x00,0x00,0x00,0x00, + 0x00,0x00,0xad,0xa0,0x00,0x00,0xff,0xff,0xff,0x1f,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0xf1,0xf0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f,0x00,0x00,0x00,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x0f, + 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xde,0xeb,0xba,0xbb, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, + 0xa0,0xab,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, + 0xde,0xea,0x00,0x0a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, + 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, + 0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05, + 0xff,0xff,0xff,0xff,0xff,0x05,0x05,0x05,0xff,0xff,0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0x05,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x05, + 0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff, + 0x05,0xff,0xff,0xff,0x05,0x05,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, + 0x05,0xff,0xff,0xff,0xff,0xff,0x05,0x05,0x05,0x05,0x05,0xff,0xff,0xff, + 0x05,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xab,0xab,0xab, + 0xab,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x00, + 0xab,0x54,0x54,0x54,0x54,0xab,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff, + 0xff,0xff,0x05,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, + 0xab,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x05,0xff,0xff,0xff,0xff,0xff,0x05,0x05, + 0x05,0x05,0x05,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xab,0x2a, + 0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xff,0x05,0xff,0xff, + 0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, + 0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00, + 0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff,0x2a,0xff, + 0x2a,0x2a,0xab,0x00,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xff,0xff,0xff,0xff,0x05,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xff,0xff, + 0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00,0x00, + 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0x33, + 0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, + 0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xf0,0x00,0x00,0x07,0xe8,0x00, + 0x00,0x0f,0xdc,0x00,0x00,0x1f,0xbe,0x00,0x00,0x3f,0x7f,0x00,0x00,0x7e, + 0xff,0x80,0x00,0xfd,0xff,0xc0,0x01,0xfb,0xe3,0xe0,0x03,0xf7,0xe7,0xf0, + 0x07,0xef,0xed,0xf8,0x0f,0xdf,0x73,0x7c,0x1f,0xbf,0xbe,0xfe,0x1f,0x7c, + 0x1d,0x7f,0x7f,0xbe,0xff,0xbe,0x87,0xdf,0x77,0xfc,0xff,0xef,0xf7,0xf8, + 0x80,0x27,0xc1,0xf0,0x88,0x2b,0xf7,0xe0,0x90,0x2d,0xf7,0xc0,0xa5,0x2e, + 0xff,0x80,0x80,0x2f,0x7f,0x00,0xff,0xef,0xbe,0x00,0x0a,0x0f,0xdc,0x00, + 0x1f,0x07,0xe8,0x00,0x11,0x03,0xf0,0x00,0x1f,0x01,0xe0,0x00,0x15,0x00, + 0x00,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xf0,0x00,0x00,0x07, + 0xf8,0x00,0x00,0x0f,0xfc,0x00,0x00,0x1f,0xfe,0x00,0x00,0x3f,0xff,0x00, + 0x00,0x7f,0xff,0x80,0x00,0xff,0xff,0xc0,0x01,0xff,0xff,0xe0,0x03,0xff, + 0xff,0xf0,0x07,0xff,0xff,0xf8,0x0f,0xff,0xff,0xfc,0x1f,0xff,0xff,0xfe, + 0x1f,0xff,0xff,0xff,0x7f,0xff,0xff,0xfe,0xff,0xff,0xff,0xfc,0xff,0xff, + 0xff,0xf8,0xff,0xef,0xff,0xf0,0xff,0xff,0xff,0xe0,0xff,0xef,0xff,0xc0, + 0xff,0xff,0xff,0x80,0xff,0xef,0xff,0x00,0xff,0xff,0xfe,0x00,0x0e,0x0f, + 0xfc,0x00,0x1f,0x07,0xf8,0x00,0x1f,0x03,0xf0,0x00,0x1f,0x01,0xe0,0x00, + 0x1f,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, + 0xff,0xff,0x00,0x00,0x00,0x40,0x00,0x00,0x03,0xe0,0x07,0x70,0x0e,0xf8, + 0x1d,0x9c,0x3b,0xfe,0x76,0x67,0xfb,0xff,0xfd,0x9e,0xa6,0xfc,0xf7,0x78, + 0xff,0xb0,0x73,0xe0,0x71,0xc0,0xff,0xfe,0xff,0xfe,0x00,0x00,0x03,0xe0, + 0x07,0xf0,0x0f,0xf8,0x1f,0xfc,0x3f,0xfe,0x7f,0xff,0xff,0xff,0xff,0xfe, + 0xff,0xfc,0xff,0xf8,0xff,0xf0,0x7f,0xe0,0x7f,0xc0,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x0f,0xff,0x1f,0xff,0x00,0x00, + 0x00,0x00,0xff,0xf1,0xff,0xff,0xf0,0x00,0x00,0x0f,0xff,0x1f,0xf1,0x1f, + 0xff,0x00,0x00,0xff,0xf1,0xff,0xff,0xff,0xff,0xf0,0x0f,0xff,0x1f,0xf1, + 0x1f,0xf1,0x1f,0xff,0xef,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xee,0xef, + 0xff,0x1f,0xf1,0x1f,0xff,0xf0,0xec,0xfc,0xce,0xf1,0xff,0xff,0xff,0x00, + 0xef,0xff,0xce,0xff,0x1f,0xff,0xf0,0x00,0xee,0xee,0xee,0xff,0xf1,0xff, + 0x00,0x00,0x0f,0xff,0x00,0xff,0xff,0xf0,0x00,0x00,0x0f,0xff,0x00,0x0f, + 0xff,0x00,0x00,0x00,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0xea,0xaa, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x05,0xff,0xff,0x05, + 0x05,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x05,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0x05,0xff, + 0xff,0x05,0x05,0xff,0xff,0x05,0x05,0xff,0xff,0xff,0xab,0xff,0xff,0xff, + 0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xab,0xab, + 0xab,0xff,0xff,0xff,0x05,0xff,0xff,0x05,0x05,0xff,0xff,0xff,0xff,0x00, + 0xab,0x2a,0xff,0x2a,0x2a,0xab,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0xab,0xff,0xff,0xff,0x2a,0xab,0xff,0xff,0x05,0xff,0xff,0xff, + 0xff,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xff,0xff,0xff,0x05, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, + 0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x03,0x71,0xf1,0xe4,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, + 0x03,0x71,0xee,0xb0,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, + 0xf3,0xdc,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf5,0x84, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf0,0x2c,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf0,0xe4 +}; +#endif /* NeXT */ +#endif /* HOST_ICON */ + +#ifndef HOST_ICON +#ifdef AIX +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* IBMicon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x20,0x03,0x9d, + 0x1f,0x00,0x00,0x00,0x00,0xf0,0x20,0xff,0xff,0x00,0x03,0x9d,0x27,0x00, + 0x00,0x00,0x00,0xf0,0x37,0xff,0x07,0x49,0x42,0x4d,0x69,0x63,0x6f,0x6e, + 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00, + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x1d,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x37,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x00,0x00,0x00,0xf0,0x3e,0xff,0xff,0x00,0x03,0x9f,0x1c,0x00,0x00,0x00, + 0x00,0xf0,0x3c,0xff,0xff,0x00,0x03,0x9f,0x84,0x00,0x00,0x00,0x00,0xf0, + 0x30,0xff,0xff,0x00,0x03,0xa0,0x12,0x00,0x00,0x00,0x00,0xf0,0x32,0xff, + 0xff,0x00,0x03,0xa5,0x06,0x00,0x00,0x00,0x00,0xf0,0x20,0xff,0xff,0x00, + 0x03,0xa6,0x0e,0x00,0x00,0x00,0x00,0xf0,0x3f,0xff,0xff,0x00,0x03,0xaf, + 0x41,0x00,0x00,0x00,0x00,0xf0,0x37,0xff,0xff,0x00,0x03,0xf6,0x52,0x00, + 0x00,0x00,0x00,0xf0,0x20,0xff,0xff,0x00,0x03,0xaf,0x5f,0x00,0x00,0x00, + 0x00,0xf0,0x20,0xff,0xff,0x00,0x03,0xaf,0x81,0x00,0x00,0x00,0x00,0xf0, + 0x37,0xff,0xff,0x00,0x03,0xb0,0x85,0x00,0x00,0x00,0x00,0xf0,0x38,0x01, + 0x8d,0x00,0x03,0xb1,0x00,0x00,0x02,0x00,0x06,0x66,0x60,0x06,0x66,0x66, + 0x66,0x00,0x00,0x66,0x66,0x60,0x00,0x66,0x66,0x60,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x66, + 0x60,0x06,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x60,0x00,0x66,0x66,0x60, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x66,0x00,0x00,0x66,0x00,0x00,0x66,0x60,0x06,0x66,0x60, + 0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x00,0x00,0x66,0x66,0x66,0x66, + 0x00,0x06,0x66,0x60,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x00,0x00, + 0x66,0x66,0x66,0x66,0x00,0x06,0x60,0x66,0x06,0x60,0x66,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x66,0x00,0x00,0x66,0x00,0x00,0x66,0x60,0x06,0x60,0x66,0x06,0x60, + 0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x06,0x66,0x60,0x06,0x66,0x66,0x66,0x66,0x00,0x66, + 0x60,0x06,0x66,0x00,0x66,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x66,0x60,0x06,0x66,0x66, + 0x66,0x00,0x00,0x66,0x60,0x00,0x60,0x00,0x66,0x60,0x0e,0xee,0xe0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77, + 0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xee,0xee,0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe7,0x77,0xf7,0x77,0x77,0xe0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x7f,0x77,0x77,0x77,0xe0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0xf7,0x7f,0x7f,0x77,0xe0, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77,0x77,0x77, + 0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xee,0xee, + 0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xab,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xde,0xeb,0xba,0xbb, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, + 0xa0,0xab,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, + 0xde,0xea,0x00,0x0a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, + 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0xec, + 0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec, + 0xec,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xec,0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec, + 0xec,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0xec,0xec, + 0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x00,0x00, + 0x00,0x00,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xec, + 0xec,0xec,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec, + 0xec,0xec,0xec,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0x00, + 0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec, + 0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00, + 0x00,0xec,0xec,0x00,0xec,0xec,0x00,0xec,0xec,0x00,0xec,0xec,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec, + 0x00,0x00,0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xec,0xec,0x00,0xec,0xec, + 0x00,0xec,0xec,0x00,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, + 0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec, + 0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xec,0xec, + 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0xec, + 0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0x00, + 0x00,0x00,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xab,0xab,0xab, + 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, + 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a, + 0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff,0x2a,0xff, + 0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0x33, + 0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, + 0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x79,0xfc, + 0x3e,0x3e,0x00,0x00,0x00,0x00,0x79,0xff,0x3e,0x3e,0x00,0x00,0x00,0x00, + 0x30,0xc3,0x9e,0x3c,0x00,0x00,0x00,0x00,0x30,0xff,0x1e,0x3c,0x00,0x00, + 0x00,0x00,0x30,0xff,0x1b,0x6c,0x00,0x00,0x00,0x00,0x30,0xc3,0x9b,0x6c, + 0x00,0x00,0x00,0x00,0x79,0xff,0x39,0xce,0x00,0x00,0x00,0x00,0x79,0xfc, + 0x38,0x8e,0x78,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0xff,0xe0,0x00,0x00, + 0x80,0x20,0x00,0x00,0x88,0x20,0x00,0x00,0x90,0x20,0x00,0x00,0xa5,0x20, + 0x00,0x00,0x80,0x20,0x00,0x00,0xff,0xe0,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x15,0x00, + 0x00,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, + 0x79,0xfc,0x3e,0x3e,0x79,0xfc,0x3e,0x3e,0x79,0xff,0x3e,0x3e,0x30,0xc3, + 0x1e,0x3c,0x30,0xc3,0x9e,0x3c,0x30,0xc3,0x1e,0x3c,0x30,0xff,0x1e,0x3c, + 0x30,0xfe,0x1a,0x2c,0x30,0xff,0x1b,0x6c,0x30,0xc3,0x1b,0x6c,0x30,0xc3, + 0x9b,0x6c,0x30,0xc3,0x19,0xcc,0x79,0xff,0x39,0xce,0x79,0xfc,0x38,0x8e, + 0x79,0xfc,0x38,0x8e,0x78,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0xff,0xe0, + 0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00, + 0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0x0e,0x00, + 0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, + 0xff,0xff,0x00,0x00,0x00,0x40,0xfe,0x77,0xff,0x77,0x49,0xf6,0x4f,0x76, + 0x4f,0x7e,0x49,0xfe,0xff,0x7b,0xfe,0x6b,0xfc,0x00,0xa4,0x00,0xf4,0x00, + 0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff,0xfe,0x77,0xff,0x77, + 0x49,0xf6,0x4f,0x76,0x4f,0x7e,0x49,0xfe,0xff,0x7b,0xfe,0x6b,0xfc,0x00, + 0xfc,0x00,0xfc,0x00,0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x80,0x66,0x66,0x66,0x60,0x06,0x66,0x06,0x66,0x66,0x66, + 0x66,0x66,0x06,0x66,0x06,0x66,0x06,0x00,0x60,0x06,0x66,0x66,0x06,0x60, + 0x06,0x00,0x66,0x66,0x06,0x66,0x06,0x60,0x06,0x00,0x66,0x66,0x06,0x66, + 0x66,0x60,0x06,0x00,0x60,0x06,0x66,0x66,0x66,0x60,0x66,0x66,0x66,0x66, + 0x06,0x66,0x60,0x66,0xee,0xe6,0x66,0x60,0x06,0x60,0x60,0x66,0xee,0xee, + 0xee,0x00,0x00,0x00,0x00,0x00,0xe7,0xf7,0x7e,0x00,0x00,0x00,0x00,0x00, + 0xef,0xff,0x7e,0x00,0x00,0x00,0x00,0x00,0xee,0xee,0xee,0x00,0x00,0x00, + 0x00,0x00,0x0f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0xee,0xbb,0xaa,0xaa,0xaa,0xaa,0xae,0xed,0xee,0xab, + 0xbb,0xbb,0xbb,0xbb,0xbe,0xed,0x00,0x00,0x01,0x00,0xec,0xec,0xec,0xec, + 0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec, + 0xec,0xec,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0x00,0xec,0xec,0xec, + 0x00,0xec,0x00,0x00,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0xec, + 0xec,0x00,0x00,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec, + 0x00,0xec,0xec,0x00,0x00,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0xec, + 0xec,0xec,0xec,0xec,0xec,0x00,0x00,0xec,0x00,0x00,0xec,0x00,0x00,0xec, + 0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec, + 0xec,0xec,0x00,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec, + 0xec,0xec,0xec,0x00,0x00,0xec,0xec,0x00,0xec,0x00,0xec,0xec,0xab,0xab, + 0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xab,0x2a,0xff,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xab,0xff,0xff,0xff,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x03,0x71,0xf1,0x94,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, + 0x03,0x71,0xee,0xb8,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, + 0xf1,0x8c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xe0,0xa8, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf5,0x58,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf5,0x64 +}; +#endif /* AIX */ +#endif /* HOST_ICON */ + +#ifndef HOST_ICON +#if defined(ultrix) || defined(vax) || defined(__alpha) +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* DIGITALicon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0xff,0xff,0xff,0xff,0x00,0x9c,0x00,0xd2,0x11,0x01,0xa0,0x00, + 0x82,0xa0,0x00,0x8c,0x01,0x00,0x0a,0xff,0xff,0xff,0xff,0x00,0x9c,0x00, + 0xd2,0x09,0x00,0x00,0x00,0x00,0x0b,0x44,0x49,0x47,0x49,0x54,0x41,0x4c, + 0x69,0x63,0x6f,0x6e,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53, + 0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x37,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x00,0x09,0x00,0x03,0x06,0x47,0x65,0x6e,0x65,0x76,0x61,0x03,0x00,0x03, + 0x0d,0x00,0x0c,0x2b,0x0e,0x40,0x1e,0x54,0x68,0x65,0x20,0x42,0x75,0x69, + 0x6c,0x74,0x2d,0x49,0x6e,0x20,0x44,0x69,0x67,0x69,0x74,0x69,0x7a,0x65, + 0x72,0x20,0x63,0x61,0x6e,0x6e,0x6f,0x74,0x20,0x2b,0x0a,0x10,0x1b,0x64, + 0x69,0x73,0x70,0x6c,0x61,0x79,0x20,0x76,0x69,0x64,0x65,0x6f,0x20,0x77, + 0x68,0x69,0x6c,0x65,0x20,0x69,0x6e,0x20,0x74,0x68,0x65,0x20,0x2b,0x02, + 0x10,0x19,0x63,0x75,0x72,0x72,0x65,0x6e,0x74,0x20,0x6e,0x75,0x6d,0x62, + 0x65,0x72,0x20,0x6f,0x66,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x73,0x2e,0xa0, + 0x00,0x97,0xa0,0x00,0x8d,0xa0,0x00,0x83,0xff,0x00,0x00,0x00,0xe4,0x00, + 0xe4,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x33,0x33,0x30,0x33,0x30,0x33, + 0x33,0x30,0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x33,0x33,0x33,0x30,0x33, + 0x30,0x33,0x33,0x30,0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x33,0x33,0x30, + 0x30,0x33,0x30,0x33,0x33,0x30,0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x03, + 0x33,0x30,0x30,0x30,0x30,0x33,0x33,0x30,0x30,0x30,0x30,0x30,0x33,0x33, + 0x03,0x03,0x33,0x30,0x30,0x33,0x30,0x33,0x33,0x30,0x33,0x30,0x30,0x30, + 0x30,0x03,0x03,0x03,0x30,0x00,0x30,0x30,0x30,0x30,0x00,0x30,0x30,0x30, + 0x00,0x00,0x33,0x30,0x03,0x03,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, + 0x30,0x30,0x30,0x30,0x30,0x00,0x03,0x03,0x30,0x30,0x30,0x30,0x30,0x30, + 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x03,0x03,0x30,0x00,0x30,0x30, + 0x30,0x30,0x00,0x30,0x30,0x30,0x33,0x00,0x30,0x00,0x03,0x03,0x33,0x33, + 0x30,0x33,0x30,0x33,0x30,0x30,0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x33, + 0x33,0x33,0x30,0x33,0x30,0x33,0x30,0x30,0x33,0x30,0x33,0x30,0x33,0x33, + 0x03,0x33,0x33,0x33,0x30,0x33,0x30,0x30,0x03,0x30,0x33,0x30,0x33,0x30, + 0x33,0x33,0x03,0x33,0x33,0x33,0x30,0x33,0x30,0x33,0x33,0x30,0x33,0x30, + 0x33,0x30,0x33,0x33,0x03,0x33,0x33,0x33,0x30,0x33,0x30,0x33,0x33,0x30, + 0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x33,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0xee,0xe0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77, + 0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xee,0xee,0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe7,0x77,0xf7,0x77,0x77,0xe0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x7f,0x77,0x77,0x77,0xe0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0xf7,0x7f,0x7f,0x77,0xe0, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77,0x77,0x77, + 0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xee,0xee, + 0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xad,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xde,0xeb,0xba,0xbb, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, + 0xa0,0xab,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, + 0xde,0xea,0x00,0x0a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, + 0xee,0xdd,0x00,0x00,0x04,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, + 0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, + 0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a, + 0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, + 0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a, + 0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a, + 0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a, + 0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, + 0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, + 0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00, + 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, + 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x00, + 0x00,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x00,0x6a,0x00, + 0x6a,0x00,0x6a,0x00,0x00,0x00,0x00,0x00,0x6a,0x6a,0x6a,0x00,0x00,0x6a, + 0x00,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, + 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, + 0x00,0x00,0x00,0x6a,0x00,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, + 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, + 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a,0x6a,0x00,0x00,0x00, + 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x00,0x6a,0x00,0x6a,0x00, + 0x6a,0x00,0x6a,0x6a,0x00,0x00,0x6a,0x00,0x00,0x00,0x00,0x6a,0x00,0x6a, + 0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, + 0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a, + 0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, + 0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, + 0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00, + 0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, + 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a, + 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00, + 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a, + 0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, + 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, + 0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0xab,0xab, + 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, + 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a, + 0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff,0x2a,0xff, + 0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0x33, + 0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, + 0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0xfb,0xbe, + 0xee,0xf7,0xfb,0xbe,0xee,0xf7,0xeb,0xbe,0xee,0xf5,0xea,0xbe,0xaa,0xf5, + 0xeb,0xbe,0xea,0x95,0x8a,0xa2,0xa0,0xe5,0xaa,0xaa,0xaa,0x85,0xaa,0xaa, + 0xaa,0xa5,0x8a,0xa2,0xac,0x85,0xfb,0xba,0xee,0xf7,0xfb,0xba,0xee,0xf7, + 0xfb,0xa6,0xee,0xf7,0xfb,0xbe,0xee,0xf7,0xfb,0xbe,0xee,0xf7,0x00,0x00, + 0x00,0x00,0x78,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0xff,0xe0,0x00,0x00, + 0x80,0x20,0x00,0x00,0x88,0x20,0x00,0x00,0x90,0x20,0x00,0x00,0xa5,0x20, + 0x00,0x00,0x80,0x20,0x00,0x00,0xff,0xe0,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x15,0x00, + 0x00,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0xff,0xe0, + 0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00, + 0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0x0e,0x00, + 0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, + 0xff,0xff,0x00,0x00,0x00,0x40,0xff,0xff,0xaf,0x5e,0xbf,0xce,0xa9,0x52, + 0x29,0x52,0xfd,0xff,0xfb,0xff,0xe0,0x00,0xfc,0x00,0xa4,0x00,0xf4,0x00, + 0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xfe,0xff,0xfe,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x00,0xfc,0x00, + 0xfc,0x00,0xfc,0x00,0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x80,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x30, + 0x33,0x33,0x03,0x03,0x33,0x30,0x30,0x33,0x33,0x33,0x33,0x00,0x33,0x30, + 0x30,0x30,0x30,0x03,0x03,0x03,0x00,0x30,0x00,0x30,0x30,0x03,0x03,0x03, + 0x00,0x30,0x33,0x33,0x33,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x33, + 0x33,0x33,0x33,0x33,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0xee,0xee, + 0xee,0x00,0x00,0x00,0x00,0x00,0xec,0xfc,0xce,0x00,0x00,0x00,0x00,0x00, + 0xef,0xff,0xce,0x00,0x00,0x00,0x00,0x00,0xee,0xee,0xee,0x00,0x00,0x00, + 0x00,0x00,0x0f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0xea,0xaa, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0x00,0x00,0x01,0x00,0x6a,0x6a,0x6a,0x6a, + 0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00, + 0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00, + 0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x00,0x6a,0x6a, + 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a,0x00,0x6a, + 0x00,0x00,0x6a,0x00,0x00,0x00,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a, + 0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a, + 0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00, + 0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0xab,0xab,0xab,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0xab, + 0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xab,0x2a,0xff,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xab,0xff,0xff,0xff,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x03,0x71,0xf0,0x9c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, + 0x03,0x71,0xf3,0x28,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, + 0xf4,0xfc,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf0,0x80, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf5,0x78,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf0,0x08 +}; +#endif /* ultrix || vax || __alpha */ +#endif /* HOST_ICON */ + +#ifndef HOST_ICON +#ifdef aux +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* AUXicon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x41,0x55,0x58,0x69,0x63,0x6f,0x6e, + 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00, + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x38,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x08,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x80,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x88,0x80,0x08,0x80,0x08,0x88,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x88,0x88,0x08,0x80,0x88, + 0x88,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x88,0x88,0x88,0x88, + 0x88,0x88,0x88,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x11,0x11, + 0x11,0x11,0x11,0x11,0x11,0x11,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x11, + 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x10,0x00,0x00,0x00,0x00,0x00, + 0x01,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x10,0x00,0x00,0x00,0x00, + 0x00,0x00,0x02,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x22,0x22,0x22,0x22,0x22,0x22,0x22, + 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x00,0x0e,0xee,0xe0,0x00, + 0x00,0x04,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x40,0x00,0xe7,0x77, + 0x7e,0x00,0x00,0x04,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x40,0x00, + 0xee,0xee,0xee,0xee,0xee,0xe0,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44, + 0x00,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x00,0x66,0x66,0x66,0x66,0x66, + 0x66,0x60,0x00,0x00,0xe7,0x77,0xf7,0x77,0x77,0xe0,0x00,0x06,0x66,0x60, + 0x66,0x66,0x66,0x00,0x00,0x00,0xe7,0x7f,0x77,0x77,0x77,0xe0,0x00,0x00, + 0x66,0x00,0x06,0x66,0x00,0x00,0x00,0x00,0xe7,0xf7,0x7f,0x7f,0x77,0xe0, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77,0x77,0x77, + 0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xee,0xee, + 0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xad,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xde,0xeb,0xba,0xbb, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, + 0xa0,0xab,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, + 0xde,0xea,0x00,0x0a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, + 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3, + 0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0x00,0x00,0xe3,0xe3,0x00,0x00,0xe3, + 0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0x00,0xe3, + 0xe3,0x00,0xe3,0xe3,0xe3,0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0xe3, + 0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x05, + 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, + 0x05,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, + 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, + 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0x17,0x17,0x17, + 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17, + 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, + 0x17,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, + 0x17,0x17,0x17,0x17,0x17,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0xab,0xab,0xab, + 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00, + 0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, + 0xab,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec, + 0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0xab,0x2a, + 0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0xec, + 0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00, + 0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00, + 0x00,0x00,0x00,0x00,0xec,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff,0x2a,0xff, + 0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0x33, + 0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, + 0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x18,0x00,0x00,0x07,0x99,0xc0, + 0x00,0x0f,0xdb,0xe0,0x00,0x1f,0xff,0xf0,0x00,0x3f,0xff,0xf8,0x00,0x3f, + 0xff,0xf8,0x00,0x7f,0xff,0xe0,0x00,0x7f,0xff,0xc0,0x00,0x7f,0xff,0xc0, + 0x00,0x7f,0xff,0xc0,0x00,0x7f,0xff,0xc0,0x00,0x3f,0xff,0xe0,0x00,0x3f, + 0xff,0xf8,0x78,0x1f,0xff,0xf8,0x84,0x1f,0xff,0xf8,0xff,0xef,0xff,0xf0, + 0x80,0x23,0xff,0xe0,0x88,0x21,0xef,0xc0,0x90,0x20,0xc7,0x00,0xa5,0x20, + 0x00,0x00,0x80,0x20,0x00,0x00,0xff,0xe0,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x15,0x00, + 0x00,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, + 0x00,0x00,0x06,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x18,0x00,0x00,0x07, + 0x99,0xc0,0x00,0x0f,0xdb,0xe0,0x00,0x1f,0xff,0xf0,0x00,0x3f,0xff,0xf8, + 0x00,0x3f,0xff,0xf8,0x00,0x7f,0xff,0xe0,0x00,0x7f,0xff,0xc0,0x00,0x7f, + 0xff,0xc0,0x00,0x7f,0xff,0xc0,0x00,0x7f,0xff,0xc0,0x00,0x3f,0xff,0xe0, + 0x00,0x3f,0xff,0xf8,0x78,0x1f,0xff,0xf8,0xfc,0x0f,0xff,0xf8,0xff,0xe7, + 0xff,0xf0,0xff,0xe3,0xff,0xe0,0xff,0xe1,0xef,0xc0,0xff,0xe0,0xc7,0x00, + 0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0x0e,0x00, + 0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, + 0xff,0xff,0x00,0x00,0x00,0x40,0x00,0x30,0x03,0xf8,0x07,0xfc,0x07,0xfe, + 0x0f,0xfc,0x0f,0xf8,0x0f,0xfc,0xe7,0xfe,0xff,0xfe,0xa5,0xfc,0xf4,0xb0, + 0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff,0x00,0x30,0x03,0xf8, + 0x07,0xfc,0x07,0xfe,0x0f,0xfc,0x0f,0xf8,0x0f,0xfc,0xe7,0xfe,0xff,0xfe, + 0xfd,0xfc,0xfc,0xb0,0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x00, + 0x00,0x88,0x88,0x88,0x80,0x00,0x00,0x00,0x08,0x88,0x88,0x88,0x88,0x00, + 0x00,0x00,0x01,0x11,0x11,0x11,0x11,0x10,0x00,0x00,0x22,0x22,0x22,0x22, + 0x21,0x00,0x00,0x00,0x22,0x22,0x22,0x22,0x20,0x00,0x00,0x00,0x33,0x33, + 0x33,0x33,0x33,0x00,0xee,0xe0,0x04,0x44,0x44,0x44,0x44,0x40,0xee,0xee, + 0xee,0x44,0x44,0x44,0x44,0x40,0xe7,0xf7,0x7e,0x06,0x66,0x66,0x66,0x00, + 0xef,0xff,0x7e,0x00,0x60,0x66,0x00,0x00,0xee,0xee,0xee,0x00,0x00,0x00, + 0x00,0x00,0x0f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0xee,0xbb,0xaa,0xaa,0xaa,0xaa,0xae,0xed,0xee,0xab, + 0xbb,0xbb,0xbb,0xbb,0xbe,0xed,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x05,0x05,0x05,0x05,0x05,0x05, + 0x05,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0x17,0x17,0x17,0x17, + 0x17,0x17,0x17,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0x17,0x17, + 0x17,0x17,0x17,0x17,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xab,0xab,0xab,0x00, + 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0xab,0xab, + 0xab,0xab,0xab,0xab,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00, + 0xab,0x2a,0xff,0x2a,0x2a,0xab,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec, + 0x00,0x00,0xab,0xff,0xff,0xff,0x2a,0xab,0x00,0x00,0xec,0x00,0xec,0xec, + 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x03,0x71,0xd6,0x90,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, + 0x03,0x71,0xf2,0x24,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, + 0xf4,0xd8,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf6,0x48, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf5,0x2c,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xef,0xbc +}; +#endif /* aux */ +#endif /* HOST_ICON */ + +#ifndef HOST_ICON +#ifdef sgi +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* SGIicon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x08,0x00,0x09,0x37,0x07,0xe7,0x0b,0x68,0x0d,0x2b,0x08,0xc8, + 0x04,0x62,0x05,0x53,0x05,0x53,0x0c,0x49,0x0b,0xce,0x08,0xe5,0x07,0x1a, + 0x05,0x53,0x09,0xb2,0x08,0xc8,0x07,0x53,0x47,0x49,0x69,0x63,0x6f,0x6e, + 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00, + 0x00,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00,0x00,0x68,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x38,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x06,0x39,0x06,0x39,0x06,0x39,0x0d,0x53,0x0d,0x53,0x0c,0xa3,0x0d,0x53, + 0x0c,0x72,0x0c,0x72,0x0c,0x72,0x05,0x53,0x05,0x53,0x05,0x53,0x05,0x53, + 0x05,0x53,0x05,0x53,0x08,0xe5,0x05,0x53,0x05,0x53,0x05,0x53,0x05,0x53, + 0x04,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03, + 0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05, + 0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03, + 0x04,0x05,0x02,0x03,0x04,0x05,0x00,0x09,0x08,0x50,0x61,0x6c,0x61,0x74, + 0x69,0x6e,0x6f,0x02,0x06,0x07,0x02,0x06,0x08,0x02,0x06,0x09,0x03,0x06, + 0x08,0x09,0x01,0x2d,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x55,0x50,0x00,0x55,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x05,0x55,0x55,0x05,0x55,0x55,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x55,0x50,0x55,0x05,0x50,0x55,0x50,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x55,0x00,0x55,0x05,0x50,0x05,0x55, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x50,0x00,0x55,0x05,0x50, + 0x00,0x55,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x00,0x00,0x55, + 0x05,0x50,0x00,0x05,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x55,0x50, + 0x00,0x55,0x05,0x50,0x00,0x55,0x50,0x50,0x00,0x00,0x00,0x00,0x05,0x55, + 0x05,0x55,0x00,0x55,0x05,0x50,0x05,0x55,0x05,0x55,0x00,0x00,0x00,0x00, + 0x05,0x55,0x50,0x55,0x50,0x55,0x05,0x50,0x55,0x50,0x55,0x55,0x00,0x00, + 0x00,0x00,0x05,0x55,0x55,0x05,0x55,0x05,0x05,0x05,0x55,0x05,0x55,0x55, + 0x00,0x00,0x00,0x00,0x05,0x50,0x55,0x50,0x55,0x50,0x00,0x55,0x50,0x55, + 0x50,0x55,0x00,0x00,0x00,0x00,0x05,0x50,0x05,0x55,0x05,0x55,0x05,0x55, + 0x05,0x55,0x00,0x55,0x00,0x00,0x00,0x00,0x05,0x50,0x00,0x55,0x50,0x55, + 0x55,0x50,0x55,0x50,0x00,0x55,0x0e,0xee,0xe0,0x00,0x05,0x50,0x00,0x05, + 0x55,0x05,0x55,0x05,0x55,0x00,0x00,0x55,0xe7,0x77,0x7e,0x00,0x05,0x50, + 0x00,0x50,0x55,0x50,0x50,0x55,0x50,0x50,0x00,0x55,0xee,0xee,0xee,0xee, + 0xee,0xee,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0x55,0xe7,0x77, + 0x77,0x77,0x77,0x7e,0x55,0x50,0x50,0x55,0x05,0x50,0x50,0x55,0x50,0x55, + 0xe7,0x77,0x77,0x7f,0x77,0x7e,0x55,0x05,0x55,0x55,0x05,0x55,0x55,0x05, + 0x55,0x55,0xe7,0x77,0x77,0xf7,0x77,0x7e,0x50,0x55,0x50,0x55,0x05,0x50, + 0x55,0x50,0x55,0x55,0xe7,0x77,0x7f,0x77,0x77,0x7e,0x05,0x55,0x00,0x55, + 0x05,0x50,0x05,0x55,0x05,0x55,0xe7,0x77,0xf7,0x77,0x77,0x7e,0x55,0x50, + 0x00,0x55,0x05,0x50,0x00,0x55,0x50,0x50,0xe7,0x7f,0x77,0xf7,0xf7,0x7e, + 0x55,0x00,0x00,0x55,0x05,0x50,0x00,0x05,0x50,0x00,0xe7,0x77,0x77,0x77, + 0x77,0x7e,0x55,0x50,0x00,0x55,0x05,0x50,0x00,0x55,0x50,0x00,0xee,0xee, + 0xee,0xee,0xee,0xee,0x05,0x55,0x00,0x55,0x05,0x50,0x05,0x55,0x00,0x00, + 0x00,0x00,0x0a,0xca,0x00,0x00,0x00,0x55,0x50,0x55,0x05,0x50,0x55,0x50, + 0x00,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x05,0x55,0x55,0x05,0x55, + 0x55,0x00,0x00,0x00,0x00,0x00,0xf3,0x33,0xf0,0x00,0x00,0x00,0x55,0x50, + 0x00,0x55,0x50,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0xab,0xa0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0xee,0xbb,0xab, + 0xba,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcc,0xbb, + 0xba,0x0a,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, + 0xdd,0xee,0xa0,0x00,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, + 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00, + 0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0, + 0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00, + 0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0, + 0x00,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00,0x00, + 0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x00, + 0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00, + 0xb0,0xb0,0xb0,0x00,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0x00,0xb0, + 0xb0,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00, + 0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0, + 0x00,0xb0,0xb0,0xb0,0x00,0xb0,0x00,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0, + 0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0, + 0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0, + 0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, + 0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0, + 0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00, + 0xb0,0xb0,0x00,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00, + 0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, + 0x00,0x00,0x00,0x00,0xb0,0xb0,0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00, + 0x00,0xb0,0xb0,0x00,0x00,0x00,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0x00, + 0xb0,0xb0,0xb0,0x00,0xb0,0x00,0x00,0x00,0xb0,0xb0,0xab,0xab,0xab,0xab, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xb0,0xb0,0xb0,0x00,0xb0, + 0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0xb0,0xb0, + 0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0xb0,0xb0, + 0xb0,0x00,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0x00,0xb0,0xb0, + 0xb0,0x00,0xb0,0xb0,0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xfe,0x2a,0x2a, + 0x2a,0xab,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, + 0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, + 0xfe,0x2a,0x2a,0x2a,0x2a,0xab,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0, + 0x00,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xab,0x2a, + 0x2a,0x2a,0x2a,0xfe,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xb0,0xb0,0xb0, + 0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0, + 0xb0,0xb0,0xab,0x2a,0x2a,0x2a,0xfe,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab, + 0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00, + 0xb0,0xb0,0xb0,0x00,0xb0,0x00,0xab,0x2a,0x2a,0xfe,0x2a,0x2a,0xfe,0x2a, + 0xfe,0x2a,0x2a,0xab,0xb0,0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00,0xb0, + 0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0xb0,0xb0,0xb0,0x00,0x00,0x00, + 0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xb0, + 0xb0,0xb0,0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0xb0, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0xf8,0xfd,0x00,0x00, + 0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00, + 0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0, + 0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33, + 0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xf9,0xfb,0xfb, + 0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, + 0xf6,0xf6,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0xf6,0xf6,0xf9,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x00, + 0xe3,0x80,0x00,0x01,0xf7,0xc0,0x00,0x03,0xb6,0xe0,0x00,0x07,0x36,0x70, + 0x00,0x0e,0x36,0x38,0x00,0x0c,0x36,0x18,0x00,0x2e,0x36,0x3a,0x00,0x77, + 0x36,0x77,0x00,0x7b,0xb6,0xef,0x00,0x7d,0xd5,0xdf,0x00,0x6e,0xe3,0xbb, + 0x00,0x67,0x77,0x73,0x00,0x63,0xbe,0xe3,0x78,0x61,0xdd,0xc3,0x84,0x62, + 0xeb,0xa3,0xff,0xf7,0x77,0x73,0x80,0x1e,0xb6,0xbb,0x81,0x1d,0xf7,0xdf, + 0x82,0x1b,0xb6,0xef,0x84,0x17,0x36,0x77,0x88,0x1e,0x36,0x3a,0x92,0x9c, + 0x36,0x18,0x80,0x1e,0x36,0x38,0xff,0xf7,0x36,0x70,0x05,0x03,0xb6,0xe0, + 0x0f,0x81,0xf7,0xc0,0x0f,0x80,0xe3,0x80,0x0f,0x80,0x00,0x00,0x0a,0x80, + 0x00,0x00,0x32,0x7f,0xff,0xfc,0x05,0x00,0x00,0x00,0x38,0xff,0xff,0xfc, + 0x00,0x00,0xe3,0x80,0x00,0x01,0xf7,0xc0,0x00,0x03,0xb6,0xe0,0x00,0x07, + 0x36,0x70,0x00,0x0e,0x36,0x38,0x00,0x0c,0x36,0x18,0x00,0x2e,0x36,0x3a, + 0x00,0x77,0x36,0x77,0x00,0x7b,0xb6,0xef,0x00,0x7d,0xd5,0xdf,0x00,0x6e, + 0xe3,0xbb,0x00,0x67,0x77,0x73,0x00,0x63,0xbe,0xe3,0x78,0x61,0xdd,0xc3, + 0xfc,0x62,0xeb,0xa3,0xff,0xf7,0x77,0x73,0xff,0xfe,0xb6,0xbb,0xff,0xfd, + 0xf7,0xdf,0xff,0xfb,0xb6,0xef,0xff,0xf7,0x36,0x77,0xff,0xfe,0x36,0x3a, + 0xff,0xfc,0x36,0x18,0xff,0xfe,0x36,0x38,0xff,0xf7,0x36,0x70,0x07,0x03, + 0xb6,0xe0,0x0f,0x81,0xf7,0xc0,0x0f,0x80,0xe3,0x80,0x0f,0x80,0x00,0x00, + 0x0f,0x80,0x00,0x00,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xf8,0xff, + 0xff,0xff,0x00,0x00,0x00,0x40,0x01,0xf8,0x03,0xfc,0x07,0x76,0x0f,0x77, + 0x0f,0xff,0x0f,0xff,0xed,0xdd,0xfd,0xfd,0x97,0xff,0xb7,0xff,0xff,0x77, + 0xff,0x76,0x39,0xfc,0x38,0xd8,0x7f,0xfe,0x7f,0xfe,0x01,0xf8,0x03,0xfc, + 0x03,0xfe,0x0f,0xff,0x0f,0xff,0x0f,0xff,0xef,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xfe,0x3f,0xfc,0x3f,0xf8,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x05,0x55,0x55,0x50,0x00,0x00,0x00, + 0x00,0x55,0x55,0x55,0x55,0x00,0x00,0x00,0x05,0x55,0x05,0x55,0x05,0x50, + 0x00,0x00,0x55,0x55,0x05,0x55,0x05,0x55,0x00,0x00,0x55,0x55,0x55,0x55, + 0x55,0x55,0x00,0x00,0x55,0x55,0x55,0x55,0x55,0x55,0xee,0xe0,0x55,0x05, + 0x55,0x05,0x55,0x05,0xee,0xee,0x55,0x05,0x55,0x55,0x55,0x05,0xec,0xcf, + 0xce,0x55,0x55,0x55,0x55,0x55,0xec,0xff,0xce,0x55,0x55,0x55,0x55,0x55, + 0xef,0xff,0xfe,0x55,0x05,0x55,0x05,0x55,0xee,0xee,0xee,0x55,0x05,0x55, + 0x05,0x50,0x00,0xff,0xf0,0x05,0x55,0x55,0x55,0x00,0x00,0xff,0xf0,0x00, + 0x55,0x05,0x50,0x00,0xde,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0xde,0xaa, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0, + 0xb0,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, + 0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0, + 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0, + 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xab,0xab,0xab,0x00,0xb0,0xb0, + 0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xab,0xab,0xab,0xab, + 0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xab,0x2a, + 0x2a,0xfe,0x2a,0xab,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0, + 0xab,0x2a,0xfe,0xfe,0x2a,0xab,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0, + 0xb0,0xb0,0xab,0xfe,0xfe,0xfe,0xfe,0xab,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, + 0x00,0xb0,0xb0,0xb0,0xab,0xab,0xab,0xab,0xab,0xab,0xb0,0xb0,0x00,0xb0, + 0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0xb0, + 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00, + 0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0xf9,0xfb,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xf9,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x00,0xec,0xa7,0x58,0x1f,0x38,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, + 0x00,0xec,0xb2,0x44,0xbf,0xb9,0xff,0xff,0x00,0x00,0x06,0x08,0x00,0xec, + 0xa7,0xa0,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x00,0x00,0x00,0x00, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x00,0x00,0x00,0x00,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x07,0xd4,0x00,0x00,0x00,0x00 +}; +#endif /* sgi */ +#endif /* HOST_ICON */ + +#ifndef HOST_ICON +#ifdef hpux +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* HPicon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x66,0x66,0x00,0x5e,0x99,0x99,0x66,0x66,0x33,0x33,0x00,0x5f, + 0x99,0x99,0x66,0x66,0x00,0x00,0x00,0x60,0x99,0x99,0x33,0x33,0xff,0xff, + 0x00,0x61,0x99,0x99,0x33,0x33,0x06,0x48,0x50,0x69,0x63,0x6f,0x6e,0x48, + 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00, + 0x00,0xd0,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00,0x00,0xd0,0x00,0x80,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x6d,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x00,0x00,0x00,0x00,0x00,0x6c,0x66,0x66,0xff,0xff,0xff,0xff,0x00,0x6d, + 0x66,0x66,0xff,0xff,0xcc,0xcc,0x00,0x6e,0x66,0x66,0xff,0xff,0x99,0x99, + 0x00,0x6f,0x66,0x66,0xff,0xff,0x66,0x66,0x00,0x70,0x66,0x66,0xff,0xff, + 0x33,0x33,0x00,0x71,0x66,0x66,0xff,0xff,0x00,0x00,0x00,0x72,0x66,0x66, + 0xcc,0xcc,0xff,0xff,0x00,0x73,0x66,0x66,0xcc,0xcc,0xcc,0xcc,0x00,0x74, + 0x66,0x66,0xcc,0xcc,0x99,0x99,0x00,0x75,0x66,0x66,0xcc,0xcc,0x66,0x66, + 0x00,0x76,0x66,0x66,0xcc,0xcc,0x33,0x33,0x00,0x77,0x66,0x66,0xcc,0xcc, + 0x00,0x00,0x00,0x78,0x66,0x66,0x99,0x99,0xff,0xff,0x00,0x79,0x66,0x66, + 0x99,0x99,0xcc,0xcc,0x00,0x7a,0x66,0x66,0x99,0x99,0x99,0x99,0x00,0x7b, + 0x66,0x66,0x99,0x99,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0xec,0xec,0xec,0xec,0x00,0x00, + 0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, + 0x7f,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, + 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0xec,0x7f,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0x7f,0x7f,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0xec, + 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0x00, + 0xec,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0x00, + 0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, + 0xec,0xec,0xec,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0xec, + 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, + 0xec,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0xec,0xec,0x00,0x00, + 0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0xec, + 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0xec,0xec,0xec,0xec,0xec,0xec, + 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0xec,0xec,0xfd,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f, + 0xec,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,0x7f,0x7f,0xec,0xfd,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0xec,0xfd,0x33,0xfd,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0xec,0xff,0xff,0xff, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f, + 0xec,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,0xec,0xec,0xec,0xec,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd, + 0x33,0xfd,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xf9, + 0xfb,0xfb,0xfb,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb, + 0xf9,0xf9,0xf7,0xf7,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0xf7,0xf7,0xf9,0xf9,0xfb,0xfb,0xfb,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0x00,0x00, + 0x00,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf5,0x55,0xf0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf5,0x5f,0xf0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x55,0x5f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0x55,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xf5,0x55,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x0f,0xf5,0x5f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x55,0x5f,0xff,0xff,0x00,0x00,0x0f,0xff,0xff, + 0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x55,0x55,0x55,0x5f,0x00,0x00,0xff, + 0x55,0x55,0x55,0xf0,0x00,0x00,0x00,0x00,0xf5,0x55,0xff,0x55,0x55,0xf0, + 0x00,0xf5,0x55,0x55,0x55,0x5f,0x00,0x00,0x00,0x0f,0xf5,0x5f,0xf0,0xf5, + 0x55,0xf0,0x0f,0xf5,0x5f,0xff,0x55,0x5f,0x00,0x00,0x00,0x0f,0x55,0x5f, + 0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f,0x00,0xf5,0x5f,0x00,0x00,0x00,0xff, + 0x55,0xff,0x0f,0xf5,0x5f,0xf0,0xff,0x55,0xff,0x00,0xf5,0x5f,0x00,0x00, + 0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f,0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f, + 0x00,0x00,0x0f,0xf5,0x5f,0xf0,0xff,0x55,0xff,0x0f,0xf5,0x5f,0xf0,0xff, + 0x55,0xff,0x00,0x00,0x0f,0x55,0x5f,0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f, + 0x00,0xf5,0x55,0xf0,0x00,0x00,0xff,0x55,0xff,0x0f,0xf5,0x5f,0xf0,0xff, + 0x55,0xff,0x0f,0xf5,0x5f,0xf0,0x00,0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f, + 0x00,0xf5,0x55,0xff,0xff,0x55,0x5f,0x00,0x00,0x0f,0xf5,0x5f,0xf0,0xff, + 0x55,0xff,0x0f,0xf5,0x55,0x55,0x55,0x55,0xff,0x00,0x00,0x0f,0xff,0xff, + 0x00,0xff,0xff,0xf0,0x0f,0x55,0x5f,0xff,0xff,0xff,0xf0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x55,0xff,0xa0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x55,0xfb,0xa0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf5,0x5f,0xab,0xa0, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf5,0x5f, + 0xab,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f, + 0x55,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0x55,0xff,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0xff,0xff,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, + 0x00,0x00,0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab,0xba,0xbb, + 0xaa,0xae,0xee,0xdd,0xcc,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb, + 0xa0,0xab,0xbb,0xbb,0xbb,0xcc,0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa, + 0xaa,0xaa,0x00,0x0a,0xaa,0xae,0xee,0xdd,0x00,0x00,0x01,0x00,0x00,0x0f, + 0x80,0x00,0x00,0x08,0x80,0x00,0x00,0x19,0x80,0x00,0x00,0x11,0x00,0x00, + 0x00,0x33,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x47, + 0xc1,0xfc,0x00,0xc0,0x43,0x02,0x00,0x8c,0x22,0x01,0x01,0x9a,0x26,0x71, + 0x01,0x12,0x24,0x49,0x03,0x36,0x6c,0xc9,0x02,0x24,0x48,0x91,0x06,0x6c, + 0xd9,0xb3,0x04,0x48,0x91,0x22,0x0c,0xd9,0xb3,0x66,0x08,0x91,0x23,0xc4, + 0x19,0xb3,0x60,0x0c,0x1f,0x3e,0x47,0xf8,0x00,0x00,0x4e,0x00,0x00,0x00, + 0xca,0x00,0x00,0x00,0x9a,0x00,0x00,0x01,0x9a,0x00,0x00,0x01,0x3f,0x00, + 0x00,0x03,0x31,0x00,0x00,0x03,0xf1,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x15,0x00,0xbf,0xff,0xe4,0xfd,0x00,0x00,0x0a,0x00,0xbf,0xff,0xf1,0xfd, + 0x00,0x0f,0x80,0x00,0x00,0x0f,0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f, + 0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x7e,0x00,0x00, + 0x00,0x7f,0xc1,0xfc,0x00,0xff,0xc3,0xfe,0x00,0xff,0xe3,0xff,0x01,0xfb, + 0xe7,0xff,0x01,0xf3,0xe7,0xcf,0x03,0xf7,0xef,0xcf,0x03,0xe7,0xcf,0x9f, + 0x07,0xef,0xdf,0xbf,0x07,0xcf,0x9f,0x3e,0x0f,0xdf,0xbf,0x7e,0x0f,0x9f, + 0x3f,0xfc,0x1f,0xbf,0x7f,0xfc,0x1f,0x3e,0x7f,0xf8,0x00,0x00,0x7e,0x00, + 0x00,0x00,0xfe,0x00,0x00,0x00,0xfe,0x00,0x00,0x01,0xfe,0x00,0x00,0x01, + 0xff,0x00,0x00,0x03,0xff,0x00,0x00,0x03,0xff,0x00,0x00,0x00,0x1f,0x00, + 0x00,0x00,0x1f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff, + 0xf1,0xff,0x00,0x00,0x00,0x40,0x03,0x80,0x07,0x80,0x05,0x00,0x0f,0x9e, + 0x0a,0xd1,0x1f,0x7f,0x17,0xef,0x3e,0xfd,0x2f,0xdf,0x7f,0xfe,0x00,0xb0, + 0x01,0xf0,0x01,0x70,0x01,0xf0,0xff,0xff,0xff,0xff,0x03,0x80,0x07,0x80, + 0x07,0x00,0x0f,0x9e,0x0f,0xdf,0x1f,0xff,0x1f,0xff,0x3f,0xff,0x3f,0xff, + 0x7f,0xfe,0x00,0xf0,0x01,0xf0,0x01,0xf0,0x01,0xf0,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x66,0x60,0x00,0x00,0x00,0x00,0x00, + 0x06,0x66,0x60,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,0x00, + 0x00,0x00,0x66,0x66,0x60,0x06,0x66,0x60,0x00,0x00,0x60,0x60,0x66,0x06, + 0x00,0x06,0x00,0x06,0x66,0x66,0x06,0x66,0x66,0x66,0x00,0x06,0x06,0x66, + 0x66,0x60,0x66,0x66,0x00,0x66,0x66,0x60,0x66,0x66,0x66,0x06,0x00,0x60, + 0x66,0x66,0x66,0x06,0x66,0x66,0x06,0x66,0x66,0x66,0x66,0x66,0x66,0x60, + 0x00,0x00,0x00,0x00,0x60,0x6f,0x00,0x00,0x00,0x00,0x00,0x06,0x66,0xff, + 0x00,0x00,0x00,0x00,0x00,0x06,0x06,0xff,0x00,0x00,0x00,0x00,0x00,0x06, + 0x66,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xec,0x00,0xec,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0xec, + 0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0xec,0x00,0xec,0x00,0xec,0xec, + 0x00,0xec,0x00,0x00,0x00,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec, + 0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0xec,0x00,0xec, + 0xec,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0xec,0xec, + 0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0xec,0x00,0x00, + 0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec, + 0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec, + 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x00,0xec,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0xec, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, + 0x00,0xec,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xec,0xec,0xec,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x00,0xec,0xa7,0x58,0x1f,0x96,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x38,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x34, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0xec,0x9e,0x9c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x04,0x04, + 0x00,0x00,0x00,0x00,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x00,0xec, + 0x94,0x14,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x00,0x00,0x00,0x00, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x00,0x00,0x00,0x00,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x07,0xd4,0x00,0x00,0x00,0x00 +}; +#endif /* hpux */ +#endif /* HOST_ICON */ + +#ifndef HOST_ICON +#ifdef linux +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* LINUXicon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x66,0xff,0x0c,0x0c,0x0c,0x0f,0x00,0x00,0x00,0x0f,0xc0,0xc0, + 0xc0,0xcf,0x60,0x66,0x00,0x0f,0xc0,0xc0,0xc0,0xcf,0x00,0x00,0x00,0x0f, + 0x0c,0x0c,0x0c,0x0f,0x66,0x00,0x09,0x4c,0x49,0x4e,0x55,0x58,0x69,0x63, + 0x6f,0x6e,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, + 0x05,0x00,0x00,0x68,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00,0x00,0x68,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x39,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0xcc,0xcc,0xcc,0xc3,0xcf,0xc0,0xc0,0xcf,0x00,0x00,0x00,0x0f,0x0c,0x0c, + 0x0f,0xcc,0xcc,0xcc,0xcc,0xcc,0xcf,0x0c,0x0c,0x0f,0x00,0x00,0x00,0x0f, + 0xc0,0xc0,0xcf,0xff,0xff,0xff,0xff,0xff,0xff,0xc0,0xc0,0xcf,0x00,0x00, + 0x00,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f, + 0x00,0x00,0x00,0x0f,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, + 0xc0,0xcf,0x00,0x00,0x00,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, + 0x0c,0x0c,0x0c,0x0f,0x00,0x00,0x00,0x0f,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, + 0xc0,0xc0,0xc0,0xc0,0xc0,0xcf,0x00,0x00,0x00,0x0f,0x0c,0x0c,0x0c,0x0c, + 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x00,0x00,0x00,0x0f,0xc0,0xc0, + 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xf0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x22,0xff,0xfc,0xcc,0xcf, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0xff,0xcc, + 0xff,0xcc,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0xcc,0xff,0xcc,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xfc,0xcc,0xcf,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xfc,0xcc,0xcf,0xff,0xff,0xf0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xcf,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, + 0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf3,0x78,0xf3, + 0x78,0xf3,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0xff,0xff,0xff,0xff,0xff,0x00,0x0e,0xee,0xe0,0x00,0x00,0x00,0x00,0x00, + 0x0f,0xcc,0xff,0xff,0xff,0xf7,0xf8,0xf0,0xe7,0x77,0x7e,0x00,0x00,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xee,0xee,0xee,0xee, + 0xee,0xee,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0xff,0xe7,0x77, + 0x77,0x77,0x77,0x7e,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0, + 0xe7,0x77,0x77,0x7f,0x77,0x7e,0x00,0x0f,0xff,0xff,0xff,0xff,0xff,0xff, + 0xf0,0x0f,0xe7,0x77,0x77,0xf7,0x77,0x7e,0x00,0x00,0xfc,0xcc,0xff,0xff, + 0xff,0xff,0xf0,0x00,0xe7,0x77,0x7f,0x77,0x77,0x7e,0x00,0x00,0xfc,0xcc, + 0xff,0xff,0xff,0xff,0xf0,0x00,0xe7,0x77,0xf7,0x77,0x77,0x7e,0x00,0x00, + 0xfc,0xcc,0xff,0xff,0xff,0xff,0xf0,0x00,0xe7,0x7f,0x77,0xf7,0xf7,0x7e, + 0x00,0x00,0xfc,0xcc,0xff,0xff,0xff,0xff,0xf0,0x00,0xe7,0x77,0x77,0x77, + 0x77,0x7e,0x00,0x00,0xfc,0xcc,0xff,0xff,0xff,0xff,0xf0,0x00,0xee,0xee, + 0xee,0xee,0xee,0xee,0x00,0x00,0xfc,0xcc,0xff,0xff,0xff,0xff,0xf0,0x00, + 0x00,0x00,0x0a,0xba,0x00,0x00,0x00,0x00,0x0f,0xcc,0xff,0xff,0xff,0xff, + 0xf0,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00,0x0f,0xcc,0xcf,0xff, + 0xff,0xff,0xf0,0x00,0x00,0x00,0xf3,0x33,0xf0,0x00,0x00,0x00,0x00,0xfc, + 0xcc,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00, + 0x0f,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0xab,0xab,0xa0,0x00, + 0x00,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xde,0xea,0xbb,0xab, + 0xba,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, + 0xba,0x0a,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, + 0xde,0xea,0xa0,0x00,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, + 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0x17,0xff,0xff,0xff,0x2b, + 0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17, + 0xff,0xff,0x2b,0x2b,0xff,0xff,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0xff,0x2b,0x2b,0xff,0xff,0x2b,0x2b,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0x2b, + 0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0x2b,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8, + 0xc0,0xe3,0xff,0xd8,0xc0,0xe3,0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xc0,0xff,0xe3,0xff,0x00,0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00, + 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xab,0xab,0xab,0xab, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff, + 0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0x00,0xff,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xff,0x2a,0x2a, + 0x2a,0xab,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, + 0xff,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0x2b, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xab,0x2a, + 0x2a,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00, + 0xff,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, + 0x00,0x00,0xab,0x2a,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab, + 0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0xff,0x2a, + 0xff,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0x2b,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0xff,0x2b, + 0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00, + 0x00,0x00,0xff,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b, + 0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33, + 0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0xfd, + 0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, + 0xf7,0x33,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0xfd,0x00,0x00,0x00,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0xc0,0x00,0x00,0x3f,0xe0,0x00,0x00,0xff,0xf0, + 0x00,0x07,0xe1,0xf0,0x00,0x03,0xcc,0xf8,0x00,0x00,0xcc,0xf8,0x00,0x00, + 0x21,0xf8,0x00,0x00,0x21,0xf8,0x00,0x00,0x17,0xf0,0x00,0x00,0x3f,0xf8, + 0x00,0x00,0x62,0x28,0x00,0x00,0x3f,0xfc,0x78,0x00,0x4f,0xea,0x84,0x3f, + 0xff,0xfe,0xff,0xff,0xff,0xf7,0x80,0x17,0xff,0xfa,0x81,0x11,0xff,0xf9, + 0x82,0x10,0x8f,0xf8,0x84,0x10,0x8f,0xf8,0x88,0x10,0x8f,0xf8,0x92,0x90, + 0x8f,0xf8,0x80,0x10,0x8f,0xf8,0xff,0xf0,0x8f,0xf8,0x05,0x00,0x4f,0xf8, + 0x0f,0x80,0x47,0xf8,0x08,0x80,0x23,0xf0,0x0f,0x80,0x7f,0xe0,0x0a,0x81, + 0xff,0xf0,0xf2,0x7f,0xff,0xff,0x05,0x00,0x00,0x00,0xf8,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xc0,0x00,0x00,0x3f,0xe0,0x00,0x00, + 0xff,0xf0,0x00,0x07,0xff,0xf0,0x00,0x03,0xff,0xf8,0x00,0x00,0xff,0xf8, + 0x00,0x00,0x3f,0xf8,0x00,0x00,0x3f,0xf8,0x00,0x00,0x1f,0xf0,0x00,0x00, + 0x3f,0xf8,0x00,0x00,0x7f,0xf8,0x00,0x00,0x3f,0xfc,0x78,0x00,0x7f,0xfe, + 0xfc,0x3f,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xf7,0xff,0xfa,0xff,0xf1, + 0xff,0xf9,0xff,0xf0,0xff,0xf8,0xff,0xf0,0xff,0xf8,0xff,0xf0,0xff,0xf8, + 0xff,0xf0,0xff,0xf8,0xff,0xf0,0xff,0xf8,0xff,0xf0,0xff,0xf8,0x07,0x00, + 0x7f,0xf8,0x0f,0x80,0x7f,0xf8,0x0f,0x80,0x3f,0xf0,0x0f,0x80,0x7f,0xe0, + 0x0f,0x81,0xff,0xf0,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xf8,0xff, + 0xff,0xff,0x00,0x00,0x00,0x40,0x00,0x38,0x00,0xfc,0x03,0xde,0x00,0xfe, + 0x00,0x7e,0x00,0xd6,0xe0,0xff,0xff,0xff,0xff,0xff,0xfc,0xbe,0xfc,0xbe, + 0xfc,0xbe,0x38,0xbe,0x38,0xfc,0xff,0xfe,0xff,0xfe,0x00,0x38,0x00,0xfc, + 0x03,0xfe,0x00,0xfe,0x00,0x7e,0x00,0xfe,0xe0,0xff,0xff,0xff,0xff,0xff, + 0xfc,0xfe,0xfc,0xfe,0xfc,0xfe,0x38,0xfe,0x38,0xfc,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0x00,0x00,0x00, + 0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x22,0xff,0x0f,0xff,0xf0, + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x0f,0xff, + 0xff,0xf0,0x00,0x00,0x00,0x00,0x38,0x73,0x87,0xf0,0xee,0xe0,0x00,0x00, + 0xff,0xff,0xff,0xf8,0xee,0xee,0xef,0xff,0xff,0xff,0xff,0xf3,0xe7,0x7f, + 0x7e,0xff,0xff,0xff,0xff,0xf7,0xe7,0xff,0x7e,0x00,0xfc,0xff,0xff,0xf0, + 0xef,0xff,0xfe,0x00,0xfc,0xff,0xff,0xf0,0xee,0xee,0xee,0x00,0xfc,0xff, + 0xff,0xf0,0x00,0xff,0xf0,0x00,0xfc,0xff,0xff,0xf0,0x00,0xff,0xf0,0x00, + 0xff,0xff,0xff,0x00,0xee,0xbb,0xba,0xaf,0xff,0xff,0xff,0xed,0xee,0xba, + 0xbb,0xbb,0xbb,0xbb,0xbe,0xed,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0xff,0xff,0x00,0xff,0xff,0xff, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xd8,0xe3,0xc0,0xd8,0xe3,0xc0,0xff,0x00,0xfc,0xfc,0xfc,0x00,0x00,0x00, + 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe3,0xfc,0xfc,0xfc,0xfc, + 0xfc,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd8,0xfc,0xc0, + 0xc0,0xff,0xc0,0xfc,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc0, + 0xfc,0xc0,0xff,0xff,0xc0,0xfc,0x00,0x00,0xff,0x2b,0xff,0xff,0xff,0xff, + 0xff,0x00,0xfc,0xff,0xff,0xff,0xff,0xfc,0x00,0x00,0xff,0x2b,0xff,0xff, + 0xff,0xff,0xff,0x00,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0x00,0x00,0xff,0x2b, + 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00, + 0xff,0x2b,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00, + 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xfc,0xfc,0x5e,0x5e, + 0x5e,0x89,0x89,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0xf9,0xfc,0xfc, + 0x5e,0x89,0x5e,0x5e,0x5e,0x5e,0x5e,0x5e,0x5e,0x5e,0x5e,0xfc,0xfc,0xf9, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x00,0xec,0xb8,0x74,0x1b,0x8c,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0xec,0xb6,0x4c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, + 0x00,0xec,0xb6,0xc4,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x00,0x00, + 0x00,0x00,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x00,0x00,0x00,0x00, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x00,0xec,0xb6,0x80,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x07,0xd4,0x00,0xec,0xb6,0x3c +}; +#endif /* linux */ +#endif /* HOST_ICON */ +#endif /* USE_HOST_ICON */ + +/* default for System V */ + +#ifndef HOST_ICON +#ifdef SYSTYPE_SYSV +#define HOST_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* AT&Ticon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x08,0x41,0x54,0x26,0x54,0x69,0x63,0x6f, + 0x6e,0x26,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, + 0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x39,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x06,0x66,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, + 0x09,0x99,0x08,0xc8,0x09,0x99,0x09,0x99,0x0b,0x68,0x08,0xc8,0x08,0xc8, + 0x08,0xc8,0x09,0x99,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x03,0x33, + 0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03, + 0x30,0x00,0x00,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x33,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x33,0x33,0x33,0x30,0x00,0x00, + 0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33, + 0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x00, + 0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x00,0x00,0x00,0x03,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x03,0x30, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x30,0x00,0x00, + 0x33,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33, + 0x00,0x00,0x30,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x00,0x00, + 0x00,0x03,0x00,0x00,0x33,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03, + 0x33,0x33,0x33,0x33,0x00,0x00,0x33,0x33,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x30,0x00,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x33,0x33,0x30,0x00, + 0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x33,0x33, + 0x33,0x30,0x00,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00, + 0x33,0x00,0x00,0x03,0x33,0x33,0x33,0x30,0x00,0x00,0x00,0x00,0x00,0x33, + 0x00,0x00,0x33,0x33,0x33,0x33,0x30,0x00,0x03,0x33,0x33,0x33,0x33,0x33, + 0x33,0x3a,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x3a,0x00,0x00,0x03,0x30,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0xda,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xda,0x00,0x00,0x00,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x3a,0xda,0x00,0x00,0x00,0x00, + 0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0a,0xda,0x00,0x00, + 0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x0a,0xda, + 0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x00, + 0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x03,0x33,0x00,0x00,0x00,0x03,0x33, + 0x00,0x00,0xf3,0x33,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x33,0x33, + 0x30,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0xab,0xa0,0x00,0xdd,0xee,0xea,0xaa, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xbb,0xab,0xbe,0xed,0xcc,0xbb, + 0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xba,0x0a,0xbb,0xbc, + 0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xa0,0x00, + 0xae,0xed,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0x00, + 0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, + 0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0x00,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8, + 0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00, + 0x00,0x00,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8, + 0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0x00,0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xfd,0x00,0x00,0x00,0x00,0x00,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xfd,0x00,0x00, + 0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8, + 0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xfd,0x33,0xfd,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0xfd,0x33,0xfd, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, + 0x00,0xfd,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd,0x00,0x00,0x00,0xf9,0xf9,0xfb,0xfb, + 0xfb,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0x33,0x33,0xfd,0x33,0x33,0xfb,0xfb,0xf9, + 0xf7,0xf7,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xfd,0x00,0xfd, + 0x33,0x33,0x33,0xf7,0xf9,0xf9,0xfb,0xfb,0xfb,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0x00,0x00,0x00,0xfd,0xfb,0xfb,0xf9,0x00,0x00,0x01,0x00,0x00,0x7f, + 0xc0,0x00,0x01,0x83,0xf0,0x00,0x03,0xff,0x0c,0x00,0x04,0x00,0xfe,0x00, + 0x08,0x00,0x7f,0x00,0x1f,0xff,0xc0,0x80,0x30,0x00,0x3f,0xc0,0x20,0x00, + 0x1f,0xc0,0x7f,0xff,0xe0,0x20,0x60,0x00,0x1f,0xe0,0xe0,0x00,0x1f,0xf0, + 0xbf,0xff,0xe0,0x10,0xe0,0x00,0x1f,0xf0,0xf0,0x00,0x3f,0xf0,0x8f,0xff, + 0xc0,0x10,0xf8,0x00,0x7f,0xf0,0xfe,0x00,0xff,0xf0,0xc1,0xfe,0x00,0x30, + 0xff,0x87,0xff,0xf0,0x7f,0xff,0xff,0xf0,0x60,0x00,0x00,0x50,0x3f,0xff, + 0xff,0xd0,0x3f,0xff,0xff,0xd0,0x08,0x00,0x01,0x50,0x07,0xff,0xfe,0x50, + 0x07,0xff,0xfc,0xf8,0x01,0xc0,0x70,0x88,0x00,0x3f,0x80,0xf8,0x00,0x00, + 0x00,0xa8,0xff,0xff,0xff,0x27,0x00,0x00,0x00,0x50,0xff,0xff,0xff,0x8f, + 0x00,0x7f,0xc0,0x00,0x01,0xff,0xf0,0x00,0x03,0xff,0xfc,0x00,0x07,0xff, + 0xfe,0x00,0x0f,0xff,0xff,0x00,0x1f,0xff,0xff,0x80,0x3f,0xff,0xff,0xc0, + 0x3f,0xff,0xff,0xc0,0x7f,0xff,0xff,0xe0,0x7f,0xff,0xff,0xe0,0xff,0xff, + 0xff,0xf0,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xf0, + 0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xf0,0xff,0xff, + 0xff,0xf0,0xff,0xff,0xff,0xf0,0x7f,0xff,0xff,0xf0,0x7f,0xff,0xff,0xf0, + 0x3f,0xff,0xff,0xf0,0x3f,0xff,0xff,0xf0,0x1f,0xff,0xfe,0x70,0x0f,0xff, + 0xfe,0x70,0x07,0xff,0xfc,0x70,0x03,0xff,0xf0,0xf8,0x00,0x3f,0x80,0xf8, + 0x00,0x00,0x00,0xf8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xff,0xff, + 0xff,0x8f,0x00,0x00,0x00,0x40,0x1f,0xc0,0x3b,0xf0,0x7f,0xf8,0x60,0xf8, + 0xff,0xfc,0xc0,0x7c,0xff,0xfc,0xe0,0xfc,0xff,0xfc,0xfb,0xfc,0xff,0xfc, + 0x7f,0xfc,0x3f,0xfe,0x1f,0xce,0x7f,0xff,0x7f,0xff,0x1f,0xc0,0x3f,0xf0, + 0x7f,0xf8,0x7f,0xf8,0xff,0xfc,0xff,0xfc,0xff,0xfc,0xff,0xfc,0xff,0xfc, + 0xff,0xfc,0xff,0xfc,0x7f,0xfc,0x3f,0xfc,0x1f,0xfe,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x80,0x00,0x03,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x33, + 0x30,0x33,0x33,0x33,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x30,0x00, + 0x03,0x30,0x00,0x00,0x33,0x33,0x30,0x00,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x00,0x33,0x00,0x00,0x00,0x03,0x33,0x33,0x00,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x00,0x33,0x30,0x00,0x00,0x33,0x33,0x33,0x00,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x00,0x33,0x33,0x30,0x33,0x33,0x33,0x3a,0x00, + 0x33,0x33,0x33,0x33,0x33,0x33,0x3a,0x00,0x03,0x33,0x33,0x33,0x33,0x33, + 0xaa,0x00,0x00,0x33,0x33,0x33,0x33,0x33,0xff,0xf0,0x00,0x03,0x33,0x33, + 0x33,0x00,0xff,0xf0,0xde,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xde,0xaa, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xd8,0xd8,0xd8,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, + 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, + 0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8, + 0xd8,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xd8,0xd8,0xd8,0x00, + 0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00, + 0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xfd, + 0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xfd,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xfd,0xfd,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xff,0xff,0xff,0x00,0xf9,0xfb,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xfb, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x03,0x71,0xef,0x04,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, + 0x03,0x71,0xea,0xe4,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, + 0xf5,0xdc,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf1,0xc4, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf3,0x8c,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf2,0x90 +}; +#endif /* SYSTYPE_SYSV */ +#endif /* HOST_ICON */ + +/* default */ + +#ifndef HOST_ICON +#define BSD_ICON 1 +unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* BSDicon */ + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x00,0x60,0x00,0x03,0x00,0x70,0x00,0x30,0x06,0x4a,0x80,0x66, + 0x06,0x70,0x01,0x60,0x00,0x02,0xf2,0x24,0x6b,0x1d,0x8e,0x2d,0x6b,0x00, + 0x20,0xff,0xf4,0x42,0x44,0x42,0x08,0x53,0x55,0x4e,0x20,0x69,0x63,0x6f, + 0x6e,0x4e,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, + 0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, + 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xb0,0x0e,0x36,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, + 0x00,0x1c,0x14,0x70,0x00,0x10,0x06,0x0c,0x40,0x00,0x2e,0x67,0x00,0x00, + 0x84,0x70,0x00,0x10,0x06,0x0c,0x40,0x00,0xe5,0x67,0x78,0x70,0x00,0x10, + 0x06,0x4a,0x80,0x67,0x7e,0x70,0x10,0xc0,0x2c,0x00,0x0b,0x67,0x34,0x4a, + 0x44,0x67,0x06,0x70,0x00,0x60,0x00,0x02,0x64,0x48,0x7a,0x02,0x76,0x2f, + 0x0c,0x4e,0xba,0x8b,0x20,0x4a,0x00,0x50,0x4f,0x67,0x14,0x70,0x00,0x10, + 0x2c,0x00,0x1b,0xe1,0x88,0x72,0x00,0x12,0x2c,0x00,0x1a,0x38,0x01,0xd8, + 0x40,0x60,0x3a,0x70,0x00,0x60,0x00,0x02,0x3a,0x4a,0x43,0x67,0x06,0x70, + 0x00,0x60,0x00,0x02,0x30,0x48,0x7a,0x02,0x36,0x2f,0x0c,0x4e,0xba,0x8a, + 0xec,0x4a,0x00,0x50,0x4f,0x67,0x14,0x70,0x00,0x10,0x2c,0x00,0x1b,0xe1, + 0x88,0x72,0x00,0x12,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0f,0xf0,0x00,0xff,0xff,0xf0,0x00,0xff,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0f,0x3f,0x0f,0x33,0x33,0x3f,0x0f,0x3f,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0xf3,0x3f,0xff,0x33,0xf3,0x3f,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x3f,0xff,0x33,0xf3,0x3f, + 0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0xff,0x00,0xf3, + 0x3f,0x33,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0xf0, + 0x00,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0xf0,0xff,0xf0,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xf0,0xff,0xf0,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0xf0,0xff,0xf0,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0xf0,0x00,0x0f,0x3f,0x33,0x3f,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0xf0,0x00,0xf3,0x3f,0x33, + 0x3f,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0xff,0x3f, + 0xf3,0x33,0x3f,0x00,0xe0,0x0e,0x0e,0xee,0xe0,0x00,0x00,0x00,0xf3,0x33, + 0xf3,0x33,0x33,0x33,0x3f,0x00,0xe0,0xe0,0xe7,0x77,0x7e,0x00,0x00,0x00, + 0xf3,0x33,0xf3,0x3f,0xff,0x33,0xf0,0x00,0xee,0x00,0xee,0xee,0xee,0xee, + 0xee,0xee,0x0f,0xff,0x33,0x33,0xf3,0x33,0xf0,0x0e,0xe0,0x0e,0xe7,0x77, + 0x77,0x77,0x77,0x7e,0x00,0xf3,0x33,0x3f,0x33,0x3f,0xee,0xe0,0x0e,0xe0, + 0xe7,0x77,0x77,0x7f,0x77,0x7e,0x00,0x0f,0xff,0xff,0xff,0xf3,0xf0,0x00, + 0x00,0x00,0xe7,0x77,0x77,0xf7,0x77,0x7e,0x00,0xf3,0x33,0x33,0xf3,0x33, + 0xf0,0x00,0x0f,0xf0,0xe7,0x77,0x7f,0x77,0x77,0x7e,0x00,0xff,0xf3,0x3f, + 0x33,0x33,0xf0,0x00,0xf0,0x0f,0xe7,0x77,0xf7,0x77,0x77,0x7e,0x0f,0x33, + 0x3f,0xef,0x33,0x3f,0xf0,0x00,0xf0,0x0f,0xe7,0x7f,0x77,0xf7,0xf7,0x7e, + 0x0f,0x33,0x3f,0x33,0xff,0xf3,0xf0,0x0f,0x00,0xf0,0xe7,0x77,0x77,0x77, + 0x77,0x7e,0x0f,0xf3,0x3f,0x33,0x33,0x33,0xf0,0xf0,0x0f,0x00,0xee,0xee, + 0xee,0xee,0xee,0xee,0x00,0xef,0xf3,0x3f,0x33,0x33,0xff,0x00,0x0f,0x00, + 0x00,0x00,0x0a,0xca,0x00,0x00,0x0e,0xf3,0x33,0x33,0x33,0x3f,0xf0,0x00, + 0xf0,0x00,0x00,0x00,0xaa,0xaa,0xa0,0x00,0xe0,0x0f,0x33,0x33,0x33,0x3f, + 0x00,0xf0,0xf0,0xf0,0x00,0x00,0xa3,0x33,0xa0,0x00,0x00,0x00,0xf3,0x3f, + 0x33,0x3f,0x00,0x0f,0xff,0x00,0x00,0x00,0xaa,0xaa,0xa0,0x00,0x00,0x00, + 0xf3,0x3f,0x33,0x3f,0x00,0x00,0xf0,0x00,0x00,0x00,0xab,0xab,0xa0,0x00, + 0x00,0x00,0xf3,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0xdd,0xee,0xbb,0xab, + 0xba,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcc,0xbb, + 0xba,0x0a,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, + 0xdd,0xee,0xa0,0x00,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, + 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xd8,0xff, + 0x00,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0xff,0xd8,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0xff,0xd8,0xd8,0xff,0xd8, + 0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0xff,0xff,0xff,0xd8,0xd8, + 0xff,0xd8,0xd8,0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff, + 0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00, + 0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xfe,0xfe,0x00,0xff,0xfe,0xfe,0x00,0x00,0xff,0xd8,0xff, + 0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xfe,0xfe,0x00,0xff,0xfe,0xfe,0x00, + 0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xfe,0xfe,0x00, + 0xff,0xfe,0xfe,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8, + 0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xd8, + 0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xd8,0xff,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xfc,0x00, + 0x00,0xfc,0x00,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff, + 0x00,0x00,0xfc,0x00,0xfc,0x00,0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0xff, + 0xd8,0xd8,0xff,0x00,0x00,0x00,0xfc,0xfc,0x00,0x00,0xab,0xab,0xab,0xab, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xff,0xff,0xff,0xd8,0xd8, + 0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xfc,0xfc,0x00,0x00,0xfc, + 0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00, + 0xff,0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xfc,0xfc,0xfc,0xfc,0x00, + 0x00,0xfc,0xfc,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xfe,0x2a,0x2a, + 0x2a,0xab,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd8, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, + 0xfe,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xd8,0xd8, + 0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0xab,0x2a, + 0x2a,0x2a,0x2a,0xfe,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0xff,0xff, + 0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xff,0x00, + 0x00,0xff,0xab,0x2a,0x2a,0x2a,0xfe,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab, + 0x00,0xff,0xd8,0xd8,0xd8,0xff,0xfc,0xff,0xd8,0xd8,0xd8,0xff,0xff,0x00, + 0x00,0x00,0xff,0x00,0x00,0xff,0xab,0x2a,0x2a,0xfe,0x2a,0x2a,0xfe,0x2a, + 0xfe,0x2a,0x2a,0xab,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff, + 0xff,0xd8,0xff,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0xab,0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xff,0xff,0xd8,0xd8,0xff, + 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00, + 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00, + 0xfc,0xff,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0xff,0x00,0x00, + 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0xf8,0xfd,0x00,0x00, + 0x00,0x00,0x00,0xfc,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff, + 0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0xff,0xff,0xff,0x00,0x00,0x00,0xfc,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0x00, + 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00, + 0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33, + 0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8, + 0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xf9,0xfb,0xfb, + 0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, + 0xf6,0xf6,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0xf6,0xf6,0xf9,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x0c, + 0x01,0x80,0x00,0x18,0xf8,0xc0,0x00,0x15,0x05,0x40,0x00,0x12,0x72,0x40, + 0x00,0x0d,0xc9,0x40,0x00,0x13,0x24,0xc0,0x00,0x22,0x14,0x40,0x00,0x3b, + 0x94,0x40,0x00,0x3b,0x94,0x40,0x00,0x3b,0x94,0x40,0x00,0x22,0x14,0x40, + 0x00,0x22,0x24,0x44,0x00,0x1f,0xd8,0x49,0x78,0x08,0x80,0x4a,0x84,0x08, + 0x9c,0x8c,0xff,0xf7,0x08,0x99,0x80,0x12,0x11,0xe6,0x81,0x11,0xfe,0x80, + 0x82,0x12,0x08,0x86,0x84,0x13,0x90,0x89,0x88,0x14,0x71,0x89,0x92,0x94, + 0x4e,0x92,0x80,0x16,0x40,0xa4,0xff,0xf3,0x90,0xc4,0x05,0x06,0x01,0x88, + 0x0f,0x89,0x01,0x2a,0x08,0x80,0x91,0x1c,0x0f,0x80,0x91,0x08,0x0a,0x80, + 0x91,0x00,0xf2,0x7f,0xff,0xff,0x05,0x00,0x00,0x00,0xf8,0xff,0xff,0xff, + 0x00,0x0c,0x01,0x80,0x00,0x18,0xf8,0xc0,0x00,0x1d,0xfd,0xc0,0x00,0x1f, + 0xff,0xc0,0x00,0x0f,0xff,0xc0,0x00,0x1f,0xff,0xc0,0x00,0x3f,0xff,0xc0, + 0x00,0x3f,0xff,0xc0,0x00,0x3f,0xff,0xc0,0x00,0x3f,0xff,0xc0,0x00,0x3f, + 0xff,0xc0,0x00,0x3f,0xff,0xc4,0x00,0x1f,0xff,0xc9,0x78,0x0f,0xff,0xca, + 0xfc,0x0f,0xff,0x8c,0xff,0xf7,0xff,0x99,0xff,0xf3,0xff,0xe6,0xff,0xf1, + 0xff,0x80,0xff,0xf3,0xff,0x86,0xff,0xf3,0xff,0x89,0xff,0xf7,0xff,0x89, + 0xff,0xf7,0xff,0x92,0xff,0xf7,0xff,0xa4,0xff,0xf3,0xff,0xc4,0x07,0x07, + 0xff,0x88,0x0f,0x89,0xff,0x2a,0x0f,0x80,0xff,0x1c,0x0f,0x80,0xff,0x08, + 0x0f,0x80,0xff,0x00,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xf8,0xff, + 0xff,0xff,0x00,0x00,0x00,0x40,0x06,0x30,0x04,0xd0,0x07,0xf0,0x05,0x58, + 0x05,0x58,0x07,0xd8,0xe6,0xf9,0xff,0xf2,0x95,0xff,0xb5,0x68,0xfd,0x59, + 0xfd,0xff,0x39,0xf8,0x38,0xf0,0x7f,0xf8,0x00,0x00,0x06,0x30,0x04,0xf0, + 0x07,0xf0,0x07,0xf8,0x07,0xf8,0x07,0xf8,0xe7,0xf9,0xff,0xf2,0xff,0xff, + 0xff,0xf8,0xff,0xf9,0xff,0xff,0x3f,0xf8,0x3f,0xf0,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x80,0x00,0x00,0x0f,0xf0,0x00,0xff,0x00,0x00,0x00,0x00, + 0x0f,0x00,0xff,0x0f,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x00,0x00, + 0x00,0x00,0x0f,0x0f,0x0f,0x33,0xf0,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x33, + 0xf0,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0xf0,0x00,0xee,0xe0,0x03,0xff, + 0xf3,0x33,0xf0,0x0f,0xee,0xee,0xee,0x33,0x33,0x33,0x00,0xf0,0xec,0xcf, + 0xce,0x03,0x33,0xff,0xff,0xff,0xec,0xff,0xce,0x03,0x3f,0xf3,0xf0,0x00, + 0xef,0xff,0xfe,0x03,0x3f,0xff,0xf0,0x0f,0xee,0xee,0xee,0x0f,0xff,0x33, + 0xff,0xff,0x00,0xaa,0x30,0x0f,0x33,0x3f,0xf0,0x00,0x00,0x33,0x30,0x00, + 0xff,0x3f,0x00,0x00,0xda,0xaa,0xaa,0xaa,0xff,0xaf,0xad,0xdc,0xdd,0xdd, + 0xdd,0xdd,0xdd,0xdd,0xdd,0xdc,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xd8,0xd8, + 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff, + 0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, + 0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xab,0xab,0xab,0x00,0x00,0xd8, + 0xff,0xff,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff,0xab,0xab,0xab,0xab, + 0xab,0xab,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xff,0x00,0xab,0x54, + 0x54,0xfe,0x54,0xab,0x00,0xd8,0xd8,0xd8,0xff,0xff,0xff,0xff,0xff,0xff, + 0xab,0x54,0xfe,0xfe,0x54,0xab,0x00,0xd8,0xd8,0xff,0xff,0xd8,0xff,0x00, + 0x00,0x00,0xab,0xfe,0xfe,0xfe,0xfe,0xab,0x00,0xd8,0xd8,0xff,0xff,0xff, + 0xff,0x00,0x00,0xff,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xff,0xff,0xff, + 0xd8,0xd8,0xff,0xff,0xff,0xff,0x00,0x00,0xfd,0xfd,0xd8,0x00,0x00,0xff, + 0xd8,0xd8,0xd8,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0x00, + 0x00,0x00,0xff,0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0xf9,0xfd,0xfd,0xfd, + 0xfd,0xfd,0xfd,0xfd,0xff,0xff,0xfd,0xff,0xfd,0xf9,0xf9,0xf6,0xf9,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9,0xf6, + 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, + 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, + 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, + 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, + 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, + 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, + 0x00,0x00,0x03,0x71,0xf4,0x4c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, + 0x03,0x71,0xee,0xa4,0xbf,0xb9,0xff,0xff,0x00,0x00,0x06,0x08,0x03,0x71, + 0xf5,0xa4,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf0,0x78, + 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf4,0x68,0xbf,0xb9, + 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf5,0x54 +}; +#endif /* HOST_ICON */ + +/* + * Finder info for our Icon file, + * name "Icon^M", type 'rsrc', owner 'RSED', invisible, custom_icon + * + */ +unsigned char color_cap_icon_fndr[ICON_FNDR_LEN] = { /* FinderInfo */ + 0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x45,0x00,0x00,0x9c,0x00,0x80, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x10,0xda,0x02,0x49,0x63,0x6f,0x6e, + 0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x63,0x6f,0x6e,0x0d, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xda,0x03,0x33,0xe7,0xfe,0x70,0x33,0xe7,0xfe, + 0xca,0x33,0xe8,0x7d,0xfe,0x00 +}; + +char icon_path[MAXPATHLEN]; + +/* + * check if vol/Icon:0d file already in place + * + */ + +int +icon_exists(vol) +char *vol; +{ + sprintf(icon_path, "%s/%s", vol, ICON_NAME); + if (access(icon_path, R_OK) == 0) + return(1); + + return(0); +} + +/* + * create three forks of the Icon:0d file + * in the specified volume root + * + */ + +int +icon_create(vol) +char *vol; +{ + int fd; + char *cp; + IDirP pdir; + + /* + * data fork, zero length + * + */ + sprintf(icon_path, "%s/%s", vol, ICON_NAME); + if ((fd = open(icon_path, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) + return(fd); + close(fd); + + /* + * resource fork + * + */ + sprintf(icon_path, "%s/.resource/%s", vol, ICON_NAME); + if ((fd = open(icon_path, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) + return(fd); + (void)write(fd, color_cap_icon, sizeof(color_cap_icon)); + close(fd); + + /* + * Finder information + * + */ + sprintf(icon_path, "%s/.finderinfo/%s", vol, ICON_NAME); + if ((fd = open(icon_path, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) + return(fd); +#ifdef USE_MAC_DATES + (void)write(fd, color_cap_icon_fndr, ICON_FNDR_LEN); +#else /* USE_MAC_DATES */ + (void)write(fd, color_cap_icon_fndr, ICON_FNDR_WAS); +#endif /* USE_MAC_DATES */ + close(fd); + + /* + * set the volume comment field + * + */ + strncpy(icon_path, vol, sizeof(icon_path)); + if ((cp = (char *)rindex(icon_path, '/')) == NULL) + return(0); + *cp++ = '\0'; +#ifdef BSD_ICON + if ((pdir = Idirid(icon_path)) != NILDIR) + OSSetComment(pdir, cp, INFO_MESSAGE1, strlen(INFO_MESSAGE1)); +#else /* BSD_ICON */ + if ((pdir = Idirid(icon_path)) != NILDIR) + OSSetComment(pdir, cp, INFO_MESSAGE2, strlen(INFO_MESSAGE2)); +#endif /* BSD_ICON */ + + return(0); +} diff --git a/applications/aufs/aufsicon.c b/applications/aufs/aufsicon.c new file mode 100644 index 0000000..46f090e --- /dev/null +++ b/applications/aufs/aufsicon.c @@ -0,0 +1,2179 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:49:40 $ + * $Header: /mac/src/cap60/applications/aufs/RCS/aufsicon.c,v 2.9 1996/06/18 10:49:40 djh Rel djh $ + * $Revision: 2.9 $ + */ + +/* + * aufsicon.c - aufs icon. + * + * Display a CAP volume ICON, and where possible + * represent the underlying hardware platform. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in + * the City of New York. + * + * Edit History: + * + * March 1987 Schilit Created. + * April 1987 CCKim Clean up, put rep. of icon in + * Feb. 1991 Max Tardiveau Added icons for NeXT and Sun + * May 1991 Rakesh Patel Automatic ICN# selection + * May 1991 djh Edit icons & masks, add IBM ICN# + * May 1991 Kim Holburn New SUN, Pyramid Icons + * May 1991 Gavin Longmuir New SUN SPARC ICN# + * May 1991 djh Major revision & yet more ICN#s + * June 1991 Mike Moya Added a Gould and "cleaner" Digital Icon + * Feb 1992 Jarmo Sorvari Add ICN# for HP-UX + * + */ + +/* + * The ICN#s in this file are intended for non-commercial CAP use, + * they are provided to visually link the CAP server and host type. + * Components of the images are copyright by the respective hardware + * manufacturers. + * + * The "4.3BSD System Daemon" is Copyright (c) 1988 Kirk Mckusick, UC. + * + * The "Penguin with scarf" icon is Copyright (c) 1996 David Hornsby. + * + * In general, there are two version of each ICON. The first is the + * normal "network platter" representation, the second the usual CAP + * "network folder" version. Define PLATTER_ICON for the former. To + * enable the automatic host ICON selection, define USE_HOST_ICON in + * the m4.features file. To use the alternative set of contributed + * ICONs, define USE_ALT_ICONS instead. + * + */ + +#include + +#define ICONSIZE 256 + +/* +#define PLATTER_ICON +*/ + +/* Automatic host/ICON selection */ + +#ifdef USE_HOST_ICON +#ifdef sun +#define INDEX 1 +#endif sun +#ifdef pyr +#define INDEX 2 +#endif pyr +#ifdef NeXT +#define INDEX 3 +#endif NeXT +#ifdef AIX +#define INDEX 4 +#endif AIX +#ifdef ultrix +#define INDEX 5 +#endif ultrix +#ifdef vax +#define INDEX 5 +#endif vax +#ifdef __alpha +#define INDEX 5 +#endif __alpha +#ifdef aux +#define INDEX 6 +#endif aux +#ifdef sgi +#define INDEX 7 +#endif sgi +#ifdef encore +#define INDEX 8 +#endif encore +#ifdef gould +#define INDEX 12 +#endif gould +#ifdef hpux +#define INDEX 14 +#endif hpux +#endif USE_HOST_ICON + +/* Alternative host/ICONs */ + +#ifdef USE_ALT_ICONS +#ifdef sun +#ifdef sparc +#define INDEX 9 +#else sparc +#define INDEX 10 +#endif sparc +#endif sun +#ifdef NeXT +#define INDEX 11 +#endif NeXT +#ifdef vax +#define INDEX 13 +#endif vax +#ifdef ultrix +#define INDEX 13 +#endif ultrix +#endif USE_ALT_ICONS + +#define NUM_ICONS 17 + +/* default for System V */ + +#ifndef INDEX +#ifdef SYSTYPE_SYSV +#define INDEX 15 +#endif SYSTYPE_SYSV +#endif INDEX + +/* default for Linux */ + +#ifndef INDEX +#ifdef linux +#define INDEX 16 +#endif linux +#endif INDEX + +/* default "BSD Daemon" */ + +#ifndef INDEX +#define INDEX 0 /* default CAP BSD ICN# */ +#endif INDEX + +export byte icons[NUM_ICONS][ICONSIZE] = { + + /* ICON 0, BSD default */ +#ifdef PLATTER_ICON + 0x00, 0x0C, 0x01, 0x80, /* xx xx */ + 0x00, 0x18, 0xF8, 0xC0, /* xx xxxxx xx */ + 0x00, 0x15, 0x05, 0x40, /* x x x x x x */ + 0x00, 0x12, 0x72, 0x40, /* x x xxx x x */ + 0x00, 0x0D, 0xC9, 0x40, /* xx xxx x x x */ + 0x00, 0x13, 0x24, 0xC0, /* x xx x x xx */ + 0x00, 0x22, 0x14, 0x40, /* x x x x x */ + 0x00, 0x3B, 0x94, 0x40, /* xxx xxx x x x */ + 0x00, 0x3B, 0x94, 0x40, /* xxx xxx x x x */ + 0x00, 0x3B, 0x94, 0x40, /* xxx xxx x x x */ + 0x00, 0x22, 0x14, 0x40, /* x x x x x */ + 0x00, 0x22, 0x24, 0x44, /* x x x x x x */ + 0x00, 0x1F, 0xD8, 0x49, /* xxxxxxx xx x x x*/ + 0x00, 0x08, 0x80, 0x4A, /* x x x x x */ + 0x00, 0x08, 0x9C, 0x8C, /* x x xxx x xx */ + 0x00, 0x07, 0x08, 0x99, /* xxx x x xx x*/ + 0x00, 0x02, 0x11, 0xE6, /* x x xxxx xx */ + 0x00, 0x01, 0xFE, 0x80, /* xxxxxxxx x */ + 0x00, 0x02, 0x08, 0x8E, /* x x x xxx */ + 0x00, 0x03, 0x90, 0x91, /* xxx x x x x*/ + 0x0B, 0x04, 0x71, 0x95, /* x xx x xxx xx x x x*/ + 0x0E, 0x8C, 0x4E, 0x96, /* xxx x xx x xxx x x xx */ + 0x0A, 0xF6, 0x50, 0xA7, /* x x xxxx xx x x x x xxx*/ + 0x0A, 0x03, 0x80, 0xC0, /* x x xxx xx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x40, 0x02, 0x00, 0x02, /* x x x */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x08, 0x1F, 0x00, /* x xxxxx */ + 0x00, 0x00, 0x1F, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xE4, 0xFF, /*xxxxxxxxxxxxxxxxxxx x xxxxxxxx*/ + 0x00, 0x00, 0x0A, 0x00, /* x x */ + 0xFF, 0xFF, 0xF1, 0xFF, /*xxxxxxxxxxxxxxxxxxxx xxxxxxxxx*/ + /* MASK */ + 0x00, 0x0C, 0x01, 0x80, /* xx xx */ + 0x00, 0x18, 0xF8, 0xC0, /* xx xxxxx xx */ + 0x00, 0x1D, 0xFD, 0xC0, /* xxx xxxxxxx xxx */ + 0x00, 0x1F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxx */ + 0x00, 0x0F, 0xFF, 0xC0, /* xxxxxxxxxxxxxx */ + 0x00, 0x1F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC4, /* xxxxxxxxxxxxxxxx x */ + 0x00, 0x1F, 0xFF, 0xC9, /* xxxxxxxxxxxxxxx x x*/ + 0x00, 0x0F, 0xFF, 0xCA, /* xxxxxxxxxxxxxx x x */ + 0x00, 0x0F, 0xFF, 0x8C, /* xxxxxxxxxxxxx xx */ + 0x00, 0x0F, 0xFF, 0x99, /* xxxxxxxxxxxxx xx x*/ + 0x00, 0x03, 0xFF, 0xE6, /* xxxxxxxxxxxxx xx */ + 0x00, 0x03, 0xFF, 0x80, /* xxxxxxxxxxx */ + 0x00, 0x03, 0xFF, 0x8E, /* xxxxxxxxxxx xxx */ + 0x00, 0x03, 0xFF, 0x91, /* xxxxxxxxxxx x x*/ + 0x0B, 0x07, 0xFF, 0x95, /* x xx xxxxxxxxxxxx x x x*/ + 0x0F, 0x8F, 0xFF, 0x96, /* xxxxx xxxxxxxxxxxxx x xx */ + 0x0F, 0xFF, 0xFF, 0xA7, /* xxxxxxxxxxxxxxxxxxxxx x xxx*/ + 0x0F, 0xFF, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x08, 0x1F, 0x00, /* x xxxxx */ + 0x00, 0x00, 0x1F, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFB, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxxxx*/ + 0xFF, 0xFF, 0xF1, 0xFF, /*xxxxxxxxxxxxxxxxxxxx xxxxxxxxx*/ +#else PLATTER_ICON + 0x00, 0x0C, 0x01, 0x80, /* xx xx */ + 0x00, 0x18, 0xF8, 0xC0, /* xx xxxxx xx */ + 0x00, 0x15, 0x05, 0x40, /* x x x x x x */ + 0x00, 0x12, 0x72, 0x40, /* x x xxx x x */ + 0x00, 0x0D, 0xC9, 0x40, /* xx xxx x x x */ + 0x00, 0x13, 0x24, 0xC0, /* x xx x x xx */ + 0x00, 0x22, 0x14, 0x40, /* x x x x x */ + 0x00, 0x3B, 0x94, 0x40, /* xxx xxx x x x */ + 0x00, 0x3B, 0x94, 0x40, /* xxx xxx x x x */ + 0x00, 0x3B, 0x94, 0x40, /* xxx xxx x x x */ + 0x00, 0x22, 0x14, 0x40, /* x x x x x */ + 0x00, 0x22, 0x24, 0x44, /* x x x x x x */ + 0x00, 0x1F, 0xD8, 0x49, /* xxxxxxx xx x x x*/ + 0x78, 0x08, 0x80, 0x4A, /* xxxx x x x x x */ + 0x84, 0x08, 0x9C, 0x8C, /*x x x x xxx x xx */ + 0xFF, 0xF7, 0x08, 0x99, /*xxxxxxxxxxxx xxx x x xx x*/ + 0x80, 0x12, 0x11, 0xE6, /*x x x x xxxx xx */ + 0x81, 0x11, 0xFE, 0x80, /*x x x xxxxxxxx x */ + 0x82, 0x12, 0x08, 0x86, /*x x x x x x xx */ + 0x84, 0x13, 0x90, 0x89, /*x x x xxx x x x x*/ + 0x88, 0x14, 0x71, 0x89, /*x x x x xxx xx x x*/ + 0x92, 0x94, 0x4E, 0x92, /*x x x x x x x xxx x x x */ + 0x80, 0x16, 0x40, 0xA4, /*x x xx x x x x */ + 0xFF, 0xF3, 0x90, 0xC4, /*xxxxxxxxxxxx xxx x xx x */ + 0x05, 0x06, 0x01, 0x88, /* x x xx xx x */ + 0x0F, 0x89, 0x01, 0x2A, /* xxxxx x x x x x x */ + 0x08, 0x80, 0x91, 0x1C, /* x x x x x xxx */ + 0x0F, 0x80, 0x91, 0x08, /* xxxxx x x x x */ + 0x0A, 0x80, 0x91, 0x00, /* x x x x x x */ + 0xF2, 0x7F, 0xFF, 0xFF, /*xxxx x xxxxxxxxxxxxxxxxxxxxxxx*/ + 0x05, 0x00, 0x00, 0x00, /* x x */ + 0xF8, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0x00, 0x0C, 0x01, 0x80, /* xx xx */ + 0x00, 0x18, 0xF8, 0xC0, /* xx xxxxx xx */ + 0x00, 0x1D, 0xFD, 0xC0, /* xxx xxxxxxx xxx */ + 0x00, 0x1F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxx */ + 0x00, 0x0F, 0xFF, 0xC0, /* xxxxxxxxxxxxxx */ + 0x00, 0x1F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xC4, /* xxxxxxxxxxxxxxxx x */ + 0x00, 0x1F, 0xFF, 0xC9, /* xxxxxxxxxxxxxxx x x*/ + 0x78, 0x0F, 0xFF, 0xCA, /* xxxx xxxxxxxxxxxxxx x x */ + 0xFC, 0x0F, 0xFF, 0x8C, /*xxxxxx xxxxxxxxxxxxx xx */ + 0xFF, 0xFF, 0xFF, 0x99, /*xxxxxxxxxxxxxxxxxxxxxxxxx xx x*/ + 0xFF, 0xF3, 0xFF, 0xE6, /*xxxxxxxxxxxx xxxxxxxxxxxxx xx */ + 0xFF, 0xF3, 0xFF, 0x80, /*xxxxxxxxxxxx xxxxxxxxxxx */ + 0xFF, 0xF3, 0xFF, 0x86, /*xxxxxxxxxxxx xxxxxxxxxxx xx */ + 0xFF, 0xF3, 0xFF, 0x89, /*xxxxxxxxxxxx xxxxxxxxxxx x x*/ + 0xFF, 0xF7, 0xFF, 0x89, /*xxxxxxxxxxxx xxxxxxxxxxxx x x*/ + 0xFF, 0xF7, 0xFF, 0x92, /*xxxxxxxxxxxx xxxxxxxxxxxx x x */ + 0xFF, 0xF7, 0xFF, 0xA4, /*xxxxxxxxxxxx xxxxxxxxxxxx x x */ + 0xFF, 0xF3, 0xFF, 0xC4, /*xxxxxxxxxxxx xxxxxxxxxxxx x */ + 0x07, 0x07, 0xFF, 0x88, /* xxx xxxxxxxxxxxx x */ + 0x0F, 0x8B, 0xFF, 0x2A, /* xxxxx x xxxxxxxxxx x x x */ + 0x0F, 0x80, 0xFF, 0x1C, /* xxxxx xxxxxxxx xxx */ + 0x0F, 0x80, 0xFF, 0x08, /* xxxxx xxxxxxxx x */ + 0x0F, 0x80, 0xFF, 0x00, /* xxxxx xxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFD, 0xFF, 0xFF, 0xFF, /*xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF8, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 1, SUN */ +#ifdef PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x01, 0xC0, 0x00, /* xxx */ + 0x00, 0x03, 0xE0, 0x00, /* xxxxx */ + 0x00, 0x03, 0x70, 0x00, /* xx xxx */ + 0x00, 0x0B, 0xB8, 0x00, /* x xxx xxx */ + 0x00, 0x1D, 0xDC, 0x00, /* xxx xxx xxx */ + 0x00, 0x2E, 0xEE, 0x00, /* x xxx xxx xxx */ + 0x00, 0x77, 0x77, 0x00, /* xxx xxx xxx xxx */ + 0x00, 0xBB, 0xBA, 0x80, /* x xxx xxx xxx x x */ + 0x01, 0xDD, 0xDD, 0xC0, /* xxx xxx xxx xxx xxx */ + 0x03, 0xAE, 0xEB, 0xA0, /* xxx x xxx xxx x xxx x */ + 0x07, 0x77, 0x67, 0x70, /* xxx xxx xxx xx xxx xxx */ + 0x0E, 0xE3, 0xEE, 0xE0, /* xxx xxx xxxxx xxx xxx */ + 0x1D, 0xDD, 0xDD, 0xDC, /* xxx xxx xxx xxx xxx xxx xxx */ + 0x3B, 0xBE, 0x3B, 0xBE, /* xxx xxx xxxxx xxx xxx xxxxx */ + 0x37, 0x76, 0x37, 0x76, /* xx xxx xxx xx xx xxx xxx xx */ + 0x3E, 0xEE, 0x3E, 0xEE, /* xxxxx xxx xxx xxxxx xxx xxx */ + 0x1D, 0xDD, 0xDD, 0xDC, /* xxx xxx xxx xxx xxx xxx xxx */ + 0x03, 0xBB, 0xE3, 0xB8, /* xxx xxx xxxxx xxx xxx */ + 0x07, 0x73, 0x77, 0x70, /* xxx xxx xx xxx xxx xxx */ + 0x02, 0xEB, 0xBA, 0xE0, /* x xxx x xxx xxx x xxx */ + 0x7F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x20, 0x00, 0x00, 0x02, /* x x */ + 0x1F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x08, 0x80, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0A, 0x80, /* x x x */ + 0xFF, 0xFF, 0xF2, 0x7F, /*xxxxxxxxxxxxxxxxxxxx x xxxxxxx*/ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ + /* MASK */ + 0x00, 0x01, 0x40, 0x00, /* x x */ + 0x00, 0x03, 0xE0, 0x00, /* xxxxx */ + 0x00, 0x07, 0xF0, 0x00, /* xxxxxxx */ + 0x00, 0x0B, 0xF8, 0x00, /* x xxxxxxx */ + 0x00, 0x1F, 0xFC, 0x00, /* xxxxxxxxxxx */ + 0x00, 0x3F, 0xFE, 0x00, /* xxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0x00, /* xxxxxxxxxxxxxxx */ + 0x00, 0xFF, 0xFF, 0x80, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxxxx */ + 0x03, 0xFF, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxxxxx */ + 0x07, 0xFF, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxxxxxxxx */ + 0x0F, 0xFF, 0xEF, 0xF8, /* xxxxxxxxxxxxxxx xxxxxxxxx */ + 0x1F, 0xF7, 0xFF, 0xF4, /* xxxxxxxxx xxxxxxxxxxxxxxx x */ + 0x3F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x3F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x3F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x17, 0xFF, 0xF7, 0xFC, /* x xxxxxxxxxxxxxxx xxxxxxxxx */ + 0x0F, 0xFB, 0xFF, 0xF8, /* xxxxxxxxx xxxxxxxxxxxxxxx */ + 0x07, 0xFF, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x3F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x1F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFD, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx*/ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ +#else PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x01, 0xC0, 0x00, /* xxx */ + 0x00, 0x03, 0xE0, 0x00, /* xxxxx */ + 0x00, 0x03, 0x70, 0x00, /* xx xxx */ + 0x00, 0x0B, 0xB8, 0x00, /* x xxx xxx */ + 0x00, 0x1D, 0xDC, 0x00, /* xxx xxx xxx */ + 0x00, 0x2E, 0xEE, 0x00, /* x xxx xxx xxx */ + 0x00, 0x77, 0x77, 0x00, /* xxx xxx xxx xxx */ + 0x00, 0xBB, 0xBA, 0x80, /* x xxx xxx xxx x x */ + 0x01, 0xDD, 0xDD, 0xC0, /* xxx xxx xxx xxx xxx */ + 0x03, 0xAE, 0xEB, 0xA0, /* xxx x xxx xxx x xxx x */ + 0x07, 0x77, 0x67, 0x70, /* xxx xxx xxx xx xxx xxx */ + 0x0E, 0xE3, 0xEE, 0xE0, /* xxx xxx xxxxx xxx xxx */ + 0x1D, 0xDD, 0xDD, 0xDC, /* xxx xxx xxx xxx xxx xxx xxx */ + 0x03, 0xBE, 0x3B, 0xBE, /* xxx xxxxx xxx xxx xxxxx */ + 0x7B, 0x76, 0x37, 0x76, /* xxxx xx xxx xx xx xxx xxx xx */ + 0x84, 0x0E, 0x3E, 0xEE, /*x x xxx xxxxx xxx xxx */ + 0xFF, 0xED, 0xDD, 0xDC, /*xxxxxxxxxxx xx xxx xxx xxx xxx */ + 0x80, 0x2B, 0xE3, 0xB8, /*x x x xxxxx xxx xxx */ + 0x88, 0x23, 0x77, 0x70, /*x x x xx xxx xxx xxx */ + 0x90, 0x2B, 0xBA, 0xE0, /*x x x x xxx xxx x xxx */ + 0xA5, 0x2D, 0xDD, 0xC0, /*x x x x x xx xxx xxx xxx */ + 0x80, 0x2E, 0xEE, 0x80, /*x x xxx xxx xxx x */ + 0xFF, 0xE7, 0x77, 0x00, /*xxxxxxxxxxx xxx xxx xxx */ + 0x0A, 0x0B, 0xBA, 0x00, /* x x x xxx xxx x */ + 0x1F, 0x1D, 0xDC, 0x00, /* xxxxx xxx xxx xxx */ + 0x11, 0x0E, 0xE8, 0x00, /* x x xxx xxx x */ + 0x1F, 0x07, 0x60, 0x00, /* xxxxx xxx xx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x03, 0xE0, 0x00, /* xxxxx */ + 0x00, 0x07, 0xF0, 0x00, /* xxxxxxx */ + 0x00, 0x0B, 0xF8, 0x00, /* x xxxxxxx */ + 0x00, 0x1F, 0xFC, 0x00, /* xxxxxxxxxxx */ + 0x00, 0x3F, 0xFE, 0x00, /* xxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0x00, /* xxxxxxxxxxxxxxx */ + 0x00, 0xFF, 0xFF, 0x80, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxxxx */ + 0x03, 0xFF, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxxxxx */ + 0x07, 0xFF, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxxxxxxxx */ + 0x0F, 0xFF, 0xEF, 0xF8, /* xxxxxxxxxxxxxxx xxxxxxxxx */ + 0x1F, 0xF7, 0xFF, 0xF4, /* xxxxxxxxx xxxxxxxxxxxxxxx x */ + 0x3F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x03, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7B, 0xFF, 0xFF, 0xFE, /* xxxx xxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFC, 0x0F, 0xFF, 0xFE, /*xxxxxx xxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0xFE, /*xxxxxxxxxxx xxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xF7, 0xFC, /*xxxxxxxxxxx xxxxxxxx xxxxxxxxx */ + 0xFF, 0xEB, 0xFF, 0xF8, /*xxxxxxxxxxx x xxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0xF0, /*xxxxxxxxxxx xxxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0xE0, /*xxxxxxxxxxx xxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0xC0, /*xxxxxxxxxxx xxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0x80, /*xxxxxxxxxxx xxxxxxxxxxxxx */ + 0x0E, 0x0F, 0xFF, 0x00, /* xxx xxxxxxxxxxxx */ + 0x1F, 0x3F, 0xFE, 0x00, /* xxxxx xxxxxxxxxxxxx */ + 0x1F, 0x1F, 0xFC, 0x00, /* xxxxx xxxxxxxxxxx */ + 0x1F, 0x0F, 0xE8, 0x00, /* xxxxx xxxxxxx x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 2, Pyramid */ +#ifdef PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x01, 0x00, 0x00, /* x */ + 0x00, 0x01, 0x80, 0x00, /* xx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x0F, 0xE0, 0x00, /* xxxxxxx */ + 0x00, 0x01, 0xF0, 0x00, /* xxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x7F, 0xFC, 0x00, /* xxxxxxxxxxxxx */ + 0x00, 0x01, 0xFE, 0x00, /* xxxxxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x03, 0xFF, 0xFF, 0x80, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x01, 0xFF, 0xC0, /* xxxxxxxxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x1F, 0xFF, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x01, 0xFF, 0xF8, /* xxxxxxxxxxxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xFF, 0xFF, 0xFF, 0xFE, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x40, 0x00, 0x00, 0x04, /* x x */ + 0x3F, 0xFF, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x08, 0x80, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0A, 0x80, /* x x x */ + 0xFF, 0xFF, 0xF2, 0x7F, /*xxxxxxxxxxxxxxxxxxxx x xxxxxxx*/ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x01, 0x00, 0x00, /* x */ + 0x00, 0x03, 0x80, 0x00, /* xxx */ + 0x00, 0x07, 0xC0, 0x00, /* xxxxx */ + 0x00, 0x0F, 0xE0, 0x00, /* xxxxxxx */ + 0x00, 0x1F, 0xF0, 0x00, /* xxxxxxxxx */ + 0x00, 0x3F, 0xF8, 0x00, /* xxxxxxxxxxx */ + 0x00, 0x7F, 0xFC, 0x00, /* xxxxxxxxxxxxx */ + 0x00, 0xFF, 0xFE, 0x00, /* xxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x03, 0xFF, 0xFF, 0x80, /* xxxxxxxxxxxxxxxxxxx */ + 0x07, 0xFF, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxxxxxx */ + 0x0F, 0xFF, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxxxxxxx */ + 0x1F, 0xFF, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3F, 0xFF, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFE, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFE, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3F, 0xFF, 0xFF, 0xFA, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx x */ + 0x3F, 0xFF, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFD, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx*/ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ +#else PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x02, 0x00, 0x00, /* x */ + 0x00, 0x03, 0x00, 0x00, /* xx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x1F, 0xC0, 0x00, /* xxxxxxx */ + 0x00, 0x03, 0xE0, 0x00, /* xxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0xFF, 0xF8, 0x00, /* xxxxxxxxxxxxx */ + 0x00, 0x03, 0xFC, 0x00, /* xxxxxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x07, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x03, 0xFF, 0x80, /* xxxxxxxxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x79, 0xFF, 0xFF, 0xE0, /* xxxx xxxxxxxxxxxxxxxxxxxx */ + 0x84, 0x03, 0xFF, 0xF0, /*x x xxxxxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x80, 0x2F, 0xFF, 0xFC, /*x x xxxxxxxxxxxxxxxxxx */ + 0x88, 0x23, 0xFF, 0xFE, /*x x x xxxxxxxxxxxxxxxxx */ + 0x90, 0x20, 0x00, 0x00, /*x x x */ + 0xA5, 0x20, 0x00, 0x00, /*x x x x x */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x02, 0x00, 0x00, /* x */ + 0x00, 0x07, 0x00, 0x00, /* xxx */ + 0x00, 0x0F, 0x80, 0x00, /* xxxxx */ + 0x00, 0x1F, 0xC0, 0x00, /* xxxxxxx */ + 0x00, 0x3F, 0xE0, 0x00, /* xxxxxxxxx */ + 0x00, 0x7F, 0xF0, 0x00, /* xxxxxxxxxxx */ + 0x00, 0xFF, 0xF8, 0x00, /* xxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFC, 0x00, /* xxxxxxxxxxxxxxx */ + 0x03, 0xFF, 0xFE, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x07, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxxxx */ + 0x0F, 0xFF, 0xFF, 0x80, /* xxxxxxxxxxxxxxxxxxxxx */ + 0x1F, 0xFF, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxxxxxxxx */ + 0x2F, 0xFF, 0xFF, 0xE0, /* x xxxxxxxxxxxxxxxxxxxxxxx */ + 0x79, 0xFF, 0xFF, 0xF0, /* xxxx xxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0xFC, /*xxxxxxxxxxx xxxxxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0xFE, /*xxxxxxxxxxx xxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0E, 0x00, 0x00, 0x00, /* xxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 3, NeXT */ +#ifdef PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x03, 0xF0, 0x00, /* xxxxxx */ + 0x00, 0x07, 0xE8, 0x00, /* xxxxxx x */ + 0x00, 0x0F, 0xDC, 0x00, /* xxxxxx xxx */ + 0x00, 0x1F, 0xBE, 0x00, /* xxxxxx xxxxx */ + 0x00, 0x3F, 0x7F, 0x00, /* xxxxxx xxxxxxx */ + 0x00, 0x7E, 0xFF, 0x80, /* xxxxxx xxxxxxxxx */ + 0x00, 0xFD, 0xFF, 0xC0, /* xxxxxx xxxxxxxxxxx */ + 0x01, 0xFB, 0xE3, 0xE0, /* xxxxxx xxxxx xxxxx */ + 0x03, 0xF7, 0xE7, 0xF0, /* xxxxxx xxxxxx xxxxxxx */ + 0x07, 0xEF, 0xED, 0xF8, /* xxxxxx xxxxxxx xx xxxxxx */ + 0x0F, 0xDF, 0x73, 0x7C, /* xxxxxx xxxxx xxx xx xxxxx */ + 0x1F, 0xBF, 0xBE, 0xFE, /* xxxxxx xxxxxxx xxxxx xxxxxxx */ + 0x3F, 0x7C, 0x1D, 0x7F, /* xxxxxx xxxxx xxx x xxxxxxx*/ + 0x1F, 0xBE, 0xFF, 0xBE, /* xxxxxx xxxxx xxxxxxxxx xxxxx */ + 0x0F, 0xDF, 0x77, 0xFC, /* xxxxxx xxxxx xxx xxxxxxxxx */ + 0x07, 0xEF, 0xF7, 0xF8, /* xxxxxx xxxxxxxx xxxxxxxx */ + 0x03, 0xF7, 0xC1, 0xF0, /* xxxxxx xxxxx xxxxx */ + 0x01, 0xFB, 0xF7, 0xE0, /* xxxxxx xxxxxx xxxxxx */ + 0x00, 0xFD, 0xF7, 0xC0, /* xxxxxx xxxxx xxxxx */ + 0x7F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x20, 0x00, 0x00, 0x02, /* x x */ + 0x1F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x08, 0x80, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0A, 0x80, /* x x x */ + 0xFF, 0xFF, 0xF2, 0x7F, /*xxxxxxxxxxxxxxxxxxxx x xxxxxxx*/ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x03, 0xF0, 0x00, /* xxxxxx */ + 0x00, 0x07, 0xF8, 0x00, /* xxxxxxxx */ + 0x00, 0x0F, 0xFC, 0x00, /* xxxxxxxxxx */ + 0x00, 0x1F, 0xFE, 0x00, /* xxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0x00, /* xxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0x80, /* xxxxxxxxxxxxxxxx */ + 0x00, 0xFF, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxxxx */ + 0x03, 0xFF, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxxxxxxx */ + 0x07, 0xFF, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxxxxxxx */ + 0x0F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x1F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x1F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x0F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x07, 0xFF, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxxxxxxx */ + 0x03, 0xFF, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxxxx */ + 0x00, 0xFF, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x3F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x1F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFD, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx*/ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ +#else PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x03, 0xF0, 0x00, /* xxxxxx */ + 0x00, 0x07, 0xE8, 0x00, /* xxxxxx x */ + 0x00, 0x0F, 0xDC, 0x00, /* xxxxxx xxx */ + 0x00, 0x1F, 0xBE, 0x00, /* xxxxxx xxxxx */ + 0x00, 0x3F, 0x7F, 0x00, /* xxxxxx xxxxxxx */ + 0x00, 0x7E, 0xFF, 0x80, /* xxxxxx xxxxxxxxx */ + 0x00, 0xFD, 0xFF, 0xC0, /* xxxxxx xxxxxxxxxxx */ + 0x01, 0xFB, 0xE3, 0xE0, /* xxxxxx xxxxx xxxxx */ + 0x03, 0xF7, 0xE7, 0xF0, /* xxxxxx xxxxxx xxxxxxx */ + 0x07, 0xEF, 0xED, 0xF8, /* xxxxxx xxxxxxx xx xxxxxx */ + 0x0F, 0xDF, 0x73, 0x7C, /* xxxxxx xxxxx xxx xx xxxxx */ + 0x1F, 0xBF, 0xBE, 0xFE, /* xxxxxx xxxxxxx xxxxx xxxxxxx */ + 0x1F, 0x7C, 0x1D, 0x7F, /* xxxxx xxxxx xxx x xxxxxxx*/ + 0x7F, 0xBE, 0xFF, 0xBE, /* xxxxxxxx xxxxx xxxxxxxxx xxxxx */ + 0x87, 0xDF, 0x77, 0xFC, /*x xxxxx xxxxx xxx xxxxxxxxx */ + 0xFF, 0xEF, 0xF7, 0xF8, /*xxxxxxxxxxx xxxxxxxx xxxxxxxx */ + 0x80, 0x27, 0xC1, 0xF0, /*x x xxxxx xxxxx */ + 0x88, 0x2B, 0xF7, 0xE0, /*x x x x xxxxxx xxxxxx */ + 0x90, 0x2D, 0xF7, 0xC0, /*x x x xx xxxxx xxxxx */ + 0xA5, 0x2E, 0xFF, 0x80, /*x x x x x xxx xxxxxxxxx */ + 0x80, 0x2F, 0x7F, 0x00, /*x x xxxx xxxxxxx */ + 0xFF, 0xEF, 0xBE, 0x00, /*xxxxxxxxxxx xxxxx xxxxx */ + 0x0A, 0x0F, 0xDC, 0x00, /* x x xxxxxx xxx */ + 0x1F, 0x07, 0xE8, 0x00, /* xxxxx xxxxxx x */ + 0x11, 0x03, 0xF0, 0x00, /* x x xxxxxx */ + 0x1F, 0x01, 0xE0, 0x00, /* xxxxx xxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x03, 0xF0, 0x00, /* xxxxxx */ + 0x00, 0x07, 0xF8, 0x00, /* xxxxxxxx */ + 0x00, 0x0F, 0xFC, 0x00, /* xxxxxxxxxx */ + 0x00, 0x1F, 0xFE, 0x00, /* xxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0x00, /* xxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0x80, /* xxxxxxxxxxxxxxxx */ + 0x00, 0xFF, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxxxx */ + 0x03, 0xFF, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxxxxxxx */ + 0x07, 0xFF, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxxxxxxx */ + 0x0F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x1F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x1F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFC, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0xF0, /*xxxxxxxxxxx xxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xE0, /*xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0xC0, /*xxxxxxxxxxx xxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0x80, /*xxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0x00, /*xxxxxxxxxxx xxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFE, 0x00, /*xxxxxxxxxxxxxxxxxxxxxxx */ + 0x0E, 0x0F, 0xFC, 0x00, /* xxx xxxxxxxxxx */ + 0x1F, 0x07, 0xF8, 0x00, /* xxxxx xxxxxxxx */ + 0x1F, 0x03, 0xF0, 0x00, /* xxxxx xxxxxx */ + 0x1F, 0x01, 0xE0, 0x00, /* xxxxx xxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 4, IBM RISC 6000 */ +#ifdef PLATTER_ICON + 0x79, 0xFC, 0x3E, 0x3E, /* xxxx xxxxxxx xxxxx xxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x79, 0xFF, 0x3E, 0x3E, /* xxxx xxxxxxxxx xxxxx xxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x30, 0xC3, 0x9E, 0x3C, /* xx xx xxx xxxx xxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x30, 0xFF, 0x1E, 0x3C, /* xx xxxxxxxx xxxx xxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x30, 0xFF, 0x1B, 0x6C, /* xx xxxxxxxx xx xx xx xx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x30, 0xC3, 0x9B, 0x6C, /* xx xx xxx xx xx xx xx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x79, 0xFF, 0x39, 0xCE, /* xxxx xxxxxxxxx xxx xxx xxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x79, 0xFC, 0x38, 0x8E, /* xxxx xxxxxxx xxx x xxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x20, 0x00, 0x00, 0x04, /* x x */ + 0x1F, 0xFF, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x08, 0x80, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0A, 0x80, /* x x x */ + 0xFF, 0xFF, 0xF2, 0x7F, /*xxxxxxxxxxxxxxxxxxxx x xxxxxxx*/ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ + /* MASK */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x1F, 0xFF, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFD, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx*/ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ +#else PLATTER_ICON + 0x79, 0xFC, 0x3E, 0x3E, /* xxxx xxxxxxx xxxxx xxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x79, 0xFF, 0x3E, 0x3E, /* xxxx xxxxxxxxx xxxxx xxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x30, 0xC3, 0x9E, 0x3C, /* xx xx xxx xxxx xxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x30, 0xFF, 0x1E, 0x3C, /* xx xxxxxxxx xxxx xxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x30, 0xFF, 0x1B, 0x6C, /* xx xxxxxxxx xx xx xx xx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x30, 0xC3, 0x9B, 0x6C, /* xx xx xxx xx xx xx xx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x79, 0xFF, 0x39, 0xCE, /* xxxx xxxxxxxxx xxx xxx xxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x79, 0xFC, 0x38, 0x8E, /* xxxx xxxxxxx xxx x xxx */ + 0x78, 0x00, 0x00, 0x00, /* xxxx */ + 0x84, 0x00, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0x88, 0x20, 0x00, 0x00, /*x x x */ + 0x90, 0x20, 0x00, 0x00, /*x x x */ + 0xA5, 0x20, 0x00, 0x00, /*x x x x x */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFC, 0x00, 0x00, 0x00, /*xxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0E, 0x00, 0x00, 0x00, /* xxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 5, Digital/VAX/ULTRIX */ +#ifdef PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xeb, 0xbe, 0xee, 0xf5, /*xxx x xxx xxxxx xxx xxx xxxx x x*/ + 0xea, 0xbe, 0xaa, 0xf5, /*xxx x x x xxxxx x x x x xxxx x x*/ + 0xeb, 0xbe, 0xea, 0x95, /*xxx x xxx xxxxx xxx x x x x x x*/ + 0x8a, 0xa2, 0xa0, 0xe5, /*x x x x x x x x xxx x x*/ + 0xaa, 0xaa, 0xaa, 0x85, /*x x x x x x x x x x x x x x x*/ + 0xaa, 0xaa, 0xaa, 0xa5, /*x x x x x x x x x x x x x x x x*/ + 0x8a, 0xa2, 0xac, 0x85, /*x x x x x x x x xx x x x*/ + 0xfb, 0xba, 0xee, 0xf7, /*xxxxx xxx xxx x xxx xxx xxxx xxx*/ + 0xfb, 0xba, 0xee, 0xf7, /*xxxxx xxx xxx x xxx xxx xxxx xxx*/ + 0xfb, 0xa6, 0xee, 0xf7, /*xxxxx xxx x xx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xff, 0xff, 0xff, 0xff, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x40, 0x00, 0x00, 0x02, /* x x */ + 0x3f, 0xff, 0xff, 0xfc, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x01, 0x40, /* x x */ + 0x00, 0x00, 0x01, 0x40, /* x x */ + 0x00, 0x00, 0x01, 0x40, /* x x */ + 0x00, 0x00, 0x01, 0x40, /* x x */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x02, 0x20, /* x x */ + 0x00, 0x00, 0x02, 0x20, /* x x */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x02, 0xa0, /* x x x */ + 0xff, 0xff, 0xfc, 0x9f, /*xxxxxxxxxxxxxxxxxxxxxx x xxxxx*/ + 0x00, 0x00, 0x01, 0x40, /* x x */ + 0xff, 0xff, 0xfe, 0x3f, /*xxxxxxxxxxxxxxxxxxxxxxx xxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xff, 0xff, 0xff, 0xff, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7f, 0xff, 0xff, 0xfe, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3f, 0xff, 0xff, 0xfc, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x01, 0xc0, /* xxx */ + 0x00, 0x00, 0x01, 0xc0, /* xxx */ + 0x00, 0x00, 0x01, 0xc0, /* xxx */ + 0x00, 0x00, 0x01, 0xc0, /* xxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0xff, 0xff, 0xff, 0xff, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xff, 0xff, 0xff, 0x7f, /*xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxx*/ + 0xff, 0xff, 0xfe, 0x3f, /*xxxxxxxxxxxxxxxxxxxxxxx xxxxxx*/ +#else PLATTER_ICON + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xeb, 0xbe, 0xee, 0xf5, /*xxx x xxx xxxxx xxx xxx xxxx x x*/ + 0xea, 0xbe, 0xaa, 0xf5, /*xxx x x x xxxxx x x x x xxxx x x*/ + 0xeb, 0xbe, 0xea, 0x95, /*xxx x xxx xxxxx xxx x x x x x x*/ + 0x8a, 0xa2, 0xa0, 0xe5, /*x x x x x x x x xxx x x*/ + 0xaa, 0xaa, 0xaa, 0x85, /*x x x x x x x x x x x x x x x*/ + 0xaa, 0xaa, 0xaa, 0xa5, /*x x x x x x x x x x x x x x x x*/ + 0x8a, 0xa2, 0xac, 0x85, /*x x x x x x x x xx x x x*/ + 0xfb, 0xba, 0xee, 0xf7, /*xxxxx xxx xxx x xxx xxx xxxx xxx*/ + 0xfb, 0xba, 0xee, 0xf7, /*xxxxx xxx xxx x xxx xxx xxxx xxx*/ + 0xfb, 0xa6, 0xee, 0xf7, /*xxxxx xxx x xx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x78, 0x00, 0x00, 0x00, /* xxxx */ + 0x84, 0x00, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0x88, 0x20, 0x00, 0x00, /*x x x */ + 0x90, 0x20, 0x00, 0x00, /*x x x */ + 0xA5, 0x20, 0x00, 0x00, /*x x x x x */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0xfb, 0xbe, 0xee, 0xf7, /*xxxxx xxx xxxxx xxx xxx xxxx xxx*/ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x78, 0x00, 0x00, 0x00, /* xxxx */ + 0xFC, 0x00, 0x00, 0x00, /*xxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0E, 0x00, 0x00, 0x00, /* xxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 6, Apple A/UX */ +#ifdef PLATTER_ICON + 0x00, 0x00, 0x06, 0x00, /* xx */ + 0x00, 0x00, 0x0C, 0x00, /* xx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x00, 0x07, 0x99, 0xC0, /* xxxx xx xxx */ + 0x00, 0x0F, 0xDB, 0xE0, /* xxxxxx xx xxxxx */ + 0x00, 0x1F, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x1F, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x1F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxx */ + 0x00, 0x0F, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x03, 0xFF, 0xE0, /* xxxxxxxxxxxxx */ + 0x00, 0x01, 0xEF, 0xC0, /* xxxx xxxxxx */ + 0x00, 0x00, 0xC7, 0x00, /* xx xxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x40, 0x00, 0x00, 0x02, /* x x */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x08, 0x80, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0A, 0x80, /* x x x */ + 0xFF, 0xFF, 0xF2, 0x7F, /*xxxxxxxxxxxxxxxxxxxx x xxxxxxx*/ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x06, 0x00, /* xx */ + 0x00, 0x00, 0x0C, 0x00, /* xx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x00, 0x07, 0x99, 0xC0, /* xxxx xx xxx */ + 0x00, 0x0F, 0xDB, 0xE0, /* xxxxxx xx xxxxx */ + 0x00, 0x1F, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x1F, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x1F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxx */ + 0x00, 0x0F, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x03, 0xFF, 0xE0, /* xxxxxxxxxxxxx */ + 0x00, 0x01, 0xEF, 0xC0, /* xxxx xxxxxx */ + 0x00, 0x00, 0xC7, 0x00, /* xx xxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFD, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx*/ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ +#else PLATTER_ICON + 0x00, 0x00, 0x06, 0x00, /* xx */ + 0x00, 0x00, 0x0C, 0x00, /* xx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x00, 0x07, 0x99, 0xC0, /* xxxx xx xxx */ + 0x00, 0x0F, 0xDB, 0xE0, /* xxxxxx xx xxxxx */ + 0x00, 0x1F, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxx */ + 0x78, 0x1F, 0xFF, 0xFC, /* xxxx xxxxxxxxxxxxxxxxxxx */ + 0x84, 0x1F, 0xFF, 0xF8, /*x x xxxxxxxxxxxxxxxxxx */ + 0xFF, 0xEF, 0xFF, 0xF0, /*xxxxxxxxxxx xxxxxxxxxxxxxxxx */ + 0x80, 0x23, 0xFF, 0xE0, /*x x xxxxxxxxxxxxx */ + 0x88, 0x21, 0xEF, 0xC0, /*x x x xxxx xxxxxx */ + 0x90, 0x20, 0xC7, 0x00, /*x x x xx xxx */ + 0xA5, 0x20, 0x00, 0x00, /*x x x x x */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x06, 0x00, /* xx */ + 0x00, 0x00, 0x0C, 0x00, /* xx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x00, 0x07, 0x99, 0xC0, /* xxxx xx xxx */ + 0x00, 0x0F, 0xDB, 0xE0, /* xxxxxx xx xxxxx */ + 0x00, 0x1F, 0xFF, 0xF0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xF8, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFF, 0xC0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xE0, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0x3F, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxx */ + 0x78, 0x1F, 0xFF, 0xFC, /* xxxx xxxxxxxxxxxxxxxxxxx */ + 0xFC, 0x0F, 0xFF, 0xF8, /*xxxxxx xxxxxxxxxxxxxxxxx */ + 0xFF, 0xE7, 0xFF, 0xF0, /*xxxxxxxxxxx xxxxxxxxxxxxxxx */ + 0xFF, 0xE3, 0xFF, 0xE0, /*xxxxxxxxxxx xxxxxxxxxxxxx */ + 0xFF, 0xE1, 0xEF, 0xC0, /*xxxxxxxxxxx xxxx xxxxxx */ + 0xFF, 0xE0, 0xC7, 0x00, /*xxxxxxxxxxx xx xxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0E, 0x00, 0x00, 0x00, /* xxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 7, Silicon Graphics/IRIX */ +#ifdef PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x06, 0xC1, 0xCE, /* xx xx xxx xxx */ + 0x00, 0x0E, 0xE2, 0x10, /* xxx xxx x x */ + 0x00, 0x1A, 0xB1, 0xD6, /* xx x x xx xxx x xx */ + 0x00, 0x32, 0x98, 0x52, /* xx x x xx x x x */ + 0x00, 0x32, 0x9B, 0x8E, /* xx x x xx xxx xxx */ + 0x00, 0xDA, 0xB6, 0x00, /* xx xx x x xx xx */ + 0x01, 0xEE, 0xEF, 0x00, /* xxxx xxx xxx xxxx */ + 0x01, 0xB7, 0xDB, 0x00, /* xx xx xxxxx xx xx */ + 0x01, 0x9F, 0xF3, 0x00, /* xx xxxxxxxxx xx */ + 0x01, 0x9F, 0xF3, 0x00, /* xx xxxxxxxxx xx */ + 0x01, 0xB7, 0xDB, 0x00, /* xx xx xxxxx xx xx */ + 0x01, 0xEE, 0xEF, 0x00, /* xxxx xxx xxx xxxx */ + 0x00, 0xDA, 0xB6, 0x00, /* xx xx x x xx xx */ + 0x00, 0x32, 0x98, 0x00, /* xx x x xx */ + 0x00, 0x32, 0x98, 0x00, /* xx x x xx */ + 0x00, 0x1A, 0xB0, 0x00, /* xx x x xx */ + 0x00, 0x0E, 0xE0, 0x00, /* xxx xxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x40, 0x00, 0x00, 0x02, /* x x */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x08, 0x80, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0A, 0x80, /* x x x */ + 0xFF, 0xFF, 0xF2, 0x7F, /*xxxxxxxxxxxxxxxxxxxx x xxxxxxx*/ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x0F, 0xFF, /* xxxxxxxxxxxx*/ + 0x00, 0x07, 0xCF, 0xFF, /* xxxxx xxxxxxxxxxxx*/ + 0x00, 0x0F, 0xEF, 0xFF, /* xxxxxxx xxxxxxxxxxxx*/ + 0x00, 0x1F, 0xF7, 0xFF, /* xxxxxxxxx xxxxxxxxxxx*/ + 0x00, 0x3F, 0xFB, 0xFF, /* xxxxxxxxxxx xxxxxxxxxx*/ + 0x00, 0x7F, 0xFD, 0xFF, /* xxxxxxxxxxxxx xxxxxxxxx*/ + 0x00, 0xFF, 0xFE, 0xFF, /* xxxxxxxxxxxxxxx xxxxxxxx*/ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0xFF, 0xFE, 0x00, /* xxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFC, 0x00, /* xxxxxxxxxxxxx */ + 0x00, 0x3F, 0xF8, 0x00, /* xxxxxxxxxxx */ + 0x00, 0x1F, 0xF0, 0x00, /* xxxxxxxxx */ + 0x00, 0x0F, 0xE0, 0x00, /* xxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFD, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx*/ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ +#else PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x06, 0xC1, 0xCE, /* xx xx xxx xxx */ + 0x00, 0x0E, 0xE2, 0x10, /* xxx xxx x x */ + 0x00, 0x1A, 0xB1, 0xD6, /* xx x x xx xxx x xx */ + 0x00, 0x32, 0x98, 0x52, /* xx x x xx x x x */ + 0x00, 0x32, 0x9B, 0x8E, /* xx x x xx xxx xxx */ + 0x00, 0xDA, 0xB6, 0x00, /* xx xx x x xx xx */ + 0x01, 0xEE, 0xEF, 0x00, /* xxxx xxx xxx xxxx */ + 0x01, 0xB7, 0xDB, 0x00, /* xx xx xxxxx xx xx */ + 0x01, 0x9F, 0xF3, 0x00, /* xx xxxxxxxxx xx */ + 0x01, 0x9F, 0xF3, 0x00, /* xx xxxxxxxxx xx */ + 0x01, 0xB7, 0xDB, 0x00, /* xx xx xxxxx xx xx */ + 0x01, 0xEE, 0xEF, 0x00, /* xxxx xxx xxx xxxx */ + 0x00, 0xDA, 0xB6, 0x00, /* xx xx x x xx xx */ + 0x00, 0x32, 0x98, 0x00, /* xx x x xx */ + 0x78, 0x32, 0x98, 0x00, /* xxxx xx x x xx */ + 0x84, 0x1A, 0xB0, 0x00, /*x x xx x x xx */ + 0xFF, 0xEE, 0xE0, 0x00, /*xxxxxxxxxxx xxx xxx */ + 0x80, 0x26, 0xC0, 0x00, /*x x xx xx */ + 0x88, 0x20, 0x00, 0x00, /*x x x */ + 0x90, 0x20, 0x00, 0x00, /*x x x */ + 0xA5, 0x20, 0x00, 0x00, /*x x x x x */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x0F, 0xFF, /* xxxxxxxxxxxx*/ + 0x00, 0x07, 0xCF, 0xFF, /* xxxxx xxxxxxxxxxxx*/ + 0x00, 0x0F, 0xEF, 0xFF, /* xxxxxxx xxxxxxxxxxxx*/ + 0x00, 0x1F, 0xF7, 0xFF, /* xxxxxxxxx xxxxxxxxxxx*/ + 0x00, 0x3F, 0xFB, 0xFF, /* xxxxxxxxxxx xxxxxxxxxx*/ + 0x00, 0x7F, 0xFD, 0xFF, /* xxxxxxxxxxxxx xxxxxxxxx*/ + 0x00, 0xFF, 0xFE, 0xFF, /* xxxxxxxxxxxxxxx xxxxxxxx*/ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x01, 0xFF, 0xFF, 0x00, /* xxxxxxxxxxxxxxxxx */ + 0x00, 0xFF, 0xFE, 0x00, /* xxxxxxxxxxxxxxx */ + 0x00, 0x7F, 0xFC, 0x00, /* xxxxxxxxxxxxx */ + 0x78, 0x3F, 0xF8, 0x00, /* xxxx xxxxxxxxxxx */ + 0xFC, 0x1F, 0xF0, 0x00, /*xxxxxx xxxxxxxxx */ + 0xFF, 0xEF, 0xE0, 0x00, /*xxxxxxxxxxx xxxxxxx */ + 0xFF, 0xE7, 0xC0, 0x00, /*xxxxxxxxxxx xxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0E, 0x00, 0x00, 0x00, /* xxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 8, Encore MultiMax */ +#ifdef PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x1F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x1F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x00, 0x00, 0x03, /* xx*/ + 0xDF, 0xFF, 0xFF, 0xFB, /*xx xxxxxxxxxxxxxxxxxxxxxxxxxx xx*/ + 0xD1, 0x68, 0x88, 0x8B, /*xx x x xx x x x x x xx*/ + 0xD7, 0x2B, 0xAA, 0xBB, /*xx x xxx x x xxx x x x x xxx xx*/ + 0xD1, 0x0B, 0xA8, 0x8B, /*xx x x x xxx x x x x xx*/ + 0xD7, 0x4B, 0xAA, 0xBB, /*xx x xxx x x xxx x x x x xxx xx*/ + 0xD1, 0x68, 0x8A, 0x8B, /*xx x x xx x x x x x x xx*/ + 0xDF, 0xFF, 0xFF, 0xFB, /*xx xxxxxxxxxxxxxxxxxxxxxxxxxx xx*/ + 0xC0, 0x00, 0x00, 0x00, /*xx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x40, 0x00, 0x00, 0x02, /* x x */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x08, 0x80, /* x x */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0A, 0x80, /* x x x */ + 0xFF, 0xFF, 0xF2, 0x7F, /*xxxxxxxxxxxxxxxxxxxx x xxxxxxx*/ + 0x00, 0x00, 0x05, 0x00, /* x x */ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x1F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x1F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x3F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFC, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x07, 0x00, /* xxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0x00, 0x00, 0x0F, 0x80, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFD, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx*/ + 0xFF, 0xFF, 0xF8, 0xFF, /*xxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ +#else PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x1F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x1F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x00, 0x00, 0x03, /* xx*/ + 0xDF, 0xFF, 0xFF, 0xFB, /*xx xxxxxxxxxxxxxxxxxxxxxxxxxx xx*/ + 0xD1, 0x68, 0x88, 0x8B, /*xx x x xx x x x x x xx*/ + 0xD7, 0x2B, 0xAA, 0xBB, /*xx x xxx x x xxx x x x x xxx xx*/ + 0xD1, 0x0B, 0xA8, 0x8B, /*xx x x x xxx x x x x xx*/ + 0xD7, 0x4B, 0xAA, 0xBB, /*xx x xxx x x xxx x x x x xxx xx*/ + 0xD1, 0x68, 0x8A, 0x8B, /*xx x x xx x x x x x x xx*/ + 0xDF, 0xFF, 0xFF, 0xFB, /*xx xxxxxxxxxxxxxxxxxxxxxxxxxx xx*/ + 0xC0, 0x00, 0x00, 0x00, /*xx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x7C, 0x00, 0x00, 0x00, /* xxxxx */ + 0x82, 0x00, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0x88, 0x20, 0x00, 0x00, /*x x x */ + 0x90, 0x20, 0x00, 0x00, /*x x x */ + 0xA5, 0x20, 0x00, 0x00, /*x x x x x */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x1F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x1F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x3F, 0xFF, 0xFF, 0xFF, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFC, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xFF, 0xFF, 0xFF, 0xF8, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x7C, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFE, 0x00, 0x00, 0x00, /*xxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0E, 0x00, 0x00, 0x00, /* xxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + +/* Start of contributed ICN# range */ + + /* ICON 9, SPARCStation */ + 0x00, 0x00, 0x00, 0xc0, /* xx */ + 0x00, 0x00, 0x01, 0x80, /* xx */ + 0x00, 0x00, 0x03, 0x04, /* xx x */ + 0x00, 0x00, 0x06, 0x1c, /* xx xxx */ + 0x00, 0x00, 0x0c, 0x98, /* xx x xx */ + 0x00, 0x00, 0x19, 0xeb, /* xx xxxx x xx*/ + 0x00, 0x00, 0x13, 0xfa, /* x xxxxxxx x */ + 0x00, 0x00, 0x27, 0x9e, /* x xxxx xxxx */ + 0x00, 0x00, 0x2c, 0x00, /* x xx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x80, 0x00, /* x */ + 0x00, 0x01, 0x00, 0x00, /* x */ + 0x00, 0x03, 0x80, 0x00, /* xxx */ + 0x00, 0x01, 0x3f, 0x52, /* x xxxxxx x x x */ + 0x78, 0x02, 0x40, 0x55, /* xxxx x x x x x x*/ + 0x84, 0x04, 0x3f, 0x55, /*x x x xxxxxx x x x x*/ + 0xff, 0xe0, 0x00, 0x55, /*xxxxxxxxxxx x x x x*/ + 0x80, 0x20, 0x7e, 0x55, /*x x xxxxxx x x x x*/ + 0x88, 0x20, 0x01, 0x25, /*x x x x x x x*/ + 0x90, 0x20, 0x7e, 0x00, /*x x x xxxxxx */ + 0xa5, 0x20, 0x00, 0x3f, /*x x x x x xxxxxx*/ + 0x80, 0x20, 0x52, 0x40, /*x x x x x x */ + 0xff, 0xe0, 0x55, 0x3f, /*xxxxxxxxxxx x x x x xxxxxx*/ + 0x0a, 0x00, 0x55, 0x00, /* x x x x x x */ + 0x1f, 0x00, 0x55, 0x7e, /* xxxxx x x x x xxxxxx */ + 0x11, 0x00, 0x55, 0x01, /* x x x x x x x*/ + 0x1f, 0x00, 0x25, 0x7e, /* xxxxx x x x xxxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xe4, 0xff, 0xff, 0xff, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0a, 0x00, 0x00, 0x00, /* x x */ + 0xf1, 0xff, 0xff, 0xff, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* Mask */ + 0x00, 0x00, 0x00, 0xff, /* xxxxxxxx*/ + 0x00, 0x00, 0x01, 0xff, /* xxxxxxxxx*/ + 0x00, 0x00, 0x03, 0xff, /* xxxxxxxxxx*/ + 0x00, 0x00, 0x07, 0xff, /* xxxxxxxxxxx*/ + 0x00, 0x00, 0x0f, 0xff, /* xxxxxxxxxxxx*/ + 0x00, 0x00, 0x1f, 0xff, /* xxxxxxxxxxxxx*/ + 0x00, 0x00, 0x1f, 0xfe, /* xxxxxxxxxxxx */ + 0x00, 0x00, 0x3f, 0x9e, /* xxxxxxx xxxx */ + 0x00, 0x00, 0x3c, 0x00, /* xxxx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x00, 0x00, 0xc0, 0x00, /* xx */ + 0x00, 0x01, 0xc0, 0x00, /* xxx */ + 0x00, 0x03, 0xc0, 0x00, /* xxxx */ + 0x00, 0x07, 0xc0, 0x00, /* xxxxx */ + 0x00, 0x07, 0xbf, 0xfe, /* xxxx xxxxxxxxxxxxx */ + 0x78, 0x07, 0x7f, 0xff, /* xxxx xxx xxxxxxxxxxxxxxx*/ + 0xfc, 0x0e, 0x7f, 0xff, /*xxxxxx xxx xxxxxxxxxxxxxxx*/ + 0xff, 0xec, 0x7f, 0xff, /*xxxxxxxxxxx xx xxxxxxxxxxxxxxx*/ + 0xff, 0xe0, 0x7f, 0xff, /*xxxxxxxxxxx xxxxxxxxxxxxxxx*/ + 0xff, 0xe0, 0x7f, 0xff, /*xxxxxxxxxxx xxxxxxxxxxxxxxx*/ + 0xff, 0xe0, 0x7f, 0xff, /*xxxxxxxxxxx xxxxxxxxxxxxxxx*/ + 0xff, 0xe0, 0x7f, 0xff, /*xxxxxxxxxxx xxxxxxxxxxxxxxx*/ + 0xff, 0xe0, 0x7f, 0xff, /*xxxxxxxxxxx xxxxxxxxxxxxxxx*/ + 0xff, 0xe0, 0x7f, 0xff, /*xxxxxxxxxxx xxxxxxxxxxxxxxx*/ + 0x0e, 0x00, 0x7f, 0xff, /* xxx xxxxxxxxxxxxxxx*/ + 0x1f, 0x00, 0x7f, 0xff, /* xxxxx xxxxxxxxxxxxxxx*/ + 0x1f, 0x00, 0x7f, 0xff, /* xxxxx xxxxxxxxxxxxxxx*/ + 0x1f, 0x00, 0x3f, 0xfe, /* xxxxx xxxxxxxxxxxxx */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0xff, 0xff, 0xff, 0xff, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xfb, 0xff, 0xff, 0xff, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xf1, 0xff, 0xff, 0xff, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + + /* ICON 10, original SUN */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x03, 0x80, /* xxx */ + 0x00, 0x00, 0x0a, 0x40, /* x x x */ + 0x00, 0x00, 0x29, 0x40, /* x x x x */ + 0x00, 0x01, 0xa5, 0x20, /* xx x x x x */ + 0x00, 0x06, 0x14, 0xa0, /* xx x x x x */ + 0x00, 0x18, 0x52, 0x90, /* xx x x x x x */ + 0x00, 0x61, 0x8a, 0x40, /* xx xx x x x */ + 0x00, 0x86, 0x09, 0x08, /* x xx x x x */ + 0x00, 0x98, 0xe6, 0x30, /* x xx xxx xx xx */ + 0x00, 0x63, 0x10, 0xc0, /* xx xx x xx */ + 0x00, 0x0c, 0x63, 0x0c, /* xx xx xx xx */ + 0x00, 0x31, 0x8c, 0x30, /* xx xx xx xx */ + 0x00, 0x06, 0x08, 0xc6, /* xx x xx xx */ + 0x00, 0x18, 0xe7, 0x1a, /* xx xxx xxx xx x */ + 0x78, 0x02, 0x90, 0x62, /* xxxx x x x xx x */ + 0x84, 0x0a, 0x51, 0x84, /*x x x x x x xx x */ + 0xff, 0xe9, 0x4a, 0x18, /*xxxxxxxxxxx x x x x x xx */ + 0x80, 0x45, 0x28, 0x60, /*x x x x x x xx */ + 0x88, 0x44, 0xa5, 0x80, /*x x x x x x x xx */ + 0x90, 0x42, 0x94, 0x00, /*x x x x x x x */ + 0xa5, 0x42, 0x50, 0x00, /*x x x x x x x x */ + 0x80, 0x41, 0xc0, 0x00, /*x x xxx */ + 0xff, 0xe0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0a, 0x00, 0x00, 0x00, /* x x */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xe4, 0xff, 0xff, 0xff, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0a, 0x00, 0x00, 0x00, /* x x */ + 0xf1, 0xff, 0xff, 0xff, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* Mask */ + 0x00, 0x00, 0x07, 0xc0, /* xxxxx */ + 0x00, 0x00, 0x1f, 0xc0, /* xxxxxxx */ + 0x00, 0x00, 0x7f, 0xe0, /* xxxxxxxxxx */ + 0x00, 0x01, 0xff, 0xf0, /* xxxxxxxxxxxxx */ + 0x00, 0x07, 0xff, 0xf0, /* xxxxxxxxxxxxxxx */ + 0x00, 0x1f, 0xff, 0xf8, /* xxxxxxxxxxxxxxxxxx */ + 0x00, 0x7f, 0xff, 0xf8, /* xxxxxxxxxxxxxxxxxxxx */ + 0x01, 0xff, 0xff, 0xfc, /* xxxxxxxxxxxxxxxxxxxxxxx */ + 0x01, 0xff, 0xff, 0xfc, /* xxxxxxxxxxxxxxxxxxxxxxx */ + 0x01, 0xff, 0xff, 0xfe, /* xxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0xff, 0xff, 0xfe, /* xxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0xff, 0xff, 0xfe, /* xxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x7f, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x7f, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x3f, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxxx*/ + 0x78, 0x3f, 0xff, 0xff, /* xxxx xxxxxxxxxxxxxxxxxxxxxx*/ + 0xfc, 0x1f, 0xff, 0xff, /*xxxxxx xxxxxxxxxxxxxxxxxxxxx*/ + 0xff, 0xff, 0xff, 0xfe, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0xff, 0xef, 0xff, 0xf8, /*xxxxxxxxxxx xxxxxxxxxxxxxxxxx */ + 0xff, 0xef, 0xff, 0xe0, /*xxxxxxxxxxx xxxxxxxxxxxxxxx */ + 0xff, 0xef, 0xff, 0x80, /*xxxxxxxxxxx xxxxxxxxxxxxx */ + 0xff, 0xe7, 0xfc, 0x00, /*xxxxxxxxxxx xxxxxxxxx */ + 0xff, 0xe7, 0xf0, 0x00, /*xxxxxxxxxxx xxxxxxx */ + 0xff, 0xe3, 0xc0, 0x00, /*xxxxxxxxxxx xxxx */ + 0x0e, 0x00, 0x00, 0x00, /* xxx */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0xff, 0xff, 0xff, 0xff, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xfb, 0xff, 0xff, 0xff, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xf1, 0xff, 0xff, 0xff, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + + /* ICON 11, original NeXT */ + 0x00, 0x00, 0x00, 0xc0, /* xx */ + 0x00, 0x00, 0x03, 0xc0, /* xxxx */ + 0x00, 0x00, 0x0f, 0xe0, /* xxxxxxx */ + 0x00, 0x00, 0x3c, 0xe0, /* xxxx xxx */ + 0x00, 0x00, 0xfa, 0x70, /* xxxxx x xxx */ + 0x00, 0x03, 0xd8, 0xf0, /* xxx xxx xxxx */ + 0x00, 0x0f, 0xcb, 0xd8, /* xxxxx x x xx xxx */ + 0x00, 0x0e, 0x6c, 0x38, /* xxx xx xx xxx */ + 0x00, 0x1b, 0x07, 0xec, /* xx xx xxxxxx xx */ + 0x00, 0x1b, 0xb7, 0x8c, /* xx xxx xx xxxx xx */ + 0x00, 0x3d, 0x9d, 0x1e, /* xxxx xx xxx x xxxx */ + 0x00, 0x3d, 0xfd, 0xce, /* xxxx xxxxxxx xxx xxx */ + 0x00, 0x1e, 0xc1, 0xef, /* xxxx xx xxxx xxxx*/ + 0x00, 0x1e, 0xf0, 0xfd, /* xxxx xxxx xxxxxx x*/ + 0x00, 0x0f, 0x77, 0xf3, /* xxxx xxx xxxxxxx xx*/ + 0x78, 0x0f, 0x77, 0xce, /* xxxx xxxx xxx xxxxx xxx */ + 0x84, 0x07, 0xbf, 0x3e, /*x x xxxx xxxxxx xxxxx */ + 0xff, 0xe7, 0xbc, 0xfc, /*xxxxxxxxxxx xxxx xxxx xxxxxx */ + 0x80, 0x23, 0xd3, 0xf0, /*x x xxxx x xxxxxx */ + 0x88, 0x23, 0xcf, 0xc0, /*x x x xxxx xxxxxx */ + 0x90, 0x21, 0xdf, 0x00, /*x x x xxx xxxxx */ + 0xa5, 0x21, 0xbc, 0x00, /*x x x x x xx xxxx */ + 0x80, 0x20, 0xb0, 0x00, /*x x x xx */ + 0xff, 0xe0, 0xc0, 0x00, /*xxxxxxxxxxx xx */ + 0x0a, 0x00, 0x00, 0x00, /* x x */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xe4, 0xff, 0xff, 0xff, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0a, 0x00, 0x00, 0x00, /* x x */ + 0xf1, 0xff, 0xff, 0xff, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* Mask */ + 0x00, 0x00, 0x01, 0xe0, /* xxxx */ + 0x00, 0x00, 0x07, 0xe0, /* xxxxxx */ + 0x00, 0x00, 0x1f, 0xf0, /* xxxxxxxxx */ + 0x00, 0x00, 0x7f, 0xf0, /* xxxxxxxxxxx */ + 0x00, 0x01, 0xff, 0xf8, /* xxxxxxxxxxxxxx */ + 0x00, 0x07, 0xff, 0xf8, /* xxxxxxxxxxxxxxxx */ + 0x00, 0x1f, 0xff, 0xfc, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x1f, 0xff, 0xfc, /* xxxxxxxxxxxxxxxxxxx */ + 0x00, 0x3f, 0xff, 0xfe, /* xxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x3f, 0xff, 0xfe, /* xxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x7f, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x7f, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x3f, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x3f, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x1f, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxx*/ + 0x78, 0x1f, 0xff, 0xff, /* xxxx xxxxxxxxxxxxxxxxxxxxx*/ + 0xfc, 0x0f, 0xff, 0xff, /*xxxxxx xxxxxxxxxxxxxxxxxxxx*/ + 0xff, 0xef, 0xff, 0xfe, /*xxxxxxxxxxx xxxxxxxxxxxxxxxxxxx */ + 0xff, 0xe7, 0xff, 0xf8, /*xxxxxxxxxxx xxxxxxxxxxxxxxxx */ + 0xff, 0xe7, 0xff, 0xc8, /*xxxxxxxxxxx xxxxxxxxxxxxxx */ + 0xff, 0xe3, 0xff, 0x80, /*xxxxxxxxxxx xxxxxxxxxxx */ + 0xff, 0xe3, 0xfe, 0x00, /*xxxxxxxxxxx xxxxxxxxx */ + 0xff, 0xe1, 0xf8, 0x00, /*xxxxxxxxxxx xxxxxx */ + 0xff, 0xe1, 0xe0, 0x00, /*xxxxxxxxxxx xxxx */ + 0x0e, 0x00, 0x00, 0x00, /* xxx */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1f, 0x00, 0x00, 0x00, /* xxxxx */ + 0xff, 0xff, 0xff, 0xff, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xfb, 0xff, 0xff, 0xff, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xf1, 0xff, 0xff, 0xff, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + + /* ICON 12, gould */ +#ifdef PLATTER_ICON + 0x00, 0x03, 0xf8, 0x00, /* xxxxxxx */ + 0x00, 0x01, 0xf8, 0x00, /* xxxxxx */ + 0x00, 0x00, 0xf8, 0x00, /* xxxxx */ + 0x00, 0x00, 0x78, 0x00, /* xxxx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x07, 0xff, 0xcf, 0xf0, /* xxxxxxxxxxxxx xxxxxxxx */ + 0x07, 0xff, 0xc7, 0xf0, /* xxxxxxxxxxxxx xxxxxxx */ + 0x07, 0xff, 0xc3, 0xf0, /* xxxxxxxxxxxxx xxxxxx */ + 0x07, 0xff, 0xc1, 0xf0, /* xxxxxxxxxxxxx xxxxx */ + 0x07, 0xff, 0xc3, 0xf0, /* xxxxxxxxxxxxx xxxxxx */ + 0x07, 0xff, 0xc7, 0xf0, /* xxxxxxxxxxxxx xxxxxxx */ + 0x07, 0xff, 0xcf, 0xf0, /* xxxxxxxxxxxxx xxxxxxxx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x00, 0x00, 0x78, 0x00, /* xxxx */ + 0x00, 0x00, 0xf8, 0x00, /* xxxxx */ + 0x00, 0x01, 0xf8, 0x00, /* xxxxxx */ + 0x00, 0x03, 0xf8, 0x00, /* xxxxxxx */ + 0x7f, 0xff, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x20, 0x00, 0x00, 0x02, /* x x */ + 0x1f, 0xff, 0xff, 0xfc, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x01, 0x40, /* x x */ + 0x00, 0x00, 0x01, 0x40, /* x x */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x02, 0x20, /* x x */ + 0x00, 0x00, 0x02, 0x20, /* x x */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x02, 0xa0, /* x x x */ + 0xff, 0xff, 0xfc, 0x9f, /*xxxxxxxxxxxxxxxxxxxxxx x xxxxx*/ + 0x00, 0x00, 0x01, 0x40, /* x x */ + 0xff, 0xff, 0xfe, 0x3f, /*xxxxxxxxxxxxxxxxxxxxxxx xxxxxx*/ + /* Mask */ + 0x00, 0x03, 0xf8, 0x00, /* xxxxxxx */ + 0x00, 0x01, 0xf8, 0x00, /* xxxxxx */ + 0x00, 0x00, 0xf8, 0x00, /* xxxxx */ + 0x00, 0x00, 0x78, 0x00, /* xxxx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x07, 0xff, 0xcf, 0xf0, /* xxxxxxxxxxxxx xxxxxxxx */ + 0x07, 0xff, 0xc7, 0xf0, /* xxxxxxxxxxxxx xxxxxxx */ + 0x07, 0xff, 0xc3, 0xf0, /* xxxxxxxxxxxxx xxxxxx */ + 0x07, 0xff, 0xc1, 0xf0, /* xxxxxxxxxxxxx xxxxx */ + 0x07, 0xff, 0xc3, 0xf0, /* xxxxxxxxxxxxx xxxxxx */ + 0x07, 0xff, 0xc7, 0xf0, /* xxxxxxxxxxxxx xxxxxxx */ + 0x07, 0xff, 0xcf, 0xf0, /* xxxxxxxxxxxxx xxxxxxxx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x00, 0x00, 0x78, 0x00, /* xxxx */ + 0x00, 0x00, 0xf8, 0x00, /* xxxxx */ + 0x00, 0x01, 0xf8, 0x00, /* xxxxxx */ + 0x00, 0x03, 0xf8, 0x00, /* xxxxxxx */ + 0x7f, 0xff, 0xff, 0xff, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x3f, 0xff, 0xff, 0xfe, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x1f, 0xff, 0xff, 0xfc, /* xxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x01, 0xc0, /* xxx */ + 0x00, 0x00, 0x01, 0xc0, /* xxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0x00, 0x00, 0x03, 0xe0, /* xxxxx */ + 0xff, 0xff, 0xff, 0xff, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xff, 0xff, 0xff, 0x7f, /*xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxx*/ + 0xff, 0xff, 0xfe, 0x3f, /*xxxxxxxxxxxxxxxxxxxxxxx xxxxxx*/ +#else PLATTER_ICON + 0x00, 0x03, 0xf8, 0x00, /* xxxxxxx */ + 0x00, 0x01, 0xf8, 0x00, /* xxxxxx */ + 0x00, 0x00, 0xf8, 0x00, /* xxxxx */ + 0x00, 0x00, 0x78, 0x00, /* xxxx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x07, 0xff, 0xcf, 0xf0, /* xxxxxxxxxxxxx xxxxxxxx */ + 0x07, 0xff, 0xc7, 0xf0, /* xxxxxxxxxxxxx xxxxxxx */ + 0x07, 0xff, 0xc3, 0xf0, /* xxxxxxxxxxxxx xxxxxx */ + 0x07, 0xff, 0xc1, 0xf0, /* xxxxxxxxxxxxx xxxxx */ + 0x07, 0xff, 0xc3, 0xf0, /* xxxxxxxxxxxxx xxxxxx */ + 0x07, 0xff, 0xc7, 0xf0, /* xxxxxxxxxxxxx xxxxxxx */ + 0x07, 0xff, 0xcf, 0xf0, /* xxxxxxxxxxxxx xxxxxxxx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x78, 0x00, 0x78, 0x00, /* xxxx xxxx */ + 0x84, 0x00, 0xf8, 0x00, /*x x xxxxx */ + 0xff, 0xe1, 0xf8, 0x00, /*xxxxxxxxxxx xxxxxx */ + 0x80, 0x23, 0xf8, 0x00, /*x x xxxxxxx */ + 0x88, 0x20, 0x00, 0x00, /*x x x */ + 0x90, 0x20, 0x00, 0x00, /*x x x */ + 0xA5, 0x20, 0x00, 0x00, /*x x x x x */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* Mask */ + 0x00, 0x03, 0xf8, 0x00, /* xxxxxxx */ + 0x00, 0x01, 0xf8, 0x00, /* xxxxxx */ + 0x00, 0x00, 0xf8, 0x00, /* xxxxx */ + 0x00, 0x00, 0x78, 0x00, /* xxxx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x07, 0xff, 0xcf, 0xf0, /* xxxxxxxxxxxxx xxxxxxxx */ + 0x07, 0xff, 0xc7, 0xf0, /* xxxxxxxxxxxxx xxxxxxx */ + 0x07, 0xff, 0xc3, 0xf0, /* xxxxxxxxxxxxx xxxxxx */ + 0x07, 0xff, 0xc1, 0xf0, /* xxxxxxxxxxxxx xxxxx */ + 0x07, 0xff, 0xc3, 0xf0, /* xxxxxxxxxxxxx xxxxxx */ + 0x07, 0xff, 0xc7, 0xf0, /* xxxxxxxxxxxxx xxxxxxx */ + 0x07, 0xff, 0xcf, 0xf0, /* xxxxxxxxxxxxx xxxxxxxx */ + 0x00, 0x00, 0x18, 0x00, /* xx */ + 0x00, 0x00, 0x38, 0x00, /* xxx */ + 0x78, 0x00, 0x78, 0x00, /* xxxx xxxx */ + 0xfc, 0x00, 0xf8, 0x00, /*xxxxxx xxxxx */ + 0xff, 0xe1, 0xf8, 0x00, /*xxxxxxxxxxx xxxxxx */ + 0xff, 0xe3, 0xf8, 0x00, /*xxxxxxxxxxx xxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0E, 0x00, 0x00, 0x00, /* xxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 13, original Digital/ULTRIX */ +#ifdef PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xFB, 0xBD, 0xDE, 0xF7, /*xxxxx xxx xxxx xxx xxxx xxxx xxx*/ + 0xFB, 0xBD, 0xDE, 0xF7, /*xxxxx xxx xxxx xxx xxxx xxxx xxx*/ + 0xEA, 0xBD, 0x56, 0xF5, /*xxx x x x xxxx x x x xx xxxx x x*/ + 0xEB, 0xBD, 0xD2, 0xF5, /*xxx x xxx xxxx xxx x x xxxx x x*/ + 0xCA, 0xA5, 0x56, 0xD5, /*xx x x x x x x x x xx xx x x x*/ + 0xAA, 0xA5, 0x56, 0x95, /*x x x x x x x x x x xx x x x x*/ + 0x8A, 0xB5, 0x56, 0x95, /*x x x x xx x x x x xx x x x x*/ + 0xFB, 0xA5, 0xDE, 0xF7, /*xxxxx xxx x x xxx xxxx xxxx xxx*/ + 0xFB, 0xBD, 0xDE, 0xF7, /*xxxxx xxx xxxx xxx xxxx xxxx xxx*/ + 0xFB, 0xBD, 0xDE, 0xF7, /*xxxxx xxx xxxx xxx xxxx xxxx xxx*/ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x40, 0x00, 0x00, 0x02, /* x x */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x02, 0x80, /* x x */ + 0x00, 0x00, 0x02, 0x80, /* x x */ + 0x00, 0x00, 0x02, 0x80, /* x x */ + 0x00, 0x00, 0x02, 0x80, /* x x */ + 0x00, 0x00, 0x02, 0x80, /* x x */ + 0x00, 0x00, 0x02, 0x80, /* x x */ + 0x00, 0x00, 0x02, 0x80, /* x x */ + 0x00, 0x00, 0x07, 0xC0, /* xxxxx */ + 0x00, 0x00, 0x04, 0x40, /* x x */ + 0x00, 0x00, 0x07, 0xC0, /* xxxxx */ + 0x00, 0x00, 0x05, 0x40, /* x x x */ + 0xFF, 0xFF, 0xF9, 0x3F, /*xxxxxxxxxxxxxxxxxxxxx x xxxxxx*/ + 0x00, 0x00, 0x02, 0x80, /* x x */ + 0xFF, 0xFF, 0xFC, 0x7F, /*xxxxxxxxxxxxxxxxxxxxxx xxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x7F, 0xFF, 0xFF, 0xFE, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x3F, 0xFF, 0xFF, 0xFC, /* xxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + 0x00, 0x00, 0x03, 0x80, /* xxx */ + 0x00, 0x00, 0x03, 0x80, /* xxx */ + 0x00, 0x00, 0x03, 0x80, /* xxx */ + 0x00, 0x00, 0x03, 0x80, /* xxx */ + 0x00, 0x00, 0x03, 0x80, /* xxx */ + 0x00, 0x00, 0x03, 0x80, /* xxx */ + 0x00, 0x00, 0x03, 0x80, /* xxx */ + 0x00, 0x00, 0x07, 0xC0, /* xxxxx */ + 0x00, 0x00, 0x07, 0xC0, /* xxxxx */ + 0x00, 0x00, 0x07, 0xC0, /* xxxxx */ + 0x00, 0x00, 0x07, 0xC0, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFE, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx*/ + 0xFF, 0xFF, 0xFC, 0x7F, /*xxxxxxxxxxxxxxxxxxxxxx xxxxxxx*/ +#else PLATTER_ICON + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xFB, 0xBD, 0xDE, 0xF7, /*xxxxx xxx xxxx xxx xxxx xxxx xxx*/ + 0xFB, 0xBD, 0xDE, 0xF7, /*xxxxx xxx xxxx xxx xxxx xxxx xxx*/ + 0xEA, 0xBD, 0x56, 0xF5, /*xxx x x x xxxx x x x xx xxxx x x*/ + 0xEB, 0xBD, 0xD2, 0xF5, /*xxx x xxx xxxx xxx x x xxxx x x*/ + 0xCA, 0xA5, 0x56, 0xD5, /*xx x x x x x x x x xx xx x x x*/ + 0xAA, 0xA5, 0x56, 0x95, /*x x x x x x x x x x xx x x x x*/ + 0x8A, 0xB5, 0x56, 0x95, /*x x x x xx x x x x xx x x x x*/ + 0xFB, 0xA5, 0xDE, 0xF7, /*xxxxx xxx x x xxx xxxx xxxx xxx*/ + 0xFB, 0xBD, 0xDE, 0xF7, /*xxxxx xxx xxxx xxx xxxx xxxx xxx*/ + 0xFB, 0xBD, 0xDE, 0xF7, /*xxxxx xxx xxxx xxx xxxx xxxx xxx*/ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x78, 0x00, 0x00, 0x00, /* xxxx */ + 0x84, 0x00, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0x88, 0x20, 0x00, 0x00, /*x x x */ + 0x90, 0x20, 0x00, 0x00, /*x x x */ + 0xA5, 0x20, 0x00, 0x00, /*x x x x x */ + 0x80, 0x20, 0x00, 0x00, /*x x */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x11, 0x00, 0x00, 0x00, /* x x */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x15, 0x00, 0x00, 0x00, /* x x x */ + 0xE4, 0xFF, 0xFF, 0xFF, /*xxx x xxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x0A, 0x00, 0x00, 0x00, /* x x */ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x78, 0x00, 0x00, 0x00, /* xxxx */ + 0xFC, 0x00, 0x00, 0x00, /*xxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0xFF, 0xE0, 0x00, 0x00, /*xxxxxxxxxxx */ + 0x0E, 0x00, 0x00, 0x00, /* xxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0x1F, 0x00, 0x00, 0x00, /* xxxxx */ + 0xFF, 0xFF, 0xFF, 0xFF, /*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xFB, 0xFF, 0xFF, 0xFF, /*xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx*/ + 0xF1, 0xFF, 0xFF, 0xFF, /*xxxx xxxxxxxxxxxxxxxxxxxxxxxxx*/ +#endif PLATTER_ICON + + /* ICON 14, Hewlett-Packard */ +#ifdef PLATTER_ICON + 0x00, 0x0f, 0x80, 0x00, /* XXXXX */ + 0x00, 0x08, 0x80, 0x00, /* X X */ + 0x00, 0x19, 0x80, 0x00, /* XX XX */ + 0x00, 0x11, 0x00, 0x00, /* X X */ + 0x00, 0x33, 0x00, 0x00, /* XX XX */ + 0x00, 0x22, 0x00, 0x00, /* X X */ + 0x00, 0x66, 0x00, 0x00, /* XX XX */ + 0x00, 0x47, 0xc1, 0xfc, /* X XXXXX XXXXXXX */ + 0x00, 0xc0, 0x43, 0x02, /* XX X XX X */ + 0x00, 0x8c, 0x22, 0x01, /* X XX X X X */ + 0x01, 0x9a, 0x26, 0x71, /* XX XX X X XX XXX X */ + 0x01, 0x12, 0x24, 0x49, /* X X X X X X X X */ + 0x03, 0x36, 0x6c, 0xc9, /* XX XX XX XX XX XX X X */ + 0x02, 0x24, 0x48, 0x91, /* X X X X X X X X */ + 0x06, 0x6c, 0xd9, 0xb3, /* XX XX XX XX XX XX XX XX */ + 0x04, 0x48, 0x91, 0x22, /* X X X X X X X X */ + 0x0c, 0xd9, 0xb3, 0x66, /* XX XX XX XX XX XX XX XX */ + 0x08, 0x91, 0x23, 0xc4, /* X X X X X XXXX X */ + 0x19, 0xb3, 0x60, 0x0c, /* XX XX XX XX XX XX */ + 0x1f, 0x3e, 0x47, 0xf8, /* XXXXX XXXXX X XXXXXXXX */ + 0xff, 0xff, 0xcf, 0xff, /* XXXXXXXXXXXXXXXXXX XXXXXXXXXXXX */ + 0x40, 0x00, 0xc8, 0x02, /* X XX X X */ + 0x3f, 0xff, 0x9f, 0xfc, /* XXXXXXXXXXXXXXX XXXXXXXXXXX */ + 0x00, 0x01, 0x9a, 0x00, /* XX XX X */ + 0x00, 0x01, 0x3f, 0x00, /* X XXXXXX */ + 0x00, 0x03, 0x31, 0x00, /* XX XX X */ + 0x00, 0x03, 0xf1, 0x00, /* XXXXXX X */ + 0x00, 0x00, 0x1f, 0x00, /* XXXXX */ + 0x00, 0x00, 0x15, 0x00, /* X X X */ + 0xff, 0xff, 0xe4, 0xff, /* XXXXXXXXXXXXXXXXXXX X XXXXXXXX */ + 0x00, 0x00, 0x0a, 0x00, /* X X */ + 0xff, 0xff, 0xf1, 0xff, /* XXXXXXXXXXXXXXXXXXXX XXXXXXXXX */ + /* MASK */ + 0x00, 0x0f, 0x80, 0x00, /* XXXXX */ + 0x00, 0x0f, 0x80, 0x00, /* XXXXX */ + 0x00, 0x1f, 0x80, 0x00, /* XXXXXX */ + 0x00, 0x1f, 0x00, 0x00, /* XXXXX */ + 0x00, 0x3f, 0x00, 0x00, /* XXXXXX */ + 0x00, 0x3e, 0x00, 0x00, /* XXXXX */ + 0x00, 0x7e, 0x00, 0x00, /* XXXXXX */ + 0x00, 0x7f, 0xc1, 0xfc, /* XXXXXXXXX XXXXXXX */ + 0x00, 0xff, 0xc3, 0xfe, /* XXXXXXXXXX XXXXXXXXX */ + 0x00, 0xff, 0xe3, 0xff, /* XXXXXXXXXXX XXXXXXXXXX */ + 0x01, 0xfb, 0xe7, 0xff, /* XXXXXX XXXXX XXXXXXXXXXX */ + 0x01, 0xf3, 0xe7, 0xff, /* XXXXX XXXXX XXXXXXXXXXX */ + 0x03, 0xf7, 0xef, 0xff, /* XXXXXX XXXXXX XXXXXXXXXXXX */ + 0x03, 0xe7, 0xcf, 0xff, /* XXXXX XXXXX XXXXXXXXXXXX */ + 0x07, 0xef, 0xdf, 0xff, /* XXXXXX XXXXXX XXXXXXXXXXXXX */ + 0x07, 0xcf, 0x9f, 0xfe, /* XXXXX XXXXX XXXXXXXXXXXX */ + 0x0f, 0xcf, 0xbf, 0xfe, /* XXXXXX XXXXX XXXXXXXXXXXXX */ + 0x0f, 0xdf, 0x3f, 0xfc, /* XXXXXX XXXXX XXXXXXXXXXXX */ + 0x1f, 0x9f, 0x7f, 0xfc, /* XXXXXX XXXXX XXXXXXXXXXXXX */ + 0x1f, 0x3e, 0x7f, 0xf8, /* XXXXX XXXXX XXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xff, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xfe, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xfc, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x00, 0x01, 0xfe, 0x00, /* XXXXXXXX */ + 0x00, 0x01, 0xff, 0x00, /* XXXXXXXXX */ + 0x00, 0x03, 0xff, 0x00, /* XXXXXXXXXX */ + 0x00, 0x03, 0xff, 0x00, /* XXXXXXXXXX */ + 0x00, 0x00, 0x1f, 0x00, /* XXXXX */ + 0x00, 0x00, 0x1f, 0x00, /* XXXXX */ + 0xff, 0xff, 0xff, 0xff, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xff, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xf1, 0xff, /* XXXXXXXXXXXXXXXXXXXX XXXXXXXXX */ +#else PLATTER_ICON + 0x00, 0x0f, 0x80, 0x00, /* XXXXX */ + 0x00, 0x08, 0x80, 0x00, /* X X */ + 0x00, 0x19, 0x80, 0x00, /* XX XX */ + 0x00, 0x11, 0x00, 0x00, /* X X */ + 0x00, 0x33, 0x00, 0x00, /* XX XX */ + 0x00, 0x22, 0x00, 0x00, /* X X */ + 0x00, 0x66, 0x00, 0x00, /* XX XX */ + 0x00, 0x47, 0xc1, 0xfc, /* X XXXXX XXXXXXX */ + 0x00, 0xc0, 0x43, 0x02, /* XX X XX X */ + 0x00, 0x8c, 0x22, 0x01, /* X XX X X X */ + 0x01, 0x9a, 0x26, 0x71, /* XX XX X X XX XXX X */ + 0x01, 0x12, 0x24, 0x49, /* X X X X X X X X */ + 0x03, 0x36, 0x6c, 0xc9, /* XX XX XX XX XX XX X X */ + 0x02, 0x24, 0x48, 0x91, /* X X X X X X X X */ + 0x06, 0x6c, 0xd9, 0xb3, /* XX XX XX XX XX XX XX XX */ + 0x04, 0x48, 0x91, 0x22, /* X X X X X X X X */ + 0x0c, 0xd9, 0xb3, 0x66, /* XX XX XX XX XX XX XX XX */ + 0x08, 0x91, 0x23, 0xc4, /* X X X X X XXXX X */ + 0x19, 0xb3, 0x60, 0x0c, /* XX XX XX XX XX XX */ + 0x1f, 0x3e, 0x47, 0xf8, /* XXXXX XXXXX X XXXXXXXX */ + 0x00, 0x00, 0x4e, 0x00, /* X XXX */ + 0x00, 0x00, 0xca, 0x00, /* XX X X */ + 0x00, 0x00, 0x9a, 0x00, /* X XX X */ + 0x00, 0x01, 0x9a, 0x00, /* XX XX X */ + 0x00, 0x01, 0x3f, 0x00, /* X XXXXXX */ + 0x00, 0x03, 0x31, 0x00, /* XX XX X */ + 0x00, 0x03, 0xf1, 0x00, /* XXXXXX X */ + 0x00, 0x00, 0x1f, 0x00, /* XXXXX */ + 0x00, 0x00, 0x15, 0x00, /* X X X */ + 0xbf, 0xff, 0xe4, 0xfd, /* X XXXXXXXXXXXXXXXXX X XXXXXX X */ + 0x00, 0x00, 0x0a, 0x00, /* X X */ + 0xbf, 0xff, 0xf1, 0xfd, /* X XXXXXXXXXXXXXXXXXX XXXXXXX X */ + /* MASK */ + 0x00, 0x0f, 0x80, 0x00, /* XXXXX */ + 0x00, 0x0f, 0x80, 0x00, /* XXXXX */ + 0x00, 0x1f, 0x80, 0x00, /* XXXXXX */ + 0x00, 0x1f, 0x00, 0x00, /* XXXXX */ + 0x00, 0x3f, 0x00, 0x00, /* XXXXXX */ + 0x00, 0x3e, 0x00, 0x00, /* XXXXX */ + 0x00, 0x7e, 0x00, 0x00, /* XXXXXX */ + 0x00, 0x7f, 0xc1, 0xfc, /* XXXXXXXXX XXXXXXX */ + 0x00, 0xff, 0xc3, 0xfe, /* XXXXXXXXXX XXXXXXXXX */ + 0x00, 0xff, 0xe3, 0xff, /* XXXXXXXXXXX XXXXXXXXXX */ + 0x01, 0xfb, 0xe7, 0xff, /* XXXXXX XXXXX XXXXXXXXXXX */ + 0x01, 0xf3, 0xe7, 0xff, /* XXXXX XXXXX XXXXXXXXXXX */ + 0x03, 0xf7, 0xef, 0xff, /* XXXXXX XXXXXX XXXXXXXXXXXX */ + 0x03, 0xe7, 0xcf, 0xff, /* XXXXX XXXXX XXXXXXXXXXXX */ + 0x07, 0xef, 0xdf, 0xff, /* XXXXXX XXXXXX XXXXXXXXXXXXX */ + 0x07, 0xcf, 0x9f, 0xfe, /* XXXXX XXXXX XXXXXXXXXXXX */ + 0x0f, 0xdf, 0xbf, 0xfe, /* XXXXXX XXXXXX XXXXXXXXXXXXX */ + 0x0f, 0x9f, 0x3f, 0xfc, /* XXXXX XXXXX XXXXXXXXXXXX */ + 0x1f, 0xbf, 0x7f, 0xfc, /* XXXXXX XXXXXX XXXXXXXXXXXXX */ + 0x1f, 0x3e, 0x7f, 0xf8, /* XXXXX XXXXX XXXXXXXXXXXX */ + 0x00, 0x00, 0x7e, 0x00, /* XXXXXX */ + 0x00, 0x00, 0xfe, 0x00, /* XXXXXXX */ + 0x00, 0x00, 0xfe, 0x00, /* XXXXXXX */ + 0x00, 0x01, 0xfe, 0x00, /* XXXXXXXX */ + 0x00, 0x01, 0xff, 0x00, /* XXXXXXXXX */ + 0x00, 0x03, 0xff, 0x00, /* XXXXXXXXXX */ + 0x00, 0x03, 0xff, 0x00, /* XXXXXXXXXX */ + 0x00, 0x00, 0x1f, 0x00, /* XXXXX */ + 0x00, 0x00, 0x15, 0x00, /* X X X */ + 0xbf, 0xff, 0xe4, 0xfd, /* X XXXXXXXXXXXXXXXXX X XXXXXX X */ + 0x00, 0x00, 0x0a, 0x00, /* X X */ + 0xbf, 0xff, 0xf1, 0xfd, /* X XXXXXXXXXXXXXXXXXX XXXXXXX X */ +#endif PLATTER_ICON + + /* ICON 15, default System V */ +#ifdef PLATTER_ICON + /* ICON */ + 0x00, 0x7f, 0xc0, 0x00, /* XXXXXXXXX */ + 0x01, 0x83, 0xf0, 0x00, /* XX XXXXXX */ + 0x03, 0xff, 0x0c, 0x00, /* XXXXXXXXXX XX */ + 0x04, 0x00, 0xfe, 0x00, /* X XXXXXXX */ + 0x08, 0x00, 0x7f, 0x00, /* X XXXXXXX */ + 0x1f, 0xff, 0xc0, 0x80, /* XXXXXXXXXXXXXXX X */ + 0x30, 0x00, 0x3f, 0xc0, /* XX XXXXXXXX */ + 0x20, 0x00, 0x1f, 0xc0, /* X XXXXXXX */ + 0x7f, 0xff, 0xe0, 0x20, /* XXXXXXXXXXXXXXXXXX X */ + 0x60, 0x00, 0x1f, 0xe0, /* XX XXXXXXXX */ + 0xe0, 0x00, 0x1f, 0xf0, /* XXX XXXXXXXXX */ + 0xbf, 0xff, 0xe0, 0x10, /* X XXXXXXXXXXXXXXXXX X */ + 0xe0, 0x00, 0x1f, 0xf0, /* XXX XXXXXXXXX */ + 0xf0, 0x00, 0x3f, 0xf0, /* XXXX XXXXXXXXXX */ + 0x8f, 0xff, 0xc0, 0x10, /* X XXXXXXXXXXXXXX X */ + 0xf8, 0x00, 0x7f, 0xf0, /* XXXXX XXXXXXXXXXX */ + 0xfe, 0x00, 0xff, 0xf0, /* XXXXXXX XXXXXXXXXXXX */ + 0x41, 0xfe, 0x00, 0x20, /* X XXXXXXXX X */ + 0x7f, 0x87, 0xff, 0xe0, /* XXXXXXXX XXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xc0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x10, 0x00, 0x00, 0x80, /* X X */ + 0xff, 0xff, 0xff, 0xff, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x60, 0x00, 0x00, 0x02, /* XX X */ + 0x3f, 0xff, 0xff, 0xfc, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x00, 0x00, 0x00, 0x50, /* X X */ + 0x00, 0x00, 0x00, 0xf8, /* XXXXX */ + 0x00, 0x00, 0x00, 0x88, /* X X */ + 0x00, 0x00, 0x00, 0xf8, /* XXXXX */ + 0x00, 0x00, 0x00, 0xa8, /* X X X */ + 0xff, 0xff, 0xff, 0x27, /* XXXXXXXXXXXXXXXXXXXXXXXX X XXX */ + 0x00, 0x00, 0x00, 0x50, /* X X */ + 0xff, 0xff, 0xff, 0x8f, /* XXXXXXXXXXXXXXXXXXXXXXXXX XXXX */ + /* MASK */ + 0x00, 0x7f, 0xc0, 0x00, /* XXXXXXXXX */ + 0x01, 0xff, 0xf0, 0x00, /* XXXXXXXXXXXXX */ + 0x03, 0xff, 0xfc, 0x00, /* XXXXXXXXXXXXXXXX */ + 0x07, 0xff, 0xfe, 0x00, /* XXXXXXXXXXXXXXXXXX */ + 0x0f, 0xff, 0xff, 0x00, /* XXXXXXXXXXXXXXXXXXXX */ + 0x1f, 0xff, 0xff, 0x80, /* XXXXXXXXXXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xc0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xc0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xe0, /* XXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xe0, /* XXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xe0, /* XXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xc0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x1f, 0xff, 0xff, 0x80, /* XXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xff, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xfe, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xfc, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x00, 0x00, 0x00, 0x78, /* XXXX */ + 0x00, 0x00, 0x00, 0x70, /* XXX */ + 0x00, 0x00, 0x00, 0xf8, /* XXXXX */ + 0x00, 0x00, 0x00, 0xf8, /* XXXXX */ + 0x00, 0x00, 0x00, 0xf8, /* XXXXX */ + 0xff, 0xff, 0xff, 0xff, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xdf, /* XXXXXXXXXXXXXXXXXXXXXXXXXX XXXXX */ + 0xff, 0xff, 0xff, 0x8f, /* XXXXXXXXXXXXXXXXXXXXXXXXX XXXX */ +#else PLATTER_ICON + /* ICON */ + 0x00, 0x7f, 0xc0, 0x00, /* XXXXXXXXX */ + 0x01, 0x83, 0xf0, 0x00, /* XX XXXXXX */ + 0x03, 0xff, 0x0c, 0x00, /* XXXXXXXXXX XX */ + 0x04, 0x00, 0xfe, 0x00, /* X XXXXXXX */ + 0x08, 0x00, 0x7f, 0x00, /* X XXXXXXX */ + 0x1f, 0xff, 0xc0, 0x80, /* XXXXXXXXXXXXXXX X */ + 0x30, 0x00, 0x3f, 0xc0, /* XX XXXXXXXX */ + 0x20, 0x00, 0x1f, 0xc0, /* X XXXXXXX */ + 0x7f, 0xff, 0xe0, 0x20, /* XXXXXXXXXXXXXXXXXX X */ + 0x60, 0x00, 0x1f, 0xe0, /* XX XXXXXXXX */ + 0xe0, 0x00, 0x1f, 0xf0, /* XXX XXXXXXXXX */ + 0xbf, 0xff, 0xe0, 0x10, /* X XXXXXXXXXXXXXXXXX X */ + 0xe0, 0x00, 0x1f, 0xf0, /* XXX XXXXXXXXX */ + 0xf0, 0x00, 0x3f, 0xf0, /* XXXX XXXXXXXXXX */ + 0x8f, 0xff, 0xc0, 0x10, /* X XXXXXXXXXXXXXX X */ + 0xf8, 0x00, 0x7f, 0xf0, /* XXXXX XXXXXXXXXXX */ + 0xfe, 0x00, 0xff, 0xf0, /* XXXXXXX XXXXXXXXXXXX */ + 0xc1, 0xfe, 0x00, 0x30, /* XX XXXXXXXX XX */ + 0xff, 0x87, 0xff, 0xf0, /* XXXXXXXXX XXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x60, 0x00, 0x00, 0x50, /* XX X X */ + 0x3f, 0xff, 0xff, 0xd0, /* XXXXXXXXXXXXXXXXXXXXXXXX X */ + 0x3f, 0xff, 0xff, 0xd0, /* XXXXXXXXXXXXXXXXXXXXXXXX X */ + 0x08, 0x00, 0x01, 0x50, /* X X X X */ + 0x07, 0xff, 0xfe, 0x50, /* XXXXXXXXXXXXXXXXXX X X */ + 0x07, 0xff, 0xfc, 0xf8, /* XXXXXXXXXXXXXXXXX XXXXX */ + 0x01, 0xc0, 0x70, 0x88, /* XXX XXX X X */ + 0x00, 0x3f, 0x80, 0xf8, /* XXXXXXX XXXXX */ + 0x00, 0x00, 0x00, 0xa8, /* X X X */ + 0xff, 0xff, 0xff, 0x27, /* XXXXXXXXXXXXXXXXXXXXXXXX X XXX */ + 0x00, 0x00, 0x00, 0x50, /* X X */ + 0xff, 0xff, 0xff, 0x8f, /* XXXXXXXXXXXXXXXXXXXXXXXXX XXXX */ + /* MASK */ + 0x00, 0x7f, 0xc0, 0x00, /* XXXXXXXXX */ + 0x01, 0xff, 0xf0, 0x00, /* XXXXXXXXXXXXX */ + 0x03, 0xff, 0xfc, 0x00, /* XXXXXXXXXXXXXXXX */ + 0x07, 0xff, 0xfe, 0x00, /* XXXXXXXXXXXXXXXXXX */ + 0x0f, 0xff, 0xff, 0x00, /* XXXXXXXXXXXXXXXXXXXX */ + 0x1f, 0xff, 0xff, 0x80, /* XXXXXXXXXXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xc0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xc0, /* XXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xe0, /* XXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xe0, /* XXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x7f, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x3f, 0xff, 0xff, 0xf0, /* XXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0x1f, 0xff, 0xfe, 0x70, /* XXXXXXXXXXXXXXXXXXXX XXX */ + 0x0f, 0xff, 0xfe, 0x70, /* XXXXXXXXXXXXXXXXXXX XXX */ + 0x07, 0xff, 0xfc, 0x70, /* XXXXXXXXXXXXXXXXX XXX */ + 0x03, 0xff, 0xf0, 0xf8, /* XXXXXXXXXXXXXX XXXXX */ + 0x00, 0x3f, 0x80, 0xf8, /* XXXXXXX XXXXX */ + 0x00, 0x00, 0x00, 0xf8, /* XXXXX */ + 0xff, 0xff, 0xff, 0xff, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xdf, /* XXXXXXXXXXXXXXXXXXXXXXXXXX XXXXX */ + 0xff, 0xff, 0xff, 0x8f, /* XXXXXXXXXXXXXXXXXXXXXXXXX XXXX */ +#endif PLATTER_ICON + + /* ICON 16, default Linux */ + /* ICON */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x0f, 0xc0, /* XXXXXX */ + 0x00, 0x00, 0x3f, 0xe0, /* XXXXXXXXX */ + 0x00, 0x00, 0xff, 0xf0, /* XXXXXXXXXXXX */ + 0x00, 0x07, 0xe1, 0xf0, /* XXXXXX XXXXX */ + 0x00, 0x03, 0xcc, 0xf8, /* XXXX XX XXXXX */ + 0x00, 0x00, 0xcc, 0xf8, /* XX XX XXXXX */ + 0x00, 0x00, 0x21, 0xf8, /* X XXXXXX */ + 0x00, 0x00, 0x21, 0xf8, /* X XXXXXX */ + 0x00, 0x00, 0x17, 0xf0, /* X XXXXXXX */ + 0x00, 0x00, 0x3f, 0xf8, /* XXXXXXXXXXX */ + 0x00, 0x00, 0x62, 0x28, /* XX X X X */ + 0x00, 0x00, 0x3f, 0xfc, /* XXXXXXXXXXXX */ + 0x78, 0x00, 0x4f, 0xea, /* XXXX X XXXXXXX X X */ + 0x84, 0x3f, 0xff, 0xfe, /* X X XXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xf7, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXX XXX */ + 0x80, 0x17, 0xff, 0xfa, /* X X XXXXXXXXXXXXXXXX X */ + 0x81, 0x11, 0xff, 0xf9, /* X X X XXXXXXXXXXXXXX X */ + 0x82, 0x10, 0x8f, 0xf8, /* X X X X XXXXXXXXX */ + 0x84, 0x10, 0x8f, 0xf8, /* X X X X XXXXXXXXX */ + 0x88, 0x10, 0x8f, 0xf8, /* X X X X XXXXXXXXX */ + 0x92, 0x90, 0x8f, 0xf8, /* X X X X X X XXXXXXXXX */ + 0x80, 0x10, 0x8f, 0xf8, /* X X X XXXXXXXXX */ + 0xff, 0xf0, 0x8f, 0xf8, /* XXXXXXXXXXXX X XXXXXXXXX */ + 0x05, 0x00, 0x4f, 0xf8, /* X X X XXXXXXXXX */ + 0x0f, 0x80, 0x47, 0xf8, /* XXXXX X XXXXXXXX */ + 0x08, 0x80, 0x23, 0xf0, /* X X X XXXXXX */ + 0x0f, 0x80, 0x7f, 0xe0, /* XXXXX XXXXXXXXXX */ + 0x0a, 0x81, 0xff, 0xf0, /* X X X XXXXXXXXXXXXX */ + 0xf2, 0x7f, 0xff, 0xff, /* XXXX X XXXXXXXXXXXXXXXXXXXXXXX */ + 0x05, 0x00, 0x00, 0x00, /* X X */ + 0xf8, 0xff, 0xff, 0xff, /* XXXXX XXXXXXXXXXXXXXXXXXXXXXXX */ + /* MASK */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x0f, 0xc0, /* XXXXXX */ + 0x00, 0x00, 0x3f, 0xe0, /* XXXXXXXXX */ + 0x00, 0x00, 0xff, 0xf0, /* XXXXXXXXXXXX */ + 0x00, 0x07, 0xff, 0xf0, /* XXXXXXXXXXXXXXX */ + 0x00, 0x03, 0xff, 0xf8, /* XXXXXXXXXXXXXXX */ + 0x00, 0x00, 0xff, 0xf8, /* XXXXXXXXXXXXX */ + 0x00, 0x00, 0x3f, 0xf8, /* XXXXXXXXXXX */ + 0x00, 0x00, 0x3f, 0xf8, /* XXXXXXXXXXX */ + 0x00, 0x00, 0x1f, 0xf0, /* XXXXXXXXX */ + 0x00, 0x00, 0x3f, 0xf8, /* XXXXXXXXXXX */ + 0x00, 0x00, 0x7f, 0xf8, /* XXXXXXXXXXXX */ + 0x00, 0x00, 0x3f, 0xfc, /* XXXXXXXXXXXX */ + 0x78, 0x00, 0x7f, 0xfe, /* XXXX XXXXXXXXXXXXXX */ + 0xfc, 0x3f, 0xff, 0xfe, /* XXXXXX XXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xff, 0xff, 0xff, /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + 0xff, 0xf7, 0xff, 0xfa, /* XXXXXXXXXXXX XXXXXXXXXXXXXXXX X */ + 0xff, 0xf1, 0xff, 0xf9, /* XXXXXXXXXXXX XXXXXXXXXXXXXX X */ + 0xff, 0xf0, 0xff, 0xf8, /* XXXXXXXXXXXX XXXXXXXXXXXXX */ + 0xff, 0xf0, 0xff, 0xf8, /* XXXXXXXXXXXX XXXXXXXXXXXXX */ + 0xff, 0xf0, 0xff, 0xf8, /* XXXXXXXXXXXX XXXXXXXXXXXXX */ + 0xff, 0xf0, 0xff, 0xf8, /* XXXXXXXXXXXX XXXXXXXXXXXXX */ + 0xff, 0xf0, 0xff, 0xf8, /* XXXXXXXXXXXX XXXXXXXXXXXXX */ + 0xff, 0xf0, 0xff, 0xf8, /* XXXXXXXXXXXX XXXXXXXXXXXXX */ + 0x07, 0x00, 0x7f, 0xf8, /* XXX XXXXXXXXXXXX */ + 0x0f, 0x80, 0x7f, 0xf8, /* XXXXX XXXXXXXXXXXX */ + 0x0f, 0x80, 0x3f, 0xf0, /* XXXXX XXXXXXXXXX */ + 0x0f, 0x80, 0x7f, 0xe0, /* XXXXX XXXXXXXXXX */ + 0x0a, 0x81, 0xff, 0xf0, /* X X X XXXXXXXXXXXXX */ + 0xf2, 0x7f, 0xff, 0xff, /* XXXX X XXXXXXXXXXXXXXXXXXXXXXX */ + 0x05, 0x00, 0x00, 0x00, /* X X */ + 0xf8, 0xff, 0xff, 0xff /* XXXXX XXXXXXXXXXXXXXXXXXXXXXXX */ +}; + +export int aufsiconsize = ICONSIZE; +export byte *aufsicon = icons[INDEX]; + diff --git a/applications/aufs/aufsv.c b/applications/aufs/aufsv.c new file mode 100644 index 0000000..7d945c7 --- /dev/null +++ b/applications/aufs/aufsv.c @@ -0,0 +1,2 @@ +int aufs_version[2] = { 3 , 4 }; +char *aufs_versiondate = " Sat Feb 16 17:30:14 EST 1991 "; diff --git a/applications/aufs/design.notes b/applications/aufs/design.notes new file mode 100644 index 0000000..7d2fc9d --- /dev/null +++ b/applications/aufs/design.notes @@ -0,0 +1,537 @@ +Internal Directory IDs +---------------------- + +As of Sun Apr 26. + +The current implementation of the Macintosh Hierarichal File System +uses "fixed dirids" which means that every directory has a unique id +fixed for the lifetime of the file system and which is never reused. + +A directory id should to be fixed over invocations of the server +because the macintosh remembers directory ids. An example of this is +the standard file package which likes to pickup the selection in the +directory you last left off. + +Unfortunately, Under unix there is no mechanism available for +generating consistent directory ids in the manner required. The +directory/file inode would be suitable since it is unique, but (a) it +is derived from the file's disk address and it is possible to see +recycled ids (even in the same session) and there is no efficient +"unix version independent" method of translating from inode to file. + +We have determined that the minimum functionality that the AFP client +requires is that the directory IDs be unique and constant over the +lifetime of the server. It would be nice if a full function variable +dirid filesystem would be available on the macintosh side; however, +evidently Apple has no plans to do this. + +The mechanism we choose is to return indices of a table which contains +pointers to nodes in an internal tree representing the volumes +directory structure. To minimize the effect of "same dirid, but +different directory" we randomly assign a "base" to the range of +dirids (e.g. pick a number like 300 out of the hat and add it to every +index of the table when translating from internal to external). The +internal structure is called an IDir: + + typedef struct idir { /* local directory info (internal) */ + char *name; /* the directory name */ + struct idir *next; /* ptr to next at same level */ + struct idir *subs; /* ptr to children */ + struct idir *pdir; /* ptr to parent */ + } IDir, *IDirP; + +The name stored is the component name, not the full path name. The +next pointer links directories at the same level. The subs pointer is +a list of subdirectories, and the pdir pointer is a pointer to the +parent directory. + +So, given an IDir you can generate the path by following the parent +links until pdir == rootd. + +The mount point for a volume is not necessarily at "/" so there is +also the notion of the volume's root directory or mount point. This +is needed in order to translate between the special ids 1 and 2 which +are parent of root directory and root directory. + +Using the IDir structure you can either build the tree completely by +scanning the unix filesystem at startup or you can add nodes as +needed. We choose to add nodes as needed. + +The internal directory structure needs to be modified to be kept in +sync with the actual filesystem directory structure. Because of this +the FPMove and FPRename calls can result in the modification of the +internal representation. + +There some large drawbacks in this design: + +1. Does not handle the case of other servers or unix users changing + the directory structure. +2. Does not generate constant directory ids between invocations. + +The benefits: + +1. Very simple and quick resolution of directory ids to paths. + +Given the alternatives we will be keeping this method for the time +being and including a validation word (or magic word) in the IDir +nodes to prevent random memory references. + +We have considered other methods which include using the directory's +inode in the IDir node, and using the inode as the directory id. In +this case the IDir would have a second thread(s) for looking up by +inode number. This would marginally improve the situation and would +increase the complexity of the code substantially. + + +Folder groups and creators +-------------------------- + +As of Aug 1987 + +Under AFP a folder has a group and creator. These attributes are used +in resolving access privileges. + +Unix maps AFP groups and creators into unix groups and creators. This +means that a file you would have access to under unix by virtue of +group/creator modes are also available under the server. + +Under BSD unix you need to be su in order to change a files creator, +thus you may not change the owners (available under sys v machines). + + +Protections +----------- + +As of Aug 1987 + +Under AFP only folders, aka directories, have protections. These +protections are: See Files (read), See Folders (search), write objects. + +This does not map well into the unix protection scheme. Our +"solution" is simply to respect the unix protections as best as +possible. The ramifications are: + + (1) we do not distinguish between see files and see folders + (2) read and write ability is based upon the protection of + individual files - not folders + +We do not see (1) as a serious problem. (2) does not present any +problems if the user is not the owner of the file. (2) does present a +minor problem in that there is no way for the user to change the +permissions through AFP. + +Specifically, when mapping from unix to mac protections: + Read access is translated to search folders/files (search, read) + Write access is translated to write access (write) +Note: The owner protection is the same as the user if the user owns +the file, else group if you have group access, else other access. The +elses are important because if you have write access as other, but not +as group or user, then you will not be able to write to a directory - +this is the way unix interprets the protections (right or wrong). + +From Mac to unix, we take: + See Files (read), to be unix read/search(execute) + Make Changes (write), to be unix write + See Folders (search), is not mapped + +When a "folder" protection is changed, all files in the folder also +have their protections changed to the same protection. + + +On file or folder creates, the protection is taken from the protection +of the superior "folder" (directory). + + +Newline conversion + +As of Tue July 22, 1987 + +On Unix the newline character is \n (code 012, lf), however, most Macintosh +applications use \r (code 015, cr) as newline. + +We now translate lf to cr on reads and cr to lfs on writes if the file +creator is unix and the file type is TEXT. These are the defaults for +"unix" files. + +In addition, lf and cr are defined internally as INEWLINE (internal +unix newline) and ENEWLINE (external mac newline), and conversion +between them is carried out in the following case: + 1. NONLXLATE must not be defined when compiling module afpos.c. + The symbol NONNLXLATE disables newline conversion. + 2. If conversion code is enabled and FPRead is issued with + NewLineMask equal to 0xFF, NewLineChar equal to + ENEWLINE, then the read terminates on either an ENEWLINE + or INEWLINE, and the INEWLINE is converted to ENEWLINE. +In summary, for this method: conversion only occurs when reading from +the unix filesystem and FPRead is issued in the special break on +newline mode, and the requested break character is the expected +macintosh newline character. There is no conversion when writing to +the Unix filesystem. + + +Name Conversion +--------------- + +Filenames on the mac can contain characters which are illegal in unix +file names. These include all 8 bit characters and /. The only chars +which are illegal on the mac are ":" and null. + +The current name conversion maps special mac characters to be ":" +followed by two hex digits. For example the name "Copy/Hey" on the +mac would convert to "Copy:2fHey" on unix. A ":" found on a unix file +which does not have 2 hex digits following is mapped into "|" on the +mac. + +When a unix file name is encountered a check is made to see if the +converted name is longer than MAXLFLEN (31) chars. If the name is +longer than the mac allows, then it is skipped completely and is not +seen by the mac. + +The algorithm for name conversion is more specifically: + Mac to Unix + if (char is ascii and not control and is printable and + is not "/") then + leave as is + else + replace with ":" followed by two hexidecimal + digits that is a direct encoding of the binary + value of the char + Unix to Mac: + if (":" followed by two hex digits) then + replace with binary value of hex digits + if (":" not followed by two hex digits) then + replace ":" with "|". + + +File Format (.finderinfo, .resources, data) + +Macintosh files are currently stored in a directory in three main +files. We have the resource fork, the data fork, and various file +specific finder information to store. Since the data fork is the +closest match to a unix file, it is stored as-is in the +specified directory, the resource fork and the so +called "finder info" fork are "special" and can be +stored by the same name in special subdirectories of the specified +directory. To be concrete, the Mac file "keeper" stored in a +directory "stuff" would be stored by Aufs on the unix file system as: + stuff/keeper - data fork + stuff/.finderinfo/keeper - "finder info" fork + stuff/.resource/keeper - resource fork +It is important to note that the .finderinfo and .resource directories +are only created by a "create directory" afp (Finder new folder) +command. This prevents these directories from drifting into places +people probably don't want them. However, these directories are +created iff the superior directory also had them. + +Getting to the finder info and resource fork is a pain under unix, but +how often do you really need to anyway? + +The defaults for a file that has no finder information is + type: TEXT [first four bytes] + creator: unix [second four bytes] + rest is set to zeros + file attributes: none + comment: + "This is a Unix\252 created file." (\252 is the tm sign) + +A directory with no finder information defaults the comment to one of: + This is a unix directory + This is an Aufs Macintosh directory + This is an Aufs unix directory (.finderinfo only) +if it has a no .finderinfo and .resource directory or just a resource +directory, both a .finderinfo and .resource directory, or just a +.finderinfo directory respectively. + +Turning on SMART_FINDERINFO in afpudb.c will yield more information; +however, it is unix variant dependent and slows things down +considerably. + +See MAJOR FILE FORMATS below for the finderinfo formats. + + +Desktop databases. + +As of Feb 1988 + +The icon data base is stored in .IDeskTop in the volume's root. A new +Icon are always appended to end unless it replaces an old one in which +case the old space will be reused if possible. New icons are written +out when received. The .IDeskTop is only read on the inital +"open desk" call. Locking is done if possible to prevent corruption. +(cf. section on locking). + +The APPL mappings are stored in .ADeskTop in the volume's root. We +store for each mapping the File creator, the path relative to the +volume root to the application, and the application name. Modified or +changed entries are appended to the .ADeskTop on every "flush" if you +have write access. It is possible for this database to grow rapidly +or be corrupted. The problems lie in the fact that we always append +(the solution for now is to rebuild the desktop now and then). It may +get corrupted because people move directories around (though we try to +minimize this). Also, note that entries are never deleted from the +.ADeskTop - there should be a mechanism to do this. Like the Icon +database, the APPL mappings are read only when the inital desktop open +is issued. + +See MAJOR FILE FORMATS below for the .ADeskTop and .IDeskTop file formats. + +MAJOR FILE FORMATS + + +Aufs Version 3 File Formats (CURRENT) +------------------------------------- +In the following: + byte: unsigned 8 bits + word: unsigned 16 bits + dword: unsigned 32 bits + sdword: signed 32 bits + +Important: all items are stored in network order! This means on a vax +you use htons/ntohs on words and htonl/ntohl on dwords. + +.ADeskTop format: + +The Applications mapping database is kept as an array of the +APPLFileRecords and associated data as shown following. The +associated data is the parent directory name relative to the volume +root and the application name as null terminated strings. + + /* never use zero or 0x1741 as the major version */ + #define AFR_MAGIC 0x00010002 + /* version 1.2 (don't use 1.1, 2.2, etc) */ + /* version 1.0 (version 0x1741.0000/0x1741) */ + + typedef struct { /* APPL information */ + byte a_FCreator[4]; /* creator of application */ + byte a_ATag[4]; /* user bytes */ + } APPLInfo; + + typedef struct { /* File Format APPL record */ + dword afr_magic; /* magic number for check */ + APPLInfo afr_info; /* the appl info */ + sdword afr_pdirlen; /* length of (relative) parent directory */ + sdword afr_fnamlen; /* length of application name */ + /* names follows */ + } APPLFileRecord; + + +.IDeskTop format: + +The Applications mapping database is kept as an array of the +ICONFileRecords and associated data as shown following. The +associated data is the bitmap. + +IconInfo in the below is padded to a double word boundary. Hopefully, +this is good enough to prevent differences in structure size in +ICONFileRecord on different machines. + + /* never use zero or 0x2136 as the major version */ + #define IFR_MAGIC 0x00010002 /* Version 1.2, skip 1.1, 2.2, etc. */ + /* version 1.0: 0x2136.0000/0x2136 */ + + #define FCreatorSize 4 + #define FTypeSize 4 + #define ITagSize 4 + + typedef struct { /* Icon Information */ + sdword i_bmsize; /* 4: size of the icon bitmap */ + byte i_FCreator[FCreatorSize]; /* 4[8]: file's creator type */ + byte i_FType[FTypeSize]; /* 4[12] file's type */ + byte i_IType; /* 1[13] icon type */ + byte i_pad1; /* 1[14] */ + byte i_ITag[ITagSize]; /* 4[18] user bytes */ + byte i_pad2[2]; /* 2[20] pad to double word boundary */ + } IconInfo; + + typedef struct { /* File Format ICON record */ + dword ifr_magic; /* the magic check */ + IconInfo ifr_info; /* the icon info */ + /* bitmap follows this */ + } IconFileRecord; + +.finderinfo format: + +In the following space for all entries is allocated. The bitmap +merely tells us if the indicated items are valid. + + #define FINFOLEN 32 + #define MAXCLEN 199 + typedef struct { + byte fi_fndr[FINFOLEN]; /* finder info */ + word fi_attr; /* attributes */ + #define FI_MAGIC1 255 + byte fi_magic1; /* was: length of comment */ + #define FI_VERSION 0x10 /* version major 1, minor 0 */ + /* if more than 8 versions then */ + /* something wrong anyway */ + byte fi_version; /* version number */ + #define FI_MAGIC 0xda + byte fi_magic; /* magic word check */ + byte fi_bitmap; /* bitmap of included info */ + #define FI_BM_SHORTFILENAME 0x1 /* is this included? */ + #define FI_BM_MACINTOSHFILENAME 0x2 /* is this included? */ + byte fi_shortfilename[12+1]; /* possible short file name */ + byte fi_macfilename[32+1]; /* possible macintosh file name */ + byte fi_comln; /* comment length */ + byte fi_comnt[MAXCLEN+1]; /* comment string */ + } FileInfo; + + +Aufs Version 1 and Version 2 FILE FORMATS +----------------------------------------- +In the following, "bit.x" defines a type of x bits. "str" means an +ascii string (256 character ascii set) terminated by a null. The +formats are defined below in the formats section. + +IMPORTANT: THESE FILES WERE STORED IN THE HOST MACHINE ORDER. You +cannot transport a these files from a byte swapped machine to a +non-bytes swapped machine. + +.ADeskTop format: +The .ADeskTop file contains an array of the following structure: + bit.32 afr_magic; /* magic number for check */ + bit.8 a_FCreator[4]; /* creator of application */ + bit.8 a_ATag[4]; /* user tag information */ + bit.32 afr_pdirlen; /* length of parent directory name */ + bit.32 afr_fnamlen; /* length of application name */ + str pdir[afr_pdirlen]; /* path to directory holding */ + /* appl. relative to volume root */ + str file[afr_fnamlen]; /* file name */ +The file names are stored are the unix file names. Note: the +directory path is relative to the volume root directory. The magic +number is a consistency check and is currently: AFR_MAGIC (8107+8556). + +.IDeskTop format +The .IDeskTop file contains an array of the following structure: + bit.32 ifr_magic; /* the magic check */ + bit.8 i_FCreator[4]; /* file's creator type */ + bit.8 i_FType[4]; /* file's type */ + bit.8 i_IType; /* icon type */ + bit.8 i_ITag[4]; /* user bytes */ + bit.32 i_bmsize; /* size of the icon bitmap */ + bit.8 i_icon[i_bmsize]; /* icon */ +The magic number is a consistency check and is currently: IFR_MAGIC +(8107+5750). + +The .finderinfo files contain the following information: + bit.8 fi_fndr[32]; /* finder info */ + bit.16 fi_attr; /* attributes */ + bit.8 fi_comln; /* length of comment */ + bit.8 fi_comnt[200]; /* comment string */ + + + +LOCKING + +Draft 2: Jan, 1988 +Charlie C. Kim +User Services +Columbia University + +Coordination of multiple access to files is best done through system +calls that implement the locks internal to the system. Advisory locks +allow coordination of multiple Aufs processes (if they all honor the +locks), but processes external to Aufs may cause problems. "Hard" +locks would be real real nice, but we haven't seen them. + +Two systems calls, "lockf" and "flock", are known to exist in a number +of different unix systems to allow "advisory" locks. Where these +exist, they can be used to coordinate Aufs processes (c.f. +INSTALLATION notes). The basic semantics of these calls (as known) +are: + flock - for an open file, establish a "shared" or "exclusive" +lock. A "exclusive lock" may be placed iff no locks are in place. A +shared lock may be upgraded to an exclusive lock iff no other locks +are in place. Multiple shared locks are allowed :-). Locks go away +when the file is closed. Allow locks to be tested and removed (can't +distingush between exclusive and shared on test though). + lockf - for an open file, allow exclusive locks at various +offsets for particular lengths. Also allow locking of the entire +file. Locks only allow if the file is open for read/write (sigh). +Locks can be removed and/or tested. (Do locks go away when file +closes?). + +FPOpen + +FPOpen allows "deny read", "deny write", and "deny r/w" and "deny +none" "locks" to be place on a file. We still do not implement these +(major pain because it requires access to lock information AND +previous open statuses). + +File-locks + +Certain files, such as, .ADeskTop, .IDeskTop and the file info files +must be coordinated between servers (ignore outside access). Two +system calls (exists in various unixs) help do this: flock and lockf. +Coordination can be easily accomplished by using the "flock" system +call if it exists. flock allows exclusive and shared locks. +Basically, when a file is "read", then a shared lock is set first. If +a file is to be written, then an exclusive lock must be set first - +this fails if a shared lock is already set. Some systems might have +"lockf" available which allows "exclusive" locks only. In theory this +would be okay (though you can't have multiple readers then) too; +however, "lockf" only works if the file is open for write, so if a +process has "read-only" access to one of the above files, then it +can't be guaranteed that the data is okay. + +ByteRangeLock + +The only available unix system call option for this is "lockf". This +allows pretty much what is necessary except you cannot lock +"read-only" files. (Reading Inside Mac Volume 4 seems to lead me to +believe that this is correct, but the AFP specification doesn't +really make this clear). + +Warning: NFS systems may not allow locks across remotely mounted file +systems. Even when they are allowed, special daemons must be run +since locking is not within the NFS protocol. + + + +NORMALIZING CHARACTER SETS + +Dan Sahlin of the Swedish Institute of Computer Science pointed out +the need to normalize between Unix character sets and the Macintosh +character set. Previously, Aufs provided this feature in a very +limited fashion: it would map between cr and lf when the file type was +"TEXT" and creator was "unix" (defaults for unix files). This +provided "good" functionality in the US. + +However, people outside the United States need to make use of various +international character sets that must be mapped to the Macintosh +character sets to be useful. The primary intent of this mapping is to +allow unix files to be mapped; however, it is also possible to allow a +large class of files to be mapped (such as all text files ending in +.swe, etc). + +The design is quite simple: define a mac to unix and unix to mac table +of 256 entries each that contain a direct mapping (must be one +character to one since file sizes, etc. require this). + +The routine that decides whether mapping is necessary or not bases it +decision on an internal table (should be per volume, not per server as +it is now). For each normalizing set of tables, Aufs records a file +extention, file creator, and file type of which any can be null. In +addition it stores a "conjuction" operator. It decides whether to +apply one when file has the specified extention "conjuction" file type +and file creator matches. null entries are treated as always true. + +For the old unix files, the table entry is: + extension: NULL + creator: unix + type: TEXT +and for Swedish D47: + extension: .swe + creator: NULL + type: TEXT +which means any file of type TEXT with the extension .swe will have +normalization applied. + +Note: the defaults for Swedish D47, Swedish-Finnish E47, and IOS +8859-1 Latin 1 were establish by Dan Sahlin. + + + +Packing unpacking packets. +Enumeration cache. + diff --git a/applications/aufs/makefile b/applications/aufs/makefile new file mode 100644 index 0000000..9f55e93 --- /dev/null +++ b/applications/aufs/makefile @@ -0,0 +1,168 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 14:00:17 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +I=/usr/include +# +# SEE INSTALLATION for documentation +# + +# valid are NONXLATE,FULL_NCS_SUPPORT,USECHOWN +# USESTATFS or USEGETMNT +# USEQUOTA or USESUNQUOTA +# and GGTYPE="gid_t" +OSDEFS= -DUSESTATFS -DUSESUNQUOTA +AFPLIB=-lafp +CAPLIB=-lcap + +# for other libraries (like BSD on hpux) +SLIB= + +# used mainly for debugging +CAPFILES= + +# aufs.c definitions: USEVPRINTF - use vprintf in logging +AUFSDEFS=-DUSEVPRINTF + +# to get "more" information about files with a speed penalty +# Also, is specific to 4.2 BSD. May not work on some machines +#AFPUDB=-DSMART_UNIX_FINDERINFO + +#For hpux (you have you may need to supply a routine that does rename) +# (Other limitations apply!!!!) +# RENAME=rename.o + +# make sure that you define point getopt to att_getopt.o if your system +# doesn't have it builtin +GETOPT= + +# This encodes the assumed location of certain directories +EXTRAS=../../extras +# Set the following approriately +DESTDIR=/usr/local/cap + +# +# End of configurable options +# +SRCS=afpos.c afpvols.c afpfile.c afpdir.c afpfork.c \ + afpmisc.c afpserver.c aufsicon.c abmisc2.c \ + afpdt.c afpdid.c afposenum.c afpavl.c \ + afposfi.c afpgc.c afppasswd.c afposlock.c aufsv.c \ + afpudb.c afposncs.c afpspd.c sizeserver.c +OBJS=afpos.o afpvols.o afpfile.o \ + afpmisc.o afpserver.o aufsicon.o abmisc2.o \ + afpdt.o afpdir.o afpfork.o afpdid.o afposenum.o afpavl.o \ + afposfi.o afpgc.o afppasswd.o aufsv.o \ + afpudb.o afposncs.o afpspd.o sizeserver.o +SYMLINKS=att_getopt.c + +all: aufs + +aufs: aufs.o $(OBJS) $(CAPFILES) ${RENAME} $(GETOPT) + ${CC} $(LFLAGS) -o aufs aufs.o $(OBJS) $(CAPFILES) ${RENAME} \ + $(GETOPT) ${AFPLIB} ${CAPLIB} ${SLIB} + +newver: + /bin/sh aufs_vers.sh `cat aufs_vers` aufs_vers aufsv.c + make all + +aufsv.c: aufs_vers + /bin/sh aufs_vers.sh `cat aufs_vers` useold aufsv.c + +clean: + -rm -f *.o aufs ${SYMLINKS} + +lint: aufs.c $(SRCS) + lint aufs.c $(SRCS) + +install: aufs + -strip aufs + ${INSTALLER} aufs $(DESTDIR) + +dist: + @cat todist + +att_getopt.o: att_getopt.c + +att_getopt.c: + ln -s ${EXTRAS}/att_getopt.c + +afpos.o: afpos.c + ${CC} ${OSDEFS} ${CFLAGS} -c afpos.c + +afposncs.o: afposncs.c + ${CC} ${OSDEFS} ${CFLAGS} -c afposncs.c + +afpudb.o: afpudb.c + ${CC} ${CFLAGS} ${AFPUDB} -c afpudb.c + +aufs.o: aufs.c + ${CC} ${OSDEFS} ${CFLAGS} ${AUFSDEFS} -c aufs.c + +# Dependencies +afpos.o: afpos.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h \ + afpvols.h $I/netat/afpcmd.h +afpudb.o: afpudb.c $I/netat/appletalk.h afpudb.h +afpfork.o: afpfork.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpntoh.h +afpdir.o: afpdir.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpntoh.h +afposfi.o: afposfi.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h afpgc.h afpudb.h +afpvols.o: afpvols.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpvols.h \ + afpntoh.h +afpfile.o: afpfile.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpntoh.h +afpmisc.o: afpmisc.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h +afpserver.o: afpserver.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + $I/netat/afpcmd.h afpntoh.h +aufsicon.o: aufsicon.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h +afpcmd.o: afpcmd.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h $I/netat/afpcmd.h +abmisc2.o: abmisc2.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h \ + $I/netat/afpcmd.h +afpdt.o: afpdt.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h \ + $I/netat/afp.h $I/netat/afpcmd.h \ + afpvols.h afpdt.h afpavl.h \ + afpntoh.h afpudb.h +afpdid.o: afpdid.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h +afposenum.o: afposenum.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afp.h \ + afpdt.h afpavl.h +afppacks.o: afppacks.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h $I/netat/afpcmd.h +afpavl.o: afpavl.c afpavl.h +afperr.o: afperr.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/afp.h +afpgc.o: afpgc.c afpgc.h +afppasswd.o: afppasswd.c $I/netat/sysvcompat.h afppasswd.h +afposncs.o: afposncs.c $I/netat/appletalk.h $I/netat/afp.h \ + afposncs.h afps.h +sizeserver.o: sizeserver.c sizeserver.h diff --git a/applications/aufs/sizeserver.c b/applications/aufs/sizeserver.c new file mode 100644 index 0000000..668ba34 --- /dev/null +++ b/applications/aufs/sizeserver.c @@ -0,0 +1,89 @@ +#ifdef SIZESERVER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * files could be in + * /usr/include/sys + * +#include +#include + * + */ +#include "sizeserver.h" + +main() +{ + register int uid; + struct volsize vs; + char path[BUFSIZ]; + + uid = getuid(); + for( ; ; ) { + if(read(0, path, BUFSIZ) <= 0) + exit(0); + volumesize(path, uid, &vs.total, &vs.free); + write(0, (char *)&vs, sizeof(vs)); + } +} + +volumesize(path, uid, ntot, nfree) +char *path; +int uid; +long *ntot, *nfree; +{ + register int fd; + register long total, avail, used; + register int i; + struct stat statbuf, statbuf2; + struct fstab *fsp; + struct fs super; + + if(stat(path, &statbuf) < 0) { +unknown: + *ntot = 0x1000000; + *nfree = 0x1000000; + return; + } + setfsent(); + while(fsp = getfsent()) { + if(stat(fsp->fs_spec, &statbuf2) == 0 && + statbuf2.st_rdev == statbuf.st_dev) { + path = fsp->fs_spec; + break; + } + } + endfsent(); + if(fsp == NULL) + goto unknown; + if((fd = open(path, O_RDONLY, 0)) < 0) + goto unknown; + (void)lseek(fd, (long)(SBLOCK * DEV_BSIZE), 0); + i = read(fd, (char *)&super, sizeof(super)); + (void)close(fd); + if(i != sizeof(super)) + goto unknown; + total = super.fs_dsize; + used = total - (super.fs_cstotal.cs_nbfree * super.fs_frag + + super.fs_cstotal.cs_nffree); + avail = (avail = total * (100 - super.fs_minfree) / 100) > used ? + (avail - used) : 0; + *nfree = (uid == 0 ? (total - used) : avail) * super.fs_fsize; + *ntot = total * super.fs_fsize; +} + +#else SIZESERVER +#include +main() +{ + printf("sizeserver: not compiled with -DSIZESERVER\n"); +} +#endif SIZESERVER diff --git a/applications/aufs/sizeserver.h b/applications/aufs/sizeserver.h new file mode 100644 index 0000000..34b7c97 --- /dev/null +++ b/applications/aufs/sizeserver.h @@ -0,0 +1,4 @@ +struct volsize { + long total; + long free; +}; diff --git a/applications/aufs/todo b/applications/aufs/todo new file mode 100644 index 0000000..8639855 --- /dev/null +++ b/applications/aufs/todo @@ -0,0 +1,76 @@ + +Tue Mar 31 19:33:34 1987 + +High: + + +Medium: +Setting directory owner is a bad idea under unix? (Allow under SysV) +do something to the DT if the magic number is wrong + +Low: +make xbin do the correct file names and finder info format. +Add OpenDir, CloseDir +Make conflicting aufs processes handle conflicts with .finderinfo, + desktop for systems without flock, lockf + +Clean: + +run lint on the whole mess and get rid of any unused routines +cleanup interface +system admin manual page, user manual page +modules needing init should be called from InitServer(), name Init_xxx(); + +Bugs: + +Folders gets locked/in use and can't get rid of it. + +Done + +4/1/87 Bill - move afperr() from aufs into (new) afperr.c do char *afperr() +4/1/87 Bill - error codes prefixed aeX to prevent conflict with other codes. +4/1/87 Bill - clean up command line args/help +4/2/87 Bill - Icon database resides in each volumes root directory +4/2/87 Bill - Make tilde available in path name on volumes in afpvols file. +4/2/87 Bill - Volume passwords. +4/2/87 Bill - Clean up os interface some. +4/2/87 Bill - don't show desktop files... +4/2/87 Bill - APPL database (afpdt) mostly working +4/3/87 Bill - default finder information should have TEXT... +4/3/87 Bill - if no finderinfo, item is locked, comment is "Unix created.." +4/3/87 Bill - Comment strings (afpdt). +4/3/87 Bill - write returns bad offset on return (cck fix) +4/3/87 Bill - abmisc2 should be moved into other ab modules (cck did most) +4/4/87 Bill - fpenumerate should use the max replysize instead of current hack +4/4/87 Bill - fpenumerate does not count an item when request bitmap is 0. +4/4/87 Bill - OSFileDirInfo doesn't consider bitmap, does unnecessary work +4/4/87 Bill - SetFileParms, SetDirParms, SetFileDirParms +4/4/87 Bill - SetDirParms for access mode +4/4/87 Bill - move/rename for dirs needs to modify IDir structure better! +4/4/87 Bill - volmodified should be set in more places. +4/4/87 Bill - APPL information should be written to disk. +4/4/87 Bill - some routines are doing packing by hand, they should be modified. +4/4/87 Bill - packing routines searches a table for AFPCmd... chuck it +4/6/87 B&C - name mapping... disallow characters which confuse unix. +4/6/87 CCK - add spawning on login. Debug server prevents this. +4/16/87 Bill - implement FPCopyFile +4/16/87 Bill - use a master afpvols files; switch is -V +4/16/87 CCK - log login/logout to a log file. +4/16/87 Bill - make very quite if debugging turned off. +4/16/87 Bill - allow newlinechar in read +4/16/87 Bill - see if most OS error codes are translated ok. +4/16/87 Bill - using the path name for volume problems; use component name +4/16/87 Bill - move table out of afpcmd.c +4/16/87 Bill - build a combined common header file... keep others seperate +6/24/87 cck - Add SetForkParms and allow setfiledirparms to set fork lengths. + bunch of other changes too. +6/24/87 cck - Setting directory group forks off the chgrp program if neces. +6/26/87 cck - allow guest login, cmd line option to set guest id? +7/22/87 cck - use citi mechanism for converting files - change default + file type to TEXT, creator "unix". Files like this will + have lf mapped to cr on read and vice versa on write +8/01/87 cck - time conversion is off because mac does not use AFP + time - only in Appleshare verison 1.0 + +1/88 - Fix problems with byte order in .ADeskTop, .IDeskTop and +.finderinfo files. diff --git a/applications/aufs/user.doc b/applications/aufs/user.doc new file mode 100644 index 0000000..3a93e83 --- /dev/null +++ b/applications/aufs/user.doc @@ -0,0 +1,163 @@ +Brief introduction to the AppleShare Unix File Server (AUFS) + +Introduction + +This document explains what you must do before you attempt to use Aufs +from a mac. In addition, it explains some of the design decisions +that will affect you. + +Setup + +This describes the initial setup. + +Create some subdirectory, let's assume it is "unix" in your top-level +directory. This will be the repository for your MacIntosh files. +Assuming you are connected to your top level directory, type: + "mkdir unix" + +You must also create two subdirectories in this directory called +.finderinfo and .resource. e.g. type: + "mkdir unix/.finderinfo" + "mkdir unix/.resource" + +AppleShare has a concept of something called volumes. Under the +MacIntosh AppleShare, each volume would be a different disk drive. +Under aufs, you are allowed to set various directories as "volumes". +Thus, you must create a file called "afpvols" or ".afpvols" (Aufs will +take "afpvols" over ".afpvols" first) in your top level (home) directory that +tells aufs what MacIntosh volumes you have. (Yes, you may have more +than one volume). The format of lines in this file is: + :[:] + path is the specification of the directory that will store the unix +files. In the above example, it would be "~/unix". is +the name the AppleShare client will show when it asks you which volume +to mount. The , if set, is used when you attempt +to mount the volume. You probably don't want it - it is included for +system volumes. + +We should note that ~/unix should be considered a "MacIntosh" volume +and you should be very careful about changes you make to it with unix +utilities. (If you're not sure, do a "Get Info" from the Finder on +the Folder/Volume) In generally, it's better to move things around, +delete things, etc. using the Mac AppleShare client. + + +Structure + +o How MacIntosh files are stored + +MacIntosh Operating System files have two parts known as the data and +resource "fork" (these forks have nothing to do with processes, etc). +Suffice it to say, that data is usually stored in the data fork and +programs and associated resources in the resources fork. In addition, +there is also an "information" fork which keeps information like the +file creator, file type, and location on the desktop, etc. + +However, Unix doesn't expect a file to be of more than one part. So, +to get around this, we store a MacIntosh file in a particular +directory as follows. Each directory that stores MacIntosh files will +have a ".finderinfo" and ".resource" subdirectory created. The file +information and resource forks of a file are stored in the respective +subdirectories while the data fork is stored in the directory. For +example, given the MacIntosh file "MacWrite" stored in the directory +"macfiles", the listing of the directory would be as follows: + mac/MacWrite + mac/.resource/MacWrite + mac/.finderinfo/MacWrite + +Normally, you will find it more difficult to use or see the resource +or finder information files; however, since the data in those files are +really very specific to the MacIntosh, this shouldn't pose a great +problem. + +There is one more peculiar thing that happens. Directories need to +have "finder information" stored. Well, a directory is treated almost +the same as a file - we simply store the finder information in the +.finderinfo subdirectory of the containing directory. No resource +file/fork is created though. For example, suppose we create the +directory "paints" in the directory "mac", then the structure would +look like: + mac/paints <- a directory + mac/.finderinfo/paints <- finder infomation file + +To obfusctiate the issue even more, we should note that the directory +paints also has ".finderinfo" and ".resource" forks. + + +o How Unix files and directories are treated + +Unix directories are directories without a .finderinfo or .resource +subdirectory. The main things to know here are: + o you cannot copy a macintosh file with a resource fork to a + directory without a .resource subdirectory. + o "New Folder" will create a new directory. It will have a + .finderinfo or .resource directory if the superior directory does. + +Unix-only files are essentially files with only a data fork. +Following are some notes about them. + - Unix files used to come up locked, THEY NO LONGER DO. + - Unix files come up with creator "unix" and type "TEXT" by default. + - Unix files use line feed (lf) as a line terminator while the + Macintosh Operating System uses carriage returns (cr) - this can + cause problems. To work around this, if a file has creator "unix" + and type "TEXT", then lf's are mapped to cr's on reads and vice + versa on writes. The way to stop this is to set the file's creator + or type to something else using DiskTop, ResEdit or whatever (note, + if it is a file in a directory without a .finderinfo directory, + then the modified file creator/type may reset to unix/TEXT at any + time). + - In the same vein, anytime "line at a time" reads with "cr" + (mac line terminator) as the end of line terminator are done, then + both "cr" and "lf" are used as end of line terminators regardless + of the file creator and type. An example of a program that does + this is BinHex. + - Copying a Unix file to or rewriting a Unix file in a directory + with .resource and .finderinfo subdirectories will cause the + resultant file to be a MacIntosh file. + - The Finder really needs to be able to store the finderinfo to work + efficiently. If you have a unix directory that you plan to + manipulate a lot, then you can speed up things a lot by creating a + .finderinfo subdirectory - however, remember this will cause Aufs + to create a .finderinfo file for each file in the directory. + +o Volumes + +As we noted before, volumes on a MacIntosh AppleShare server +correspond to disk drives and on under Aufs they correspond to +directories listed in the afpvols file. + +If the top level directory of an Aufs volume has a .finderinfo +subdirectory, Aufs assumes that the volume will primarily be used for +Macintosh files. In this case, it will create two files in that +directory called: .ADeskTop and .IDeskTop. The two files correspond +to the "DeskTop" file on the MacIntosh and are seperated for efficency +reasons. + +The .IDeskTop file maintains a database of the icons. The .ADeskTop +file records the file creator to application mappings - basically, it +tells what application to launch when you double click on a document +with that file creator. Like the Desktop on the Macintosh, neither of +these files shrink. Unlike the Desktop on the Macintosh, if you want +to rebuild the desktop with only entries on the volume, you must first +remove these files. + +You can have volumes in your afpvols file that overlap. You should be +careful about operation between the two volumes when mounted at the +same time. + +o Other notes + +Unix distinguishes case when opening and getting information about +files. The MacIntosh Operating System doesn't. This will cause +problems with some applications (such as MPW). + +The specification for the Appletalk Filing Protocol used by AppleShare +and AUFS and the specification for the Hierachial File System used by +MacIntoshes state that directory ids are fixed across the lifetime of +a volume. In addition, directory ids are not reused. Unfortunately, +aufs breaks this rule. Directory ids are unique only for a particular +session. Some programs and packages store away directory ids and you +may see some unexpected things happening (but it shoudn't be anything +bad). + + diff --git a/applications/aufs/whatiswhat b/applications/aufs/whatiswhat new file mode 100644 index 0000000..5ca5f7b --- /dev/null +++ b/applications/aufs/whatiswhat @@ -0,0 +1,33 @@ +Notes on files. + +README - Generally useful information +INSTALLATION - Installation considerations +design.notes - design consideration +todo - things left to do or done +afpvols - sample volume file +user.doc - user documentation +abmisc2.c+ - server register routine +afpavl.[c,h] - implements balanced trees (general) +afpdid.[c,h]* - directory id routines +afpdir.c@ - Handles directories +afpdt.[c,h]*+@ - Handles desktop +afpfile.c@ - File handling routines +afpfork.c@+ - Fork handling routines +afpgc.[c,h] - Generalized caching management +afpmisc.c - miscellaneous, yet useful routines +afpntoh.h - translator file for packs +afpos.c*+ - Most os dependent routines +afposenum.c* - OS dependent part of file enumeration +afposfi.c* - OS dependent finder information handling +afposncs.[c,h] - OS dependent character set normalization +afppasswd.c - auxillary password routines +afpserver.c+@ - main server loop +afpvols.[c,h]@ - handles volumes +aufs.c+ - main driver +aufsicon.c - icon for aufs +afps.h - general header file +afpudb.[c,h]* - holds Unix desktop information + +*'ed items are heavily os dependent ++'ed items have CAP library dependencies +@'ed items are primary AFP drivers diff --git a/applications/lwsrv/DBfile b/applications/lwsrv/DBfile new file mode 100644 index 0000000..a32993e --- /dev/null +++ b/applications/lwsrv/DBfile @@ -0,0 +1,552 @@ +plain300 = ( + Query ADOIsBinaryOK? True; + FeatureQuery *?Resolution 300dpi; + FeatureQuery *ColorDevice False; + FeatureQuery *FaxSupport None; + FeatureQuery *LanguageLevel '"1"'; + FeatureQuery *TTRasterizer None; + Query ADOSpooler spooler; +); + +"COMPAQ PAGEMARQ 15" = ( + include "LaserWriter Plus"; + FeatureQuery *PSVersion '"(2012.015) 13"'; + FeatureQuery *?Resolution 400x800dpi; + FeatureQuery *ColorDevice Unknown; + FeatureQuery *FreeVM '"2397046"'; + FeatureQuery *Product '"(COMPAQ PAGEMARQ 15)"'; + Query Product '"(COMPAQ PAGEMARQ 15)"'; + FeatureQuery *FaxSupport Base; + Query ADORamSize '"10485760"'; +); + +"Dataproducts LZR 1260" = ( + include "LaserWriter Plus"; + FeatureQuery *PSVersion '"(47.0) 0"'; + FeatureQuery *FreeVM '"2389815"'; + FeatureQuery *Product '"(Dataproducts LZR 1260)"'; + Query Product '"(Dataproducts LZR 1260)"'; + FeatureQuery *TTRasterizer Accept68K; + Query ADORamSize '"4194304"'; +); + +LaserWriter = ( + include plain300; + font ( + Courier, + Courier-Bold, + Courier-BoldOblique, + Courier-Oblique, + Helvetica, + Helvetica-Bold, + Helvetica-BoldOblique, + Helvetica-Oblique, + Symbol, + Times-Bold, + Times-BoldItalic, + Times-Italic, + Times-Roman, + ); + FeatureQuery *PSVersion '"(23.0) 0"'; + FeatureQuery *FreeVM '"173936"'; + FeatureQuery *Product '"(LaserWriter)"'; + Query Product '"(LaserWriter)"'; + Query ADORamSize '"2097152"'; +); + +"LaserWriter II NT" = ( + include "LaserWriter Plus"; + FeatureQuery *PSVersion '"(47.0) 1"'; + FeatureQuery *FreeVM '"434204"'; + FeatureQuery *Product '"(LaserWriter II NT)"'; + Query Product '"(LaserWriter II NT)"'; + FeatureQuery *TTRasterizer Accept68K; +); + +"LaserWriter II NTX" = ( + include "LaserWriter II NT"; + FeatureQuery *PSVersion '"(47.0) 1"'; + FeatureQuery *FreeVM '"433810"'; + FeatureQuery *Product '"(LaserWriter II NTX)"'; + Query Product '"(LaserWriter II NTX)"'; +); + +"LaserWriter IIf" = ( + include "LaserWriter Plus"; + FeatureQuery *ColorDevice Unknown; + FeatureQuery *LanguageLevel '"2"'; + FeatureQuery *PSVersion '"(2010.113) 1"'; + FeatureQuery *FreeVM '"2381689"'; + FeatureQuery *TTRasterizer Type42; + FeatureQuery *Product '"(LaserWriter IIf)"'; + Query Product '"(LaserWriter IIf)"'; + Query ADORamSize '"8388608"'; +); + +"LaserWriter IIg" = ( + include "LaserWriter IIf"; + FeatureQuery *PSVersion '"(2010.130) 2"'; + FeatureQuery *FreeVM '"2150107"'; + FeatureQuery *Product '"(LaserWriter IIg)"'; + Query Product '"(LaserWriter IIg)"'; +); + +"LaserWriter Plus" = ( + include LaserWriter; + font ( + AvantGarde-Book, + AvantGarde-BookOblique, + AvantGarde-Demi, + AvantGarde-DemiOblique, + Bookman-Demi, + Bookman-DemiItalic, + Bookman-Light, + Bookman-LightItalic, + Courier, + Courier-Bold, + Courier-BoldOblique, + Courier-Oblique, + Helvetica, + Helvetica-Bold, + Helvetica-BoldOblique, + Helvetica-Narrow, + Helvetica-Narrow-Bold, + Helvetica-Narrow-BoldOblique, + Helvetica-Narrow-Oblique, + Helvetica-Oblique, + NewCenturySchlbk-Bold, + NewCenturySchlbk-BoldItalic, + NewCenturySchlbk-Italic, + NewCenturySchlbk-Roman, + Palatino-Bold, + Palatino-BoldItalic, + Palatino-Italic, + Palatino-Roman, + Symbol, + Times-Bold, + Times-BoldItalic, + Times-Italic, + Times-Roman, + ZapfChancery-MediumItalic, + ZapfDingbats, + ); + FeatureQuery *PSVersion '"(42.2) 3"'; + FeatureQuery *FreeVM '"172414"'; + FeatureQuery *Product '"(LaserWriter Plus)"'; + Query Product '"(LaserWriter Plus)"'; +); + +"LaserWriter Pro 630" = ( + include "LaserWriter IIg"; + FeatureQuery *PSVersion '"(2010.130) 1"'; + FeatureQuery *FreeVM '"1698253"'; + FeatureQuery *Product '"(LaserWriter Pro 630)"'; + Query Product '"(LaserWriter Pro 630)"'; + FeatureQuery *?Resolution 600dpi; +); + +"PrintServer 20" = ( + include "plain300"; + FeatureQuery *PSVersion '"(48.3) 19"'; + FeatureQuery *FreeVM '"964599"'; + FeatureQuery *Product '"(PrintServer 20)"'; + Query Product '"(PrintServer 20)"'; + Query ADORamSize '"2621440"'; + font ( + AvantGarde-Book, + AvantGarde-BookOblique, + AvantGarde-Demi, + AvantGarde-DemiOblique, + Courier, + Courier-Bold, + Courier-BoldOblique, + Courier-Oblique, + Helvetica, + Helvetica-Bold, + Helvetica-BoldOblique, + Helvetica-Oblique, + LubalinGraph-Book, + LubalinGraph-BookOblique, + LubalinGraph-Demi, + LubalinGraph-DemiOblique, + NewCenturySchlbk-Bold, + NewCenturySchlbk-BoldItalic, + NewCenturySchlbk-Italic, + NewCenturySchlbk-Roman, + Souvenir-Demi, + Souvenir-DemiItalic, + Souvenir-Light, + Souvenir-LightItalic, + Symbol, + Times-Bold, + Times-BoldItalic, + Times-Italic, + Times-Roman, + ); +); + +"QMS-PS 410" = ( + include "LaserWriter Plus"; + FeatureQuery *PSVersion '"(52.4) 94"'; + FeatureQuery *FreeVM '"336596"'; + FeatureQuery *Product '"(QMS-PS 410)"'; + Query Product '"(QMS-PS 410)"'; + font ( + AGaramond-Bold, + AGaramond-BoldItalic, + AGaramond-Italic, + AGaramond-Regular, + AGaramond-Semibold, + AGaramond-SemiboldItalic, + AvantGarde-Book, + AvantGarde-BookOblique, + AvantGarde-Demi, + AvantGarde-DemiOblique, + Bookman-Demi, + Bookman-DemiItalic, + Bookman-Light, + Bookman-LightItalic, + Courier, + Courier-Bold, + Courier-BoldOblique, + Courier-Oblique, + Helvetica, + Helvetica-Bold, + Helvetica-BoldOblique, + Helvetica-Condensed, + Helvetica-Condensed-Bold, + Helvetica-Condensed-BoldObl, + Helvetica-Condensed-Oblique, + Helvetica-Narrow, + Helvetica-Narrow-Bold, + Helvetica-Narrow-BoldOblique, + Helvetica-Narrow-Oblique, + Helvetica-Oblique, + NewCenturySchlbk-Bold, + NewCenturySchlbk-BoldItalic, + NewCenturySchlbk-Italic, + NewCenturySchlbk-Roman, + Palatino-Bold, + Palatino-BoldItalic, + Palatino-Italic, + Palatino-Roman, + Symbol, + Times-Bold, + Times-BoldItalic, + Times-Italic, + Times-Roman, + ZapfChancery-MediumItalic, + ZapfDingbats, + ); +); + +"Silentwriter 95" = ( + include "LaserWriter IIg"; + FeatureQuery *PSVersion '"(2010.121) 1"'; + FeatureQuery *FreeVM '"251597"'; + FeatureQuery *TTRasterizer None; + FeatureQuery *Product '"(Silentwriter 95)"'; + Query Product '"(Silentwriter 95)"'; + Query ADORamSize '"2097152"'; +); + +"Varityper" = ( + include "LaserWriter Plus"; + FeatureQuery *?Resolution 1270dpi; + FeatureQuery *PSVersion '"(52.3) 4.7"'; + FeatureQuery *FreeVM '"2298400"'; + FeatureQuery *Product '"(Varityper)"'; + Query Product '"(Varityper)"'; + FeatureQuery *ColorDevice Unknown; + Query ADORamSize '"16777212"'; +); + +"SPARCprinter" = ( + include "LaserWriter Plus"; + FeatureQuery *PSVersion '"(3.010) 0"'; + FeatureQuery *FreeVM '"1147258"'; + FeatureQuery *Product '"(NeWS Server)"'; + Query Product '"(NeWS Server)"'; + Query ADORamSize '"138936320"'; + font ( + AvantGarde-Book, + AvantGarde-BookOblique, + AvantGarde-Demi, + AvantGarde-DemiOblique, + Bembo, + Bembo-Bold, + Bembo-BoldItalic, + Bembo-Italic, + Bookman-Demi, + Bookman-DemiItalic, + Bookman-Light, + Bookman-LightItalic, + Courier, + Courier-Bold, + Courier-BoldOblique, + Courier-Oblique, + GillSans, + GillSans-Bold, + GillSans-BoldItalic, + GillSans-Italic, + Helvetica, + Helvetica-Bold, + Helvetica-BoldOblique, + Helvetica-Narrow, + Helvetica-Narrow-Bold, + Helvetica-Narrow-BoldOblique, + Helvetica-Narrow-Oblique, + Helvetica-Oblique, + LucidaBright, + LucidaBright-Demi, + LucidaBright-DemiItalic, + LucidaBright-Italic, + LucidaSans, + LucidaSans-Bold, + LucidaSans-BoldItalic, + LucidaSans-Italic, + LucidaSans-Typewriter, + LucidaSans-TypewriterBold, + NewCenturySchlbk-Bold, + NewCenturySchlbk-BoldItalic, + NewCenturySchlbk-Italic, + NewCenturySchlbk-Roman, + Palatino-Bold, + Palatino-BoldItalic, + Palatino-Italic, + Palatino-Roman, + Rockwell, + Rockwell-Bold, + Rockwell-BoldItalic, + Rockwell-Italic, + Symbol, + Times-Bold, + Times-BoldItalic, + Times-Italic, + Times-Roman, + ZapfChancery-MediumItalic, + ZapfDingbats, + ); +); + +"OKI ML801PS" = ( + include "LaserWriter Plus"; + FeatureQuery *PSVersion '"(52.3) 0"'; + FeatureQuery *?Resolution 400dpi; + FeatureQuery *FreeVM '"3947324"'; + FeatureQuery *TTRasterizer Accept68K; + FeatureQuery *Product '"(ML801PS)"'; + Query Product '"(ML801PS)"'; + Query ADORamSize '"8388608"'; + font ( + Courier, + Courier-Bold, + Courier-BoldOblique, + Courier-Oblique, + Helvetica, + Helvetica-Bold, + Helvetica-BoldOblique, + Helvetica-Oblique, + Symbol, + Times-Bold, + Times-BoldItalic, + Times-Italic, + Times-Roman, + PCTimes-Roman, + PCHelvetica, + Mincho-PC-Hiragana, + Mincho-PC-Katakana, + NotDefFont, + Ryumin-Light-H, + Ryumin-Light-V, + Ryumin-Light-EUC-H, + Ryumin-Light-EUC-V, + Ryumin-Light-SuppA-H, + Ryumin-Light-SuppA-V, + Ryumin-Light-SuppB-HV, + Ryumin-Light-RKSJ-H, + Ryumin-Light-RKSJ-V, + Ryumin-Light-RKSJ-UserGaiji, + Ryumin-Light-83pv-SuppA-H, + Ryumin-Light-83pv-SuppB-H, + Ryumin-Light-83pv-RKSJ-H, + Ryumin-Light-NWP-H, + Ryumin-Light-NWP-V, + Ryumin-Light-Ext-H, + Ryumin-Light-Ext-V, + Ryumin-Light-Ext-SuppA-H, + Ryumin-Light-Ext-SuppA-V, + Ryumin-Light-Ext-SuppB-HV, + Ryumin-Light-Ext-RKSJ-H, + Ryumin-Light-Ext-RKSJ-V, + Ryumin-Light-Add-H, + Ryumin-Light-Add-V, + Ryumin-Light-Add-SuppA-H, + Ryumin-Light-Add-SuppA-V, + Ryumin-Light-Add-SuppB-HV, + Ryumin-Light-Add-RKSJ-H, + Ryumin-Light-Add-RKSJ-V, + GothicBBB-Medium-H, + GothicBBB-Medium-V, + GothicBBB-Medium-EUC-H, + GothicBBB-Medium-EUC-V, + GothicBBB-Medium-SuppA-H, + GothicBBB-Medium-SuppA-V, + GothicBBB-Medium-SuppB-HV, + GothicBBB-Medium-RKSJ-H, + GothicBBB-Medium-RKSJ-V, + GothicBBB-Medium-RKSJ-UserGaiji, + GothicBBB-Medium-83pv-SuppA-H, + GothicBBB-Medium-83pv-SuppB-H, + GothicBBB-Medium-83pv-RKSJ-H, + GothicBBB-Medium-NWP-H, + GothicBBB-Medium-NWP-V, + GothicBBB-Medium-Ext-H, + GothicBBB-Medium-Ext-V, + GothicBBB-Medium-Ext-SuppA-H, + GothicBBB-Medium-Ext-SuppA-V, + GothicBBB-Medium-Ext-SuppB-HV, + GothicBBB-Medium-Ext-RKSJ-H, + GothicBBB-Medium-Ext-RKSJ-V, + GothicBBB-Medium-Add-H, + GothicBBB-Medium-Add-V, + GothicBBB-Medium-Add-SuppA-H, + GothicBBB-Medium-Add-SuppA-V, + GothicBBB-Medium-Add-SuppB-HV, + GothicBBB-Medium-Add-RKSJ-H, + GothicBBB-Medium-Add-RKSJ-V, + Helvetica-Narrow, + Helvetica-Narrow-Bold, + Helvetica-Narrow-BoldOblique, + Helvetica-Narrow-Oblique, + Palatino-Bold, + Palatino-BoldItalic, + Palatino-Italic, + Palatino-Roman, + ZapfChancery-MediumItalic, + ZapfDingbats, + Bookman-Demi, + Bookman-DemiItalic, + Bookman-Light, + Bookman-LightItalic, + NewCenturySchlbk-Bold, + NewCenturySchlbk-BoldItalic, + NewCenturySchlbk-Italic, + NewCenturySchlbk-Roman, + AvantGarde-Book, + AvantGarde-BookOblique, + AvantGarde-Demi, + AvantGarde-DemiOblique, + ); +); + +"EPSON LP-9000PS2" = ( + include "LaserWriter Plus"; + FeatureQuery *PSVersion '"(2014.107) 1"'; + FeatureQuery *?Resolution 600dpi; + FeatureQuery *FreeVM '"1880311"'; + FeatureQuery *TTRasterizer Type42; + FeatureQuery *Product '"(EPSON LP-9000PS2)"'; + Query Product '"(EPSON LP-9000PS2)"'; + Query ADORamSize '"11534336"'; + font ( + Courier, + Courier-Bold, + Courier-BoldOblique, + Courier-Oblique, + Helvetica, + Helvetica-Bold, + Helvetica-BoldOblique, + Helvetica-Oblique, + Symbol, + Times-Bold, + Times-BoldItalic, + Times-Italic, + Times-Roman, + PCTimes-Roman, + PCHelvetica, + Mincho-PC-Hiragana, + Mincho-PC-Katakana, + NotDefFont, + Ryumin-Light-H, + Ryumin-Light-V, + Ryumin-Light-EUC-H, + Ryumin-Light-EUC-V, + Ryumin-Light-SuppA-H, + Ryumin-Light-SuppA-V, + Ryumin-Light-SuppB-HV, + Ryumin-Light-RKSJ-H, + Ryumin-Light-RKSJ-V, + Ryumin-Light-RKSJ-UserGaiji, + Ryumin-Light-83pv-SuppA-H, + Ryumin-Light-83pv-SuppB-H, + Ryumin-Light-83pv-RKSJ-H, + Ryumin-Light-NWP-H, + Ryumin-Light-NWP-V, + Ryumin-Light-Ext-H, + Ryumin-Light-Ext-V, + Ryumin-Light-Ext-SuppA-H, + Ryumin-Light-Ext-SuppA-V, + Ryumin-Light-Ext-SuppB-HV, + Ryumin-Light-Ext-RKSJ-H, + Ryumin-Light-Ext-RKSJ-V, + Ryumin-Light-Add-H, + Ryumin-Light-Add-V, + Ryumin-Light-Add-SuppA-H, + Ryumin-Light-Add-SuppA-V, + Ryumin-Light-Add-SuppB-HV, + Ryumin-Light-Add-RKSJ-H, + Ryumin-Light-Add-RKSJ-V, + GothicBBB-Medium-H, + GothicBBB-Medium-V, + GothicBBB-Medium-EUC-H, + GothicBBB-Medium-EUC-V, + GothicBBB-Medium-SuppA-H, + GothicBBB-Medium-SuppA-V, + GothicBBB-Medium-SuppB-HV, + GothicBBB-Medium-RKSJ-H, + GothicBBB-Medium-RKSJ-V, + GothicBBB-Medium-RKSJ-UserGaiji, + GothicBBB-Medium-83pv-SuppA-H, + GothicBBB-Medium-83pv-SuppB-H, + GothicBBB-Medium-83pv-RKSJ-H, + GothicBBB-Medium-NWP-H, + GothicBBB-Medium-NWP-V, + GothicBBB-Medium-Ext-H, + GothicBBB-Medium-Ext-V, + GothicBBB-Medium-Ext-SuppA-H, + GothicBBB-Medium-Ext-SuppA-V, + GothicBBB-Medium-Ext-SuppB-HV, + GothicBBB-Medium-Ext-RKSJ-H, + GothicBBB-Medium-Ext-RKSJ-V, + GothicBBB-Medium-Add-H, + GothicBBB-Medium-Add-V, + GothicBBB-Medium-Add-SuppA-H, + GothicBBB-Medium-Add-SuppA-V, + GothicBBB-Medium-Add-SuppB-HV, + GothicBBB-Medium-Add-RKSJ-H, + GothicBBB-Medium-Add-RKSJ-V, + Helvetica-Narrow, + Helvetica-Narrow-Bold, + Helvetica-Narrow-BoldOblique, + Helvetica-Narrow-Oblique, + Palatino-Bold, + Palatino-BoldItalic, + Palatino-Italic, + Palatino-Roman, + ZapfChancery-MediumItalic, + ZapfDingbats, + Bookman-Demi, + Bookman-DemiItalic, + Bookman-Light, + Bookman-LightItalic, + NewCenturySchlbk-Bold, + NewCenturySchlbk-BoldItalic, + NewCenturySchlbk-Italic, + NewCenturySchlbk-Roman, + AvantGarde-Book, + AvantGarde-BookOblique, + AvantGarde-Demi, + AvantGarde-DemiOblique, + ); +); diff --git a/applications/lwsrv/LWFonts b/applications/lwsrv/LWFonts new file mode 100644 index 0000000..055b299 --- /dev/null +++ b/applications/lwsrv/LWFonts @@ -0,0 +1,18 @@ +% Fonts returned by lsf for the LaserWriter +% AppleDict version 40 + +Times-Roman +Helvetica +Symbol +Courier-Bold +|______Seattle +Helvetica-Bold +Times-Bold +Courier-Oblique +Helvetica-BoldOblique +Times-Italic +Courier-BoldOblique +|______Courier +Courier +Helvetica-Oblique +Times-BoldItalic diff --git a/applications/lwsrv/LWIIfgFonts b/applications/lwsrv/LWIIfgFonts new file mode 100644 index 0000000..bbfcc72 --- /dev/null +++ b/applications/lwsrv/LWIIfgFonts @@ -0,0 +1,37 @@ +NewCenturySchlbk-Italic +Bookman-LightItalic +Helvetica +Courier-Bold +Helvetica-Narrow-Bold +Courier-BoldOblique +Times-Italic +Times-Bold +NewCenturySchlbk-Roman +Helvetica-Narrow-Oblique +Bookman-DemiItalic +Symbol +Bookman-Demi +Helvetica-BoldOblique +Bookman-Light +Helvetica-Oblique +AvantGarde-Book +AvantGarde-DemiOblique +|______Seattle +AvantGarde-BookOblique +AvantGarde-Demi +|______Courier +Helvetica-Narrow +ZapfChancery-MediumItalic +|______Symbol +Courier-Oblique +NewCenturySchlbk-BoldItalic +Helvetica-Bold +Times-Roman +Times-BoldItalic +Helvetica-Narrow-BoldOblique +NewCenturySchlbk-Bold +Palatino-Bold +Courier +Palatino-Italic +Palatino-Roman +Palatino-BoldItalic diff --git a/applications/lwsrv/LWPlusFonts b/applications/lwsrv/LWPlusFonts new file mode 100644 index 0000000..eb7ac47 --- /dev/null +++ b/applications/lwsrv/LWPlusFonts @@ -0,0 +1,38 @@ +NewCenturySchlbk-Italic +Bookman-LightItalic +Helvetica +Courier-Bold +Helvetica-Narrow-Bold +Courier-BoldOblique +Times-Italic +Times-Bold +NewCenturySchlbk-Roman +Helvetica-Narrow-Oblique +Bookman-DemiItalic +Symbol +Bookman-Demi +Helvetica-BoldOblique +Bookman-Light +Helvetica-Oblique +AvantGarde-Book +AvantGarde-DemiOblique +|______Seattle +AvantGarde-BookOblique +AvantGarde-Demi +|______Courier +Helvetica-Narrow +ZapfChancery-MediumItalic +|______Symbol +Courier-Oblique +NewCenturySchlbk-BoldItalic +Helvetica-Bold +Times-Roman +Times-BoldItalic +Helvetica-Narrow-BoldOblique +NewCenturySchlbk-Bold +Palatino-Bold +Courier +Palatino-Italic +Palatino-Roman +Palatino-BoldItalic +ZapfDingbats diff --git a/applications/lwsrv/Makefile.m4 b/applications/lwsrv/Makefile.m4 new file mode 100644 index 0000000..6ba26cf --- /dev/null +++ b/applications/lwsrv/Makefile.m4 @@ -0,0 +1,144 @@ +CFLAGS=cflags() specialcflags() +DESTDIR=capsrvrdestdir() +LWFLAGS=lwflags() +LIBCAP=libcap() +I=includedir() +YACC=yacc +LEX=lex + +# Valid are: -DADOBE_DSC2_CONFORMANT +SIMPLEFLAGS=simpleflags() + +LWSRVOBJS=fontlist.o lwsrv.o papstream.o procset.o simple.o spmisc.o +LWSRV8OBJS=fontlist8.o lwsrv8.o papstream8.o procset8.o simple8.o \ + spmisc.o query.o list.o parse.o y.tab.o +LWSRVCONFIGOBJS=lwsrvconfig.o list.o packed.o parse.o y.tab.o + +# for other libraries (like BSD on hpux) +SLIB=libspecial() +# lex library +LLIB= -ll + +# make sure that you define point getopt to att_getopt.o if your system +# doesn't have it builtin +ATT_GETOPT=ifdef([needgetopt],[needgetopt]) + +all: lwsrv lwsrv8 lwsrvconfig + +lwsrv: ${LWSRVOBJS} ${ATT_GETOPT} + ${CC} -o lwsrv ${LFLAGS} ${LWSRVOBJS} ${ATT_GETOPT} ${LIBCAP} ${SLIB} + +lwsrv8: ${LWSRV8OBJS} ${ATT_GETOPT} + ${CC} -o lwsrv8 ${LFLAGS} ${LWSRV8OBJS} ${ATT_GETOPT} ${LIBCAP} \ + ${SLIB} ${LLIB} + +lwsrvconfig: ${LWSRVCONFIGOBJS} + ${CC} -o lwsrvconfig ${LFLAGS} ${LWSRVCONFIGOBJS} ${SLIB} ${LLIB} + +clean: + -rm -f *.o lwsrv lwsrv8 lwsrvconfig att_getopt.c lex.yy.c y.tab.c + +spotless: + -rm -f *.o *.orig lwsrv lwsrv8 lwsrvconfig att_getopt.c \ + lex.yy.c y.tab.c Makefile makefile + +install: lwsrv lwsrv8 lwsrvconfig + -strip lwsrv lwsrv8 lwsrvconfig + ifdef([sysvinstall],[install -f $(DESTDIR) lwsrv], + [${INSTALLER} lwsrv ${DESTDIR}]) + ifdef([sysvinstall],[install -f $(DESTDIR) lwsrv8], + [${INSTALLER} lwsrv8 ${DESTDIR}]) + ifdef([sysvinstall],[install -f $(DESTDIR) lwsrvconfig], + [${INSTALLER} lwsrvconfig ${DESTDIR}]) + +dist: + @cat todist + +att_getopt.c: + ln -s ../../extras/att_getopt.c + +simple.o: simple.c + ${CC} ${CFLAGS} ${SIMPLEFLAGS} -c simple.c + +lwsrv.o: lwsrv.c + ${CC} ${CFLAGS} ${LWFLAGS} -c lwsrv.c + +simple8.o: simple.c + ${CC} -DLWSRV8 ${CFLAGS} ${SIMPLEFLAGS} -o simple8.o -c simple.c + +lwsrv8.o: lwsrv.c + ${CC} -DLWSRV8 ${CFLAGS} ${LWFLAGS} -o lwsrv8.o -c lwsrv.c + +fontlist8.o: fontlist.c + ${CC} -DLWSRV8 ${CFLAGS} ${LWFLAGS} -o fontlist8.o -c fontlist.c + +papstream8.o: papstream.c + ${CC} -DLWSRV8 ${CFLAGS} ${LWFLAGS} -o papstream8.o -c papstream.c + +procset8.o: procset.c + ${CC} -DLWSRV8 ${CFLAGS} ${LWFLAGS} -o procset8.o -c procset.c + +lwsrvconfig.o: lwsrvconfig.c + ${CC} ${CFLAGS} ${LWFLAGS} -c lwsrvconfig.c + +y.tab.o: y.tab.c lex.yy.c + ${CC} ${CFLAGS} ${LWFLAGS} -c y.tab.c + +y.tab.c: parsey.y + ${YACC} parsey.y + +lex.yy.c: parsel.l + ${LEX} parsel.l + +query.o: query.c + ${CC} ${CFLAGS} ${LWFLAGS} -c query.c + +# Dependencies +lwsrv.o: lwsrv.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/compat.h papstream.h +lwsrv8.o: lwsrv.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/compat.h papstream.h fontlist.h \ + list.h query.h parse.h procset.h +simple.o: simple.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/sysvcompat.h $I/netat/compat.h \ + spmisc.h procset.h fontlist.h papstream.h +simple8.o: simple.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/sysvcompat.h $I/netat/compat.h \ + spmisc.h procset.h fontlist.h papstream.h \ + list.h query.h parse.h +fontlist.o: fontlist.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h fontlist.h \ + spmisc.h papstream.h +fontlist8.o: fontlist.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h fontlist.h \ + spmisc.h papstream.h list.h query.h parse.h +papstream.o: papstream.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h papstream.h +papstream8.o: papstream.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h papstream.h +procset.o: procset.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/sysvcompat.h $I/netat/compat.h \ + procset.h spmisc.h +procset8.o: procset.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/sysvcompat.h $I/netat/compat.h \ + procset.h spmisc.h list.h query.h +spmisc.o: spmisc.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/sysvcompat.h $I/netat/compat.h \ + spmisc.h +query.o: query.c $I/netat/appletalk.h list.h query.h parse.h \ + papstream.h +y.tab.c: parsey.y list.h parse.h +lwsrvconfig.o: lwsrvconfig.c list.h parse.h packed.h +parse.o: parse.c list.h parse.h +packed.o: packed.c packed.h diff --git a/applications/lwsrv/README b/applications/lwsrv/README new file mode 100644 index 0000000..1e0aa5e --- /dev/null +++ b/applications/lwsrv/README @@ -0,0 +1,638 @@ +cap60/applications/lwsrv/README + +Last update: +Mon Aug 28 10:08:35 EST 1995 + +LWSRV is a LaserWriter print spooler that runs on a UNIX workstation. + +In normal operation, LWSRV registers an NBP name (as specified by the -n +command line argument) of NBP type 'LaserWriter' on the local AppleTalk +Network. It accepts print jobs from Macintosh workstations and submits +them to the standard UNIX print queue (using lpr or lp) for printing. + +A single LWSRV process may be used to advertise a number of LaserWriter +spoolers. LWSRV is also multi-threaded in that it accepts multiple +incoming print jobs. + +This release of LWSRV builds as two versions, 'lwsrv' for use with +LaserWriter drivers version 7.N and earlier, and 'lwsrv8' for use with +LaserWriter drivers version 8.N and later that support level 3 DSC queries. +In some future release, 'lwsrv8' will replace the current 'lwsrv' which +is currently included for backward compatibility. + +For more information, see: + + cap60/man/lwsrv.8 + cap60/man/papif.8 + cap60/doc/print.cookbook + + http://www.cs.mu.OZ.AU/appletalk/atalk.html + + "Inside Appletalk, 2nd Edition", Sidhu/Andrews/Oppenheimer + Chapter 14, "Print Spooling Architecture". + +Setting up +------- -- + +Before you can use LWSRV, you need to already have the ability to send +UNIX print jobs to a Postcript printer using the UNIX lpr(1) or lp(1) +print commands. You can do this with a serial-line connected printer, +or via the CAP 'papif' program to an AppleTalk connected LaserWriter +(see cap60/doc/print.cookbook or if on a Solaris host, use the script +cap60/applications/papif/add_at_printer). + +The LWSRV program is normally started from the 'start-cap-servers' file +which is run at UNIX boot time by an 'rc' startup script. An example +file is provided in the CAP distribution as cap60/etc/start-cap-servers. + + +lwsrv for LaserWriter 7.N and below +----- --- ----------- --- --- ----- + +To use the 'lwsrv' program, you need to provide it with at least four, +usually five command-line arguments; + + -n nameOfPrinter + -p unixPrintQueue + -a dictionaryDirectory + -f fontFile + +The "nameOfPrinter" is the name to be advertised in the Macintosh Chooser. +It cannot be the same name as another LaserWriter or print spooler. + +The "unixPrintQueue" is the string you normally provide as the lpr -P +option to send jobs to a Postscript printer. + +"dictionaryDirectory" is a directory where LWSRV can keep copies of any +printer dictionaries (procsets) that it uploads. This would normally be +"/usr/local/lib/cap/procsets". + +"fontFile" is the name of a file that lists the fonts available for the +printer. This would normally be "/usr/local/lib/cap/LWPlusFonts" which +you copy from cap60/applications/lwsrv/LWPlusFonts. + +When using 'lwsrv' with System 7, you should also specify the -N option +which indicates to 'lwsrv' that it should not collect new Procsets. + +There are other possible command line options, including the -k option to +prevent DDP checksums from breaking some printers, see cap60/man/lwsrv.8. + + +lwsrv8 for LaserWriter 8.N +------ --- ----------- --- + +This is the version of LWSRV that supports level 3 DSC queries as used +by the Macintosh LaserWriter 8.N drivers. + +'lwsrv8' also supports use of a configuration file which can contain +the normal command line options to LWSRV, answers for level 3 DSC +queries and a pointer to a library database of templates that describe +the features associated with each printer. + +To set up 'lwsrv8', copy the file 'DBfile' to /usr/local/lib/cap, then + + cd /usr/local/lib/cap + lwsrvconfig -c DB DBfile + mkdir procsets + +This creates an ndbm(3) database of the printer descriptions available +in DBfile. If the printer you have is not included in DBfile, send +the Postscript file 'query.ps' to your printer, transcribe the printed +settings to DBFile and re-run 'lwsrvconfig'. See the 'lwsrvconfig' +description below. + +You then need to edit the file "lwsrv.conf" to add your printer NBP +name, include an appropriate printer description and UNIX printer queue. + +The following is an example of a simple "lwsrv.conf" configuration file +that contains an entry naming the library database, the 'lwsrv8' options +and the specification for spooler name and name of the UNIX print queue. + + Library = /usr/local/lib/cap/DB; + + Options = ( + DontCollect; + ProcsetDir /usr/local/lib/cap/procsets; + FontFile /usr/local/lib/cap/LW+Fonts; + ); + + "Technical Services Spool" = ( + include "LaserWriter IIf"; + printerqueue lw.tsa; + ); + +This would be run as + + lwsrv8 /usr/local/lib/cap/lwsrv.conf + +and is approximately equivalent to running 'lwsrv' as + + lwsrv -N -a /usr/local/lib/cap/procsets + -f /usr/local/lib/cap/LW+Fonts + -n "Technical Services Spool" + -p lw.tsa + +The 'include "LaserWriter IIf"' entry includes information from the DB +database that describes features associated with the printer, in this +case a LaserWriter IIf. The DBfile entry for a IIf is + + "LaserWriter IIf" = ( + include "LaserWriter Plus"; + FeatureQuery *ColorDevice Unknown; + FeatureQuery *LanguageLevel '"2"'; + FeatureQuery *PSVersion '"(2010.113) 1"'; + FeatureQuery *FreeVM '"2381689"'; + FeatureQuery *TTRasterizer Type42; + FeatureQuery *Product '"(LaserWriter IIf)"'; + Query ADORamSize '"8388608"'; + ); + +Note that a base set of entries, including a font list, is included +from the "LaserWriter Plus" DBfile description. + +There are some new compile time options for 'lwsrv8'. One new option is +-DJOBNOPAREN (append to CFLAGS in makefile). This option replaces any +parenthesis () in the job string with square brackets []. The only reason +for this if you have a spooler (like that for the DEC PrintServer 20) +that doesn't handle parenthesis very intelligently when printing the +job string on the banner page. + +Another new compile time option is -DTIMESTAMP (append to LWFLAGS in +makefile). This puts a time stamp on log messages. + + +lwsrvconfig +----------- + +The program 'lwsrvconfig' has two main uses. First, it is used to create +databases of templates, using the ndbm routines (if you don't have ndbm, +but do have the older dbm routines, use -DNONDBM). + + % lwsrvconfig -c DB DBfile + +will read the file "DBfile" and then create a set of dbm database DB.dat, +DB.dir and DB.pag (where the "DB" root in the name is the argument after +the -c). + +Secondly, 'lwsrvconfig' can be used to scan configuration files for syntax +errors. 'lwsrvconfig' reads a configuration file and parses it the same +way as 'lwsrv8' does and reports any errors it finds. It outputs the +printer options to standard output. The -v option will, in addition, +output all templates used. + + % lwsrvconfig lwsrv.conf + % lwsrvconfig -v lwsrv.conf + + +Terminology +----------- + +Options are divided into two types. Global options pertain to all printers +spooled by a lwsrv process. Per-printer options usually pertain to each +particular printer spooled by a lwsrv process. Refer to the lwsrv.8 manual +entry for command line option details. + + Global Option + Options Name + + -C LPRCommand + -S Singlefork + -X AUFSSecurity + -d Debug + -l Logfile + -v Verbose + + Per-Printer Option + Options Name + + -A DSC + -L LPRArgument + -N NoCollect + -P PassThru + -R NeXTResolution + -T TranScriptOption + -a ProcsetDir + -e AllowEEXEC + -f FontFile + -h SuppressBanner + -k NoChecksum + -q QueryFile + -r KeepSpoolFile + -t TraceFile + + +Each 'lwsrv8' spooler is specified by a spooler name and a lpr printer name. +These correspond with the -n and -p options. Global options are those that +come before the first -n and -p options. The per-printer options come +after the -n and -p options, but before the next -n and -p set. + +A per-printer option specified before the first -n and -p options becomes +global and so applies to all printers, unless overridden by on a per-printer +basis. A global option specified after the first -n and -p options still +has global scope. (Note: there is currently no way to turn off the effect +of a per-printer option used globally on a per-printer basis.) + + +Examples +-------- + + % lwsrv8 -S -n "My Spooler" -p lp2 -a myprocsets -f myfonts \ + -n "Another Spooler" -p lp4 -a procset3 -f morefonts + +The two spoolers have their own set of procset directories and font files. +The -S specifies singleforking for both spoolers. + + % lwsrv8 -a procsets -f fonts -n "Jane's Spooler" -p janelp \ + -n "John's Spooler" -p johnlp -f johnfonts -l logfile -T crtolf + +The two spoolers share the same procset directory and would have shared the +same font file if "John's Spooler" had not overridden the font file using +"johnfonts". "John's Spooler" also specifies the -T crtolf option, whereas +"Jane's Spooler" does not. Even though -l logfile is specified for +"John's Spooler", since it is a global option, it still applies to both +spoolers. + + +Using a Configuration File +----- - ------------- ---- + +You can specify a configuration file which lwsrv uses for all its options: + + % lwsrv lwsrv.conf [db] + +where db is an optional library of templates (created by lwsrvconfig). + +In the configuration file, statements have the following syntax: + + Name = Value; + +Statements with multiple values are included in parenthesis or curly brackets: + + Name = ( + Value1; + Value2 argument; + (etc...) + ); + +(The parser is free-form; indentation is used for readability only.) + +Names, values and arguments can be quoted if they contain whitespace or other +special characters. Either single or double quotes can be used, especially +to quote the other kind: + + Value '"Quoted Argument"'; + +Two single or double quotes in a row means a single occurrence of that +character: + + Value John''s; + +The configuration file is composed of three parts, of which only the last +part is required. The first (optional) part is the Library section, specified +with the "Library" keyword: + + Library = DB; + +If specified, the Library option specifies a library of templates (this is +overridden by the command line argument). In this example, DB is taken to +be the root of the ndbm files, DB.dat, DB.dir and DB.pag. + +The second (option) part are the global options, specified with the "Options" +keyword: + + Options = ( + -S; + LogFile logfile; + ); + +Options can be either the commandline option (like -S) or the option name +(like SingleFork, matched case-insensitively). + +The last part of the configuration file includes templates and printer +definitions. Here is the above examples in configuration file format: + + Options = -S; + + "My Spooler" = ( + -p lp2; + -a myprocsets; + -f myfonts; + ); + + "Another Spooler" = ( + -p lp4; + -a procset3; + -f morefonts; + ); + +Using option names for the second example: + + Options = ( + ProcsetDir procsets; + FontFile fonts; + ); + + "Jane's Spooler" = PrinterQueue janelp; + + "John's Spooler" = ( + PrinterQueue johnlp; + FontFile johnfonts; + LogFile logfile; + TranScriptOption crtolf; + ); + +These examples don't use templates, nor do they include the ability to +respond to LaserWriter 8.0 queries. Here is an example of both the use +of templates and queries: + + Options = ( + ProcsetDir procsets; + LogFile logfile; + ); + + "LaserWriter Plus" = ( + Query ADOIsBinaryOK? True; + FeatureQuery *ColorDevice False; + FeatureQuery *FaxSupport None; + FeatureQuery *LanguageLevel '"1"'; + FeatureQuery *TTRasterizer None; + Query ADOSpooler spooler; + FeatureQuery *?Resolution 300dpi; + FeatureQuery *PSVersion '"(42.2) 3"'; + FeatureQuery *FreeVM '"172414"'; + FeatureQuery *Product '"(LaserWriter Plus)"'; + font ( + AvantGarde-Book, + AvantGarde-BookOblique, + AvantGarde-Demi, + AvantGarde-DemiOblique, + Bookman-Demi, + Bookman-DemiItalic, + Bookman-Light, + Bookman-LightItalic, + Courier, + Courier-Bold, + Courier-BoldOblique, + Courier-Oblique, + Helvetica, + Helvetica-Bold, + Helvetica-BoldOblique, + Helvetica-Narrow, + Helvetica-Narrow-Bold, + Helvetica-Narrow-BoldOblique, + Helvetica-Narrow-Oblique, + Helvetica-Oblique, + NewCenturySchlbk-Bold, + NewCenturySchlbk-BoldItalic, + NewCenturySchlbk-Italic, + NewCenturySchlbk-Roman, + Palatino-Bold, + Palatino-BoldItalic, + Palatino-Italic, + Palatino-Roman, + Symbol, + Times-Bold, + Times-BoldItalic, + Times-Italic, + Times-Roman, + ZapfChancery-MediumItalic, + ZapfDingbats, + ); + ); + + "My Spooler" = ( + include "LaserWriter Plus"; + printerqueue lp1; + ); + +"LaserWriter Plus" is a template, because it does not include a printerqueue +(-p) option, and so does not correspond to a real printer. Templates can +be included in the definition of a real printer (or another template) by +the use of the include keyword. Templates can be nested to any depth, and +can even be forward-referenced in the file (the scan is two pass in nature, +so forward references are taken care of). + + Note that the "font" resource definition is used instead of + the FontFile option. + +An important aspect of templates is that when a printer (or another template) +definition includes a template, the value structures in memory are actually +shared, thus saving on memory usage. When a value is overridden, a new +value structure is created in memory. + +Note that printers can also be included, like any other template: + + "My Trace Spooler" = ( + include "My Spooler"; + TraceFile TRACE; + ); + +The library file contains printer template definitions, which can be used +by a configuration file: + + Library = DB; + + Options = ( + LogFile logfile; + ProcsetDir procsets; + ); + + "My Printer" = ( + include "QMS-PS 410"; + printerqueue qmslp; + ); + + "Another Printer" = ( + include "LaserWriter IIg"; + printerqueue lp5; + FeatureQuery *FreeVM '"3579932"'; + Query ADORamSize '"5242880"'; + ); + +Note that you can override queries, as in the above example, where "Another +Printer" has only 5 MB of memory instead of the 8 MB specified in the database. + + +Creating Templates +-------- --------- + +There is a file, "query.ps", that contains PostScript code that will print +the answers to the LaserWriter 8.0 queries. Just send the file to the printer. +Or you can edit the query.ps file and remove the beginning section of the +file, so that the responses go to the printer's standard output and then +(usually) into the log file. This is especially useful if you have lots +of fonts. + +Edit your database file (DBfile in the above examples, and as shipped with +this software), and add the next template. Then run lwsrvconfig to rebuild +the database: + + % lwsrvconfig -c DB DBfile + + +Future Possibilities +------ ------------- + +lwsrvconfig could be made to build its database from a set of files in a +directory rather than a single file. This could make maintaining the database +a bit easier. + +lwsrvconfig should have a option to dump the contents of the database into +a text format that can be used to recreate the database. + + +Summary of Changes from the Original lwsrv +------- -- ------- ---- --- -------- ----- + +Many existing routines, as well as all the new routines rely on a set of +functions provided in list.c. The List structure is a variable length list +of arbitrary objects. Normally, the order of objects in a List is significant, +or it can be sorted and a binary search can be used to search through the List. +The KVTree structure is an AVL (self-balancing, binary) tree of key-value pairs. +This is used for binary searching and replacement of key-values. + +The printer_instance structure has been moved to query.h, and has been expanded +to allow many more options to be specified on a per-printer basis. Now only +the -X, -C, -S, -d, -v and -l options apply to all printers, while -T, -e, -N, +-r, -h, -k, -a, -f, -t, -A, -L, -P and -R are per-printer flags. The -n and -p +flags are used to specify a printer. When the other per-printer flags are +specified before the first -n or -p, they are taken as defaults values for all +subsequent printers. The per-printer options used after the -n and -p options +will override the default options. + +lwsrv8 can be called without options as in: + + lwsrv8 lwsrv.config [database] + +where "lwsrv.config" is the name of a text file that contains configuration +information. This not only includes the normal options to lwsrv, but also +the answers to PostScript queries, as used in LaserWriter 8.0. For example, +the line: + + FeatureQuery *ColorDevice False; + +in "lwsrv.config" for a particular printer, causes the PostScript query: + + %%?BeginFeatureQuery: *ColorDevice + (PostScript code...) + %%?EndFeatureQuery: Unknown + +to be answered with "False". Resources (such as fonts, patterns, but not +procsets, since we still use the original lwsrv way of doing this, namely +to scan a specified directory for procset resources) can also be specified: + + font = ( + Times-Roman, + Times-Italics, + ... + ); + +A template is a set of options and/or query responses, and may include other +templates and override values, but can not specify a Unix printer name (-p). + + "LaserWriter Plus" = ( + include LaserWriter; + FeatureQuery *Product '"LaserWriter Plus"'; + ... + ); + +A printer is like a template, but does include the Unix printer name. + + "My Spooler" = ( + include "LaserWriter IIg"; + PrinterQueue lp; + ... + ); + +Options can be entered using the original lwsrv option (like -p) or with a +case-insensitive name (like PrinterQueue). + +The option second argument "database" is a ndbm (or dbm) database of templates +that will be used to satisfy unknown template references in the "config" file. +The database can also be specified in the configuration file itself, as the +first line: + + Database db; + +The reading of the configuration file and the building of the templates and +printers is handled by parse.c, parsey.y (yacc) and parsel.l (lex). There is +also an include file parse.h. + +The program lwsrvconfig is designed to do several things. First, it reads a +configuration file the same way that lwsrv would, and can be used to detect +syntax errors and other problems (syntax error checking is primitive, as it +aborts on the first error). lwsrvconfig can also be used to create the ndbm +(or dbm) database of templates. + +Set mode 0600 on normal temp file and 0644 on trace file. + +Variable "tracing" set to true if we are doing tracing. + +Tracing now includes responses sent back to client. These lines begin with +"--lwsrv=>". + +scantoken() has been totally re-written, mainly to support binary data that +can be sent by LaserWriter 8.0, but also to make it more modular and efficient. +In addition, tokens are normally passed through (before, some were and some +were not passed through) so that better post-processing of the document +structure can be done. + +tokval() was rewritten to do binary searching of the toktbl[] (it was a linear +search before). + +The toktbl[] has been changed to include a variable called changeecho, which +tells scantoken() to either leave echoing the same (ECHO_UNCHANGED), turn +off echoing before dumping out the current token (ECHO_OFF) or turn on echoing +but after trying to dump out the current token (ECHO_ON). + +A new flag -q specifies a file in which unknown queries are written. This can +show you when you haven't specified all the necessary queries for a printer, +should allow more easy updates to the query responses when future LaserWriter +driver come out. + +NewStatus() now works even multi-forked. Status is written to a shared file +named .lwsrvstatusXXXXX, where XXXXX is the process id of the original server. +abpaps.c has been modified to provide a callback function when a status reply +or an open reply occur. The callback function for the status calls checks to +see if the requesting node is one with an open connection. If so, the status +is read from the shared file and returned. For all other nodes, the status +is either "idle" if there are no children running for that spooler, or +"processing job(s)" if it is. scantoken() was also changed so that NewStatus() +is called at the beginning of each page. + +The open reply callback function is used for LWSRV_AUFS_SECURITY. Just as the +connection is to be opened, the callback does the usual LWSRV_AUFS_SECURITY +checks and returns an error message if it fails, or sets a boolean in the +aufssecurity array. The array is checked in the main loop, and the connection +closed if the appropriate flag is not set. (This has not been tested.) + +childjob() was modified to set requname if LWSRV_AUFS_SECURITY is not set +and LWSRV_LPR_LOG is set (otherwise, requname is never set and the log +message contains as the user). + +unparen() is a routine that removes the leading left and trailing right +parenthesis from a string. This is used in getjob() to remove the parenthesis +in the %%For: and %%Title: fields, which LaserWriter 8.0 now adds. Without +this, RUN_AS_USER fails, since there isn't a user name with parenthesis. + +If you define JOBNOPAREN, all (remaining) parenthesis are converted to +square brackets. This is useful for spooler that print the jobname on +the banner page, but don't handle parenthesis well (the DEC PrintServer 20 +has problems with this, so changing to square brackets fixes the problem). + +You can specify a printer specified denied access message (LWSRV_AUFS_SECURITY) +by specifying a "DeniedMessage" line in the configuration file: + + printer = ( + AUFSSecurity /tmp/xxx; + DeniedMessage "No can do from %s"; + ... + ); + +This string is passed through fprintf, with %s being replaced by the hostname. +(Note: you will need to escape percent signs by doubling %%.) + +Now, when a child is forked, all other PAP connections are closed. Before, +one child would wait until another child was done, even if the children +were for different printers. Now the children act independently, as they +should. diff --git a/applications/lwsrv/fix.serial.ps b/applications/lwsrv/fix.serial.ps new file mode 100644 index 0000000..76112ca --- /dev/null +++ b/applications/lwsrv/fix.serial.ps @@ -0,0 +1,29 @@ +%! +% fix.serial.ps +% +% This file changes the parity setting of a serially-connected PostScript +% printer to 8-bit, no parity, so that the upper 128 characters of a font +% (the special characters like bullets, diacritics, etc) will print and so +% binary data (usually color and gray-scale images produced by some programs) +% will also work. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Change the 0 below to match the system administrator password of the printer +% (zero is the default) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +serverdict begin 0 exitserver +statusdict begin + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Change the 25 below to 9 if you are using the 9-pin serial connector +% instead of the 25-pin. +% +% Change the 9600 below if needed to match the baud rate of the serial +% connection (see the printer's user manual for appropriate values). +% +% The 3 below specifies 8-bit, no parity. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + 25 9600 3 setsccbatch +end diff --git a/applications/lwsrv/fontlist.c b/applications/lwsrv/fontlist.c new file mode 100644 index 0000000..d21c9f8 --- /dev/null +++ b/applications/lwsrv/fontlist.c @@ -0,0 +1,150 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/08/30 08:13:25 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/fontlist.c,v 2.3 1995/08/30 08:13:25 djh Rel djh $"; +static char revision[] = "$Revision: 2.3 $"; + +/* + * fontlist - UNIX AppleTalk spooling program: act as a laserwriter + * handles simple font list - assumes we can place in a file + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * Created Sept 5, 1987 by cck from lwsrv. + * + * + */ + +#include +#include +#include +#include +#include +#include + +#ifdef USESTRINGDOTH +# include +#else /* USESTRINGDOTH */ +# include +#endif /* USESTRINGDOTH */ + +#ifdef LWSRV8 +#include "list.h" +#include "query.h" +#include "parse.h" +#endif /* LWSRV8 */ + +#include "fontlist.h" +#include "spmisc.h" +#include "papstream.h" + +#ifndef LWSRV8 +FontList *fontlist = (FontList *)NULL; /* fontlist header */ +#endif /* not LWSRV8 */ + +#define FBMAX 100 +extern char *myname; + +#ifndef LWSRV8 +boolean +#else /* LWSRV8 */ +List * +#endif /* LWSRV8 */ +LoadFontList(fn) +char *fn; +{ + char fb[FBMAX]; +#ifdef LWSRV8 + register List *lp; + register FILE *ff; + register char *cp; +#else /* LWSRV8 */ + FILE *ff; + FontList *fp = NULL; + + if (fontlist) /* already loaded */ + return(TRUE); +#endif /* LWSRV8 */ + + if ((ff = fopen(fn,"r")) == NULL) +#ifndef LWSRV8 + return(FALSE); +#else /* LWSRV8 */ + return(NULL); + lp = CreateList(); +#endif /* LWSRV8 */ + while (fgets(fb,FBMAX,ff) != NULL) + if (fb[0] != '%' && fb[0] != '\n') { +#ifndef LWSRV8 + fp = (FontList *) malloc(sizeof(FontList)); + fp->fl_name = (char *) malloc(strlen(fb)+1); + strcpy(fp->fl_name,fb); + fp->fl_next = fontlist; + fontlist = fp; +#else /* LWSRV8 */ + if (cp = (char *)index(fb, '\n')) + *cp = '\0'; + AddToList(lp, strdup(fb)); +#endif /* LWSRV8 */ + } + fclose(ff); +#ifndef LWSRV8 + return(TRUE); +#else /* LWSRV8 */ + SortList(lp, StringSortItemCmp); + return(lp); +#endif /* LWSRV8 */ +} + +#ifdef LWSRV8 +private char newline[] = "\n"; +private char star[] = "*\n"; +#endif /* LWSRV8 */ + +void +SendFontList(pf) +PFILE *pf; +{ +#ifndef LWSRV8 + int i = 0; + FontList *fl = fontlist; + char status[255]; +#else /* LWSRV8 */ + register int i; + register char **ip; + register char *cp; + register List *fp; + char buf[256]; +#endif /* LWSRV8 */ + + /* won't do much good unless single fork */ + NewStatus("initializing fonts"); +#ifndef LWSRV8 + while (fl != NULL) { + i++; + p_write(pf,fl->fl_name,strlen(fl->fl_name),FALSE); + fl = fl->fl_next; + } +#else /* LWSRV8 */ + if (fp = (List *)SearchKVTree(thequery, "font", strcmp)) { + cp = buf; + for (ip = (char **)AddrList(fp), i = NList(fp); i > 0; ip++, i--) { + strcpy(cp, *ip); + strcat(cp, newline); + p_write(pf,buf,strlen(buf),FALSE); + } + fprintf(stderr,"%s: Sending fontList: %d entries\n", myname, NList(fp)); + } +#endif /* LWSRV8 */ +#ifndef LWSRV8 + p_write(pf,"*\n",strlen("*\n"),FALSE); + fprintf(stderr,"lwsrv: Sending fontList: %d entries\n",i); + sprintf(status,"receiving job"); + NewStatus(status); +#else /* LWSRV8 */ + p_write(pf,star,strlen(star),FALSE); + NewStatus("receiving job"); +#endif /* LWSRV8 */ +} diff --git a/applications/lwsrv/fontlist.h b/applications/lwsrv/fontlist.h new file mode 100644 index 0000000..7f6cd23 --- /dev/null +++ b/applications/lwsrv/fontlist.h @@ -0,0 +1,30 @@ +/* "$Author: djh $ $Date: 1995/08/28 11:10:14 $" */ +/* "$Header: /mac/src/cap60/applications/lwsrv/RCS/fontlist.h,v 2.2 1995/08/28 11:10:14 djh Rel djh $" */ +/* "$Revision: 2.2 $" */ + +/* + * fontlist - UNIX AppleTalk spooling program: act as a laserwriter + * handles simple font list - assumes we can place in a file + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Created sept 5, 1987 by cck from lwsrv + * + */ + +#ifndef LWSRV8 +typedef struct Font_List { /* fontlist as read from fonts file */ + struct Font_List *fl_next; /* pointer to next font name */ + char *fl_name; /* the name itself */ +} FontList; +boolean LoadFontList(); +#else /* LWSRV8 */ +List *LoadFontList(); +#endif /* LWSRV8 */ + +void SendFontList(); diff --git a/applications/lwsrv/list.c b/applications/lwsrv/list.c new file mode 100644 index 0000000..122ea22 --- /dev/null +++ b/applications/lwsrv/list.c @@ -0,0 +1,373 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/05/08 04:19:28 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/list.c,v 2.2 1996/05/08 04:19:28 djh Rel djh $"; +static char revision[] = "$Revision: 2.2 $"; + +/* + * list.c - general list package-simple and key-value lists + * + * UNIX AppleTalk spooling program: act as a laserwriter + * AppleTalk package for UNIX (4.2 BSD). + * + */ + +#include +#include +#ifdef USESTRINGDOTH +#include +#else USESTRINGDOTH +#include +#endif USESTRINGDOTH +#include "list.h" + +#define FALSE 0 +#define TRUE 1 + +static int listcmp(); +static void sendKVTree(); +static int _AddToKVTree(/* KVTree **ent, void *key, void *val, + int (*keycmp)() */); +static KVTree *_DupKVTree(/* KVTree *lf */); +static void _FreeKVTree(/* KVTree *lp, int (*keyfree)(), int (*valfree)() */); +static void _ListKVTree(/* KVTree *kp, List *lp */); +static void *_SearchKVTree(/* KVTree *lp, void *key, int (*keycmp)() */); +char *malloc(); +char *realloc(); + +void +AddToKVTree(ent, key, val, keycmp) +KVTree **ent; +void *key, *val; +int (*keycmp)(); +{ + _AddToKVTree(ent, key, val, keycmp); +} + +/* + * Modified from "Algorithms + Data Structures = Programs", Niklaus Wirth, + * 1976, section 4.4.7 Balanced Tree Insertion, page 220-221 (AVL). + * + */ + +#define L_EQUILIBRATED 2 +#define LEFTSLANTED 1 +#define L_REBALANCE 0 + +#define R_EQUILIBRATED 0 +#define RIGHTSLANTED 1 +#define R_REBALANCE 2 + +static int +_AddToKVTree(ent, key, val, keycmp) +register KVTree **ent; +void *key, *val; +int (*keycmp)(); +{ + register KVTree *ent1, *ent2; + register int cmp; + char *malloc(); + + if (*ent == NULL) { /* not in tree, insert it */ + if ((*ent = (KVTree *)malloc(sizeof(KVTree))) == NULL) + errorexit(1, "_AddToKVTree: Out of memory\n"); + (*ent)->left = (*ent)->right = NULL; + (*ent)->bal = LEFTSLANTED; + (*ent)->key = key; + (*ent)->val = val; + return(1); + } + if ((cmp = (*keycmp)(key, (*ent)->key)) == 0) { /* match */ + (*ent)->val = val; + return(0); + } + if (cmp < 0) { + if (!_AddToKVTree(&(*ent)->left, key, val, keycmp)) + return(0); + /* left branch has grown higher */ + switch((*ent)->bal) { + case L_EQUILIBRATED: + (*ent)->bal = LEFTSLANTED; + return(0); + case LEFTSLANTED: + (*ent)->bal = L_REBALANCE; + return(1); + case L_REBALANCE: /* rebalance */ + if ((ent1 = (*ent)->left)->bal == L_REBALANCE) { + /* single LL rotation */ + (*ent)->left = ent1->right; + ent1->right = *ent; + (*ent)->bal = LEFTSLANTED; + *ent = ent1; + } else { + /* double LR rotation */ + ent2 = ent1->right; + ent1->right = ent2->left; + ent2->left = ent1; + (*ent)->left = ent2->right; + ent2->right = *ent; + (*ent)->bal = (ent2->bal == L_REBALANCE) ? L_EQUILIBRATED : LEFTSLANTED; + ent1->bal = (ent2->bal == L_EQUILIBRATED) ? L_REBALANCE : LEFTSLANTED; + *ent = ent2; + } + (*ent)->bal = LEFTSLANTED; + return(0); + } + } + if (!_AddToKVTree(&(*ent)->right, key, val, keycmp)) + return(0); + /* right branch has grown higher */ + switch((*ent)->bal) { + case R_EQUILIBRATED: + (*ent)->bal = RIGHTSLANTED; + return(0); + case RIGHTSLANTED: + (*ent)->bal = R_REBALANCE; + return(1); + case R_REBALANCE: /* rebalance */ + if ((ent1 = (*ent)->right)->bal == R_REBALANCE) { + /* single RR rotation */ + (*ent)->right = ent1->left; + ent1->left = *ent; + (*ent)->bal = RIGHTSLANTED; + *ent = ent1; + } else { + /* double RL rotation */ + ent2 = ent1->left; + ent1->left = ent2->right; + ent2->right = ent1; + (*ent)->right = ent2->left; + ent2->left = *ent; + (*ent)->bal = (ent2->bal == R_REBALANCE) ? R_EQUILIBRATED : RIGHTSLANTED; + ent1->bal = (ent2->bal == R_EQUILIBRATED) ? R_REBALANCE : RIGHTSLANTED; + *ent = ent2; + } + (*ent)->bal = RIGHTSLANTED; + return(0); + } +} + +void +AddToList(lp, item) +register List *lp; +void *item; +{ + if (lp->n >= lp->lmax && (lp->list = (void **)realloc((char *)lp->list, + (lp->lmax += LISTDELTA) * sizeof(void *))) == NULL) + errorexit(1, "AddToList: Out of memory\n"); + lp->list[lp->n++] = item; +} + +KVTree ** +CreateKVTree() +{ + register KVTree **kp; + + if ((kp = (KVTree **)malloc(sizeof(KVTree *))) == NULL) + errorexit(1, "AddToKVTree: Out of memory\n"); + *kp = NULL; + return(kp); +} + +void +CatList(to, from) +register List *to; +List *from; +{ + register int n; + register void **vp; + + for (n = from->n, vp = from->list; n > 0; n--) + AddToList(to, *vp++); +} + +List * +CreateList() +{ + register List *lp; + static char errstr[] = "CreateList: Out of memory\n"; + + if ((lp = (List *)malloc(sizeof(List))) == NULL) + errorexit(1, errstr); + if ((lp->list = (void **)malloc((lp->lmax = LISTDELTA) * sizeof(void *))) + == NULL) + errorexit(2, errstr); + lp->n = 0; + return(lp); +} + +KVTree ** +DupKVTree(lf) +register KVTree **lf; +{ + register KVTree **kp; + + if ((kp = (KVTree **)malloc(sizeof(KVTree *))) == NULL) + errorexit(1, "DupKVTree: Out of memory\n"); + *kp = *lf ? _DupKVTree(*lf) : NULL; + return(kp); +} + +static KVTree * +_DupKVTree(lf) +register KVTree *lf; +{ + register KVTree *lt; + + if ((lt = (KVTree *)malloc(sizeof(KVTree))) == NULL) + errorexit(1, "_DupKVTree: Out of memory\n"); + lt->bal = lf->bal; + lt->key = lf->key; + lt->val = lf->val; + lt->left = lf->left ? _DupKVTree(lf->left) : NULL; + lt->right = lf->right ? _DupKVTree(lf->right) : NULL; + return(lt); +} + +List * +DupList(lf) +register List *lf; +{ + register List *lt; + register void **vp; + register int i; + + lt = CreateList(); + for (i = lf->n, vp = lf->list; i > 0; i--) + AddToList(lt, *vp++); + return(lt); +} + +void +FreeKVTree(lp, keyfree, valfree) +KVTree **lp; +int (*keyfree)(), (*valfree)(); +{ + if (*lp) + _FreeKVTree(*lp, keyfree, valfree); + free((char *)lp); +} + +static void +_FreeKVTree(lp, keyfree, valfree) +register KVTree *lp; +int (*keyfree)(), (*valfree)(); +{ + if (lp->left) + _FreeKVTree(lp->left, keyfree, valfree); + if (lp->right) + _FreeKVTree(lp->right, keyfree, valfree); + if (keyfree) + (*keyfree)(lp->key); + if (valfree) + (*valfree)(lp->val); + free((char *)lp); +} + +void +FreeList(lp, itemfree) +List *lp; +int (*itemfree)(); +{ + register void **vp; + register int i; + + if (itemfree) + for (i = lp->n, vp = lp->list; i > 0; i--) + (*itemfree)(*vp++); + free((char *)lp->list); + free((char *)lp); +} + +List * +ListKVTree(kp) +KVTree **kp; +{ + register List *lp; + + lp = CreateList(); + if (*kp) + _ListKVTree(*kp, lp); + return(lp); +} + +static void +_ListKVTree(kp, lp) +register KVTree *kp; +register List *lp; +{ + if (kp->left) + _ListKVTree(kp->left, lp); + AddToList(lp, kp); + if (kp->right) + _ListKVTree(kp->right, lp); +} + +void * +SearchKVTree(lp, key, keycmp) +KVTree **lp; +void *key; +int (*keycmp)(); +{ + return(*lp ? _SearchKVTree(*lp, key, keycmp) : NULL); +} + +static void * +_SearchKVTree(lp, key, keycmp) +KVTree *lp; +register void *key; +int (*keycmp)(); +{ + register int cmp; + + if ((cmp = (*keycmp)(key, lp->key)) == 0) + return(lp->val); + if (cmp > 0) + return(lp->right ? _SearchKVTree(lp->right, key, keycmp) : NULL); + else + return(lp->left ? _SearchKVTree(lp->left, key, keycmp) : NULL); +} + +void * +SearchList(lp, item, itemcmp) +register List *lp; +register void *item; +int (*itemcmp)(); +{ + register void **ip = lp->list; + register int i, low, high, cmp; + + low = 0; + high = lp->n - 1; + while (low <= high) { + i = (low + high) / 2; + if ((cmp = (*itemcmp)(ip[i], item)) == 0) + return(ip[i]); + if (cmp > 0) + high = i - 1; + else + low = i + 1; + } + return(NULL); +} + +void +SortList(lp, itemcmp) +register List *lp; +int (*itemcmp)(); +{ + qsort((char *)lp->list, lp->n, sizeof(void *), itemcmp); +} + +int +StringSortItemCmp(a, b) +char **a, **b; +{ + return(strcmp(*a, *b)); +} + +errorexit(status, str) +int status; +char *str; +{ + fputs(str, stderr); + exit(status); +} diff --git a/applications/lwsrv/list.h b/applications/lwsrv/list.h new file mode 100644 index 0000000..e2f08b8 --- /dev/null +++ b/applications/lwsrv/list.h @@ -0,0 +1,49 @@ +/* "$Author: djh $ $Date: 1995/08/28 10:38:35 $" */ +/* "$Header: /mac/src/cap60/applications/lwsrv/RCS/list.h,v 2.1 1995/08/28 10:38:35 djh Rel djh $" */ +/* "$Revision: 2.1 $" */ + +/* + * list.h - general list package-simple and key-value lists + * + * UNIX AppleTalk spooling program: act as a laserwriter + * AppleTalk package for UNIX (4.2 BSD). + * + */ + +#ifndef _LIST_H_ +#define _LIST_H_ + +#define LISTDELTA 10 + +typedef struct KVTree { + struct KVTree *left; + struct KVTree *right; + void *key; + void *val; + unsigned char bal; /* for AVL tree balancing */ +} KVTree; +typedef struct List { + int n; + int lmax; + void **list; +} List; + +#define AddrList(lp) (((List *)(lp))->list) +#define NList(lp) (((List *)(lp))->n) + +void AddToKVTree(/* KVTree **lp, void *key, void *val, int (*keycmp)() */); +void AddToList(/* List *lp, void *item */); +void CatList(/* List *to, List *from */); +KVTree **CreateKVTree(); +List *CreateList(); +KVTree **DupKVTree(/* KVTree **lf */); +List *DupList(/* List *lf */); +void FreeKVTree(/* KVTree **lp, int (*keyfree)(), int (*valfree)() */); +void FreeList(/* List *lp, int (*itemfree)() */); +List *ListKVTree(/* KVTree **kp */); +void *SearchKVTree(/* KVTree **lp, void *key, int (*keycmp)() */); +void *SearchList(/* List *lp, void *item, int (*itemcmp)() */); +void SortList(/* List *lp, int (*itemcmp)() */); +int StringSortItemCmp(/* char **a, char **b */); + +#endif /* _LIST_H_ */ diff --git a/applications/lwsrv/lwsrv.c b/applications/lwsrv/lwsrv.c new file mode 100644 index 0000000..4ba0658 --- /dev/null +++ b/applications/lwsrv/lwsrv.c @@ -0,0 +1,2765 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/09/10 13:56:31 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/lwsrv.c,v 2.47 1996/09/10 13:56:31 djh Rel djh $"; +static char revision[] = "$Revision: 2.47 $"; + +/* + * lwsrv - UNIX AppleTalk spooling program: act as a laserwriter + * driver: handles setup and farms out incoming jobs + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Feb 15, 1987 Schilit Created, based on lsrv + * Mar 17, 1987 Schilit Fixed nonprintables, added -r + * May 15, 1987 CCKim Add support for LaserPrep 4.0 + * Make multifork by default (turn + * off by defining SINGLEFORK) + * Feb 15, 1991 djh Various cleanups & patches + * Feb 20, 1991 rapatel improve wait code, add lpr logging + * Jan 21, 1992 gkl300 add simple pass thru (for PC's) + * + */ + +char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#ifndef _TYPES +# include /* assume included by param.h */ +#endif _TYPES +#include +#include +#include +#include + +#include /* include appletalk definitions */ +#include +#include +#include +#include "../../lib/cap/abpap.h" /* urk, puke, etc */ + +#ifdef USEDIRENT +#include +#else USEDIRENT +#ifdef xenix5 +#include +#else xenix5 +#include +#endif xenix5 +#endif USEDIRENT + +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#ifdef NEEDFCNTLDOTH +# include +#endif NEEDFCNTLDOTH + +#ifdef SOLARIS +#include +#define gethostname(n,l) (sysinfo(SI_HOSTNAME,(n),(l)) == (1) ? 0 : -1) +#endif SOLARIS + +#ifdef LWSRV8 +#include "list.h" +#include "query.h" +#include "parse.h" +#include "procset.h" +#include "fontlist.h" +#endif /* LWSRV8 */ +#include "papstream.h" + +#if defined (LWSRV_AUFS_SECURITY) | defined (RUN_AS_USER) +#include +#endif LWSRV_AUFS_SECURITY | RUN_AS_USER +#ifdef RUN_AS_USER +#ifndef USER_FILE +#define USER_FILE "/usr/local/lib/cap/macusers" +#endif USER_FILE +#ifndef REFUSE_MESSAGE +#define REFUSE_MESSAGE "/usr/local/lib/cap/refused" +#endif REFUSE_MESSAGE +#else RUN_AS_USER +#undef USER_REQUIRED +#endif RUN_AS_USER + +#ifdef LWSRV8 +#define STATUSFILE ".lwsrvstatus" +#define STATUSSIZE 256 +typedef struct Slot { + AddrBlock addr; + int slot; + int pid; + int prtno; + byte status[STATUSSIZE]; +} Slot; +#endif /* LWSRV8 */ + +private char *logfile = NULL; + +#ifdef LWSRV8 +#ifdef LW_TYPE +#define LW_AT_TYPE "LaserWriter" +#else LW_TYPE +private char *prttype = "LaserWriter"; +#endif LW_TYPE +#else /* LWSRV8 */ +private u_char *prtname = NULL; +private char *tracefile = NULL; +private char *fontfile = NULL; +private char *unixpname = NULL; +private char *prttype = "LaserWriter"; +private char *dictdir = "."; /* assume local dir */ +#endif /* LWSRV8 */ + +private int rflag = FALSE; /* remove print file */ +private int hflag = TRUE; /* default to print banner */ +private int singlefork = FALSE; +private PAPStatusRec *statbuffp; /* status buffer */ +export int capture = TRUE; +export char *myname; +export int verbose = 0; + +#ifdef LWSRV8 +#ifdef PAGECOUNT +export int pcopies; +#endif PAGECOUNT +export int pagecount; +export boolean tracing = FALSE; /* set if tracing */ +export char *queryfile = NULL; +#else /* LWSRV8 */ +#ifdef PAGECOUNT +export int pagecount; +export int pcopies; +#endif PAGECOUNT +#endif /* LWSRV8 */ + +#ifdef __hpux +#define MAXJOBNAME 64 +private char username[32],jobname[MAXJOBNAME],jobtitle[MAXJOBNAME]; +#else __hpux +private char username[128],jobname[1024]; +#endif __hpux + +#ifndef LWSRV8 +private char buf[1024]; +private int srefnum; +#endif /* not LWSRV8 */ + +#ifndef TEMPFILE +# define TEMPFILE "/tmp/lwsrvXXXXXX" /* temporary file holds job */ +#endif TEMPFILE + +private char *tmpfiledir = NULL; + +#define RFLOWQ atpMaxNum +#define BUFMAX (PAPSegSize*RFLOWQ)+10 + +#ifdef LWSRV_AUFS_SECURITY +private int requid, reqgid; +private char *aufsdb = NULL; /* budd */ +#ifndef LWSRV8 +char *bin, *tempbin; +int tempbinlen; +#else /* LWSRV8 */ +private char *bin, *tempbin; +private boolean aufssecurity[NUMSPAP]; +private char openReplyMessage[STATUSSIZE]; +#endif /* LWSRV8 */ +#endif LWSRV_AUFS_SECURITY + +#ifdef LWSRV_LPR_LOG +private char *requname = NULL; +#endif LWSRV_LPR_LOG + +#ifdef NeXT +char *nextdpi = NULL; /* NeXT printer resolution */ +#endif NeXT + +#ifndef LWSRV8 +#ifdef DONT_PARSE +export int dont_parse = FALSE; +#endif DONT_PARSE +#endif /* not LWSRV8 */ + +#ifdef USESYSVLP +# ifndef LPRCMD +# define LPRCMD "/usr/bin/lp" +# endif LPRCMD +#endif USESYSVLP + +#ifndef LPRCMD +# if defined(xenix5) || defined(__FreeBSD__) +# define LPRCMD "/usr/bin/lpr" +# else /* xenix5 || __FreeBSD__ */ +# define LPRCMD "/usr/ucb/lpr" +# endif /* xenix5 || __FreeBSD__ */ +#endif LPRCMD + +private char *lprcmd = LPRCMD; + +#ifdef LPRARGS +#ifndef LWSRV8 +private char *lprargsbuf[16]; +private char **lprargs = lprargsbuf; +#else /* LWSRV8 */ +private int lprstarted; +private List *lprargs; +#endif /* LWSRV8 */ +#endif LPRARGS + +#ifndef LWSRV8 +private struct printer_instance { + u_char *prtname; /* NBP registered printername */ + char *unixpname; /* UNIX printer name */ + char *tracefile; /* .. individual tracefile option */ + PAPStatusRec statbuf; + int srefnum; /* returned by SLInit */ + char nbpbuf[128]; /* registered name:type@zone */ + int rcomp; /* flag: waiting for job? */ + int cno; /* connection number of next job */ +} prtlist[NUMSPAP], *prtp; +#else /* LWSRV8 */ +private struct printer_instance prtlist[NUMSPAP]; +struct printer_instance *prtp; +#endif /* LWSRV8 */ + +private int nprt = 0; + +#ifdef LWSRV8 +private struct printer_instance _default = { + NULL, /* default is local dir (-a) */ + 0, /* always scan dictdir */ + NULL, /* dictionary list */ + TRUE, /* doing checksum (-k) */ + TRUE, /* print banner (-h) */ + FALSE, /* don't remove file (-r) */ + NULL, /* tracefile (-t) */ + FALSE, /* maybe "eexec" in code(-e) */ + 0, /* Transcript options (-T) */ +#ifdef ADOBE_DSC2_CONFORMANT + TRUE, /* DSC2 compatibility (-A) */ +#else ADOBE_DSC2_CONFORMANT + FALSE, /* DSC2 compatibility (-A) */ +#endif ADOBE_DSC2_CONFORMANT +#ifdef LPRARGS + NULL, /* lpr arguments (-L) */ +#endif LPRARGS +#ifdef PASS_THRU + FALSE, /* pass through (-P) */ +#endif PASS_THRU +#ifdef NeXT + NULL, /* NeXT resolution (-R) */ +#endif NeXT +#ifdef LW_TYPE + LW_AT_TYPE, /* default AppleTalk type (-Y) */ +#endif LW_TYPE + TRUE, /* capture procset (-N) */ +}; +private int prtno; +private int prtcopy; + +private int statusfd = -1; +private char statusfile[256]; +private char statusidle[] = "status: idle"; +private char statusprocessing[] = "status: processing job(s)"; +private List *slots; +private int freeslot; +private int myslot; + +private void readstatus(); +private void resetstatus(); +private void writestatus(); +private Slot *GetSlot(); +private void FreeSlot(); +private byte *statusreply(); +#ifdef LWSRV_AUFS_SECURITY +private byte *openreply(); +private char *deniedmessage(); +#endif LWSRV_AUFS_SECURITY +#ifdef TIMESTAMP +private char *timestamp(); +#endif TIMESTAMP +#endif /* LWSRV8 */ + +private void +#ifndef LWSRV8 +usage(s, err) +char *s, *err; +#else /* LWSRV8 */ +usage(err) +char *err; +#endif /* LWSRV8 */ +{ +#ifndef LWSRV8 + if (err != NULL) + fprintf(stderr,"%s: %s\n",s,err); + fprintf(stderr,"usage: %s -n -p \n",s); +#else /* LWSRV8 */ + if (err != NULL) + fprintf(stderr,"%s: %s\n",myname,err); + fprintf(stderr,"usage: %s []\n",myname); + fprintf(stderr,"\t contains options and printer descriptions\n"); + fprintf(stderr,"\t of printer templates\n\n"); +#ifdef LW_TYPE + fprintf(stderr,"usage: %s -n -p ",myname); + fprintf(stderr," -Y \n"); +#else LW_TYPE + fprintf(stderr,"usage: %s -n -p \n",myname); +#endif LW_TYPE +#endif /* LWSRV8 */ + fprintf(stderr,"usage:\t\t-a -f \n"); + fprintf(stderr,"usage:\t\t[-l ] [-t ] [-r] [-h]\n"); +#ifndef LWSRV_AUFS_SECURITY + fprintf(stderr,"usage:\t\t[-T crtolf] [-T quote8bit]\n\n"); +#else LWSRV_AUFS_SECURITY + fprintf(stderr,"usage:\t\t[-T crtolf] [-T quote8bit] [-X userlogindb]\n\n"); +#endif LWSRV_AUFS_SECURITY + fprintf(stderr,"\t-p* is the unix printer to print to\n"); + fprintf(stderr,"\t-n* specifies the name %s registers.\n", + myname); +#ifdef LWSRV8 +#ifdef LW_TYPE + fprintf(stderr,"\t-Y specifies an alternate AppleTalk\n"); + fprintf(stderr,"\t type for this printer, default is: "); + fprintf(stderr,"%s\n", LW_AT_TYPE); +#endif LW_TYPE +#endif /* LWSRV8 */ + fprintf(stderr,"\t-a* is the ProcSet directory.\n"); + fprintf(stderr,"\t-f* contains an font coordination list.\n"); + fprintf(stderr,"\t-t stores session and appledict in\n"); + fprintf(stderr,"\t without printing.\n"); + fprintf(stderr,"\t-F directory to store temporary files\n"); + fprintf(stderr,"\t-l specifies a file to log the %s session\n", + myname); +#ifdef LWSRV8 + fprintf(stderr,"\t-q log unknown queries\n"); +#endif /* LWSRV8 */ + fprintf(stderr,"\t-e Allow an eexec to occur in a procset\n"); + fprintf(stderr," warning: this may cause problems, use carefully\n"); + fprintf(stderr, "\t-N Turns off capturing of new procsets\n"); + fprintf(stderr,"\t-r Will retain temp print file for inspection\n"); + fprintf(stderr,"\t-h means to print without a banner page\n"); + if (is_simple_dsc()) + fprintf(stderr,"\t-A [on*|off] means to turn on or off Adobe document\n"); + else + fprintf(stderr,"\t-A [on|off*] means to turn on or off Adobe document\n"); + fprintf(stderr,"\tstructuring revision 2 compatibility\n"); + fprintf(stderr,"\t(this can cause problems, *'ed item is default)\n"); + fprintf(stderr,"\t-C Specify print spooler rather than lp/lpr\n"); +#ifdef LPRARGS + fprintf(stderr,"\t-L Argument to pass to lpr (multiple use)\n"); +#endif LPRARGS +#ifdef PASS_THRU + fprintf(stderr,"\t-P %s pass through (no adobe preprocessing)\n", myname); +#endif PASS_THRU +#ifdef NeXT + fprintf(stderr,"\t-R Specify resolution for NeXT printer\n"); +#endif NeXT + fprintf(stderr,"\t-S single %s fork (default is multiforking)\n", myname); +#ifndef LWSRV8 +#ifdef DONT_PARSE + fprintf(stderr,"\t-D don't parse any of the file, but allow crtolf if set\n"); +#endif DONT_PARSE +#endif /* LWSRV8 */ + fprintf(stderr,"\t-T Transcript compatibilty options\n"); + fprintf(stderr,"\t-T crtolf: translate cr to lf for Transcript filters\n"); + fprintf(stderr,"\t-T quote8bit: quote 8 bit chars for Transcript\n"); + fprintf(stderr,"\t-T makenondscconformant: make non DSC conformant: use\n"); + fprintf(stderr,"\t if psrv only works with DSC 1.0 conventions\n"); +#ifdef LWSRV_AUFS_SECURITY + fprintf(stderr,"\t-X use aufs for validation\n");/*budd*/ +#endif LWSRV_AUFS_SECURITY + fprintf(stderr,"\nexample: %s -n Laser -p ps -a/usr/lib/ADicts\n",myname); + fprintf(stderr,"\t\t-f /usr/lib/LW+Fonts\n"); + fprintf(stderr,"\t(note the starred items above are required)\n"); + exit(0); +} + +private void +#ifndef LWSRV8 +inc_nprt(progname) +char *progname; +#else /* LWSRV8 */ +inc_nprt() +#endif /* LWSRV8 */ +{ +#ifndef LWSRV8 + if (prtp->unixpname == NULL) + usage(progname,"Missing Unix Printer Name"); +#endif /* LWSRV8 */ + +#ifdef LWSRV8 + if (prtp == &_default) { /* doing default */ + prtp = prtlist; + if (_default.dictdir == NULL) { + _default.dictdir = "."; + _default.dictlist = scandicts(_default.dictdir, &_default.lastried); + AddToKVTree(_default.querylist, "procset", _default.dictlist, strcmp); + } + } else { + if (prtp->unixpname == NULL) + usage("Missing Unix Printer Name"); +#endif /* LWSRV8 */ +#ifndef LWSRV8 + if (prtp->prtname == NULL) + usage(progname,"Missing AppleTalk Printer Name"); +#else /* LWSRV8 */ + if (prtp->prtname == NULL) + usage("Missing AppleTalk Printer Name"); + if (SearchKVTree(prtp->querylist, "font", strcmp) == NULL) + usage("No fonts specified"); +#ifdef LW_TYPE + if (prtp->prttype == NULL) + usage("Missing AppleTalk type"); +#endif LW_TYPE +#endif /* LWSRV8 */ +#ifndef LWSRV8 + if (++nprt >= NUMSPAP) + usage("lwsrv","too many printers"); +#else /* LWSRV8 */ + if (++nprt >= NUMSPAP) + usage("too many printers"); +#endif /* LWSRV8 */ + prtp++; +#ifndef LWSRV8 + unixpname = tracefile = NULL; + prtname = NULL; +#else /* LWSRV8 */ + } + bcopy((char *)&_default, (char *)prtp, prtcopy); + thequery = prtp->querylist = DupKVTree(_default.querylist); +#ifdef LPRARGS + lprstarted = FALSE; +#endif LPRARGS +#endif /* LWSRV8 */ +} + +#ifdef LWSRV8 +static char optlist[64] = "a:f:l:p:t:d:n:krehNT:A:C:F:Svq:"; + +private void +lwsrv_init(name) +char *name; +{ + if (myname = rindex(name, '/')) + myname++; + else + myname = name; +#ifdef LWSRV_AUFS_SECURITY + strcat(optlist, "X:"); +#endif LWSRV_AUFS_SECURITY +#ifdef LPRARGS + strcat(optlist, "L:"); +#endif LPRARGS +#ifdef PASS_THRU + strcat(optlist, "P"); +#endif PASS_THRU +#ifdef NeXT + strcat(optlist, "R:"); +#endif NeXT +#ifdef LW_TYPE + strcat(optlist, "Y:"); +#endif LW_TYPE + + thequery = _default.querylist = CreateKVTree(); + prtcopy = (char *)&_default.prtname - (char *)&_default; + nprt = 0; + prtp = &_default; +#ifdef LPRARGS + lprstarted = FALSE; +#endif LPRARGS +} + +export void +doargs(argc,argv) +int argc; +char **argv; +{ + register int c; + register KVTree **kp; + register List *lp; + time_t ltime; + extern char *optarg; + extern int optind; +#ifdef ISO_TRANSLATE + void cISO2Mac(); +#endif ISO_TRANSLATE + + while ((c = getopt(argc,argv,optlist)) != EOF) { + switch (c) { + case 'a': + if (index(optarg, '/') == NULL) { + prtp->dictdir = (char *)malloc(strlen(optarg)+4); + strcpy(prtp->dictdir, "./"); + strcat(prtp->dictdir, optarg); + } else + prtp->dictdir = optarg; /* remember dictionary directory */ + ltime = 0; + if (kp = scandicts(prtp->dictdir, <ime)) { + prtp->lastried = ltime; + AddToKVTree(prtp->querylist, "procset", (prtp->dictlist = kp), strcmp); + } + break; + case 'd': + dbugarg(optarg); + break; + case 'f': + if ((lp = LoadFontList(optarg)) == NULL) { /* -f fontfile */ + fprintf(stderr,"%s: Bad FontList from %s\n", myname, optarg); + break; + } + AddToKVTree(prtp->querylist, "font", lp, strcmp); + fprintf(stderr,"%s: Font list from file %s\n", myname, optarg); + break; + case 'l': /* -l logfile */ + logfile = optarg; + break; + case 'q': /* -q queryfile */ + queryfile = optarg; + break; + case 'n': /* lwsrv printer name */ + if (prtp->prtname || prtp == &_default) + inc_nprt(); +#ifdef ISO_TRANSLATE + cISO2Mac(optarg); +#endif ISO_TRANSLATE + prtp->prtname = optarg; + break; + case 'p': /* -p unix printer name */ + if (prtp->unixpname || prtp == &_default) + inc_nprt(); + prtp->unixpname = optarg; + break; + case 'k': /* no DDP checksum */ + prtp->dochecksum = FALSE; + break; + case 'h': + prtp->hflag = FALSE; /* do not print banner */ + break; + case 'r': /* do not remove file */ + prtp->rflag = TRUE; + break; + case 't': /* -t tracefile */ + prtp->tracefile = optarg; + break; + case 'e': + prtp->eflag = TRUE; /* maybe "eexec" in code */ + break; + case 'T': + if ((c = simple_TranscriptOption(optarg)) < 0) + usage(NULL); + prtp->toptions |= c; + break; + case 'A': + if ((c = simple_dsc_option(optarg)) < 0) + usage(NULL); + prtp->dsc2 = c; + break; + case 'C': + lprcmd = optarg; + break; +#ifdef LPRARGS + case 'L': + if (!lprstarted) { + if (prtp->lprargs) + prtp->lprargs = DupList(prtp->lprargs); + else + prtp->lprargs = CreateList(); + lprstarted = TRUE; + } + AddToList(prtp->lprargs, (void *)optarg); + break; +#endif LPRARGS +#ifdef PASS_THRU + case 'P': + prtp->passthru = TRUE; /* -P pass through PC jobs */ + break; +#endif PASS_THRU + case 'S': + singlefork = TRUE; + fprintf(stderr, "%s: single fork\n", myname); + break; +#ifdef LWSRV_AUFS_SECURITY + case 'X': /* budd... */ + aufsdb = optarg; + fprintf(stderr, "%s: aufs db directory in %s\n", myname, aufsdb); + break; /* ...budd */ +#endif LWSRV_AUFS_SECURITY +#ifdef NeXT + case 'R': + prtp->nextdpi = optarg; + break; +#endif NeXT +#ifdef LW_TYPE + case 'Y': + prtp->prttype = optarg; + break; +#endif LW_TYPE + case 'F': + tmpfiledir = optarg; + break; + case 'N': + prtp->capture = FALSE; + break; + case 'v': + verbose++; + break; + case '?': /* illegal character */ + usage(NULL); /* usage and exit */ + break; + } + } + if (optind < argc) { + fprintf(stderr, "%s: surplus arguments starting from \"%s\"\n", + myname,argv[optind]); + usage(NULL); + } +} + +void +set_printer(p) +register struct printer_instance *p; +{ + extern boolean dochecksum; + + set_dict_list(p->dictlist); + dochecksum = p->dochecksum; + hflag = p->hflag; + rflag = p->rflag; + setflag_encrypted_instream(p->eflag); + set_toptions(p->toptions); + set_simple_dsc(p->dsc2); +#ifdef LPRARGS + lprargs = p->lprargs; +#endif LPRARGS +#ifdef PASS_THRU + set_simple_pass_thru(p->passthru); +#endif PASS_THRU +#ifdef NeXT + nextdpi = p->nextdpi; +#endif NeXT + capture = p->capture; + tracing = (p->tracefile != NULL); + statbuffp = &(p->statbuf); + thequery = p->querylist; + spool_setup(p->prtname, prtp->dictdir); +} + +private void +childdone(s) +int s; +{ + WSTATUS status; + + resetstatus(wait(&status)); + signal(SIGCHLD, childdone); +} + +#else /* LWSRV8 */ + +private void +doargs(argc,argv) +int argc; +char **argv; +{ + int c; + extern char *optarg; + extern int optind; + extern boolean dochecksum; + static char optlist[64] = "a:f:l:p:t:d:n:krehNT:A:C:F:Sv"; +#ifdef ISO_TRANSLATE + void cISO2Mac(); +#endif ISO_TRANSLATE + +#ifdef LWSRV_AUFS_SECURITY + strcat(optlist, "X:"); +#endif LWSRV_AUFS_SECURITY +#ifdef LPRARGS + strcat(optlist, "L:"); +#endif LPRARGS +#ifdef PASS_THRU + strcat(optlist, "P"); +#endif PASS_THRU +#ifdef NeXT + strcat(optlist, "R:"); +#endif NeXT +#ifdef DONT_PARSE + strcat(optlist, "D"); +#endif DONT_PARSE + + nprt = 0; prtp = prtlist; + + while ((c = getopt(argc,argv,optlist)) != EOF) { + switch (c) { + case 'a': + if (index(optarg, '/') == NULL) { + dictdir = (char *)malloc(strlen(optarg)+4); + strcpy(dictdir, "./"); + strcat(dictdir, optarg); + } else + dictdir = optarg; /* remember dictionary directory */ + break; + case 'd': + dbugarg(optarg); + break; + case 'f': + fontfile = optarg; /* -f fontfile */ + break; + case 'l': /* -l logfile */ + logfile = optarg; + break; + case 'n': /* lwsrv printer name */ + if (prtname != NULL) + inc_nprt(argv[0]); +#ifdef ISO_TRANSLATE + cISO2Mac(optarg); +#endif ISO_TRANSLATE + prtp->prtname = prtname = (u_char *)optarg; + break; + case 'p': /* -p unix printer name */ + if (unixpname != NULL) + inc_nprt(argv[0]); + prtp->unixpname = unixpname = optarg; + break; + case 'k': /* no DDP checksum */ + dochecksum = 0; + break; + case 'h': + hflag = FALSE; /* do not print banner */ + break; + case 'r': /* do not remove file */ + rflag = TRUE; + break; + case 't': /* -t tracefile */ + prtp->tracefile = tracefile = optarg; + break; + case 'e': + setflag_encrypted_instream(TRUE); /* maybe "eexec" in code */ + break; + case 'T': + if (simple_TranscriptOption(optarg) < 0) + usage(argv[0], NULL); + break; + case 'A': + if (simple_dsc_option(optarg) < 0) + usage(argv[0], NULL); + break; + case 'C': + lprcmd = optarg; + break; +#ifdef LPRARGS + case 'L': + *lprargs++ = optarg; + break; +#endif LPRARGS +#ifdef PASS_THRU + case 'P': + set_simple_pass_thru(); /* -P pass through PC jobs */ + break; +#endif PASS_THRU + case 'S': + singlefork = TRUE; + fprintf(stderr, "lwsrv: single fork\n"); + break; +#ifdef LWSRV_AUFS_SECURITY + case 'X': /* budd... */ + aufsdb = optarg; + fprintf(stderr, "lwsrv: aufs db directory in %s\n", aufsdb ); + break; /* ...budd */ +#endif LWSRV_AUFS_SECURITY +#ifdef NeXT + case 'R': + nextdpi = optarg; + break; +#endif NeXT +#ifdef DONT_PARSE + case 'D': + dont_parse = TRUE; + break; +#endif DONT_PARSE + case 'F': + tmpfiledir = optarg; + break; + case 'N': + capture = FALSE; + break; + case 'v': + verbose++; + break; + case '?': /* illegal character */ + usage(argv[0],NULL); /* usage and exit */ + break; + } + } + if (optind < argc) { + fprintf(stderr, "%s: surplus arguments starting from \"%s\"\n", + argv[0],argv[optind]); + usage(argv[0], NULL); + } + + inc_nprt(argv[0]); + + if (fontfile == NULL) + usage(argv[0],"No FontFile specified"); +#ifdef LPRARGS + *lprargs = NULL; + lprargs = lprargsbuf; +#endif LPRARGS + +} + +void +set_printer(p) +struct printer_instance *p; +{ + prtname = p->prtname; + unixpname = p->unixpname; + tracefile = p->tracefile; + statbuffp = &(p->statbuf); + srefnum = p->srefnum; +} + +private void +childdone() +{ + WSTATUS status; + + (void)wait(&status); + signal(SIGCHLD, childdone); +} +#endif /* LWSRV8 */ + +#ifdef LWSRV8 +private Slot * +GetSlot(prtno) +int prtno; +{ + register Slot *slotp, **sp; + register int i, n; + + if ((slotp = (Slot *)malloc(sizeof(Slot))) == NULL) + errorexit(1, "GetSlot: Out of memory\n"); + i = freeslot; + sp = (Slot **)AddrList(slots) + i; + *sp = slotp; + slotp->slot = i; + slotp->prtno = prtno; + + n = NList(slots); + for ( ; ; ) { + if (++i >= n) { /* all slots in use, make a new one */ + AddToList(slots, NULL); + freeslot = n; + break; + } + if (*++sp == NULL) { + freeslot = i; + break; + } + } + return(slotp); +} + +private void +FreeSlot(n) +int n; +{ + register Slot **sp; + + sp = (Slot **)AddrList(slots) + n; + free((char *)*sp); + *sp = NULL; + if (freeslot > n) + freeslot = n; +} + +private void +resetstatus(pid) +register int pid; +{ + register Slot **sp; + register int i, n; + register int *children; + + n = NList(slots); + for (sp = (Slot **)AddrList(slots), i = 0; i < n; sp++, i++) { + if (*sp == NULL) + continue; + if ((*sp)->pid == pid) { + children = &prtlist[(*sp)->prtno].children; + if (*children > 0 && --(*children) == 0) + cpyc2pstr(prtlist[(*sp)->prtno].statbuf.StatusStr, statusidle); + FreeSlot(i); + return; + } + } +} + +private void +readstatus(n) +int n; +{ + register Slot **sp; + + sp = (Slot **)AddrList(slots); + lseek(statusfd, n * STATUSSIZE, L_SET); + read(statusfd, sp[n]->status, STATUSSIZE); +} + +private void +writestatus(n, str) +int n; +char *str; +{ + byte message[STATUSSIZE]; + + lseek(statusfd, n * STATUSSIZE, L_SET); + cpyc2pstr(message, str); + write(statusfd, message, *message + 1); +} + +private byte * +statusreply(cno, from) +int cno; +register AddrBlock *from; +{ + register Slot **sp; + register int i, n; + + n = NList(slots); + sp = (Slot **)AddrList(slots); + for (i = 0; i < n; sp++, i++) { + if (*sp == NULL) + continue; + if (from->net == (*sp)->addr.net && from->node == (*sp)->addr.node) { + readstatus(i); + return((*sp)->status); + } + } + return(NULL); +} + +#ifdef LWSRV_AUFS_SECURITY +private char * +deniedmessage(cno) +{ + register List *lp; + register char *sp, *cp; + /* + * We rely on the fact that the cno passed by the callback corresponds to + * the index into the prtlist array. + */ + if ((lp = SearchKVTree(prtlist[cno].querylist, "DeniedMessage", strcmp)) && + NList(lp) >= 1) { + /* remove the trailing newline, if any */ + if (cp = index((sp = *(char **)AddrList(lp)), '\n')) + *cp = 0; + return(sp); + } + return("Denied: Please login to host %s via AppleShare"); +} + +private byte * +openreply(cno, from, result) +int cno; +AddrBlock *from; +int result; +{ + if (result == papPrinterBusy) + return(NULL); + /*** budd.... */ + fprintf(stderr, +#ifdef TIMESTAMP + "%s: %s: \"%s\" open request from [Network %d.%d, node %d, socket %d]\n", + myname, timestamp(), prtlist[cno].prtname, nkipnetnumber(from->net), +#else TIMESTAMP + "%s: \"%s\" open request from [Network %d.%d, node %d, socket %d]\n", + myname, prtlist[cno].prtname, nkipnetnumber(from->net), +#endif TIMESTAMP + nkipsubnetnumber(from->net), + from->node, from->skt); + + if (aufsdb != NULL) { + char fname[ 256 ]; + char filename[ 256 ]; + int f, cc, ok = 0; + struct passwd *pw; + struct stat statbuf; +#ifdef HIDE_LWSEC_FILE + char protecteddir[MAXPATHLEN]; + (void) strcpy(protecteddir, aufsdb); + make_userlogin(filename, protecteddir, from); + (void) strcpy(protecteddir, filename); + filename[0] = '\0'; + make_userlogin(filename, protecteddir, from); +#else HIDE_LWSEC_FILE + make_userlogin( filename, aufsdb, from); +#endif HIDE_LWSEC_FILE + + if ((f = open( filename, 0)) >= 0) { + if ((cc = read( f, fname, sizeof(fname)-1)) > 0) { + if (fname[cc-1] == '\n') + fname[cc-1] = '\0'; + fprintf( stderr, "%s: Found username in aufsdb (%s): %s\n", + myname, aufsdb, fname); + + if ((tempbin = rindex(fname,':')) != NULL) { + *tempbin='\0'; + tempbin++; + bin = (char *)malloc(strlen(tempbin)+1); + strcpy(bin,tempbin); + } + + (void) stat(filename, &statbuf); + if ((pw = getpwuid((int) statbuf.st_uid)) != NULL) { + if (strcmp(pw->pw_name, fname) == 0) { + requid = pw->pw_uid; + reqgid = pw->pw_gid; +#ifdef LWSRV_LPR_LOG + requname = pw->pw_name; +#endif LWSRV_LPR_LOG + ok = 1; + } + } else if ((pw = getpwnam(fname)) != NULL) { + requid = pw->pw_uid; + reqgid = pw->pw_gid; +#ifdef LWSRV_LPR_LOG + requname = pw->pw_name; +#endif LWSRV_LPR_LOG + ok = 1; /* true */ + } /* pwnam ok */ + } /* read OK */ + close(f); /* close the file */ + } /* open OK */ + if (!ok) { /* dump connection with error message */ + static byte message[STATUSSIZE]; + fprintf( stderr, "%s: No username (or invalid) confirmation.\n", + myname); + gethostname( fname, sizeof(fname)-1); /* ick */ + + sprintf(openReplyMessage, deniedmessage(cno), fname); + cpyc2pstr(message, openReplyMessage); + return(message); + } /* not ok */ + } /* have aufsdb */ + /* ....budd ***/ + aufssecurity[cno] = TRUE; + return(NULL); +} +#endif LWSRV_AUFS_SECURITY +#endif /* LWSRV8 */ + +/* + * delete the NBP name + * + */ + +private int is_child = 0; + +private void +#ifndef LWSRV8 +cleanup() +#else /* LWSRV8 */ +cleanup(s) +int s; +#endif /* LWSRV8 */ +{ +#ifndef LWSRV8 + int i; +#else /* LWSRV8 */ + int i, srefnum; +#endif /* LWSRV8 */ + + if (is_child) + exit(0); + + for (i=0; i 0) { + ioctl(i, TIOCNOTTY,(char *) 0); + close(i); + } +#endif TIOCNOTTY +#ifdef xenix5 + /* + * USG process groups: + * The fork guarantees that the child is not a process group leader. + * Then setpgrp() can work, whick loses the controllong tty. + * Note that we must be careful not to be the first to open any tty, + * or it will become our controlling tty. C'est la vie. + */ + setpgrp(); +#endif xenix5 +#else POSIX + (void) setsid(); +#endif POSIX + } + +/* dbug.db_pap = TRUE; */ + + if (!singlefork) { + sprintf(statusfile, "%s%05d", STATUSFILE, getpid()); + if ((statusfd = open(statusfile, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) { + fprintf(stderr, "%s: Can't open %s\n", myname, statusfile); + exit(1); + } + signal(SIGCHLD, childdone); + slots = CreateList(); + AddToList(slots, NULL); /* make sure there is one slot set up */ + freeslot = 0; + papstatuscallback = statusreply; + } +#ifdef LWSRV_AUFS_SECURITY + papopencallback = openreply; +#endif LWSRV_AUFS_SECURITY + + abInit(FALSE); /* initialize appletalk driver */ + nbpInit(); /* initialize NBP routines */ + PAPInit(); /* init PAP printer routines */ + + errcount = 0; + for (prtp=prtlist,i=0; iprtname); + cMac2ISO(prtp->prttype); + fprintf(stderr,"%s: Spooler starting for %s:%s@*\n", + myname,prtp->prtname,prtp->prttype); + sprintf(prtp->nbpbuf,"%s:%s@*",prtp->prtname,prtp->prttype); + cISO2Mac(prtp->prttype); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + fprintf(stderr,"%s: Spooler starting for %s:%s@*\n", + myname,prtp->prtname,prtp->prttype); + sprintf(prtp->nbpbuf,"%s:%s@*",prtp->prtname,prtp->prttype); +#endif ISO_TRANSLATE +#else LW_TYPE +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->prtname); + fprintf(stderr,"%s: Spooler starting for %s:%s@*\n", + myname,prtp->prtname,prttype); + sprintf(prtp->nbpbuf,"%s:%s@*",prtp->prtname,prttype); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + fprintf(stderr,"%s: Spooler starting for %s:%s@*\n", + myname,prtp->prtname,prttype); + sprintf(prtp->nbpbuf,"%s:%s@*",prtp->prtname,prttype); +#endif ISO_TRANSLATE +#endif LW_TYPE + cpyc2pstr(prtp->statbuf.StatusStr, statusidle); + err = SLInit(&(prtp->srefnum), prtp->nbpbuf, 8, &(prtp->statbuf)); + if (err != noErr) { + fprintf(stderr,"%s: SLInit %s failed: %d\n",myname,prtp->nbpbuf,err); + errcount++; + } + /* GetNextJob is asynchronous, so call it for each printer */ + err = GetNextJob(prtp->srefnum, &(prtp->cno), &(prtp->rcomp)); + if (err != noErr) { +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->prtname); + fprintf(stderr,"%s: GetNextJob %s failed: %d\n",myname,prtp->prtname, + err); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + fprintf(stderr,"%s: GetNextJob %s failed: %d\n",myname,prtp->prtname, + err); +#endif ISO_TRANSLATE + errcount++; + } + fprintf(stderr, "%s: %s ready and waiting\n", myname, prtp->nbpbuf); + } + + if (errcount == nprt) { + fprintf(stderr, "%s: no printers successful registered!\n", myname); + exit(1); + } + + prtno = 0; + while (TRUE) { + openlogfile(); + NewStatus("idle"); + + /* scan each printer in turn, processing each one as "normal" if there + * is work available. If no work was available then abSleep for a while + */ + while (prtno 0) prtno++; + if (prtno == nprt) { + abSleep(20,TRUE); /* wait for some work */ + prtno = 0; /* start scan again */ + continue; + } + + prtp = &prtlist[prtno]; + ltime = prtp->lastried; + if (kp = scandicts(prtp->dictdir, <ime)) { + if (prtp->dictlist) + FreeKVTree(prtp->dictlist, free, free); + prtp->dictlist = kp; + prtp->lastried = ltime; + } + set_printer(prtp); /* set the global variables etc */ + srefnum = prtp->srefnum; + cno = prtp->cno; + + /* GetNextJob is asynchronous, so announce readiness for more work */ + /* WTR: is this right - should it depend on singlefork? */ + err = GetNextJob(prtp->srefnum, &(prtp->cno), &(prtp->rcomp)); + if (err != noErr) { +#ifdef TIMESTAMP +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->prtname); + fprintf(stderr,"%s: %s: GetNextJob %s failed: %d\n", + myname, timestamp(), prtp->prtname, err); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + fprintf(stderr,"%s: %s: GetNextJob %s failed: %d\n", + myname, timestamp(), prtp->prtname, err); +#endif ISO_TRANSLATE +#else TIMESTAMP +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->prtname); + fprintf(stderr,"%s: GetNextJob %s failed: %d\n", + myname, prtp->prtname, err); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + fprintf(stderr,"%s: GetNextJob %s failed: %d\n", + myname, prtp->prtname, err); +#endif ISO_TRANSLATE +#endif TIMESTAMP + } +#ifdef LWSRV_AUFS_SECURITY + /* + * abpaps.c and lwsrv.c uses "cno" to refer to different things. The + * "cno" used by the callbacks is really "srefnum" here. Confusing! + */ + if (aufssecurity[srefnum]) { +#endif LWSRV_AUFS_SECURITY +#ifdef TIMESTAMP +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->prtname); + fprintf(stderr,"%s: %s: Starting print job for \"%s\", Connection %d\n", + myname, timestamp(), prtp->prtname, cno); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + fprintf(stderr,"%s: %s: Starting print job for \"%s\", Connection %d\n", + myname, timestamp(), prtp->prtname, cno); +#endif ISO_TRANSLATE +#else TIMESTAMP +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->prtname); + fprintf(stderr,"%s: Starting print job for \"%s\", Connection %d\n", + myname, prtp->prtname, cno); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + fprintf(stderr,"%s: Starting print job for \"%s\", Connection %d\n", + myname, prtp->prtname, cno); +#endif ISO_TRANSLATE +#endif TIMESTAMP + cpyc2pstr(prtp->statbuf.StatusStr, statusprocessing); +#ifdef LWSRV_AUFS_SECURITY + } +#endif LWSRV_AUFS_SECURITY + if (!singlefork) { + slotp = GetSlot(prtno); + myslot = slotp->slot; + if ((err = PAPGetNetworkInfo(cno, &slotp->addr)) != noErr) { + fprintf(stderr, "%s: Get Network info failed with error %d", myname, + err); + PAPClose(cno, TRUE); + FreeSlot(slotp->slot); + continue; + } + if (logfile != NULL) + close(2); /* close log file */ + if ((i = fork()) != 0) { + PAPShutdown(cno); /* kill off connection */ + slotp->pid = i; /* for resetting status */ + prtp->children++; +#ifdef LWSRV_AUFS_SECURITY + aufssecurity[srefnum] = FALSE; +#endif LWSRV_AUFS_SECURITY + continue; + } + for (i = 0; i < nprt; i++) { + SLClose(prtlist[i].srefnum); /* close all servers for child */ + PAPShutdown(prtlist[i].cno); /* kill off connection */ + } + nbpShutdown(); /* shutdown nbp for child */ + } +#ifdef LWSRV_AUFS_SECURITY + if (!aufssecurity[srefnum]) { + NewStatus(openReplyMessage); + abSleep(sectotick(20),FALSE); + PAPClose( cno, TRUE); /* arg2 is ignored!! */ + /* what does it mean? */ + if (singlefork) + continue; + else + exit(1); + } + aufssecurity[srefnum] = FALSE; +#endif LWSRV_AUFS_SECURITY + /* need for multi-forking, nice for single forking */ + /* handle the connection in cno */ + openlogfile(); +#ifdef SOLARIS + signal(SIGCHLD,SIG_DFL); +#else SOLARIS + signal(SIGCHLD,SIG_IGN); +#endif SOLARIS +#ifdef AUTHENTICATE + if (nprt != 1) + initauthenticate(*argv, prtp->prtname); +#endif AUTHENTICATE + childjob(p_opn(cno, BUFMAX)); + if (!singlefork) + exit(0); + else { + cpyc2pstr(prtp->statbuf.StatusStr, statusidle); + *username = 0; + } + } +} + +#else /* LWSRV8 */ + +main(argc,argv) +int argc; +char **argv; +{ + int err,i,cno,errcount; + int prtno,prtcount; + void cleanup(); + void childdone(); +#ifdef ISO_TRANSLATE + void cMac2ISO(), cISO2Mac(); +#endif ISO_TRANSLATE + + if (myname = rindex(argv[0], '/')) + myname++; + else + myname = argv[0]; + + doargs(argc,argv); /* handle args */ + +#ifdef LWSRV_AUFS_SECURITY + requid = getuid(); /* budd */ + reqgid = getgid(); /* budd */ +#endif LWSRV_AUFS_SECURITY + +#ifdef AUTHENTICATE + if (nprt == 1) + initauthenticate(*argv, (char *)prtlist[0].prtname); +#endif AUTHENTICATE + + signal(SIGHUP, cleanup); + signal(SIGINT, cleanup); + signal(SIGQUIT, cleanup); + signal(SIGTERM, cleanup); + + if (!dbug.db_flgs) { /* disassociate */ + if (fork()) + exit(0); /* kill off parent */ + for (i=0; i < 20; i++) + close(i); /* close all files */ + (void) open("/dev/null",0); +#ifndef NODUP2 + (void) dup2(0,1); +#else NODUP2 + (void)dup(0); /* for slot 1 */ +#endif NODUP2 + if (logfile == NULL) { +#ifndef NODUP2 + (void) dup2(0,2); +#else NODUP2 + (void) dup(0); /* for slot 2 */ +#endif NODUP2 + } else + openlogfile(); +#ifndef POSIX +#ifdef TIOCNOTTY + if ((i = open("/dev/tty",2)) > 0) { + ioctl(i, TIOCNOTTY,(char *) 0); + close(i); + } +#endif TIOCNOTTY +#ifdef xenix5 + /* + * USG process groups: + * The fork guarantees that the child is not a process group leader. + * Then setpgrp() can work, whick loses the controllong tty. + * Note that we must be careful not to be the first to open any tty, + * or it will become our controlling tty. C'est la vie. + */ + setpgrp(); +#endif xenix5 +#else POSIX + (void) setsid(); +#endif POSIX + } + +/* dbug.db_pap = TRUE; */ + + abInit(FALSE); /* initialize appletalk driver */ + nbpInit(); /* initialize NBP routines */ + PAPInit(); /* init PAP printer routines */ + + set_printer(&prtlist[0]); /* for definiteness */ +#ifdef ISO_TRANSLATE + cMac2ISO(prtname); + fprintf(stderr,"lwsrv: Spooler starting for %s.%s.*\n", + (char *)prtname, prttype); + cISO2Mac(prtname); +#else ISO_TRANSLATE + fprintf(stderr,"lwsrv: Spooler starting for %s.%s.*\n", + (char *)prtname, prttype); +#endif ISO_TRANSLATE + if (!spool_setup(tracefile, fontfile, (char *)prtname, dictdir)) { + usage(argv[0]); + exit(1); + } + + if (!singlefork) + signal(SIGCHLD, childdone); + + errcount = 0; + for (prtp=prtlist,i=0; inbpbuf,"%s:%s@*",(char *)prtp->prtname,prttype); + cpyc2pstr(prtp->statbuf.StatusStr,"status: idle"); + err = SLInit(&(prtp->srefnum), prtp->nbpbuf, 8, &(prtp->statbuf)); + if (err != noErr) { +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->nbpbuf); + fprintf(stderr,"lwsrv: SLInit %s failed: %d\n", prtp->nbpbuf, err); + cISO2Mac(prtp->nbpbuf); +#else ISO_TRANSLATE + fprintf(stderr,"lwsrv: SLInit %s failed: %d\n", prtp->nbpbuf, err); +#endif ISO_TRANSLATE + errcount++; + } + /* GetNextJob is asynchronous, so call it for each printer */ + err = GetNextJob(prtp->srefnum, &(prtp->cno), &(prtp->rcomp)); + if (err != noErr) { +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->prtname); + fprintf(stderr,"lwsrv: GetNextJob %s failed: %d\n", + (char *)prtp->prtname, err); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + fprintf(stderr,"lwsrv: GetNextJob %s failed: %d\n", + (char *)prtp->prtname, err); +#endif ISO_TRANSLATE + errcount++; + } +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->nbpbuf); + fprintf(stderr, "lwsrv: %s ready and waiting\n", prtp->nbpbuf); + cISO2Mac(prtp->nbpbuf); +#else ISO_TRANSLATE + fprintf(stderr, "lwsrv: %s ready and waiting\n", prtp->nbpbuf); +#endif ISO_TRANSLATE + } + + if (errcount == nprt) { + fprintf(stderr, "lwsrv: no printers successfully registered!\n"); + exit(1); + } + + prtno = 0; prtcount = 0; + while (TRUE) { +#ifdef LWSRV_AUFS_SECURITY + AddrBlock addr; /* budd */ +#endif LWSRV_AUFS_SECURITY + openlogfile(); + NewStatus("idle"); + + /* scan each printer in turn, processing each one as "normal" if there + * is work available. If no work was available then abSleep for a while + */ + while (prtno 0) prtno++; + if (prtno == nprt) { + if (prtcount == 0) { + abSleep(20,TRUE); /* wait for some work */ + } + prtno = 0; /* start scan again */ + continue; + } + + prtp = &prtlist[prtno]; + set_printer(prtp); /* set the global variables etc */ + cno = prtp->cno; + + /* GetNextJob is asynchronous, so announce readiness for more work */ + /* WTR: is this right - should it depend on singlefork? */ + err = GetNextJob(prtp->srefnum, &(prtp->cno), &(prtp->rcomp)); + if (err != noErr) { +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->prtname); + fprintf(stderr,"lwsrv: GetNextJob %s failed: %d\n", + (char *)prtp->prtname, err); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + fprintf(stderr,"lwsrv: GetNextJob %s failed: %d\n", + (char *)prtp->prtname, err); +#endif ISO_TRANSLATE + } + +#ifdef ISO_TRANSLATE + cMac2ISO(prtname); + fprintf(stderr,"lwsrv: Starting print job for %s\n", (char *)prtname); + cISO2Mac(prtname); +#else ISO_TRANSLATE + fprintf(stderr,"lwsrv: Starting print job for %s\n", (char *)prtname); +#endif ISO_TRANSLATE + +#ifdef LWSRV_AUFS_SECURITY + /*** budd.... */ + if ((err = PAPGetNetworkInfo(cno, &addr)) != noErr) + fprintf(stderr, "Get Network info failed with error %d", err); + else + fprintf(stderr,"Connection %d from [Network %d.%d, node %d, socket %d]\n", + cno, nkipnetnumber(addr.net), + nkipsubnetnumber(addr.net), + addr.node, addr.skt); + + if( aufsdb != NULL ) { + char fname[ 256 ]; + char filename[ 256 ]; + int f, cc, ok = 0; + struct passwd *pw; + struct stat statbuf; +#ifdef HIDE_LWSEC_FILE + char protecteddir[MAXPATHLEN]; + (void) strcpy(protecteddir, aufsdb); + make_userlogin(filename, protecteddir, addr); + (void) strcpy(protecteddir, filename); + filename[0] = '\0'; + make_userlogin(filename, protecteddir, addr); +#else HIDE_LWSEC_FILE + make_userlogin( filename, aufsdb, addr ); +#endif HIDE_LWSEC_FILE + + if( (f = open( filename, 0)) >= 0) { + if( (cc = read( f, fname, sizeof( fname )-1 )) > 0 ) { + if( fname[cc-1] == '\n' ) + fname[cc-1] = '\0'; + fprintf( stderr, "Found username in aufsdb (%s): %s\n", + aufsdb, fname ); + + if ((tempbin = rindex(fname,':')) != NULL) { + *tempbin='\0'; + tempbin++; + bin=(char *) malloc(strlen(tempbin)+1); + strcpy(bin,tempbin); + } + + (void) stat(filename, &statbuf); + if ( (pw = getpwuid((int) statbuf.st_uid)) != NULL ) { + if ( strcmp(pw->pw_name, fname) == 0) { + requid = pw->pw_uid; + reqgid = pw->pw_gid; +#ifdef LWSRV_LPR_LOG + requname = pw->pw_name; +#endif LWSRV_LPR_LOG + ok = 1; + } + } + else + if( (pw = getpwnam( fname )) != NULL ) { + requid = pw->pw_uid; + reqgid = pw->pw_gid; +#ifdef LWSRV_LPR_LOG + requname = pw->pw_name; +#endif LWSRV_LPR_LOG + ok = 1; /* true */ + } /* pwnam ok */ + } /* read OK */ + close(f); /* close the file */ + } /* open OK */ + if( !ok ) { /* dump connection with error message */ + char message[ 512 ]; + fprintf( stderr, "No username (or invalid) confirmation.\n"); + gethostname( fname, sizeof( fname )-1 ); /* ick */ + + /* THIS DOES NOT WORK! */ + sprintf(message, "error: please login to host %s via appleshare", + fname ); + NewStatus( message ); + sleep( 3 ); /* bleh!! */ +/* PAPShutdown( cno ); /* */ + PAPClose( cno, TRUE ); /* arg2 is ignored!! */ + /* what does it mean? */ + + continue; + } /* not ok */ + } /* have aufsdb */ + /* ....budd ***/ +#endif LWSRV_AUFS_SECURITY + + if (!singlefork) { + if (logfile != NULL) + close(2); /* close log file */ + if (fork() != 0) { + PAPShutdown(cno); /* kill off connection */ + continue; + } + SLClose(srefnum); /* close server for child */ + nbpShutdown(); /* shutdown nbp for child */ + } else NewStatus("busy, processing job"); + /* need for multi-forking, nice for single forking */ + /* handle the connection in cno */ + openlogfile(); +#ifdef SOLARIS + signal(SIGCHLD,SIG_DFL); +#else SOLARIS + signal(SIGCHLD,SIG_IGN); +#endif SOLARIS +#ifdef AUTHENTICATE + if (nprt != 1) + initauthenticate(*argv, (char *)prtlist[prtno].prtname); +#endif AUTHENTICATE + childjob(p_opn(cno, BUFMAX)); + if (!singlefork) + exit(0); + } +} +#endif /* LWSRV8 */ + +#ifdef RUN_AS_USER +/* + * Conversion table, macintosh ascii with diacriticals to plain ascii. + * In addition, an ':' also maps to an underscore for obvious reasons. + * All other junk maps to an underscore. + * + */ + +static unsigned char convert [256] = { +/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +/* 0 */ '_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_', +/* 1 */ '_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_', +/* 2 */ ' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/', +/* 3 */ '0','1','2','3','4','5','6','7','8','9','_',';','<','=','>','?', +/* 4 */ '@','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', +/* 5 */ 'p','q','r','s','t','u','v','w','x','y','z','[','\\',']','^','_', +/* 6 */ '`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', +/* 7 */ 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~','_', +/* 8 */ 'a','a','c','e','n','o','u','a','a','a','a','a','a','c','e','e', +/* 9 */ 'e','e','i','i','i','i','n','o','o','o','o','o','u','u','u','u', +/* a */ '_','_','c','$','_','*','_','_','r','c','_','_','_','_','_','o', +/* b */ '_','_','_','_','_','m','d','s','p','p','_','a','o','_','_','o', +/* c */ '?','!','_','_','_','_','_','_','_','_','_','a','a','o','_','_', +/* d */ '_','_','_','_','_','_','_','_','y','_','_','_','_','_','_','_', +/* e */ '_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_', +/* f */ '_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_' +}; + +static void +unixize(name) +char *name; +{ + register unsigned char *p = (unsigned char *)name; + + while (*p) { + *p = convert[*p]; + p += 1; + } +} + +static char * +getmacname(name, line, file) +char *name; /* mac name */ +char *line; /* line buffer to use */ +char *file; /* name of file containing table */ +{ + FILE *f; + char *m, *nl; + + if ((f = fopen(file, "r")) == NULL) + return(NULL); + + while (fgets(line, 255, f) != NULL) { + if (*line == '#' || *line == '\n') + continue; /* skip comment line */ + if ((m = index(line, ':')) == NULL) + continue; /* line in error */ + *m++ = '\0'; + if ((nl = index(m, '\n')) != NULL) + *nl = '\0'; /* take trailing newline */ + unixize(m); + if (strcmp(m, name) == 0) { + fclose(f); + return(line); + } + } + fclose(f); + return(NULL); +} + +/* + * We try to find a unix username to match the name from the mac. + * The approach is simple. We use a simple file to convert mac names + * to unix login names. The file needs only contain mac names that can't + * be resolved into unix names by simple means. The other ones are found + * automaticly. This means that 'Maarten' is resolved into 'maarten' without + * intervention, whereas 'El Gringo' needs to be in the file to be resolved + * to 'dolf'. + * + * Example file: + * #comment line + * dolf:El Gringo + * + */ + +static int +unixuid(macname) +char *macname; +{ + struct passwd *pw; + char line[256]; + char name[256]; + char *n; + + strcpy(name, macname); + unixize(name); + + if ((n = getmacname(name, line, USER_FILE)) == NULL) + n = name; + + if ((pw = getpwnam(n)) == NULL) + return(0); + else + return(pw->pw_uid); +} +#endif RUN_AS_USER + +#ifdef LWSRV8 +export +childjob(pf) +PFILE *pf; +{ + long t; + int argc, i; + FILE *outfile; + char tname[256],status[256]; + char pbuf[256],rhbuf[16],jbuf[1024]; + char *childargv[64]; +#ifdef LWSRV_AUFS_SECURITY + char bbuf[256]; +#endif LWSRV_AUFS_SECURITY +#ifdef NeXT + char dpistring[6]; +#endif NeXT +#ifdef RUN_AS_USER + int uid; +#endif RUN_AS_USER + + int waitret; + WSTATUS waitstatus; + + int actualprintout; + int doeof; + +#ifdef AUTHENTICATE + register PAPSOCKET *ps; + register unsigned net; +#ifdef ISO_TRANSLATE + void cMac2ISO(), cISO2Mac(); +#endif ISO_TRANSLATE + + if ((ps = cnotopapskt(pf->p_cno)) == NULL || !authenticate(net = + ntohs(ps->addr.net), ps->addr.node)) { + p_cls(pf); /* close out the pap connection */ + (void)time(&t); +#ifdef TIMESTAMP + fprintf(stderr,"%s: %s: Authentication failed: net %u.%u node %u\n", + myname, timestamp(), ((unsigned)net >> 8), (unsigned)(net & 0xff), + (unsigned)ps->addr.node); +#else TIMESTAMP + fprintf(stderr,"%s: Authentication failed: net %u.%u node %u\n", + myname, ((unsigned)net >> 8), (unsigned)(net & 0xff), + (unsigned)ps->addr.node); +#endif TIMESTAMP + return; + } +#endif AUTHENTICATE + + jobname[0] = username[0] = '\0'; + + if (tracing) { /* is this a trace? */ + strcpy(tname,prtp->tracefile); /* yes... then output is tracefile */ + if ((outfile = fopen(tname, "w+")) == NULL) + perror(tname); +#ifndef aux +#if defined(__hpux) || defined(SOLARIS) + if (setvbuf(outfile, NULL, _IOLBF, 0) != 0) + perror(tname); +#else /* __hpux || SOLARIS */ + setlinebuf(outfile); +#endif /* __hpux || SOLARIS */ +#endif aux + chmod(tname, 0644); + } else { /* otherwise use a temp file */ + if (tmpfiledir != NULL) { + strcpy(tname,tmpfiledir); + strcat(tname,"/lwsrvXXXXXX"); + } else + strcpy(tname,TEMPFILE); + mktemp(tname); + if ((outfile = fopen(tname, "w+")) == NULL) + perror(tname); + chmod(tname, 0600); + } + + NewStatus(prtp->children <= 0 ? "initializing" : statusprocessing); +#ifdef ISO_TRANSLATE + cMac2ISO(prtp->prtname); + sprintf(status,"receiving job for %s", prtp->prtname); + cISO2Mac(prtp->prtname); +#else ISO_TRANSLATE + sprintf(status,"receiving job for %s", prtp->prtname); +#endif ISO_TRANSLATE + + pagecount = -1; +#ifdef PAGECOUNT + pcopies = 0; +#endif PAGECOUNT + + /* + * Set actualprintout to -1 by default (for non-dcs conforming documents); + * If we get %!PS-Adobe, set it to zero if it is still negative. + * On a %%Page, set it to 1. + * + */ + actualprintout = -1; + while (getjob(pf,outfile,&actualprintout,&doeof)) { /* while still open... */ + NewStatus(status); + /* don't send out real eof - causes real problems */ + /* given that we are prepending procsets */ + if (doeof) + fprintf(outfile,"%% *EOF*\n"); + } + + fclose(outfile); + + (void) time(&t); + +#ifdef RUN_AS_USER +#ifdef LWSRV_AUFS_SECURITY + if (((uid = requid) != 0) + || (uid = unixuid(username))) { +#else LWSRV_AUFS_SECURITY + if ((uid = unixuid(username))) { +#ifdef LWSRV_LPR_LOG + requname = username; +#endif LWSRV_LPR_LOG +#endif LWSRV_AUFS_SECURITY + chown(tname, uid, -1); + } +#ifdef USER_REQUIRED + else if (actualprintout) { + int n; + FILE *infile; + char buffer[BUFSIZ]; + +#ifdef TIMESTAMP + fprintf(stderr, "%s: %s: Job refused for macuser %s\n", + myname, timestamp(), username); +#else TIMESTAMP + fprintf(stderr, "%s: Job refused for macuser %s\n", myname, username); +#endif TIMESTAMP + NewStatus ("Unknown user, job refused"); + unlink(tname); + if ((outfile = fopen(tname, "w+")) != NULL) { + fprintf(outfile, "\n\nMacintosh user name: %s\nJob: %s\n", + username, jobname); + fprintf(outfile, "\nJob refused. "); + fprintf(outfile, + "Can't map Macintosh user name to a Unix user name\n\n"); + if ((infile = fopen(REFUSE_MESSAGE, "r")) != NULL) { + while ((n = fread(buffer, 1, BUFSIZ, infile)) > 0) + fwrite(buffer, 1, n, outfile); + fclose(infile); + } else + fprintf(outfile, "No detailed message available\n"); + fclose(outfile); + } + } +#endif USER_REQUIRED +#endif RUN_AS_USER + + if (tracing) +#ifdef TIMESTAMP + fprintf(stderr,"%s: %s: Tracing to file: %s; job %s; user %s\n", + myname,timestamp(),prtp->tracefile,jobname,username); +#else TIMESTAMP + fprintf(stderr,"%s: Tracing to file: %s; job %s; user %s\n", + myname,prtp->tracefile,jobname,username); +#endif TIMESTAMP + else if (!actualprintout) { +#ifdef TIMESTAMP + fprintf(stderr,"%s: %s: No actual output for job %s; user %s\n", + myname,timestamp(),jobname,username); +#else TIMESTAMP + fprintf(stderr,"%s: No actual output for job %s; user %s\n", + myname,jobname,username); +#endif TIMESTAMP + unlink(tname); + } else { + if (rflag) + fprintf(stderr,"%s: Preserving file in %s\n",myname,tname); + +/* + * this way lies madness ... + */ + +#ifdef RUN_AS_USER + if (uid) +#endif RUN_AS_USER +#ifdef TIMESTAMP + fprintf(stderr,"%s: %s: Printing job: %s; user %s\n", + myname,timestamp(),jobname,username); +#else TIMESTAMP + fprintf(stderr,"%s: Printing job: %s; user %s\n", + myname,jobname,username); +#endif TIMESTAMP +#ifdef RUN_AS_USER + else +#ifdef TIMESTAMP + fprintf(stderr,"%s: %s: Printing notification: %s; user %s\n", + myname,timestamp(),jobname,username); +#else TIMESTAMP + fprintf(stderr,"%s: Printing notification: %s; user %s\n", + myname,jobname,username); +#endif TIMESTAMP +#endif RUN_AS_USER + + argc = 0; +#ifdef USESYSVLP + childargv[argc++]="lp"; +#else USESYSVLP + childargv[argc++]="lpr"; +#endif USESYSVLP +#ifdef MELBOURNE + childargv[argc++]="-v"; +#endif MELBOURNE +#ifdef VUW + childargv[argc++]="-l"; +#endif VUW +#ifdef xenix5 + childargv[argc++]="-og"; + sprintf(pbuf,"-d%s",prtp->unixpname); /* name of the printer */ + childargv[argc++]=pbuf; +#else xenix5 +#ifdef USESYSVLP + sprintf(pbuf,"-d%s",prtp->unixpname); /* name of the printer */ +#else USESYSVLP + sprintf(pbuf,"-P%s",prtp->unixpname); /* name of the printer */ +#endif USESYSVLP + childargv[argc++]=pbuf; + if (hflag) { /* want a burst page */ +#ifdef USESYSVLP +#ifdef DOCNAME +#ifdef __hpux + for (i = 0; i < MAXJOBNAME-1; i++) + if (isspace(jobname[i])) + jobtitle[i] = '_'; + else + jobtitle[i] = jobname[i]; + jobtitle[MAXJOBNAME-1] = '\0'; + sprintf(jbuf,"-tMacJobName:%s",jobtitle); +#else __hpux + sprintf(jbuf,"-tMacUser: %s Job: %s",username,jobname); /* job name */ +#endif __hpux +#else DOCNAME + sprintf(jbuf,"-tMacUser: %s",username); /* job name */ +#endif DOCNAME +#else USESYSVLP + childargv[argc++]="-J"; +#ifdef DOCNAME + sprintf(jbuf,"MacUser: %s Job: %s",username,jobname); /* job name */ +#else DOCNAME + sprintf(jbuf,"MacUser: %s",username); /* job name */ +#endif DOCNAME +#endif USESYSVLP +#ifdef PAGECOUNT + if (pagecount >= 0) { + if (pcopies <= 0) + pcopies = 1; +#ifdef __hpux + sprintf(&jbuf[strlen(jbuf)], "_Pages:_%04d", pcopies*pagecount); +#else __hpux + sprintf(&jbuf[strlen(jbuf)], " Pages: %d", pcopies*pagecount); +#endif __hpux + } +#endif PAGECOUNT + childargv[argc++]=jbuf; + } +#ifndef hpux + else +#ifdef USESYSVLP +# ifdef SOLARIS + childargv[argc++]="-onobanner"; /* suppress burst page */ +# else SOLARIS + childargv[argc++]="-o-h"; /* suppress burst page */ +# endif SOLARIS +#else USESYSVLP + childargv[argc++]="-h"; /* suppress burst page */ +#endif USESYSVLP +#endif hpux +#endif xenix5 +#ifdef LWSRV_AUFS_SECURITY + if (aufsdb && bin != NULL) { +#ifdef RUTGERS + sprintf(bbuf, "-B%s", bin); +#else RUTGERS + sprintf(bbuf, "-C%s", bin); +#endif RUTGERS + childargv[argc++]=bbuf; + } +#endif LWSRV_AUFS_SECURITY +#ifdef NeXT + if (nextdpi) { + sprintf(dpistring, "-R%s", nextdpi); + childargv[argc++]=dpistring; + } +#endif NeXT + rhbuf[0] = rhbuf[1] = '\0'; +#ifdef xenix5 + /* will this work ... ? */ + sprintf(rhbuf,"-%s%s",rflag ? "" : "r",hflag ? "" : "ob"); +#else xenix5 +#if defined(__hpux) || defined(SOLARIS) + sprintf(rhbuf,"-c"); +#else /* __hpux || SOLARIS */ + sprintf(rhbuf,"-%s",rflag ? "" : "r"); +#endif /* __hpux || SOLARIS */ +#endif xenix5 + if (rhbuf[1] != '\0') + childargv[argc++]=rhbuf; /* include h and/or r flags */ +#ifdef USELPRSYM +#ifndef USESYSVLP + childargv[argc++]="-s"; /* better for > 1M files */ +#endif USESYSVLP +#endif USELPRSYM +#ifdef LPRARGS + if (lprargs) { + register int n = NList(lprargs); + register char **ip = (char **)AddrList(lprargs); + + while (n-- > 0) + childargv[argc++] = *ip++; + } +#endif LPRARGS + childargv[argc++]=tname; /* our temporary file name */ + childargv[argc]=(char *) 0; /* end of argument list */ + + switch (fork()) { + case 0: +#ifdef RUN_AS_USER +#ifndef LWSRV_AUFS_SECURITY + setuid(uid); +#endif LWSRV_AUFS_SECURITY +#endif RUN_AS_USER +#ifdef LWSRV_AUFS_SECURITY + if (aufsdb) { + /* + * dissassociate from any tty to make sure + * lpr uses our requid instead of getlogin() + * + */ +#ifndef LWSRV_LPR_LOG + for (i = 0 ; i < 10 ; i++) + (void) close(i); + (void) open("/", 0); + (void) dup2(0, 1); + (void) dup2(0, 2); +#else LWSRV_LPR_LOG + for (i = 3 ; i < 10 ; i++) + (void) close(i); + /* + * stderr is the logfile; stdin and stdout are already + * using /dev/null, therefore disassociated from any tty; + * The Rutgers hack requires stdout to be the logfile if + * one exists. + * + */ + /* (void) open("/", 0); */ + /* (void) dup2(0, 1); */ + /* (void) dup2(0, 2); */ +#endif LWSRV_LPR_LOG + chown(tname, requid, reqgid); + setuid(requid); + setgid(reqgid); + } +#endif LWSRV_AUFS_SECURITY +#ifdef LWSRV_LPR_LOG + /* + * log all lpr invocations for troubleshooting + * printing problems + * + */ +#ifdef TIMESTAMP + fprintf(stderr, "%s: %s: Invoking lpr as user %s using execv: %s", + myname, timestamp(), (requname) ? requname : "", + lprcmd); +#else TIMESTAMP + fprintf(stderr, "%s: Invoking lpr as user %s using execv: %s", + myname, (requname) ? requname : "", lprcmd); +#endif TIMESTAMP + for ( i = 1 ; i < argc ; i++) + fprintf(stderr," %s",childargv[i]); + fprintf(stderr, "\n"); +#endif LWSRV_LPR_LOG + if (execv(lprcmd,childargv)) { +#ifdef TIMESTAMP + fprintf(stderr,"%s: %s: exec of %s failed\n", + myname, timestamp(), lprcmd); +#else TIMESTAMP + fprintf(stderr,"%s: exec of %s failed\n", myname, lprcmd); +#endif TIMESTAMP + perror("exec"); + exit(-1); + } + break; + case -1: +#ifdef TIMESTAMP + fprintf(stderr,"%s: %s: fork failed trying to run %s\n", + myname,timestamp(),lprcmd); +#else TIMESTAMP + fprintf(stderr,"%s: fork failed trying to run %s\n",myname,lprcmd); +#endif TIMESTAMP + perror("fork"); + break; + default: + + /* + * This code is important, since lwsrv relies upon lpr to remove + * the temporary files, we must remove them if lpr fails for any + * reason (unless rflag TRUE). + * + */ + + /* wait for the lpr to do its thing */ + + if ((waitret = wait(&waitstatus)) == -1) { +#ifdef UTS + waitstatus = 0; /* wait can return early */ +#else UTS + perror("wait"); + exit(-1); +#endif UTS + } + +#if defined(hpux) || defined(SOLARIS) + if (!rflag) + unlink(tname); +#else /* hpux || SOLARIS */ + if (WIFEXITED(waitstatus)) { + if (W_RETCODE(waitstatus) != 0) { +#ifdef TIMESTAMP + fprintf(stderr, + "%s: %s: lpr exited with status %d, %sremoving %s\n", + myname, timestamp(), W_RETCODE(waitstatus), + (rflag) ? "not " : "", tname); +#else TIMESTAMP + fprintf(stderr, + "%s: lpr exited with status %d, %sremoving %s\n", myname, + W_RETCODE(waitstatus), (rflag) ? "not " : "", tname); +#endif TIMESTAMP + if (!rflag) + unlink(tname); + } + } +#endif /* hpux || SOLARIS */ + break; + } + } + p_cls(pf); /* close out the pap connection */ +} + +#else /* LWSRV8 */ + +export +childjob(pf) +PFILE *pf; +{ + long t; + int argc, i; + FILE *outfile; + char tname[256],status[256]; + char pbuf[256],rhbuf[16],jbuf[1024]; + char *childargv[64]; +#ifdef LWSRV_AUFS_SECURITY + char bbuf[256]; +#endif LWSRV_AUFS_SECURITY +#ifdef NeXT + char dpistring[6]; +#endif NeXT +#ifdef RUN_AS_USER + int uid; +#endif RUN_AS_USER + + int waitret; + WSTATUS waitstatus; + +#ifdef AUTHENTICATE + register PAPSOCKET *ps; + register unsigned net; +#ifdef ISO_TRANSLATION + void cMac2ISO(), cISO2Mac(); +#endif ISO_TRANSLATION + + if((ps = cnotopapskt(pf->p_cno)) == NULL || !authenticate(net = + ntohs(ps->addr.net), ps->addr.node)) { + p_cls(pf); /* close out the pap connection */ + (void)time(&t); + fprintf(stderr,"lwsrv: Authentication failed: net %u.%u node %u; on %s", + ((unsigned)net >> 8), (unsigned)(net & 0xff), (unsigned)ps->addr.node, + ctime(&t)); + return; + } +#endif AUTHENTICATE + + jobname[0] = username[0] = '\0'; + + if (tracefile != NULL) /* is this a trace? */ + strcpy(tname,tracefile); /* yes... then output is tracefile */ + else { /* otherwise use a temp file */ + if (tmpfiledir != NULL) { + strcpy(tname,tmpfiledir); + strcat(tname,"/lwsrvXXXXXX"); + } else + strcat(tname,TEMPFILE); + mktemp(tname); + } + + if ((outfile = fopen(tname, "w+")) == NULL) + perror(tname); + + chmod(tname, 0600); + + if (singlefork) + NewStatus("initializing"); + +#ifdef ISO_TRANSLATE + cMac2ISO(prtname); + sprintf(status,"receiving job for %s", (char *)prtname); + cISO2Mac(prtname); +#else ISO_TRANSLATE + sprintf(status,"receiving job for %s", (char *)prtname); +#endif ISO_TRANSLATE + + scandicts(dictdir); + +#ifdef PAGECOUNT + pagecount = -1; + pcopies = 0; +#endif PAGECOUNT + + while (getjob(pf,outfile)) { /* while still open... */ + if (singlefork) + NewStatus(status); + /* don't send out real eof - causes real problems */ + /* given that we are prepending procsets */ + fprintf(outfile,"%% *EOF*\n"); + } + + fclose(outfile); + + (void) time(&t); + +#ifdef RUN_AS_USER +#ifdef LWSRV_AUFS_SECURITY + if (((uid = requid) != 0) + || (uid = unixuid(username))) +#else LWSRV_AUFS_SECURITY + if ((uid = unixuid(username))) +#endif LWSRV_AUFS_SECURITY + chown(tname, uid, -1); +#ifdef USER_REQUIRED + else { + int n; + FILE *infile; + char buffer[BUFSIZ]; + + fprintf(stderr, "lwsrv: Job refused for macuser %s\n", username); + /* NewStatus ("Unknown user, job refused"); */ + unlink(tname); + if ((outfile = fopen(tname, "w+")) != NULL) { + fprintf(outfile, "\n\nMacintosh user name: %s\nJob: %s\n", + username, jobname); + fprintf(outfile, "\nJob refused. "); + fprintf(outfile, + "Can't map Macintosh user name to a Unix user name\n\n"); + if ((infile = fopen(REFUSE_MESSAGE, "r")) != NULL) { + while ((n = fread(buffer, 1, BUFSIZ, infile)) > 0) + fwrite(buffer, 1, n, outfile); + fclose(infile); + } else + fprintf(outfile, "No detailed message available\n"); + fclose(outfile); + } + } +#endif USER_REQUIRED +#endif RUN_AS_USER + + if (tracefile != NULL) + fprintf(stderr,"lwsrv: Tracing to file: %s; job %s; user %s; on %s\n", + tracefile,jobname,username,ctime(&t)); + else { + if (rflag) + fprintf(stderr,"lwsrv: Preserving file in %s\n",tname); + +/* + * this way lies madness ... + */ + +#ifdef RUN_AS_USER + if (uid) +#endif RUN_AS_USER + fprintf(stderr,"lwsrv: Printing job: %s; user %s; on %s\n", + jobname,username,ctime(&t)); +#ifdef RUN_AS_USER + else + fprintf(stderr,"lwsrv: Printing notification: %s; user %s; on %s\n", + jobname,username,ctime(&t)); +#endif RUN_AS_USER + + argc = 0; +#ifdef USESYSVLP + childargv[argc++]="lp"; +#else USESYSVLP + childargv[argc++]="lpr"; +#endif USESYSVLP +#ifdef MELBOURNE + childargv[argc++]="-v"; +#endif MELBOURNE +#ifdef VUW + childargv[argc++]="-l"; +#endif VUW +#ifdef xenix5 + childargv[argc++]="-og"; + sprintf(pbuf,"-d%s",unixpname); /* name of the printer */ + childargv[argc++]=pbuf; +#else xenix5 +#ifdef USESYSVLP + sprintf(pbuf,"-d%s",unixpname); /* name of the printer */ +#else USESYSVLP + sprintf(pbuf,"-P%s",unixpname); /* name of the printer */ +#endif USESYSVLP + childargv[argc++]=pbuf; + if (hflag) { /* want a burst page */ +#ifdef USESYSVLP +#ifdef DOCNAME +#ifdef __hpux + for (i = 0; i < MAXJOBNAME-1; i++) + if (isspace(jobname[i])) + jobtitle[i] = '_'; + else + jobtitle[i] = jobname[i]; + jobtitle[MAXJOBNAME-1] = '\0'; + sprintf(jbuf,"-tMacJobName:%s",jobtitle); +#else __hpux + sprintf(jbuf,"-tMacUser: %s Job: %s",username,jobname); /* job name */ +#endif __hpux +#else DOCNAME + sprintf(jbuf,"-tMacUser: %s",username); /* job name */ +#endif DOCNAME +#else USESYSVLP + childargv[argc++]="-J"; +#ifdef DOCNAME + sprintf(jbuf,"MacUser: %s Job: %s",username,jobname); /* job name */ +#else DOCNAME + sprintf(jbuf,"MacUser: %s",username); /* job name */ +#endif DOCNAME +#endif USESYSVLP +#ifdef PAGECOUNT + if (pagecount >= 0) { + if (pcopies <= 0) + pcopies = 1; +#ifdef __hpux + sprintf(&jbuf[strlen(jbuf)], "_Pages:_%04d", pcopies*pagecount); +#else __hpux + sprintf(&jbuf[strlen(jbuf)], " Pages: %d", pcopies*pagecount); +#endif __hpux + } +#endif PAGECOUNT + childargv[argc++]=jbuf; + } +#ifndef hpux + else +#ifdef USESYSVLP +# ifdef SOLARIS + childargv[argc++]="-onobanner"; /* suppress burst page */ +# else SOLARIS + childargv[argc++]="-o-h"; /* suppress burst page */ +# endif SOLARIS +#else USESYSVLP + childargv[argc++]="-h"; /* suppress burst page */ +#endif USESYSVLP +#endif hpux +#endif xenix5 +#ifdef LWSRV_AUFS_SECURITY + if (aufsdb && bin != NULL) { +#ifdef RUTGERS + sprintf(bbuf, "-B%s", bin); +#else RUTGERS + sprintf(bbuf, "-C%s", bin); +#endif RUTGERS + childargv[argc++]=bbuf; + } +#endif LWSRV_AUFS_SECURITY +#ifdef NeXT + if (nextdpi) { + sprintf(dpistring, "-R%s", nextdpi); + childargv[argc++]=dpistring; + } +#endif NeXT + rhbuf[0] = rhbuf[1] = '\0'; +#ifdef xenix5 + /* will this work ... ? */ + sprintf(rhbuf,"-%s%s",rflag ? "" : "r",hflag ? "" : "ob"); +#else xenix5 +#if defined(__hpux) || defined(SOLARIS) + sprintf(rhbuf,"-c"); +#else /* __hpux || SOLARIS */ + sprintf(rhbuf,"-%s",rflag ? "" : "r"); +#endif /* __hpux || SOLARIS */ +#endif xenix5 + if (rhbuf[1] != '\0') + childargv[argc++]=rhbuf; /* include h and/or r flags */ +#ifdef USELPRSYM +#ifndef USESYSVLP + childargv[argc++]="-s"; /* better for > 1M files */ +#endif USESYSVLP +#endif USELPRSYM +#ifdef LPRARGS + while(*lprargs) + childargv[argc++] = *lprargs++; + lprargs=lprargsbuf; +#endif LPRARGS + childargv[argc++]=tname; /* our temporary file name */ + childargv[argc]=(char *) 0; /* end of argument list */ + + switch (fork()) { + case 0: +#ifdef RUN_AS_USER +#ifndef LWSRV_AUFS_SECURITY + setuid(uid); +#endif LWSRV_AUFS_SECURITY +#endif RUN_AS_USER +#ifdef LWSRV_AUFS_SECURITY + if (aufsdb) { + /* + * dissassociate from any tty to make sure + * lpr uses our requid instead of getlogin() + * + */ +#ifndef LWSRV_LPR_LOG + for(i = 0 ; i < 10 ; i++) + (void) close(i); + (void) open("/", 0); + (void) dup2(0, 1); + (void) dup2(0, 2); +#else LWSRV_LPR_LOG + for(i = 3 ; i < 10 ; i++) + (void) close(i); + /* + * stderr is the logfile; stdin and stdout are already + * using /dev/null, therefore disassociated from any tty; + * The Rutgers hack requires stdout to be the logfile if + * one exists. + * + */ + /* (void) open("/", 0); */ + /* (void) dup2(0, 1); */ + /* (void) dup2(0, 2); */ +#endif LWSRV_LPR_LOG + chown(tname, requid, reqgid); + setuid(requid); + setgid(reqgid); + } +#endif LWSRV_AUFS_SECURITY +#ifdef LWSRV_LPR_LOG + /* + * log all lpr invocations for troubleshooting + * printing problems + * + */ + fprintf(stderr, "Invoking lpr as user %s using execv: %s", + (requname) ? requname : "", lprcmd); + for ( i = 1 ; i < argc ; i++) + fprintf(stderr," %s",childargv[i]); + fprintf(stderr, "\n"); +#endif LWSRV_LPR_LOG + if (execv(lprcmd,childargv)) { + fprintf(stderr,"exec of %s failed\n", lprcmd); + perror("exec"); + exit(-1); + } + break; + case -1: + fprintf(stderr,"fork failed trying to run %s\n", lprcmd); + perror("fork"); + break; + default: + + /* + * This code is important, since lwsrv relies upon lpr to remove + * the temporary files, we must remove them if lpr fails for any + * reason (unless rflag TRUE). + * + */ + + /* wait for the lpr to do its thing */ + + if ((waitret = wait(&waitstatus)) == -1 ) { +#ifdef UTS + waitstatus = 0; /* wait can return early */ +#else UTS + perror("wait"); + exit(-1); +#endif UTS + } + +#if defined(hpux) || defined(SOLARIS) + if (!rflag) + unlink(tname); +#else /* hpux || SOLARIS */ + if (WIFEXITED(waitstatus)) { + if (W_RETCODE(waitstatus) != 0 ) { + fprintf(stderr,"lpr exited with status %d, %sremoving %s\n", + W_RETCODE(waitstatus), (rflag) ? "not " : "", tname); + if (!rflag) + unlink(tname); + } + } +#endif /* hpux || SOLARIS */ + break; + } + } + p_cls(pf); /* close out the pap connection */ +} +#endif /* LWSRV8 */ + +export void +setjobname(ts) +register char *ts; +{ +#ifndef LWSRV8 + strncpy(jobname, ts, sizeof(jobname)); +#else /* LWSRV8 */ + strncpy(jobname, ts, sizeof(jobname) - 1); + jobname[sizeof(jobname) - 1] = 0; +#ifdef JOBNOPAREN + /* + * replace parenthesis so that jobname doesn't screw up banner pages + * for some print spoolers + */ + while (ts = index(jobname, '(')) + *ts = '['; + while (ts = index(jobname, ')')) + *ts = ']'; +#endif JOBNOPAREN +#endif /* LWSRV8 */ +} + +setusername(ts) +register char *ts; +{ +#ifdef RUN_AS_USER + if (*username != '\0') + return; +#endif RUN_AS_USER + strcpy(username, ts); +} + +export +NewStatus(status) +char *status; +{ + char tmp[1024]; + + if (*username != '\0') +#ifndef LWSRV8 + sprintf(tmp,"job: %s for %s; status: %s", jobname,username,status); +#else /* LWSRV8 */ + sprintf(tmp,"%s; document: %s: status: %s", username, jobname, status); +#endif /* LWSRV8 */ + else + sprintf(tmp,"status: %s",status); +#ifndef LWSRV8 + cpyc2pstr(statbuffp->StatusStr, tmp); +#else /* LWSRV8 */ + if (singlefork) + cpyc2pstr(prtlist[prtno].statbuf.StatusStr, tmp); + else + writestatus(myslot, tmp); +#endif /* LWSRV8 */ + abSleep(0,TRUE); /* make sure protocol runs */ +} + +#ifdef LWSRV_AUFS_SECURITY +/**************** budd... ****************/ +/* duplicated in aufs.c and lwsrv.c sigh */ +make_userlogin(buf, dir, addrb) + char *buf, *dir; +#ifndef LWSRV8 + AddrBlock addrb; +#else /* LWSRV8 */ + AddrBlock *addrb; +#endif /* LWSRV8 */ +{ + sprintf( buf, "%s/net%d.%dnode%d", + dir, +#ifndef LWSRV8 + nkipnetnumber(addrb.net), nkipsubnetnumber(addrb.net), + addrb.node); +#else /* LWSRV8 */ + nkipnetnumber(addrb->net), nkipsubnetnumber(addrb->net), + addrb->node); +#endif /* LWSRV8 */ +} +#endif LWSRV_AUFS_SECURITY + +#ifdef LWSRV8 +#ifdef TIMESTAMP +private char * +timestamp() +{ + char *cp; + time_t t; + + time(&t); + cp = ctime(&t); + cp[24] = 0; /* kill newline */ + return(cp); +} +#endif TIMESTAMP +#endif /* LWSRV8 */ diff --git a/applications/lwsrv/lwsrv.conf b/applications/lwsrv/lwsrv.conf new file mode 100644 index 0000000..da430eb --- /dev/null +++ b/applications/lwsrv/lwsrv.conf @@ -0,0 +1,13 @@ +Library = DB; + +Options = ( + DontCollect; + ProcsetDir /usr/local/lib/cap/procsets; + FontFile /usr/local/lib/cap/LW+Fonts; + +); + +"Technical Services Spool Test" = ( + include "LaserWriter IIf"; + printerqueue lw.tsa; +); diff --git a/applications/lwsrv/lwsrvconfig.c b/applications/lwsrv/lwsrvconfig.c new file mode 100644 index 0000000..bb06748 --- /dev/null +++ b/applications/lwsrv/lwsrvconfig.c @@ -0,0 +1,710 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/09/10 14:33:00 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/lwsrvconfig.c,v 2.3 1996/09/10 14:33:00 djh Rel djh $"; +static char revision[] = "$Revision: 2.3 $"; + +/* + * lwsrvconfig - auxiliary program for testing configuration files + * and creating printer description databases + * + * AppleTalk package for UNIX (4.2 BSD). + * + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else /* USESTRINGDOTH */ +# include +#endif /* USESTRINGDOTH */ +#ifdef NEEDFCNTLDOTH +# include +#endif NEEDFCNTLDOTH +#ifndef NONDBM +# include +#else NONDBM +# include +#endif NONDBM +#include "list.h" +#include "packed.h" +#include "parse.h" + +char *myname; + +#define F_GLOBAL (1 << F_GLOBAL_BIT) +#define F_GLOBAL_BIT 0 +#define F_HASARG (1 << F_HASARG_BIT) +#define F_HASARG_BIT 1 +#define hasarg(f) ((f) & F_HASARG) +#define isglobal(f) ((f) & F_GLOBAL) +#define sf(a,g) (((a) << F_HASARG_BIT) | ((g) << F_GLOBAL_BIT)) + +struct descrip { + char *name; + short flags; +}; + +static KVTree **argname; +static struct argnamebuf { + char *arg; + struct descrip d; +} argnamebuf[] = { + {"-A", {"DSC", sf(1, 0)}}, + {"-C", {"LPRCommand", sf(1, 1)}}, +#ifdef LPRARGS + {"-L", {"LPRArgument", sf(1, 0)}}, +#endif LPRARGS + {"-N", {"DontCollect", sf(0, 0)}}, +#ifdef PASS_THRU + {"-P", {"PassThru", sf(0, 0)}}, +#endif PASS_THRU +#ifdef NeXT + {"-R", {"NeXTResolution", sf(1, 0)}}, +#endif NeXT + {"-S", {"SingleFork", sf(0, 1)}}, + {"-T", {"TranScriptOption", sf(1, 0)}}, +#ifdef LWSRV_AUFS_SECURITY + {"-X", {"AUFSSecurity", sf(1, 1)}}, +#endif LWSRV_AUFS_SECURITY +#ifdef LW_TYPE + {"-Y", {"AppleTalkType", sf(1, 0)}}, +#endif LW_TYPE + {"-a", {"ProcsetDir", sf(1, 0)}}, + {"-d", {"Debug", sf(1, 1)}}, + {"-e", {"Alloweexec", sf(0, 0)}}, + {"-f", {"FontFile", sf(1, 0)}}, + {"-h", {"SuppressBanner", sf(0, 0)}}, + {"-k", {"NoChecksum", sf(0, 0)}}, + {"-l", {"LogFile", sf(1, 1)}}, + {"-p", {"PrinterQueue", sf(1, 0)}}, + {"-q", {"QueryFile", sf(1, 1)}}, + {"-r", {"KeepSpoolFile", sf(0, 0)}}, + {"-t", {"TraceFile", sf(1, 0)}}, + {"-v", {"Verbose", sf(0, 1)}}, + {NULL, NULL}, +}; +static char *createdb = NULL; +static int datafd; +long dataoffset = 0; +#ifndef NONDBM +static DBM *db; +#endif NONDBM +static char *dbname = NULL; +static KVTree **_default; +static int firstargs = 1; +static int firstprinter = 1; +static char firsttime[127]; +static char *headerstr = NULL; +static char null[] = ""; +static int oldformat = 0; +static char *printername; +static int verbose = 0; + +int doargs(); + +static void addback(/* int argc, char **argv */); +static void display(/* char *str, KVTree **kp */); +static void displaytemplates(); +static void docreatedb(); +static KVTree **initargname(); +static int getflags(/* char *str */); +static char *keyname(/* char *str */); +static char *quotestr(/* char *str */); +static void setfirsttime(/* char *str */); +static void storedata(/* char *name, int n, char *ptr, int size */); +static unshort *ushortdup(/* int i */); +static char *untab(/* char *str */); +static void usage(); + +main(argc, argv) +int argc; +char **argv; +{ + register int c; + register KVTree **kp; + register List *lp; + register FILE *fp; + extern FILE *yyin; + extern char *optarg; + extern int optind; + + if (myname = rindex(*argv, '/')) + myname++; + else + myname = *argv; + while ((c = getopt(argc, argv, "c:l:ov?")) != EOF) { + switch(c) { + case 'c': + createdb = optarg; + break; + case 'l': + dbname = optarg; + break; + case 'o': + oldformat++; + break; + case 'v': + verbose++; + break; + case '?': + default: + usage(); + } + } + if (createdb && dbname) + usage(); + if (optind < argc) { + if ((argc - optind) > 1) + usage(); + if ((fp = fopen(argv[optind], "r")) == NULL) { + fprintf(stderr, "%s: Can't open %s\n", myname, argv[optind]); + exit(1); + } + } else + fp = stdin; + yyin = fp; /* set for flex(1) */ + argname = initargname(); + initkeyword(fp); + umask(0133); + if (yyparse()) + exit(1); + if (!dbname && libraryfile) + dbname = libraryfile; + if (createdb) + docreatedb(); + else { + _default = thequery = CreateKVTree(); + setfirsttime(specialOpts); + configargs(dbname); + } + exit(0); +} + +static void +addback(argc, argv) +register int argc; +register char **argv; +{ + register void *n; + register List *lp; + register int f; + + while (argc-- > 0) { + f = getflags(*argv); + if (hasarg(f)) { + n = (void *)*argv++; + if (isSpecialOpt(n)) { + if (firsttime[Option(n)]) { + firsttime[Option(n)] = 0; + if (lp = (List *)SearchKVTree(thequery, n, strcmp)) { + if (thequery == _default) { + AddToList(lp, (void *)*argv); + } else { + lp = DupList(lp); + AddToList(lp, (void *)*argv); + AddToKVTree(thequery, n, (void *)lp, strcmp); + } + } else { + lp = CreateList(); + AddToList(lp, (void *)*argv); + AddToKVTree(thequery, n, (void *)lp, strcmp); + } + } else { + lp = (List *)SearchKVTree(thequery, n, strcmp); + AddToList(lp, (void *)*argv); + } + } else + AddToKVTree(thequery, n, (void *)*argv, strcmp); + argv++; + argc--; + } else + AddToKVTree(thequery, (void *)*argv++, NULL, strcmp); + } +} + +/* + * doargs would normally set the arguments in lwsrv. We intercept this + * to do our printing. + */ +doargs(argc, argv) +int argc; +char **argv; +{ + register int n, i, f; + register char **a; + + argc--; + argv++; + if (firstargs && strcmp(*argv, "-n") != 0) { /* options */ + firstargs = 0; + if (verbose) { + addback(argc, argv); + display("Options", thequery); + } else { + n = argc; + a = argv; + i = 0; + while (n-- > 0) { + f = getflags(*a); + if (isglobal(f)) + i++; + if (hasarg(f)) { + a++; + n--; + } + a++; + } + if (i > 0) { + printf("Options = %s", i > 1 ? "(\n" : null); + n = argc; + a = argv; + while (n-- > 0) { + f = getflags(*a); + if (isglobal(f)) { + printf("%s%s", i > 1 ? "\t" : null, keyname(*a)); + if (hasarg(f)) { + printf(" %s;\n", quotestr(*++a)); + n--; + } else + fputs(";\n", stdout); + if (i == 1) + break; + } else { + if (hasarg(f)) { + a++; + n--; + } + } + a++; + } + if (i > 1) + fputs(");\n\n", stdout); + else + putchar('\n'); + } + addback(argc, argv); + } + } else if (strcmp(*argv, "-n") == 0) { + /* setup thequery so setargs will insert the queries in the right one */ + firstargs = 0; + if (thequery != _default) + FreeKVTree(thequery, NULL, NULL); + thequery = DupKVTree(_default); + if (verbose && firstprinter) { /* print the templates */ + firstprinter = 0; + displaytemplates(); + } + setfirsttime(specialOpts); + printername = *++argv; + } else { /* actually do the printer */ + addback(argc, argv); + display(printername, thequery); + } +} + +static void +docreatedb() +{ + register KVTree **keys; + register PackedChar *ckeys; + register PackedChar *pc; + register PackedShort *ps; + register void **v2, **v3, **vp; + register int i, j, k; + register unshort *sp, *sp2; + register char *name; + char buf[BUFSIZ]; + + if (verbose) + printf("Creating database %s\n", createdb); + strcpy(buf, createdb); + strcat(buf, ".dir"); + unlink(buf); + strcpy(buf, createdb); + strcat(buf, ".pag"); + unlink(buf); +#ifndef NONDBM + if ((db = dbm_open(createdb, O_RDWR | O_CREAT, 0755)) == NULL) { + fprintf(stderr, "%s: Can't create database %s\n", myname, createdb); + exit(1); + } +#else NONDBM + if ((i = creat(buf, 0755)) < 0) { + fprintf(stderr, "%s: Can't create %s\n", myname, buf); + exit(1); + } + if ((i = creat(buf, 0755)) < 0) { + fprintf(stderr, "%s: Can't create %s\n", myname, buf); + exit(1); + } + if (dbminit(creatdb) < 0) { + fprintf(stderr, "%s: Can't open database %s\n", myname, createdb); + exit(1); + } +#endif NONDBM + strcpy(buf, createdb); + strcat(buf, datasuffix); + unlink(buf); + if ((datafd = open(buf, O_WRONLY | O_CREAT, 0755)) < 0) { + fprintf(stderr, "%s: Can't create %s\n", myname, buf); + exit(1); + } + keys = CreateKVTree(); + ckeys = CreatePackedChar(); + for (i = NList(printerlist), vp = AddrList(printerlist); i > 0; i -= 2) { + name = (char *)*vp++; + if (verbose) + printf("\tCreating template %s\n", name); + pc = CreatePackedChar(); + /* + * Since a zero means the a string value is null, we "use up" + * the zero so that it can never occur in our data (except to mean + * that the string is NULL + */ + AddToPackedChar(pc, null); + ps = CreatePackedShort(); + AddToPackedShort(ps, (j = NList(*vp) / 2)); + sp = AllocatePackedShort(ps, 2 * j); + for (v2 = AddrList(*vp); j > 0; j--) { + if (verbose) + printf("\t\tAdding keyword %s\n", untab((char *)*v2)); + if ((sp2 = (unshort *)SearchKVTree(keys, *v2, strcmp)) == NULL) { + k = AddToPackedChar(ckeys, (char *)*v2); + sp2 = ushortdup(k); + AddToKVTree(keys, *v2, (void *)sp2, strcmp); + } + *sp++ = *sp2; + if (StringVal(*v2)) { + v2++; + *sp++ = *v2 ? AddToPackedChar(pc, (char *)*v2) : 0; + } else { + v2++; + k = NList(*v2); + *sp++ = AddToPackedShort(ps, k); + for (v3 = AddrList(*v2); k > 0; k--) + AddToPackedShort(ps, AddToPackedChar(pc, (char *)*v3++)); + } + v2++; + } + j = (NPackedShort(ps) + 1) * sizeof(unshort); + k = NPackedChar(pc) * sizeof(char); + if ((sp = (unshort *)malloc(j + k)) == NULL) + errorexit(1, "docreatedb: Out of memory\n"); + *sp = j; + bcopy((char *)AddrPackedShort(ps), (char *)(sp + 1), + NPackedShort(ps) * sizeof(unshort)); + bcopy((char *)AddrPackedChar(pc), (char *)sp + j, k); + storedata(name, strlen(name), (char *)sp, j + k); + vp++; + } + if (verbose) + printf("Adding keywords to database %s\n", createdb); + storedata(keywords_key, KEYWORDSKEYSIZE, AddrPackedChar(ckeys), + NPackedChar(ckeys) * sizeof(char), db); +#ifndef NONDBM + dbm_close(db); + if (verbose) + printf("Closing database %s\n", createdb); +#else NONDBM + /* + * There does not seem to be a way to close the old dbm file! + */ +#endif NONDBM +} + +static void +storedata(name, n, ptr, size) +char *name; +register int n; +register char *ptr; +register int size; +{ + register char *cp; + datum key, data; + Location loc; + + key.dptr = name; + key.dsize = n; + loc.magic = MagicNumber; /* to identify a real lwsrvconfig database */ + loc.offset = dataoffset; + loc.size = size; + data.dptr = (char *)&loc; + data.dsize = sizeof(Location); +#ifndef NONDBM + if (dbm_store(db, key, data, DBM_REPLACE) < 0) { + fprintf(stderr, "%s: Failed to insert %s into database %s \n", myname, + name, createdb); + exit(1); + } +#else NONDBM + if (store(key, data) < 0) { + fprintf(stderr, "%s: Failed to insert %s into database %s \n", myname, + name, createdb); + exit(1); + } +#endif NONDBM + dataoffset += size; + while (size > 0) { + if ((n = write(datafd, ptr, size)) <= 0) { + fprintf(stderr, "%s: write error on %d%s\n", myname, createdb, + datasuffix); + exit(1); + } + size -= n; + ptr += n; + } +} + +static void +display(name, kp) +char *name; +register KVTree **kp; +{ + register List *lp; + register char **cp; + register int n, i; + + lp = ListKVTree(kp); + if (headerstr) { + fputs(headerstr, stdout); + headerstr = NULL; + } + printf("%s = ", quotestr(name)); + kp = (KVTree **)AddrList(lp); + if ((n = NList(lp)) == 1) { + fputs(untab((char *)(*kp)->key), stdout); + if (ListVal((*kp)->key)) { + cp = (char **)AddrList((*kp)->val); + if ((i = NList((*kp)->val)) == 1) + printf(" %s;\n", quotestr(*cp)); + else if (isSpecialOpt((*kp)->key)) { + printf(" %s;\n", quotestr(*cp)); + while (--i > 0) { + fputs(untab((char *)(*kp)->key), stdout); + printf(" %s;\n", quotestr(*++cp)); + } + } else { + fputs(" (\n", stdout); + while (i--) { + printf("\t%s,\n", quotestr(*cp)); + cp++; + } + fputs(");\n", stdout); + } + } else { + if ((*kp)->val) + printf(" %s;\n", quotestr((char *)(*kp)->val)); + else + fputs(";\n", stdout); + } + } else { + fputs("(\n", stdout); + for (;n-- > 0; kp++) { + printf("\t%s", untab((char *)(*kp)->key)); + if (ListVal((*kp)->key)) { + cp = (char **)AddrList((*kp)->val); + if ((i = NList((*kp)->val)) == 1) + printf(" %s;\n", quotestr(*cp)); + else if (isSpecialOpt((*kp)->key)) { + printf(" %s;\n", quotestr(*cp)); + while (--i > 0) { + printf("\t%s", untab((char *)(*kp)->key)); + printf(" %s;\n", quotestr(*++cp)); + } + } else { + fputs(" (\n", stdout); + while (i--) { + printf("\t\t%s,\n", quotestr(*cp)); + cp++; + } + fputs("\t);\n", stdout); + } + } else { + if ((*kp)->val) + printf(" %s;\n", quotestr((char *)(*kp)->val)); + else + fputs(";\n", stdout); + } + } + fputs(");\n", stdout); + } +} + +static void +displaytemplates() +{ + register List *lp; + register KVTree **kp; + register int n; + + lp = ListKVTree(_printers); + headerstr = "\nTemplates:\n"; + for (kp = (KVTree **)AddrList(lp), n = NList(lp); n > 0; kp++, n--) { + if (SearchKVTree((*kp)->val, "-p", strcmp) == NULL) + display((*kp)->key, (*kp)->val, 0); + } + headerstr = "\nPrinters:\n"; + FreeList(lp, NULL); +} + +static KVTree ** +initargname() +{ + register KVTree **kp; + register struct argnamebuf *ap; + + kp = CreateKVTree(); + for (ap = argnamebuf; ap->arg; ap++) + AddToKVTree(kp, (void *)ap->arg, (void *)&ap->d, strcmp); + return(kp); +} + +static int +getflags(str) +char *str; +{ + register struct descrip *sp; + + if ((sp = (struct descrip *)SearchKVTree(argname, str, strcmp)) == NULL) { + fprintf(stderr, "%s: Unknown lwsrv argument %s\n", myname, str); + exit(1); + } + return(sp->flags); +} + +static char * +keyname(str) +char *str; +{ + register struct descrip *sp; + + if (strcmp(str, includename) == 0) + return("Include"); + if (oldformat) + return(str); + return((sp = (struct descrip *)SearchKVTree(argname, str, strcmp)) ? + sp->name : str); +} + +#define DQUOTE (1 << 0) +#define SQUOTE (1 << 1) + +static char * +quotestr(str) +register char *str; +{ + static char buf[256]; + register char *fp, *tp, *bp; + register int quote; + + if (!str) + return(""); + quote = 0; + if (index(str, '"')) + quote |= DQUOTE; + if (index(str, '\'')) + quote |= SQUOTE; + if (!quote && !index(str, ' ') && !index(str, '\t')) { + bp = str; + if (index(bp, '\n')) { + strcpy(bp = buf, str); + if (fp = index(bp, '\n')) + *fp = 0; + } + if (strlen(bp) == 2 && *bp == '-' && isalpha(bp[1])) { + sprintf(buf, "\"-%c\"", bp[1]); + bp = buf; + } + return(bp); + } + switch(quote) { + case 0: + case (DQUOTE | SQUOTE): + quote = '"'; + break; + default: + quote = (quote & SQUOTE) ? '"' : '\''; + break; + } + fp = str; + tp = buf; + *tp++ = quote; + while (*fp) { + if (*fp == '\n') { + fp++; + continue; + } + if (*fp == quote) + *tp++ = quote; + *tp++ = *fp++; + } + *tp++ = quote; + *tp = 0; + return(buf); +} + +static void +setfirsttime(str) +register char *str; +{ + while (*str) + firsttime[*str++] = 1; +} + +static unshort * +ushortdup(i) +int i; +{ + register unshort *sp; + + if ((sp = (unshort *)malloc(sizeof(unshort))) == NULL) + errorexit(1, "ushortdup: Out of memory\n"); + *sp = i; + return(sp); +} + +#if (!(defined(SOLARIS)||defined(bsdi)||defined(__NetBSD__)||defined(__FreeBSD__)||defined(linux))) +char * +strdup(str) +char *str; +{ + register char *cp; + + if ((cp = (char *)malloc(strlen(str) + 1)) == NULL) + errorexit(1, "strdup: Out of memory\n"); + strcpy(cp, str); + return(cp); +} +#endif /* SOLARIS || bsdi || __NetBSD__ */ + +static void +usage() +{ + fprintf(stderr, "Usage: %s [-l] dbname [-o] [-v] [file]\n", myname); + fprintf(stderr, " %s -c newdbname [-o] [-v] [file]\n", myname); + exit(1); +} + +static char * +untab(str) +char *str; +{ + register char *cp, *bp; + register int i; + static char buf[256]; + + if (!(bp = index(str, '\t'))) + return(keyname(str)); + strncpy(buf, str, (i = bp - str)); + buf[i] = 0; + if ((cp = keyname(str)) != str) + strcpy(buf, cp); + strcat(buf, " "); + strcat(buf, quotestr(bp + 1)); + return(buf); +} diff --git a/applications/lwsrv/makefile b/applications/lwsrv/makefile new file mode 100644 index 0000000..a676f6e --- /dev/null +++ b/applications/lwsrv/makefile @@ -0,0 +1,76 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 14:00:21 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +DESTDIR=/usr/local/cap +LWFLAGS=-DDOCNAME -DPAGECOUNT=512 +LIBCAP=-lcap +I=/usr/include + +# Valid are: -DADOBE_DSC2_CONFORMANT +SIMPLEFLAGS= + +LWSRVOBJS=fontlist.o lwsrv.o papstream.o procset.o simple.o spmisc.o + +# for other libraries (like BSD on hpux) +SLIB= + +# make sure that you define point getopt to att_getopt.o if your system +# doesn't have it builtin +ATT_GETOPT= + +lwsrv: ${LWSRVOBJS} ${ATT_GETOPT} + ${CC} -o lwsrv ${LFLAGS} ${LWSRVOBJS} ${ATT_GETOPT} ${LIBCAP} ${SLIB} + +clean: + -rm -f *.o lwsrv att_getopt.c + +install: lwsrv + -strip lwsrv + ${INSTALLER} lwsrv ${DESTDIR} + +dist: + @cat todist + +att_getopt.c: + ln -s ../../extras/att_getopt.c + +simple.o: simple.c + ${CC} ${CFLAGS} ${SIMPLEFLAGS} -c simple.c + +lwsrv.o: lwsrv.c + ${CC} ${CFLAGS} ${LWFLAGS} -c lwsrv.c + +# Dependencies +lwsrv.o: lwsrv.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/compat.h papstream.h +simple.o: simple.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/sysvcompat.h $I/netat/compat.h \ + spmisc.h \ + procset.h fontlist.h papstream.h +fontlist.o: fontlist.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h fontlist.h \ + spmisc.h papstream.h +papstream.o: papstream.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h papstream.h +procset.o: procset.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/sysvcompat.h $I/netat/compat.h \ + procset.h \ + spmisc.h +spmisc.o: spmisc.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/sysvcompat.h $I/netat/compat.h \ + spmisc.h diff --git a/applications/lwsrv/packed.c b/applications/lwsrv/packed.c new file mode 100644 index 0000000..dc3eca9 --- /dev/null +++ b/applications/lwsrv/packed.c @@ -0,0 +1,118 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/08/28 10:38:35 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/packed.c,v 2.1 1995/08/28 10:38:35 djh Rel djh $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * packed.c - create packed printer descriptions + * + * UNIX AppleTalk spooling program: act as a laserwriter + * AppleTalk package for UNIX (4.2 BSD). + * + */ + +#include +#include +#include +#include +#include +#ifdef USESTRINGDOTH +#include +#else /* USESTRINGDOTH */ +#include +#endif /* USESTRINGDOTH */ +#include "packed.h" + +char *malloc(); +char *realloc(); + +unsigned +AddToPackedChar(pc, str) +register PackedChar *pc; +char *str; +{ + register int l, n; + + l = strlen(str) + 1; + if ((n = pc->n) + l > pc->cmax && (pc->pc = realloc(pc->pc, + (pc->cmax += l + PACKEDCHARDELTA) * sizeof(char))) == NULL) + errorexit(1, "AddToPackedChar: Out of memory\n"); + strcpy(pc->pc + n, str); + /* + * This doesn't work on machines with 16 bit ints! + */ + if ((pc->n += l) > MAXPACKEDSIZE) + errorexit(2, "AddToPackedChar: PackedChar overflow\n"); + return(n); +} + +unsigned +AddToPackedShort(ps, i) +register PackedShort *ps; +unsigned i; +{ + if (ps->n >= ps->smax && (ps->ps = (unshort *)realloc(ps->ps, + (ps->smax += PACKEDSHORTDELTA) * sizeof(unshort))) == NULL) + errorexit(1, "AddToPackedShort: Out of memory\n"); + ps->ps[ps->n] = i; + return(ps->n++); +} + +unshort * +AllocatePackedShort(ps, nshort) +register PackedShort *ps; +int nshort; +{ + register int n; + + if ((n = ps->n) + nshort > ps->smax && (ps->ps = (unshort *)realloc(ps->ps, + (ps->smax += nshort + PACKEDSHORTDELTA) * sizeof(unshort))) == NULL) + errorexit(1, "AllocatePackedShort: Out of memory\n"); + ps->n += nshort; + return(ps->ps + n); +} + +PackedChar * +CreatePackedChar() +{ + register PackedChar *pc; + static char errstr[] = "CreatePackedChar: Out of memory\n"; + + if ((pc = (PackedChar *)malloc(sizeof(PackedChar))) == NULL) + errorexit(1, errstr); + if ((pc->pc = malloc((pc->cmax = PACKEDCHARDELTA) * sizeof(char))) + == NULL) + errorexit(2, errstr); + pc->n = 0; + return(pc); +} + +PackedShort * +CreatePackedShort() +{ + register PackedShort *ps; + static char errstr[] = "CreatePackedChar: Out of memory\n"; + + if ((ps = (PackedShort *)malloc(sizeof(PackedShort))) == NULL) + errorexit(1, errstr); + if ((ps->ps = (unshort *)malloc((ps->smax = PACKEDCHARDELTA) * sizeof(unshort))) + == NULL) + errorexit(2, errstr); + ps->n = 0; + return(ps); +} + +void +FreePackedChar(pc) +register PackedChar *pc; +{ + free(pc->pc); + free((char *)pc); +} + +void +FreePackedShort(ps) +register PackedShort *ps; +{ + free((char *)ps->ps); + free((char *)ps); +} diff --git a/applications/lwsrv/packed.h b/applications/lwsrv/packed.h new file mode 100644 index 0000000..acc566b --- /dev/null +++ b/applications/lwsrv/packed.h @@ -0,0 +1,46 @@ +/* "$Author: djh $ $Date: 1995/08/28 10:38:35 $" */ +/* "$Header: /mac/src/cap60/applications/lwsrv/RCS/packed.h,v 2.1 1995/08/28 10:38:35 djh Rel djh $" */ +/* "$Revision: 2.1 $" */ + +/* + * packed.h - create packed printer descriptions + * + * UNIX AppleTalk spooling program: act as a laserwriter + * AppleTalk package for UNIX (4.2 BSD). + * + */ + +#ifndef _PACKED_H_ +#define _PACKED_H_ + +#define MAXPACKEDSIZE ((unsigned)0xffff) +#define PACKEDCHARDELTA 256 +#define PACKEDSHORTDELTA 128 + +typedef unsigned short unshort; + +typedef struct PackedChar { + unsigned n; + unsigned cmax; + char *pc; +} PackedChar; +typedef struct PackedShort { + unsigned n; + unsigned smax; + unshort *ps; +} PackedShort; + +#define AddrPackedChar(p) ((p)->pc) +#define AddrPackedShort(p) ((p)->ps) +#define NPackedChar(p) ((p)->n) +#define NPackedShort(p) ((p)->n) + +unsigned AddToPackedChar(/* PackedChar *pc, char *str */); +unsigned AddToPackedShort(/* PackedShort *ps, unsigned i */); +unshort *AllocatePackedShort(/* PackedShort *ps, int nshort */); +PackedChar *CreatePackedChar(); +PackedShort *CreatePackedShort(); +void FreePackedChar(/* PackedChar *pc */); +void FreePackedShort(/* PackedShort *ps */); + +#endif /* _PACKED_H_ */ diff --git a/applications/lwsrv/papstream.c b/applications/lwsrv/papstream.c new file mode 100644 index 0000000..6d70813 --- /dev/null +++ b/applications/lwsrv/papstream.c @@ -0,0 +1,182 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/08/28 11:10:14 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/papstream.c,v 2.2 1995/08/28 11:10:14 djh Rel djh $"; +static char revision[] = "$Revision: 2.2 $"; + +/* + * papstream - UNIX AppleTalk: simple stream handling for pap connections + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Created sept 5, 1987 by cck from lwsrv + * + */ + +#include +#include +#include "papstream.h" + +extern char *myname; + +void +p_clreof(p) +PFILE *p; +{ + p->p_flg &= ~P_IOEOF; + p->p_cnt = 0; +} + +/* + * PFILE *p_opn(int cno) + * + * Initialize a stream interface for the PAP connection specified by cno. + * All buffers are dynamically allocated. Use p_cls() to close the stream + * and deallocate buffers. + * + */ + +PFILE * +p_opn(cno, bufsiz) +int cno; +int bufsiz; +{ + PFILE *p; + + p = (PFILE *)malloc(sizeof(PFILE)); /* allocate io block */ + p->p_buf = (byte *)malloc(bufsiz); /* pointer to input buffer */ + p->p_bufsiz = bufsiz; + p->p_cno = cno; /* save pap connection number */ + p->p_cnt = 0; /* count in buffer is zero */ + p->p_flg = 0; /* no flags are set */ + return(p); /* return io block */ +} + +/* + * void p_cls(PFILE *p) + * + * Close the PAP stream; deallocate buffers and do a PAPClose. + * + */ + +void +p_cls(p) +PFILE *p; +{ + PAPClose(p->p_cno,TRUE); /* close out connection */ + free((char *)p->p_buf); /* release input buffer */ + free((char *)p); /* release io block */ +} + +/* + * void p_write(PFILE *p, char *buf, int len, int sendeof) + * + * Write len characters from buf to the PAP connection specified by p. + * If sendeof is TRUE then pass along the EOF indicator to the remote + * client. + * + */ + +void +p_write(p,buf,len,sendeof) +PFILE *p; +char *buf; +int len,sendeof; +{ + int err,cmp; +#ifdef LWSRV8 + import boolean tracing; +#endif /* LWSRV8 */ + + err = PAPWrite(p->p_cno,buf,len,sendeof,&cmp); +#ifdef LWSRV8 + if (tracing) + tracewrite(buf, len); +#endif /* LWSRV8 */ + if (err != noErr) { + p->p_flg |= P_IOCLS; + fprintf(stderr,"%s: p_write error %d on write to Connection %d\n", + myname, err, p->p_cno); + } else + while (cmp > 0) /* wait for completion */ + abSleep(100,TRUE); /* returns on event anyway */ + if (cmp != noErr) + p->p_flg |= P_IOCLS; +} + +/* + * int p_fillbuf(PFILE *p) + * + * Called by p_getc to refill the input buffer for a PAP stream connection. + * Returns the next character read or EOF. + * + */ + +int +p_fillbuf(p) +PFILE *p; +{ + int peof, pcmp; + + if (p_iscls(p)) + return(EOF); + + /* if end of file set and nothing left in buffer */ + if (p_eof(p) && p->p_cnt <= 0) { + /* return eof after clearing */ + p_clreof(p); + return(EOF); + } + + if (PAPRead(p->p_cno,p->p_buf,&p->p_cnt,&peof,&pcmp) != noErr) { + p->p_flg |= P_IOCLS; + return(EOF); + } + + while (pcmp > 0) /* wait until completion */ + abSleep(100,TRUE); /* returns upon packet event */ + +#ifdef notdef /* removed by somebody once, when, who or why ? */ + if (pcmp != noErr) { + fprintf(stderr,"%s: p_fillbuf PAPRead error %d\n", myname, pcmp); + p->p_flg |= P_IOCLS; + /* always return eof in this case */ + return(EOF); /* what else to do? */ + } +#endif notdef + + if (peof) { + p->p_flg |= P_IOEOF; + if (p->p_cnt <= 0) { /* another way to get eof... */ + p_clreof(p); + return(EOF); + } + } + + p->p_ptr = p->p_buf; /* reset pointer to start of buffer */ + return(p_getc(p)); +} + +/* + * int p_getc(PFILE *p) + * + * Read a character from the PAP stream specified by p. Return the + * character or EOF. + * + */ + +#ifndef p_getc +int p_getc(p) +PFILE *p; +{ + if (--(p->p_cnt) >= 0) { + if ((p->p_cnt % ((p->p_bufsiz/RFLOWQ)/2)) == 0) + abSleep(0,TRUE); /* make sure protocol runs */ + return(*p->p_ptr++); + } else + return(p_fillbuf(p)); +} +#endif diff --git a/applications/lwsrv/papstream.h b/applications/lwsrv/papstream.h new file mode 100644 index 0000000..e78604a --- /dev/null +++ b/applications/lwsrv/papstream.h @@ -0,0 +1,55 @@ +/* "$Author: djh $ $Date: 1995/08/28 11:10:14 $" */ +/* "$Header: /mac/src/cap60/applications/lwsrv/RCS/papstream.h,v 2.2 1995/08/28 11:10:14 djh Rel djh $" */ +/* "$Revision: 2.2 $" */ + +/* + * papstream - UNIX AppleTalk: simple stream handling for pap connections + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Created sept 5, 1987 by cck from lwsrv + * + */ + +typedef struct { /* PAP stream interface PFILE */ + int p_cno; /* pap connection number */ + int p_cnt; /* count of chars in ptr */ + int p_flg; /* flags */ + byte *p_ptr; /* ptr to input chars */ + byte *p_buf; /* buf for input */ + int p_bufsiz; /* size of buffer */ +} p_iobuf, PFILE; + +#define P_IOEOF 020 /* eof occured */ +#define P_IOCLS 040 /* pap stream is closed */ +#define P_IOERR 0100 /* error occured */ + +#define p_eof(p) (((p)->p_flg & P_IOEOF) != 0) +#define p_iscls(p) (((p)->p_flg & P_IOCLS) != 0) +#define p_clrcls(p) (((p)->p_flg &= ~P_IOCLS)) +#define p_isopn(p) (((p)->p_flg & P_IOCLS) == 0) +#define p_papcno(p) ((p)->p_cno) + +/* Fast mode - just comment out if you want to use routine instead */ +#define p_getc(p) ((--((p)->p_cnt) < 0) ? p_fillbuf(p) : *(p)->p_ptr++) + +void p_clreof(); +PFILE *p_opn(); +void p_cls(); +void p_write(); +int p_fillbuf(); + +#ifndef p_getc +int p_getc(); +#endif + +#ifdef LWSRV8 +#define T_NEEDQUOTE (1 << 0) +#define T_CRTOLF (1 << 1) +#define T_NODSC (1 << 2) +#endif /* LWSRV8 */ diff --git a/applications/lwsrv/parse.c b/applications/lwsrv/parse.c new file mode 100644 index 0000000..2242384 --- /dev/null +++ b/applications/lwsrv/parse.c @@ -0,0 +1,402 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/08/28 10:38:35 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/parse.c,v 2.1 1995/08/28 10:38:35 djh Rel djh $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * parse.c - read a configuration file and parse the information + * + * UNIX AppleTalk spooling program: act as a laserwriter + * AppleTalk package for UNIX (4.2 BSD). + * + */ + +#include +#include +#include +#include +#include +#ifdef USESTRINGDOTH +#include +#else /* USESTRINGDOTH */ +#include +#endif /* USESTRINGDOTH */ +#ifdef NEEDFCNTLDOTH +#include +#endif /* NEEDFCNTLDOTH */ +#ifndef NONDBM +# include +#else /* NONDBM */ +# include +#endif /* NONDBM */ +#include "list.h" +#include "parse.h" + +typedef unsigned short unshort; + +char datasuffix[] = ".dat"; +char includename[] = "-"; +char keywords_key[] = "keywords\0001.0.0"; +KVTree **_printers; +char specialOpts[] = "LT"; +KVTree **thequery; + +extern char *myname; +extern List *optionlist; +extern List *printerlist; + +static char *keywords = NULL; +static KVTree **pass1; +#ifndef NONDBM +static DBM *printerdb; +#endif NONDBM +static int printerfd; + +char *malloc(); + +static void Construct(); +static char *FetchData(); +static void Include(); +static void _Include(); +static List *UnpackList(); +static List *Unpackdb(); +static void _configargs(); +static void setargs(); + +static void +Construct(name, kp, lp) +char *name; +register KVTree **kp; +List *lp; +{ + register void **vp; + register int i; + register void *np; + + for (vp = (void **)AddrList(lp), i = NList(lp); i > 0; i -= 2) { + if (strcmp((char *)*vp, includename) == 0) { /* include */ + Include(name, (char *)*++vp, kp); + vp++; + continue; + } + np = *vp++; + if (isOption(np) && isSpecialOpt(np)) { + if ((lp = (List *)SearchKVTree(kp, np, strcmp)) == NULL) { + lp = CreateList(); + AddToKVTree(kp, np, lp, strcmp); + } + AddToList(lp, *vp++); + } else + AddToKVTree(kp, np, *vp++, strcmp); + } +} + +static char * +FetchData(name, n) +char *name; +register int n; +{ + register char *ptr, *cp; + register int size; + datum key, data; + Location loc; + + key.dptr = name; + key.dsize = n; +#ifndef NONDBM + data = dbm_fetch(printerdb, key); +#else NONDBM + data = fetch(key); +#endif NONDBM + if (!data.dptr) + return(NULL); + if (data.dsize != sizeof(Location)) { + fprintf(stderr, "%s: location data for %s is wrong size\n", + myname, name); + exit(1); + } + bcopy((char *)data.dptr, (char *)&loc, sizeof(Location)); + if (loc.magic != MagicNumber || (size = loc.size) <= 0) { + fprintf(stderr, "%s: location data mismatch for %s\n", myname, name); + exit(1); + } + if (lseek(printerfd, loc.offset, L_SET) < 0) { + fprintf(stderr, "%s: unable to position in data file for %s\n", + myname, name); + exit(1); + } + if ((cp = ptr = malloc(size)) == NULL) { + fprintf(stderr, "%s: out of memory for %s\n", myname, name); + exit(1); + } + while (size > 0) { + if ((n = read(printerfd, cp, size)) < 0) { + fprintf(stderr, "%s: read error on %s\n", myname, name); + exit(1); + } + if (n == 0) { + fprintf(stderr, " %s: premature end of file on %s\n", myname, name); + exit(1); + } + size -= n; + cp += n; + } + return(ptr); +} + +static void +Include(name, cp, kp) +char *name, *cp; +KVTree **kp; +{ + register KVTree **ip; + register List *lp; + register char *dp; + + if (ip = (KVTree **)SearchKVTree(_printers, (void *)cp, strcmp)) { + _Include(kp, *ip); + return; + } + /* not in printer list, so try pass1 */ + if (lp = (List *)SearchKVTree(pass1, (void *)cp, strcmp)) { + ip = CreateKVTree(); + /* + * We add the new (empty) tree into master printer tree before + * constructing it so as to prevent infinite include loops. + */ + AddToKVTree(_printers, (void *)cp, ip, strcmp); + Construct(cp, ip, lp); + if (*ip) + _Include(kp, *ip); + NList(lp) = 0; /* mark the list as already processed */ + return; + } + /* not in pass1, so try database */ + if (keywords && (dp = FetchData(cp, strlen(cp)))) { + lp = Unpackdb(dp); + ip = CreateKVTree(); + /* + * We add the new (empty) tree into master printer tree before + * constructing it so as to prevent infinite include loops. + */ + AddToKVTree(_printers, cp, ip, strcmp); + Construct(cp, ip, lp); + if (*ip) + _Include(kp, *ip); + return; + } + fprintf(stderr, "%s: %s: Can't include %s\n", myname, name, cp); + exit(1); +} + +static void +_Include(kp, ip) +KVTree **kp; +register KVTree *ip; +{ + register List *lp; + + if (ip == NULL) + return; + if (isOption(ip->key) && isSpecialOpt(ip->key) && + (lp = (List *)SearchKVTree(kp, ip->key, strcmp))) { + CatList(lp, (List *)ip->val); + } else + AddToKVTree(kp, ip->key, ip->val, strcmp); + if (ip->left) + _Include(kp, ip->left); + if (ip->right) + _Include(kp, ip->right); +} + +static List * +UnpackList(sp, c) +register unshort *sp; +register char *c; +{ + register List *lp; + register int n; + + lp = CreateList(); + for (n = *sp++; n > 0; n--) + AddToList(lp, c + *sp++); + return(lp); +} + +static List * +Unpackdb(c) +register char *c; +{ + register unshort *sp, *s; + register char *cp; + register List *lp; + register int n; + + lp = CreateList(); + s = (unshort *)c; + /* + * The first unshort is offset to string section. s is now pointing to + * beginning of packed ushorts + */ + c += *s++; + /* + * The first unshort is the number of pairs in the list. Then each following + * pair of ushorts points to a name string and the value, which may be another + * string or another list. + */ + sp = s; + for (n = *sp++; n > 0; n--) { + AddToList(lp, cp = keywords + *sp++); + if (StringVal(cp)) /* value is just a string */ + AddToList(lp, *sp == 0 ? NULL : c + *sp); + else /* value is another list */ + AddToList(lp, UnpackList(s + *sp, c)); + sp++; + } + return(lp); +} + +void +configargs(dbname) +char *dbname; +{ + register KVTree **kp; + register List *lp; + register void **vp; + register int i; + register char *cp; + KVTree **_default; + char buf[BUFSIZ]; + + if (dbname) { +#ifndef NONDBM + if ((printerdb = dbm_open(dbname, O_RDONLY, 0)) == NULL) { + fprintf(stderr, "%s: Can't open dbm %s\n", myname, dbname); + exit(1); + } +#else NONDBM + if (dbminit(dbname) < 0) { + fprintf(stderr, "%s: Can't open dbm %s\n", myname, dbname); + exit(1); + } +#endif NONDBM + strcpy(buf, dbname); + strcat(buf, datasuffix); + if ((printerfd = open(buf, O_RDONLY, 0)) < 0) { + fprintf(stderr, "%s: Can't open data file %s%s\n", myname, dbname, + datasuffix); + exit(1); + } + if ((keywords = FetchData(keywords_key, KEYWORDSKEYSIZE)) == NULL) { + fprintf(stderr, "%s: %s does not contain printer templates\n", + myname, dbname); + exit(1); + } + } + if (optionlist) { + _default = CreateKVTree(); + Construct("Default", _default, optionlist); + if (NList(optionlist) > 0) + setargs(NULL, _default); + FreeKVTree(_default, NULL, NULL); + } + _printers = CreateKVTree(); + /* Pass 1 */ + pass1 = CreateKVTree(); + for (vp = AddrList(printerlist), i = NList(printerlist); i > 0; i -= 2){ + cp = (char *)*vp++; + AddToKVTree(pass1, (void *)cp, *vp++, strcmp); + } + /* Pass 2 */ + for (vp = AddrList(printerlist), i = NList(printerlist); i > 0; vp++, i -= 2) { + if (NList(*vp) <= 0) /* already processed */ + continue; + kp = CreateKVTree(); + cp = (char *)*vp++; + /* + * We add the new (empty) tree into master printer tree before + * constructing it so as to prevent infinite include loops. + */ + AddToKVTree(_printers, (void *)cp, kp, strcmp); + Construct(cp, kp, (List *)*vp); + } + if (*_printers) + _configargs(*_printers); + +#ifndef NONDBM + if (printerdb) + dbm_close(printerdb); +#else NONDBM + /* + * There does not seem to be a way to close the old dbm file! + */ +#endif NONDBM +} + +static void +_configargs(kp) +register KVTree *kp; +{ + if (kp == NULL) + return; + if (kp->left) + _configargs(kp->left); + if (SearchKVTree(kp->val, "-p", strcmp)) + setargs(kp->key, kp->val); + if (kp->right) + _configargs(kp->right); +} + +static void +setargs(name, kp) +char *name; +register KVTree **kp; +{ + register int i, j; + register List *lp, *lk; + register void **vp; + extern int optind; + + lp = CreateList(); + AddToList(lp, myname); /* simulating argc, argv, so this is argv[0] */ + if (name) { + /* + * Just do "-n name" to force thequery to be set to the correct value. + */ + AddToList(lp, "-n"); + AddToList(lp, name); + AddToList(lp, NULL); /* Just in case */ + optind = 1; + doargs(NList(lp) - 1, AddrList(lp)); + FreeList(lp, NULL); + lp = CreateList(); + AddToList(lp, myname); + } + lk = ListKVTree(kp); + for (kp = (KVTree **)AddrList(lk), i = NList(lk); i > 0; kp++, i--) { + if (!isOption((*kp)->key)) + break; + if (isSpecialOpt((*kp)->key)) { + for (j = NList((*kp)->val), vp = AddrList((*kp)->val); j > 0; j--) { + AddToList(lp, (*kp)->key); + AddToList(lp, *vp++); + } + } else { + AddToList(lp, (*kp)->key); + if ((*kp)->val) + AddToList(lp, (*kp)->val); + } + } + if (i > 0) { + while (i-- > 0) { + AddToKVTree(thequery, (*kp)->key, (*kp)->val, strcmp); + kp++; + } + } + FreeList(lk, NULL); + if (NList(lp) > 1) { + AddToList(lp, NULL); /* Just in case */ + optind = 1; + doargs(NList(lp) - 1, AddrList(lp)); + } + FreeList(lp, NULL); +} diff --git a/applications/lwsrv/parse.h b/applications/lwsrv/parse.h new file mode 100644 index 0000000..b993267 --- /dev/null +++ b/applications/lwsrv/parse.h @@ -0,0 +1,45 @@ +/* "$Author: djh $ $Date: 1995/08/30 00:53:28 $" */ +/* "$Header: /local/mulga/mac/src/cap60/applications/lwsrv/RCS/parse.h,v 2.1 1995/08/30 00:53:28 djh Rel djh $" */ +/* "$Revision: 2.1 $" */ + +/* + * parse.h - read a configuration file and parse the information + * + * UNIX AppleTalk spooling program: act as a laserwriter + * AppleTalk package for UNIX (4.2 BSD). + * + */ + +#ifndef _PARSE_H_ +#define _PARSE_H_ + +typedef struct Location { + int magic; + int offset; + int size; +} Location; + +#define KEYWORDSKEYSIZE 14 +#define OPTIONCHAR '-' + +#define MagicNumber 0x4c77436f /* 'LwCo' */ +#define StringVal(a) (isOption(a) && !isSpecialOpt(a)) +#define ListVal(a) (!isOption(a) || isSpecialOpt(a)) +#define isOption(a) (*(char *)(a) == OPTIONCHAR) +#define isSpecialOpt(a) (Option(a) && index(specialOpts, Option(a))) +#define Option(a) (((char *)(a))[1]) + +extern char datasuffix[]; +extern char includename[]; +extern char keywords_key[]; +extern char *libraryfile; +extern List *optionlist; +extern List *printerlist; +extern KVTree **_printers; +extern char specialOpts[]; +extern KVTree **thequery; + +void configargs(/* char *dbname */); +void initkeyword(/* FILE *fp */); + +#endif /* _PARSE_H_ */ diff --git a/applications/lwsrv/parsel.l b/applications/lwsrv/parsel.l new file mode 100644 index 0000000..048a422 --- /dev/null +++ b/applications/lwsrv/parsel.l @@ -0,0 +1,226 @@ +%% +\"[^"]*\" { + register int c; + +#ifdef __cplusplus + unput(c = yyinput()); +#else + unput(c = input()); +#endif + yytext[--yyleng] = 0; + if (c != '"') { + yylval.string = strdup(yytext + 1); + return(STRING); + } + yymore(); + } +'[^']*' { + register int c; + +#ifdef __cplusplus + unput(c = yyinput()); +#else + unput(c = input()); +#endif + yytext[--yyleng] = 0; + if (c != '\'') { + yylval.string = strdup(yytext + 1); + return(STRING); + } + yymore(); + } +"," { + return(COMMA); + } +";" { + return(EOS); + } +"=" { + return(EQUAL); + } +[{(] { + return(LPAREN); + } +[})] { + return(RPAREN); + } +[ \t\n]+ { + /* just ignore whitespace */ + } +"#"[^\n]*\n { + /* just ignore comments */ + } +[^ \t\n,;=(){}"'#]+ { + static int searchkeywords(); + return(searchkeywords(yytext, &yylval.string)); + } +%% +#undef input +#undef unput + +static unsigned char backbuf[256]; +static unsigned char *backp = backbuf; +static FILE *infp; +static List *keylist; +static struct keywordbuf { + char *key; + char *sval; + int ival; +} keywordbuf[] = { + {"-A", "-A", DSC}, + {"-C", "-C", LPRCMD}, +#ifdef LPRARGS + {"-L", "-L", LPRARGUMENT}, +#endif LPRARGS + {"-N", "-N", NOCOLLECT}, +#ifdef PASS_THRU + {"-P", "-P", PASSTHRU}, +#endif PASS_THRU +#ifdef NeXT + {"-R", "-R", NEXT}, +#endif NeXT + {"-S", "-S", SINGLEFORK}, + {"-T", "-T", TRANSCRIPT}, +#ifdef LWSRV_AUFS_SECURITY + {"-X", "-X", AUFSSECURITY}, +#endif LWSRV_AUFS_SECURITY +#ifdef LW_TYPE + {"-Y", "-Y", ATTYPE}, +#endif LW_TYPE + {"-a", "-a", DIRDICT}, + {"-d", "-d", DEBUG}, + {"-e", "-e", EEXEC}, + {"-f", "-f", FONTFILE}, + {"-h", "-h", BANNER}, + {"-k", "-k", CHECKSUM}, + {"-l", "-l", LOGFILE}, + {"-p", "-p", PRINTERQUEUE}, + {"-q", "-q", QUERYFILE}, + {"-r", "-r", KEEPFILE}, + {"-t", "-t", TRACEFILE}, + {"-v", "-v", VERBOSE}, + {"alloweexec", "-e", EEXEC}, +#ifdef LW_TYPE + {"appletalktype", "-Y", ATTYPE}, +#endif LW_TYPE +#ifdef LWSRV_AUFS_SECURITY + {"aufssecurity", "-X", AUFSSECURITY}, +#endif LWSRV_AUFS_SECURITY + {"debug", "-d", DEBUG}, + {"deniedmessage", "DeniedMessage", DENIEDMESSAGE}, + {"dontcollect", "-N", NOCOLLECT}, + {"dsc", "-A", DSC}, + {"encoding", "encoding", ENCODING}, + {"featurequery", "FeatureQuery", FEATUREQUERY}, + {"file", "file", FILERES}, + {"font", "font", FONT}, + {"fontfile", "-f", FONTFILE}, + {"form", "form", FORM}, + {"include", "-", INCLUDE}, + {"keepspoolfile", "-r", KEEPFILE}, + {"library", NULL, LIBRARY}, + {"logfile", "-l", LOGFILE}, +#ifdef LPRARGS + {"lprargument", "-L", LPRARGUMENT}, +#endif LPRARGS + {"lprcommand", "-C", LPRCMD}, +#ifdef NeXT + {"nextresolution", "-R", NEXT}, +#endif NeXT + {"nochecksum", "-k", CHECKSUM}, + {"options", NULL, OPTIONS}, +#ifdef PASS_THRU + {"passthru", "-P", PASSTHRU}, +#endif PASS_THRU + {"pattern", "pattern", PATTERN}, + {"printerquery", "PrinterQuery", PRINTERQUERY}, + {"printerqueue", "-p", PRINTERQUEUE}, + {"procsetdir", "-a", DIRDICT}, + {"query", "Query", QUERY}, + {"queryfile", "-q", QUERYFILE}, + {"singlefork", "-S", SINGLEFORK}, + {"suppressbanner", "-h", BANNER}, + {"tracefile", "-t", TRACEFILE}, + {"transcriptoption", "-T", TRANSCRIPT}, + {"verbose", "-v", VERBOSE}, + {"vmstatus", "VMStatus", VMSTATUS}, + {NULL, NULL, 0}, +}; + +void +initkeyword(fp) +FILE *fp; +{ + register List *lp; + register struct keywordbuf *kp; + + lp = CreateList(); + for (kp = keywordbuf; kp->key; kp++) + AddToList(lp, (void *)kp); + keylist = lp; + infp = fp; +} + +static unsigned char input_linebuf[BUFSIZ] = ""; +static unsigned char *input_lineptr = input_linebuf; + +#ifndef YY_INPUT +static int +input() +{ + register int c; + + if (backp > backbuf) + return(*--backp); + if (*input_lineptr) { + /* putc(*input_lineptr, stderr); /* DEBUG */ + return(*input_lineptr++); + } + if (fgets((char *)input_linebuf, BUFSIZ, infp) == NULL) + return(0); + linenum++; + input_lineptr = input_linebuf; + /* putc(*input_lineptr, stderr); /* DEBUG */ + return(*input_lineptr++); +} +#endif + +static int +keycmp(kp, cp) +struct keywordbuf *kp; +char *cp; +{ + return(strcmp(kp->key, cp)); +} + +static int +searchkeywords(str, outstr) +char *str, **outstr; +{ + register struct keywordbuf *kp; + register char *cp; + char buf[BUFSIZ]; + static int keycmp(); + + if (*str != '-') { + strcpy(buf, str); + for (cp = buf; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + cp = buf; + } else + cp = str; + if (kp = (struct keywordbuf *)SearchList(keylist, cp, keycmp)) { + *outstr = kp->sval; + return(kp->ival); + } + *outstr = strdup(str); + return(STRING); +} + +static int +unput(c) +int c; +{ + *backp++ = c; +} diff --git a/applications/lwsrv/parsey.y b/applications/lwsrv/parsey.y new file mode 100644 index 0000000..bdc8cd7 --- /dev/null +++ b/applications/lwsrv/parsey.y @@ -0,0 +1,478 @@ +%{ +/* + * parsey - UNIX AppleTalk spooling program: act as a laserwriter + * yacc configuration file parser + * + */ +#include +#include +#include +#include "list.h" +#include "parse.h" + +typedef struct NameVal { + char *name; + void *val; +} NameVal; + +/* + * Globals + */ + +char *libraryfile = 0; +List *optionlist = 0; +List *printerlist = 0; + +/* + * Function typecasts + */ + +static char *addnl(); +static NameVal *CreateNameVal(); + +int strcmp(); +char *strdup(); + +%} +%union { + char *string; + List *list; + NameVal *nameval; +} +%token ATTYPE AUFSSECURITY BANNER CHECKSUM COMMA DEBUG DENIEDMESSAGE DIRDICT +%token DSC EEXEC ENCODING EOS EQUAL FEATUREQUERY FILERES FONT FONTFILE FORM +%token INCLUDE KEEPFILE LIBRARY LOGFILE LPAREN LPRARGUMENT LPRCMD NEXT +%token NOCOLLECT OPTIONS PASSTHRU PATTERN PRINTERQUERY PRINTERQUEUE QUERY +%token QUERYFILE RPAREN SINGLEFORK STRING TRACEFILE TRANSCRIPT VERBOSE VMSTATUS + +%type ATTYPE AUFSSECURITY BANNER CHECKSUM DEBUG DENIEDMESSAGE DIRDICT +%type DSC EEXEC ENCODING FEATUREQUERY FILERES FONT FONTFILE FORM +%type INCLUDE KEEPFILE LOGFILE LPRARGUMENT LPRCMD NEXT NOCOLLECT +%type PASSTHRU PATTERN PRINTERQUERY PRINTERQUEUE QUERY QUERYFILE +%type SINGLEFORK STRING TRACEFILE TRANSCRIPT VERBOSE VMSTATUS +%type lwsrvnoarg lwsrvwitharg optnoarg optwitharg queryargtype +%type querynoargtype querytype shortresource special +%type block clause list optionblock optionclause optlist stringcomma +%type stringlist stringnlcomma stringnllist stringnlset stringset +%type aprinter include lwsrvoption optstatement printeroption +%type printerqueue query resource specialstrings statement + +%start configuration +%% +configuration : library options printers + ; + +library : /* empty */ + | LIBRARY EQUAL STRING EOS + { + libraryfile = $3; + } + +options : /* empty */ + | OPTIONS EQUAL optionblock + { + optionlist = $3; + } + ; + +optionblock : optstatement + { + register List *lp; + + lp = CreateList(); + AddToList(lp, (void *)$1->name); + AddToList(lp, (void *)$1->val); + free((char *)$1); + $$ = lp; + } + | optionclause EOS + ; + +optionclause : LPAREN optlist RPAREN + { + $$ = $2; + } + ; + +optlist : optstatement + { + register List *lp; + + lp = CreateList(); + AddToList(lp, (void *)$1->name); + AddToList(lp, (void *)$1->val); + free((char *)$1); + $$ = lp; + } + | optlist optstatement + { + AddToList($1, (void *)$2->name); + AddToList($1, (void *)$2->val); + free((char *)$2); + $$ = $1; + } + ; + +optstatement : lwsrvoption EOS + | printeroption EOS + | query EOS + | resource EOS + | specialstrings EOS + ; + +lwsrvoption : lwsrvwitharg STRING + { + register NameVal *np; + + np = CreateNameVal(); + np->name = $1; + np->val = (void *)$2; + $$ = np; + } + | lwsrvnoarg + { + register NameVal *np; + + np = CreateNameVal(); + np->name = $1; + np->val = NULL; + $$ = np; + } + ; + +lwsrvwitharg : LOGFILE + | DEBUG + | AUFSSECURITY + | LPRCMD + | QUERYFILE + ; + +lwsrvnoarg : VERBOSE + | SINGLEFORK + ; + +printers : aprinter + { + printerlist = CreateList(); + AddToList(printerlist, (void *)$1->name); + AddToList(printerlist, (void *)$1->val); + free((char *)$1); + } + | printers aprinter + { + AddToList(printerlist, (void *)$2->name); + AddToList(printerlist, (void *)$2->val); + free((char *)$2); + } + ; + +aprinter : STRING EQUAL block + { + register NameVal *np; + + np = CreateNameVal(); + np->name = $1; + np->val = (void *)$3; + $$ = np; + } + ; + +block : statement + { + register List *lp; + + lp = CreateList(); + AddToList(lp, (void *)$1->name); + AddToList(lp, (void *)$1->val); + free((char *)$1); + $$ = lp; + } + | clause EOS + ; + +clause : LPAREN list RPAREN + { + $$ = $2; + } + ; + +list : statement + { + register List *lp; + + lp = CreateList(); + AddToList(lp, (void *)$1->name); + AddToList(lp, (void *)$1->val); + free((char *)$1); + $$ = lp; + } + | list statement + { + AddToList($1, (void *)$2->name); + AddToList($1, (void *)$2->val); + free((char *)$2); + $$ = $1; + } + ; + +statement : printerqueue EOS + | printeroption EOS + | include EOS + | query EOS + | resource EOS + | specialstrings EOS + ; + +printerqueue : PRINTERQUEUE STRING + { + register NameVal *np; + + np = CreateNameVal(); + np->name = $1; + np->val = (void *)$2; + $$ = np; + } + ; + +printeroption : optwitharg STRING + { + register NameVal *np; + + np = CreateNameVal(); + np->name = $1; + np->val = (void *)$2; + $$ = np; + } + | optnoarg + { + register NameVal *np; + + np = CreateNameVal(); + np->name = $1; + np->val = NULL; + $$ = np; + } + ; + +optwitharg : DIRDICT + | FONTFILE + | DSC + | TRANSCRIPT + | NEXT + | ATTYPE + | LPRARGUMENT + | TRACEFILE + ; + +optnoarg : EEXEC + | BANNER + | CHECKSUM + | NOCOLLECT + | PASSTHRU + | KEEPFILE + ; + +include : INCLUDE STRING + { + register NameVal *np; + + np = CreateNameVal(); + np->name = $1; + np->val = (void *)$2; + $$ = np; + } + ; + +query : querytype stringnlset + { + register NameVal *np; + + np = CreateNameVal(); + np->name = $1; + np->val = (void *)$2; + $$ = np; + } + ; + +querytype : queryargtype STRING + { + register char *cp; + + if((cp = (char *)malloc(strlen($1) + strlen($2) + 2)) + == NULL) + errorexit(1, "parse: Out of memory\n"); + sprintf(cp, "%s\t%s", $1, $2); + free($2); + $$ = cp; + } + | querynoargtype + ; + +queryargtype : FEATUREQUERY + | QUERY + ; + +querynoargtype : PRINTERQUERY + | VMSTATUS + ; + +resource : shortresource stringset + { + register NameVal *np; + + SortList($2, StringSortItemCmp); + np = CreateNameVal(); + np->name = $1; + np->val = (void *)$2; + $$ = np; + } + ; + +shortresource : FONT + | FILERES + | PATTERN + | FORM + | ENCODING + ; + +stringnlset : STRING + { + register List *list; + + list = CreateList(); + AddToList(list, (void *)addnl($1)); + $$ = list; + } + | LPAREN stringnlcomma RPAREN + { + $$ = $2; + } + ; + +stringnlcomma : stringnllist + | stringnllist COMMA + ; + +stringnllist : STRING + { + register List *list; + + list = CreateList(); + AddToList(list, (void *)addnl($1)); + $$ = list; + } + | stringnllist COMMA STRING + { + AddToList($1, (void *)addnl($3)); + $$ = $1; + } + ; + +stringset : STRING + { + register List *list; + + list = CreateList(); + AddToList(list, (void *)$1); + $$ = list; + } + | LPAREN stringcomma RPAREN + { + $$ = $2; + } + ; + +stringcomma : stringlist + | stringlist COMMA + ; + +stringlist : STRING + { + register List *list; + + list = CreateList(); + AddToList(list, (void *)$1); + $$ = list; + } + | stringlist COMMA STRING + { + AddToList($1, (void *)$3); + $$ = $1; + } + ; + +specialstrings : special stringnlset + { + register NameVal *np; + + np = CreateNameVal(); + np->name = $1; + np->val = (void *)$2; + $$ = np; + } + ; + +special : DENIEDMESSAGE + ; + +%% +static char strbuf[256]; +int linenum = 0; + +static void _Include(/* KVTree **kp, KVTree *ip */); + +#include "lex.yy.c" + +static NameVal * +CreateNameVal() +{ + register NameVal *np; + + if((np = (NameVal *)malloc(sizeof(NameVal))) == NULL) + errorexit(1, "CreateNameVal: Out of memory\n"); + return(np); +} + +static char * +addnl(str) +char *str; +{ + register char *cp; + + if((cp = (char *)malloc(strlen(str) + 2)) == NULL) + errorexit(1, "addnl:Out of memory\n"); + strcpy(cp, str); + strcat(cp, "\n"); + free(str); + return(cp); +} + +yyerror(s) +char *s; +{ + register int i, j; + register unsigned char *tp, *fp, *cp; + unsigned char buf[BUFSIZ]; + + i = 0; + for(tp = buf, fp = input_linebuf; fp < input_lineptr; fp++) { + if(*fp == '\t') { + i += (j = 8 - (i % 8)); + while(j-- > 0) + *tp++ = ' '; + continue; + } + *tp++ = *fp; + i++; + } + cp = tp; + while(*cp++ = *fp++); + fprintf(stderr, "*** %s in configuration file on or before line %d\n", s, + linenum); + fputs((char *)buf, stderr); + if((i = (tp - buf) - 2) < 0) + i = 0; + while(i-- > 0) + putc('-', stderr); + fputs("^\n", stderr); +} diff --git a/applications/lwsrv/procset.c b/applications/lwsrv/procset.c new file mode 100644 index 0000000..60ec190 --- /dev/null +++ b/applications/lwsrv/procset.c @@ -0,0 +1,396 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/06/18 10:50:20 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/procset.c,v 2.13 1996/06/18 10:50:20 djh Rel djh $"; +static char revision[] = "$Revision: 2.13 $"; + +/* + * procset - UNIX AppleTalk spooling program: act as a laserwriter + * handles simple procset list - assumes procsets in a directory + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * Created Sept 5, 1987 by cck from lwsrv. + * + * + */ + +#include +#include +#ifndef _TYPES +# include /* assume included by param.h */ +#endif /* _TYPES */ +#include +#include +#include +#include +#include +#include + +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#ifdef USEDIRENT +# include +#else USEDIRENT +# ifdef xenix5 +# include +# else xenix5 +# include +# endif xenix5 +#endif USEDIRENT + +#if defined(xenix5) || defined(SOLARIS) +# include +#endif /* xenix5 || SOLARIS */ +#ifdef linux +# include +#endif /* linux */ + +#ifdef LWSRV8 +#include "list.h" +#include "query.h" +#endif /* LWSRV8 */ +#include "procset.h" +#include "spmisc.h" + +extern char *myname; +extern int verbose; + +#ifndef LWSRV8 +DictList *dicthead = (DictList *)NULL; /* dictionary list header */ +#else /* LWSRV8 */ +private KVTree **dictlist = NULL; /* dictionary list header */ +#endif /* LWSRV8 */ + +void +#ifndef LWSRV8 +newdictionary(dl) +#else /* LWSRV8 */ +set_dict_list(dl) +KVTree **dl; +{ + dictlist = dl; +} + +void +newdictionary(name, dl) +char *name; +#endif /* LWSRV8 */ +DictList *dl; +{ +#ifndef LWSRV8 + dl->ad_next = dicthead; /* link it into the list */ + dicthead = dl; +#else /* LWSRV8 */ + AddToKVTree(dictlist, (void *)name, (void *)dl, strcmp); +#endif /* LWSRV8 */ +} + +DictList * +GetProcSet(psn) +char *psn; +{ +#ifndef LWSRV8 + DictList *dl; + for (dl = dicthead; dl != (DictList *) NULL; dl = dl->ad_next) + if (strcmp(psn,dl->ad_ver) == 0) + return(dl); + return(NULL); +#else /* LWSRV8 */ + return((DictList *)SearchKVTree(dictlist, psn, strcmp)); +#endif /* LWSRV8 */ +} + +void +ClearProcSetsSent() +{ +#ifdef LWSRV8 + private void clearprocsent(); + clearprocsent(*dictlist); +#else /* LWSRV8 */ + DictList *dl; + for (dl = dicthead; dl != (DictList *) NULL; dl = dl->ad_next) { + dl->ad_sent = FALSE; + } +#endif /* LWSRV8 */ +} + +#ifdef LWSRV8 +private void +clearprocsent(dl) +register KVTree *dl; +{ + ((DictList *)dl->val)->ad_sent = FALSE; + if (dl->left) + clearprocsent(dl->left); + if (dl->right) + clearprocsent(dl->right); + return; +} +#endif /* LWSRV8 */ + +/* + * checks if the file is there and should be readable + * +*/ +private boolean +checkfile(fn) +char *fn; +{ + struct stat stb; + if (stat(fn, &stb) < 0) + return(FALSE); +#ifdef USEDIRENT +#if defined (AIX) || defined (drsnx) || defined(__osf__) || defined(SOLARIS) + if (S_ISREG(stb.st_mode) == 0) /* make sure regular file */ + return(FALSE); +#else /* AIX || drsnx || __osf__ || SOLARIS */ + /* sysv follows xpg standards */ + if (S_ISREG(&stb) == 0) /* make sure regular file */ + return(FALSE); +#endif /* AIX || drsnx || __osf__ || SOLARIS */ +#else USEDIRENT + if (S_ISREG(stb.st_mode) == 0) /* make sure regular file */ + return(FALSE); +#endif USEDIRENT + if (access(fn, R_OK) < 0) + return(FALSE); + return(TRUE); +} + +private boolean +dictselect(d) +#ifdef USEDIRENT +struct dirent *d; +#else USEDIRENT +struct direct *d; +#endif USEDIRENT +{ + return(checkfile(d->d_name)); +} + +#ifdef LWSRV8 +DictList * +CreateDict() +{ + register DictList *dl; + + if ((dl = (DictList *)malloc(sizeof(DictList))) == NULL) + errorexit(1, "CreateDict: Out of memory\n"); + return(dl); +} + +private void +verbosedict(lp, dfn) +register KVTree *lp; +char *dfn; +{ + if (lp->left) + verbosedict(lp->left, dfn); + fprintf(stderr,"%s: ProcSet '%s' file is '%s/%s'\n", + myname,(char *)lp->key,dfn,((DictList *)lp->val)->ad_fn); + if (lp->right) + verbosedict(lp->right, dfn); +} +#endif /* LWSRV8 */ + +/* + * scan dict dir for valid proc sets + * +*/ +#ifndef LWSRV8 +void +scandicts(dfn) +char *dfn; +#else /* LWSRV8 */ +KVTree ** +scandicts(dfn, lastried) +char *dfn; +time_t *lastried; +#endif /* LWSRV8 */ +{ + DictList *dl; +#ifdef USEDIRENT + struct dirent **namelist; + register struct dirent *dp; +#else USEDIRENT + struct direct **namelist; + register struct direct *dp; +#endif USEDIRENT + struct stat stb; + int i, j, nlst; + char path[MAXPATHLEN]; + char line[1024]; /* reasonable size */ + char *psn; + FILE *fp; +#ifndef LWSRV8 + static time_t lastried = 0; +#else /* LWSRV8 */ + register KVTree **lp; + int free(); +#endif /* LWSRV8 */ + + if (stat(dfn, &stb) >= 0) { +#ifndef LWSRV8 + if (lastried == stb.st_mtime) /* directory modified? */ + return; /* no, nothing to do */ + lastried = stb.st_mtime; +#else /* LWSRV8 */ + if (*lastried == stb.st_mtime) /* directory modified? */ + return(NULL); /* no, nothing to do */ + *lastried = stb.st_mtime; +#endif /* LWSRV8 */ + } + if (verbose) + fprintf(stderr, "%s: (Re)scanning for Procsets\n", myname); +#ifdef USEGETCWD + if (getcwd(path,sizeof(path)-1) == 0) { + fprintf(stderr, "%s: Can't get working directory: %s\n", myname, path); +#ifndef LWSRV8 + return; +#else /* LWSRV8 */ + return(NULL); +#endif /* LWSRV8 */ + } +#else /* USEGETCWD */ + if (getwd(path) == 0) { + fprintf(stderr, "%s: Can't get working directory: %s\n", myname, path); +#ifndef LWSRV8 + return; +#else /* LWSRV8 */ + return(NULL); +#endif /* LWSRV8 */ + } +#endif /* USEGETCWD */ + if (chdir(dfn) < 0) { + perror("chdir"); + fprintf(stderr, "on path %s\n",dfn); + (void)chdir(path); /* try to go back in case... */ +#ifndef LWSRV8 + return; +#else /* LWSRV8 */ + return(NULL); +#endif /* LWSRV8 */ + } + if ((nlst = scandir(".", &namelist, dictselect, NULL)) < 0) { + perror(dfn); + fprintf(stderr, "Can't find dictionary files!\n"); +#ifndef LWSRV8 + return; +#else /* LWSRV8 */ + return(NULL); +#endif /* LWSRV8 */ + } +#ifdef LWSRV8 + lp = CreateKVTree(); +#endif /* LWSRV8 */ + for (i = 0, dp = namelist[0]; i < nlst ; i++, dp = namelist[i]) { + if ((fp = fopen(dp->d_name, "r")) == NULL) + goto baddict; + if (fgets(line, sizeof(line)-1, fp) == NULL) { + fclose(fp); + goto baddict; + } + if (strncmp(line, "%%BeginProcSet: ",sizeof("%%BeginProcSet: ")-1) != 0) { + fclose(fp); + goto baddict; + } + fclose(fp); + for (j = 0; line[j] != '\0'; j++) /* treat '\r' as eol */ + if (line[j] == '\r' || line[j] == '\n') { + line[j] = '\0'; + break; + } + psn = line+sizeof("%%BeginProcSet:"); + stripspaces(psn); + if (*psn == '\0') + goto baddict; +#ifndef LWSRV8 + if ((dl=GetProcSet(psn)) != NULL) { + if (checkfile(dl->ad_fn)) + goto baddict; + free(dl->ad_fn); /* reuse old dl, but drop fn space */ + free(dl->ad_ver); /* drop ver space */ + dl->ad_fn = NULL; + } else { + if ((dl = (DictList *)malloc(sizeof(DictList))) == NULL) + goto baddict; + newdictionary(dl); /* link it into the list */ + } + dl->ad_ver = strdup(psn); /* remember proc set name */ +#else /* LWSRV8 */ + dl = CreateDict(); +#endif /* LWSRV8 */ + dl->ad_fn = strdup(dp->d_name); /* remember file name */ + dl->ad_sent = FALSE; +#ifdef LWSRV8 + AddToKVTree(lp, strdup(psn), dl, strcmp); +#endif /* LWSRV8 */ +baddict: + free(dp); + } + free(namelist); + + (void)chdir(path); + +#ifndef LWSRV8 + if (verbose) { + for (dl = dicthead; dl != (DictList *) NULL; dl = dl->ad_next) + fprintf(stderr,"lwsrv: ProcSet '%s' file is '%s/%s'\n", + dl->ad_ver,dfn,dl->ad_fn); + } +#else /* LWSRV8 */ + if (verbose) + verbosedict(*lp, dfn); + return(lp); +#endif /* LWSRV8 */ +} + +#define ADBUFZ MAXPATHLEN + +private int domultijob = FALSE; + +void +setflag_encrypted_instream(flg) +int flg; +{ + domultijob = flg; +} + +void +ListProcSet(fn, dictdir, outfile, patchprocset) +char *fn; +char *dictdir; +FILE *outfile; +int patchprocset; +{ + char adbuf[ADBUFZ+1]; + FILE *fd; + int cnt; + + sprintf(adbuf, "%s/%s",dictdir,fn); + if ((fd = fopen(adbuf,"r")) != NULL) { + if (patchprocset && domultijob) { + fprintf(stderr, "%s: Running in eexec mode\n", myname); + fprintf(outfile, "%%The following fixes problems where the prep file\n"); + fprintf(outfile, "%%assumes that it is permanently downloaded and an\n"); + fprintf(outfile, "%%eof occurs at the end of the prep (e.g. eexec)\n"); + fprintf(outfile, "2 {(%%stdin) (r) file cvx exec } repeat\n"); + } + while ((cnt = fread(adbuf,sizeof(char),ADBUFZ,fd)) > 0) + fwrite(adbuf,sizeof(char),cnt,outfile); + if (patchprocset && domultijob) + fprintf(outfile, "%%%%EOF\n"); + fclose(fd); + } else { + fprintf(stderr,"%s: ListProcSet: file not found %s\n", myname, adbuf); + fprintf(outfile,"%% ** ProcSet file %s not found **\n",adbuf); + } +} + diff --git a/applications/lwsrv/procset.h b/applications/lwsrv/procset.h new file mode 100644 index 0000000..9b4c06a --- /dev/null +++ b/applications/lwsrv/procset.h @@ -0,0 +1,38 @@ +/* "$Author: djh $ $Date: 1995/08/28 11:10:14 $" */ +/* "$Header: /mac/src/cap60/applications/lwsrv/RCS/procset.h,v 2.3 1995/08/28 11:10:14 djh Rel djh $" */ +/* "$Revision: 2.3 $" */ + +/* + * procset - UNIX AppleTalk spooling program: act as a laserwriter + * handles simple procset list - assumes procsets in a directory + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Created sept 5, 1987 by cck from lwsrv + * + */ + +typedef struct Dict_List { /* known dictionary list */ +#ifndef LWSRV8 + struct Dict_List *ad_next; /* pointer to next */ + char *ad_ver; /* the version number */ +#endif /* not LWSRV8 */ + char *ad_fn; /* the file name */ + int ad_sent; /* downloaded during this job? */ +} DictList; + +DictList *GetProcSet(); /* DictList *GetProcSet(char *) */ +void ClearProcSetsSent(); +void newdictionary(); /* void newdictionary(DictList *) */ +void ListProcSet(); + +#ifdef LWSRV8 +DictList *CreateDict(); +KVTree **scandicts(); +void set_dict_list(); +#endif /* LWSRV8 */ diff --git a/applications/lwsrv/query.c b/applications/lwsrv/query.c new file mode 100644 index 0000000..b50795a --- /dev/null +++ b/applications/lwsrv/query.c @@ -0,0 +1,201 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/08/30 08:13:25 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/query.c,v 2.2 1995/08/30 08:13:25 djh Rel djh $"; +static char revision[] = "$Revision: 2.2 $"; + +/* + * query.c - handle the LaserWriter 8 printer queries + * + * UNIX AppleTalk spooling program: act as a laserwriter + * AppleTalk package for UNIX (4.2 BSD). + * + */ + +#include +#include +#include +#include +#ifdef USESTRINGDOTH +#include +#else /* USESTRINGDOTH */ +#include +#endif /* USESTRINGDOTH */ +#include "list.h" +#include "query.h" +#include "papstream.h" + +import char *myname; + +private char newline[] = "\n"; +private char no[] = ":No\n"; +private char star[] = "*\n"; +private char yes[] = ":Yes\n"; + +private void _SendResourceKVTree(); +private char *buildProc(); + +void +SendMatchedKVTree(pf, list, prefix, str) /* only used by procset */ +PFILE *pf; +KVTree **list; +char *prefix, *str; +{ + register char *cp, *tp; + char buf[256], proc[256]; + + if (prefix == NULL) + cp = buf; + else { + strcpy(buf, prefix); + cp = buf + strlen(buf); + } + while (buildProc(&str, proc)) { + strcpy(cp, proc); + strcat(cp, SearchKVTree(list, proc, strcmp) ? yes : no); + p_write(pf,buf,strlen(buf),FALSE); + } + p_write(pf,star,strlen(star),FALSE); +} + +void +SendMatchedResources(pf, list, prefix, str) +PFILE *pf; +List *list; +char *prefix, *str; +{ + register char *cp, *tp; + char buf[256]; + + if (prefix == NULL) + cp = buf; + else { + strcpy(buf, prefix); + cp = buf + strlen(buf); + } + while (tp = nextoken(&str)) { + strcpy(cp, tp); + strcat(cp, SearchList(list, tp, strcmp) ? yes : no); + p_write(pf,buf,strlen(buf),FALSE); + } + p_write(pf,star,strlen(star),FALSE); +} + +void +SendResourceKVTree(pf, list, prefix) +PFILE *pf; +KVTree **list; +char *prefix; +{ + register char *cp; + char buf[256]; + + if (prefix == NULL) + cp = buf; + else { + strcpy(buf, prefix); + cp = buf + strlen(buf); + } + _SendResourceKVTree(pf, *list, buf, cp); + p_write(pf,star,strlen(star),FALSE); +} + +private void +_SendResourceKVTree(pf, lp, buf, cp) +PFILE *pf; +register KVTree *lp; +char *buf, *cp; +{ + if (lp == NULL) + return; + if (lp->left) + _SendResourceKVTree(pf, lp->left, buf, cp); + strcpy(cp, (char *)lp->key); + strcat(cp, newline); + p_write(pf,buf,strlen(buf),FALSE); + if (lp->right) + _SendResourceKVTree(pf, lp->right, buf, cp); +} + +void +SendResourceList(pf, list, prefix) +PFILE *pf; +List *list; +char *prefix; +{ + register int i; + register char **ip; + register char *cp; + char buf[256]; + + if (prefix == NULL) + cp = buf; + else { + strcpy(buf, prefix); + cp = buf + strlen(buf); + } + for (ip = (char **)AddrList(list), i = NList(list); i > 0; ip++, i--) { + strcpy(cp, *ip); + strcat(cp, newline); + p_write(pf,buf,strlen(buf),FALSE); + } + p_write(pf,star,strlen(star),FALSE); +} + +void +SendQueryResponse(pf, list) +PFILE *pf; +List *list; +{ + register char **lp; + register int i; + + for (i = NList(list), lp = (char **)AddrList(list); i > 0; lp++, i--) + p_write(pf, *lp, strlen(*lp), FALSE); +} + +private char * +buildProc(str, buf) +char **str; +char *buf; +{ + register char *tp; + + if (!(tp = nextoken(str))) + return(NULL); + strcpy(buf, tp); + if (!(tp = nextoken(str))) + return(buf); + strcat(buf, " "); + strcat(buf, tp); + if (!(tp = nextoken(str))) + return(buf); + strcat(buf, " "); + strcat(buf, tp); + return(buf); +} + +char * +nextoken(cp) +char **cp; +{ + register char *bp, *ep; + + bp = *cp; + while (isspace(*bp)) + bp++; + if (*bp == 0) + return(NULL); + ep = bp + 1; + if (*bp == '"') { + while (*ep && *ep != '"') + ep++; + if (*ep) + ep++; + } else { + while (*ep && !isspace(*ep)) + ep++; + } + if (*ep) + *ep++ = 0; + *cp = ep; + return(bp); +} diff --git a/applications/lwsrv/query.h b/applications/lwsrv/query.h new file mode 100644 index 0000000..3e9bf13 --- /dev/null +++ b/applications/lwsrv/query.h @@ -0,0 +1,64 @@ +/* "$Author: djh $ $Date: 1995/08/28 10:38:35 $" */ +/* "$Header: /mac/src/cap60/applications/lwsrv/RCS/query.h,v 2.1 1995/08/28 10:38:35 djh Rel djh $" */ +/* "$Revision: 2.1 $" */ + +/* + * query.h - handle the LaserWriter 8 printer queries + * + * UNIX AppleTalk spooling program: act as a laserwriter + * AppleTalk package for UNIX (4.2 BSD). + * + */ + +#ifndef _QUERY_H_ +#define _QUERY_H_ + +struct printer_instance { + char *dictdir; /* dictionary directory (-a) */ + time_t lastried; /* dictdir last modified */ + KVTree **dictlist; /* dictionary list */ + int dochecksum; /* doing checksum (-k) */ + int hflag; /* print banner (-h) */ + int rflag; /* don't remove file (-r) */ + char *tracefile; /* tracefile (-t) */ + int eflag; /* maybe "eexec" in code(-e) */ + int toptions; /* Transcript options (-T) */ + int dsc2; /* DSC2 compatibility (-A) */ +#ifdef LPRARGS + List *lprargs; /* lpr arguments (-L) */ +#endif LPRARGS +#ifdef PASS_THRU + int passthru; /* pass through (-P) */ +#endif PASS_THRU +#ifdef NeXT + char* nextdpi; /* NeXT resolution (-R) */ +#endif NeXT +#ifdef LW_TYPE + char *prttype; /* AppleTalk type (-Y) */ +#endif LW_TYPE + int capture; /* capture procset (-N) */ +/* NBP and UNIX name required */ + char *prtname; /* NBP registered printername */ + char *unixpname; /* UNIX printer name */ +/* Query list */ + KVTree **querylist; +/* AppleTalk stuff */ + PAPStatusRec statbuf; + int srefnum; /* returned by SLInit */ + char nbpbuf[128]; /* registered name:type@zone */ + int rcomp; /* flag: waiting for job? */ + int cno; /* connection number of next job */ + int children; /* number of active children */ +}; + +void SendMatchedKVTree(/* PFILE *pf, KVTree **list, char *prefix, + char *str */); +void SendMatchedResources(/* PFILE *pf, List *list, char *prefix, + char *str */); +void SendQueryResponse(/* PFILE *pf, List *list */); +void SendResourceKVTree(/* PFILE *pf, KVTree **list, char *prefix */); +void SendResourceList(/* PFILE *pf, List *list, char *prefix */); +void SendQueryResponse(/* PFILE *pf, List *list */); +char *nextoken(/* char **cp */); + +#endif /* _QUERY_H_ */ diff --git a/applications/lwsrv/query.ps b/applications/lwsrv/query.ps new file mode 100644 index 0000000..49a6aed --- /dev/null +++ b/applications/lwsrv/query.ps @@ -0,0 +1,207 @@ +%! +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% This file prints the responses to the standard LaserWriter 8.0 driver +% queries. (Note: the ADOSpooler query has been modified to output "spooler" +% rather than "0". This is to avoid confusion in what to enter in the +% database, since lwsrv should response with "spooler", not the default "0".) +% +% Remove the beginning part of the file, up to (but not including) the line +% beginning with "%!PS-Adobe 3.0 Query" if you want the query responses to +% be sent to the printer's standard output (and probably captured in a log +% file) rather than being printed. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +/D 6 dict def +D begin +/in {72 mul} def +/top 10.5 in def +/bottom .5 in def +/left 1 in def +/ypos top def +/lf 10 def +end +/Courier findfont 9 scalefont setfont +/Str 256 string def +/flush {showpage} def +/INITGRAPHICS {initgraphics} bind def +/initgraphics {currentpoint INITGRAPHICS moveto} def +/RESTORE {restore} bind def +/restore {D begin ypos end currentpoint 4 -1 roll RESTORE + moveto D begin /ypos exch def end} def +/newline {D begin /ypos ypos lf sub dup bottom lt {pop showpage top}if def + left ypos moveto end} def +D begin left ypos moveto end +/= {Str cvs show newline} def +/== {Str cvs show newline} def +/print {Str cvs (\n) search {show pop newline}if show} def +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%!PS-Adobe-3.0 Query +%%?BeginFeatureQuery: *LanguageLevel +(==>FeatureQuery *LanguageLevel) = +/languagelevel where +{ +pop languagelevel dup 2 ge{(")print 3 string cvs print("\n)}{pop("1"\n)}ifelse +}{ +("1"\n) +}ifelse +print +%%?EndFeatureQuery: Unknown +%%?BeginFeatureQuery: *PSVersion +(==>FeatureQuery *PSVersion) = +statusdict begin +("\()print version print(\) )print revision 40 string cvs print("\n)print +end +%%?EndFeatureQuery: Unknown +%%?BeginQuery: ADOSpooler +(==>Query ADOSpooler) = +(spooler) == +%%?EndQuery: spooler +%%?BeginFeatureQuery: *?Resolution +(==>FeatureQuery *?Resolution) = +/mstr 40 string def +initgraphics +72 0 dtransform +dup mul exch dup mul add sqrt .25 add round +cvi +0 72 dtransform +dup mul exch dup mul add sqrt .25 add round +cvi 2 copy eq +{ +pop +mstr cvs print +}{ +exch +mstr cvs print(x)print +mstr cvs print +}ifelse +(dpi\n)print +%%?EndFeatureQuery: Unknown +%%?BeginQuery: ADOIsBinaryOK? +(==>Query ADOIsBinaryOK?) = +(True\n) +/currentdevparams where +{ +pop +[ +{ +currentsystemparams/CurInputDevice get +currentdevparams +}stopped +{ +cleartomark 0 dict +}{ +exch pop +}ifelse +dup/Filtering known +{ +/Filtering get +/None ne +{pop(False\n)}if +}{ +pop +}ifelse +}if +print +%%?EndQuery: Unknown +%%?BeginFeatureQuery: *FreeVM +(==>FeatureQuery *FreeVM) = +(")print vmstatus exch sub 40 string cvs print("\n)print pop +%%?EndFeatureQuery: Unknown +%%?BeginFeatureQuery: *TTRasterizer +(==>FeatureQuery *TTRasterizer) = +save +true +{ +systemdict/resourcestatus known{ +42/FontType resourcestatus{pop pop false(Type42)=}{true}ifelse +}{true}ifelse +}{false}ifelse +{ +statusdict begin product end dup(LaserWriter)ne exch(LaserWriter Plus)ne and +systemdict/eexec known and +systemdict/cexec known and{ +countdictstack mark +false +<1861AEDAE118A9F95F1629C0137F8FE656811DD93DFBEA65E947502E78BA12284B8A58EF0A3 +2E272778DAA2ABEC72A84102D591E11D96BA61F57877B895A752D9BEAAC3DFD7D3220E2BDE7 +C036467464E0E836748F1DE7AB6216866F130CE7CFCEC8CE050B870C11881EE3E9D70919> +{eexec}stopped{ +cleartomark +countdictstack exch sub dup 0 gt{{end}repeat}{pop}ifelse +false +}{ +{cleartomark pop true}{cleartomark pop false}ifelse +}ifelse +}{false}ifelse{ +false +(Accept68K)= +}{true}ifelse +}{false}ifelse +{ +(None)= +}if +restore +%%?EndFeatureQuery: Unknown +%%?BeginFeatureQuery: *Product +(==>FeatureQuery *Product) = +statusdict begin +("\()print product print(\)"\n)print +end +%%?EndFeatureQuery: Unknown +%%?BeginQuery: Product +(==>Query Product) = +statusdict begin +("\()print product print(\)"\n)print +end +%%?EndQuery: Unknown +%%?BeginFeatureQuery: *ColorDevice +(==>FeatureQuery *ColorDevice) = +systemdict/colorimage known +{ +statusdict begin +/processcolors where +{ +pop processcolors 1 eq +{ +(Unknown) +}{ +(True) +}ifelse +}{ +(Unknown) +}ifelse +end +}{ +(False) +}ifelse +print(\n)print +%%?EndFeatureQuery: Unknown +%%?BeginQuery: ADORamSize +(==>Query ADORamSize) = +/languagelevel where{pop languagelevel}{1}ifelse 2 ge{ +currentsystemparams dup/RamSize known{/RamSize get}{pop -1}ifelse +}{ +statusdict/ramsize known{statusdict begin ramsize end}{-1}ifelse +}ifelse dup 0 le{ +pop vmstatus exch pop exch pop +gsave newpath matrix setmatrix clippath pathbbox grestore +mul 8 div cvi +statusdict/processcolors known{statusdict/processcolors get exec mul}if +exch pop exch pop add +cachestatus pop exch pop add exch pop add exch pop add +524288 div ceiling cvi 524288 mul +}if(")print 40 string cvs print("\n)print +%%?EndQuery: Unknown +%%?BeginFeatureQuery: *FaxSupport +(==>FeatureQuery *FaxSupport) = +(None)/resourcestatus where{pop +/HWOptions/Category resourcestatus{pop pop +/Fax/HWOptions resourcestatus{pop pop pop(Base)}if +}if +}if print(\n)print +%%?EndFeatureQuery: Unknown +%%?BeginFontListQuery +(==>FontListQuery) = +save/scratch 100 string def FontDirectory{pop =}forall systemdict/filenameforall known{(fonts/*){(.)search {pop pop pop}{dup length 6 sub 6 exch getinterval =}ifelse}scratch filenameforall}if(*) = restore +%%?EndFontListQuery: * +flush +%%EOF diff --git a/applications/lwsrv/simple.c b/applications/lwsrv/simple.c new file mode 100644 index 0000000..173728d --- /dev/null +++ b/applications/lwsrv/simple.c @@ -0,0 +1,1739 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/06/18 10:50:20 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/simple.c,v 2.13 1996/06/18 10:50:20 djh Rel djh $"; +static char revision[] = "$Revision: 2.13 $"; + +/* + * lwsrv - UNIX AppleTalk spooling program: act as a laserwriter + * simple.c - simple spooler. Understands enough of Adobe's Document + * Structuring Conventions to work, but doesn't really abide by them. + * + * should be replaced by a conforming implementation. + * + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Sept 5, 1987 created by cck + * Jan 21, 1992 gkl300 - added simple pass thru of all received files + * + */ + +#include +#include +#include +#ifndef _TYPES +/* assume included by param.h */ +# include +#endif /* _TYPES */ +#ifdef LWSRV8 +#include +#endif /* LWSRV8 */ +#include +#include +#include + +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#ifdef LWSRV8 +#include "list.h" +#include "query.h" +#include "parse.h" +#endif /* LWSRV8 */ + +#include "spmisc.h" +#include "procset.h" +#include "fontlist.h" +#include "papstream.h" + +#ifdef PAGECOUNT +#ifndef LBUFFER +#define LBUFFER PAGECOUNT +#endif LBUFFER +#endif PAGECOUNT + +#ifdef LWSRV8 +#define MAXTOKSTR 1024 +#endif /* LWSRV8 */ + +/* TOKEN types */ +/* token types above: 4095 are not valid */ +/* integer type should be at least 16 bits */ +/* top 4 bits are used in to validate, invalidate things */ +#define TOK_INVALID 0x1000 /* token shouldn't be considered */ +#define TOK_VAL_MASK 0xfff /* token value mask */ +#define TOK_EOF 200 /* end of file marker */ +#define TOK_UNK 201 /* unknown token */ +#define TOK_ADV 0 /* AppleDict Version */ +#define TOK_FLS 1 /* FontList */ +#define TOK_ENDOLD 2 /* End of one of above */ +#define TOK_TIT 3 +#define TOK_CRE 4 +#define TOK_FOR 5 +#define TOK_ENC 6 +#define TOK_BPSQ 7 /* begin procset query */ +#define TOK_EPSQ 8 /* end procset query */ +#define TOK_BFLQ 9 /* begin fontlist query */ +#define TOK_EFLQ 10 /* end fontlist query */ +#define TOK_BPS 11 +#define TOK_EPS 12 +#define TOK_IPS 13 /* include procset */ +#ifndef LWSRV8 +#ifdef PAGECOUNT +#define TOK_PGS 14 /* Number of pages */ +#endif PAGECOUNT +#else /* not LWSRV8 */ +#define TOK_PGS 14 /* Number of pages */ +#define TOK_BFOQ 15 /* begin font query */ +#define TOK_EFOQ 16 /* end font query */ +#define TOK_BFEQ 17 /* begin feature query */ +#define TOK_EFEQ 18 /* end feature query */ +#define TOK_BVQ 19 /* begin vm status query */ +#define TOK_EVQ 20 /* end vm status query */ +#define TOK_BPRQ 21 /* begin printer query */ +#define TOK_EPRQ 22 /* end printer query */ +#define TOK_BQU 23 /* begin query */ +#define TOK_EQU 24 /* end query */ +#define TOK_BFIQ 25 /* begin file query */ +#define TOK_EFIQ 26 /* end file query */ +#define TOK_BREQ 27 /* begin resource query */ +#define TOK_EREQ 28 /* end resource query */ +#define TOK_BRLQ 29 /* begin resource list query */ +#define TOK_ERLQ 30 /* end resource list query */ +#define TOK_PSA 31 /* %!PS-Adobe- */ +#define TOK_TEOF 32 /* %%EOF */ +#define TOK_PAGE 33 /* beginning of each page */ +#endif /* LWSRV8 */ +#define TOK_BEGINR 101 /* begin of query */ +#define TOK_ENDR 102 /* end query with response */ +#define TOK_BEGIN 103 /* begin item */ +#define TOK_END 104 /* end item */ +#define TOK_DROP 105 /* simply drop */ +#ifdef PROCSET_PATCH +#define TOK_PATCH 106 /* server patches (fails on non-Apple LW) */ +#endif PROCSET_PATCH + +#ifdef LWSRV8 +#define ECHO_UNCHANGED 0 +#define ECHO_ON 1 +#define ECHO_OFF 2 +#define ECHO_DROP 3 +#endif /* LWSRV8 */ + +/* token table */ +static struct atoken { + char *token; +#ifndef LWSRV8 + int toklen; + int tokval; +#else /* LWSRV8 */ + short tokval; + byte toklen; + byte changeecho; +#endif /* LWSRV8 */ +} toktbl[] = { +#ifndef LWSRV8 +#define TOKEN(token, tag) { (token), (sizeof(token)-1), (tag)} + TOKEN("%%EOF", TOK_DROP), + TOKEN("%%Title", TOK_TIT), + TOKEN("%%Creator", TOK_CRE), + TOKEN("%%For", TOK_FOR), + TOKEN("%%EndComments", TOK_ENC), + TOKEN("%%?BeginProcSetQuery", TOK_BPSQ), + TOKEN("%%?EndProcSetQuery", TOK_EPSQ), + TOKEN("%%?BeginFontListQuery", TOK_BFLQ), + TOKEN("%%?EndFontListQuery", TOK_EFLQ), + TOKEN("%%?BeginFontQuery", TOK_BEGINR), + TOKEN("%%?EndFontQuery", TOK_ENDR), + TOKEN("%%?BeginFeatureQuery", TOK_BEGINR), + TOKEN("%%?EndFeatureQuery", TOK_ENDR), + TOKEN("%%?BeginVMStatus", TOK_BEGINR), + TOKEN("%%?EndVMStatus", TOK_ENDR), + TOKEN("%%BeginExitServer", TOK_BEGIN), + TOKEN("%%EndExitServer", TOK_END), + TOKEN("%%BeginProcSet", TOK_BPS), + TOKEN("%%EndProcSet", TOK_EPS), + TOKEN("%%?BeginPrinterQuery", TOK_BEGINR), + TOKEN("%%?EndPrinterQuery", TOK_ENDR), + TOKEN("%%?BeginQuery", TOK_BEGINR), + TOKEN("%%?EndQuery", TOK_ENDR), +#else /* LWSRV8 */ +/* Now in SORTED order, because we do a binary search on it */ +#define TOKEN(token, tag, e) {(token), (tag), (sizeof(token)-1), (e)} + TOKEN("%!PS-Adobe-", TOK_PSA, ECHO_OFF), + TOKEN("%%?BeginFeatureQuery", TOK_BFEQ, ECHO_UNCHANGED), + TOKEN("%%?BeginFileQuery", TOK_BFIQ, ECHO_UNCHANGED), + TOKEN("%%?BeginFontListQuery", TOK_BFLQ, ECHO_UNCHANGED), + TOKEN("%%?BeginFontQuery", TOK_BFOQ, ECHO_UNCHANGED), + TOKEN("%%?BeginPrinterQuery", TOK_BPRQ, ECHO_UNCHANGED), + TOKEN("%%?BeginProcSetQuery", TOK_BPSQ, ECHO_UNCHANGED), + TOKEN("%%?BeginQuery", TOK_BQU, ECHO_UNCHANGED), + TOKEN("%%?BeginResourceListQuery", TOK_BRLQ, ECHO_UNCHANGED), + TOKEN("%%?BeginResourceQuery", TOK_BREQ, ECHO_UNCHANGED), + TOKEN("%%?BeginVMStatus", TOK_BVQ, ECHO_UNCHANGED), + TOKEN("%%?EndFeatureQuery", TOK_EFEQ, ECHO_UNCHANGED), + TOKEN("%%?EndFileQuery", TOK_EFIQ, ECHO_UNCHANGED), + TOKEN("%%?EndFontListQuery", TOK_EFLQ, ECHO_UNCHANGED), + TOKEN("%%?EndFontQuery", TOK_EFOQ, ECHO_UNCHANGED), + TOKEN("%%?EndPrinterQuery", TOK_EPRQ, ECHO_UNCHANGED), + TOKEN("%%?EndProcSetQuery", TOK_EPSQ, ECHO_UNCHANGED), + TOKEN("%%?EndQuery", TOK_EQU, ECHO_UNCHANGED), + TOKEN("%%?EndResourceListQuery", TOK_ERLQ, ECHO_UNCHANGED), + TOKEN("%%?EndResourceQuery", TOK_EREQ, ECHO_UNCHANGED), + TOKEN("%%?EndVMStatus", TOK_EVQ, ECHO_UNCHANGED), + TOKEN("%%BeginExitServer", TOK_BEGIN, ECHO_OFF), + TOKEN("%%BeginProcSet", TOK_BPS, ECHO_OFF), + TOKEN("%%Creator", TOK_CRE, ECHO_UNCHANGED), + TOKEN("%%EOF", TOK_TEOF, ECHO_DROP), + TOKEN("%%EndComments", TOK_ENC, ECHO_UNCHANGED), + TOKEN("%%EndExitServer", TOK_END, ECHO_ON), + TOKEN("%%EndProcSet", TOK_EPS, ECHO_ON), + TOKEN("%%For", TOK_FOR, ECHO_UNCHANGED), +#endif /* LWSRV8 */ +#ifdef ADOBE_DSC2_CONFORMANT +#ifndef LWSRV8 + TOKEN("%%IncludeProcSet", TOK_IPS), +#else /* LWSRV8 */ + TOKEN("%%IncludeProcSet", TOK_IPS, ECHO_UNCHANGED), +#endif /* LWSRV8 */ +#else ADOBE_DSC2_CONFORMANT +#ifndef LWSRV8 + TOKEN("%%IncludeProcSet", TOK_IPS|TOK_INVALID), +#else /* LWSRV8 */ + TOKEN("%%IncludeProcSet", TOK_IPS|TOK_INVALID, ECHO_UNCHANGED), +#endif /* LWSRV8 */ +#endif ADOBE_DSC2_CONFORMANT +#ifndef LWSRV8 +#ifdef PAGECOUNT + TOKEN("%%Pages", TOK_PGS), +#endif PAGECOUNT +#else /* LWSRV8 */ + TOKEN("%%Page:", TOK_PAGE, ECHO_UNCHANGED), + TOKEN("%%Pages", TOK_PGS, ECHO_UNCHANGED), +#endif /* LWSRV8 */ +#ifdef PROCSET_PATCH +#ifndef LWSRV8 + TOKEN("%%Patches", TOK_PATCH), +#else /* LWSRV8 */ + TOKEN("%%Patches", TOK_PATCH, ECHO_OFF), +#endif /* LWSRV8 */ +#endif PROCSET_PATCH +#ifdef LWSRV8 + TOKEN("%%Title", TOK_TIT, ECHO_UNCHANGED), +#endif /* LWSRV8 */ + /* very old type of queries */ +#ifndef LWSRV8 + TOKEN("%?appledict version #", TOK_ADV), + TOKEN("%?fontList", TOK_FLS), + TOKEN("%?end",TOK_ENDOLD), + {NULL, TOK_UNK, 0} +#else /* LWSRV8 */ + TOKEN("%?appledict version #", TOK_ADV, ECHO_OFF), + TOKEN("%?end",TOK_ENDOLD, ECHO_ON), + TOKEN("%?fontList", TOK_FLS, ECHO_OFF), +#endif /* LWSRV8 */ +}; + +#ifndef LWSRV8 +private char *tracefile; +#endif /* not LWSRV8 */ +private char *dictdir; +private char *prtname; +private FILE *procsetfile = NULL; +private char tmpstr[MAXPATHLEN]; +private FILE *outfile; +private int crtolf = FALSE; +private int needquote = FALSE; +private int nodsc = FALSE; +#ifdef ADOBE_DSC2_CONFORMANT +private int adobe_dsc2_conformant = TRUE; +#else ADOBE_DSC2_CONFORMANT +private int adobe_dsc2_conformant = FALSE; +#endif ADOBE_DSC2_CONFORMANT +#ifdef PASS_THRU +private int simple_pass_thru = FALSE; +#endif PASS_THRU + +import char *myname; +#ifndef LWSRV8 +#ifdef DONT_PARSE +import int dont_parse; +#endif DONT_PARSE +#else /* LWSRV8 */ +import boolean tracing; +import char *queryfile; +private char querystr[] = "Query"; +#define QUERYSTRLEN (sizeof(querystr) - 1) +private boolean query3 = FALSE; +private char newline[] = "\n"; +private FILE *qfp = NULL; +private byte tokstr[MAXTOKSTR + 1]; +private byte tokeol = 0; +private int toklen; +private int echo; +extern int capture; +#endif /* LWSRV8 */ + +export int is_simple_dsc(); +export int simple_dsc_option(); +export int simple_TranscriptOption(); +export int spool_setup(); + +private void validate_token_type(); +private void invalidate_token_type(); +private int tokval(); +private void dumpout(); +#ifdef LWSRV8 +private void dumpone(); +private void dumpoutfile(); +private void dumpprocsetfile(); +private void dumpquery(); +private void logquery(); +private int nextline(); +private char *unparen(); +#endif /* LWSRV8 */ +private int scantoken(); +int getjob(); +private void SendVAck(); + +#ifdef PASS_THRU +export int +#ifndef LWSRV8 +set_simple_pass_thru() +#else /* LWSRV8 */ +set_simple_pass_thru(flg) +int flg; +#endif /* LWSRV8 */ +{ +#ifndef LWSRV8 + simple_pass_thru = TRUE; + return(0); +#else /* LWSRV8 */ + simple_pass_thru = flg; +#endif /* LWSRV8 */ +} +#endif PASS_THRU + +export int +is_simple_dsc() +{ + return(adobe_dsc2_conformant); +} + +#ifdef LWSRV8 +export +set_simple_dsc(flg) +int flg; +{ + adobe_dsc2_conformant = flg; +} +#endif /* LWSRV8 */ + +export int +simple_dsc_option(ioption) +char *ioption; +{ + int i; + char *p; + char option[5]; /* big enough for biggest */ + + for (i = 0, p = ioption; i < 4; i++, p++) + if (*p == '\0') + break; + else + option[i] = (isupper(*p)) ? tolower(*p) : *p; + option[i] = '\0'; /* tie off string */ + + if (strcmp(option, "on") == 0) { + fprintf(stderr, "%s: simple: Turning on DSC2 compatibility, was %s\n", + myname, adobe_dsc2_conformant ? "on" : "off"); + validate_token_type(TOK_IPS); +#ifdef LWSRV8 + return(TRUE); +#endif /* LWSRV8 */ + } else if (strcmp(option, "off") == 0) { + fprintf(stderr, "%s: simple: Turning off DSC2 compatibility, was %s\n", + myname, adobe_dsc2_conformant ? "on" : "off"); +#ifndef LWSRV8 + adobe_dsc2_conformant = FALSE; +#endif /* LWSRV8 */ + invalidate_token_type(TOK_IPS); +#ifdef LWSRV8 + return(FALSE); +#endif /* LWSRV8 */ + } else { + fprintf(stderr,"%s: simple: unknown Transcript compatiblity option: %s\n", + myname, option); + return(-1); + } +#ifndef LWSRV8 + return(0); +#endif /* not LWSRV8 */ +} + +/* + * establish transcript compatibility options if any + * + */ + +export int +simple_TranscriptOption(ioption) +char *ioption; +{ + register char *p; + register char *q; + int i; + char option[100]; /* big enough for biggest option */ + + /* leave one for null (99 vs. 100) */ + for (p = ioption, q = option, i = 0; i < 99; i++, p++) + if (*p == '\0') { + break; + } else if (*p == ' ' || *p == '\t' || *p == '-' || *p == '_') + continue; + else if (isupper(*p)) + *q++ = tolower(*p); + else + *q++ = *p; + *q = '\0'; + + if (strcmp(option, "quote8bit") == 0) { + fprintf(stderr, "%s: simple: quoting 8 bit characters\n", myname); +#ifndef LWSRV8 + needquote = TRUE; +#else /* LWSRV8 */ + return(T_NEEDQUOTE); +#endif /* LWSRV8 */ + } else if (strcmp(option, "crtolf") == 0) { + fprintf(stderr,"%s: simple: translate carrage return to line feed\n", + myname); +#ifndef LWSRV8 + crtolf = TRUE; +#else /* LWSRV8 */ + return(T_CRTOLF); +#endif /* LWSRV8 */ + } else if (strcmp(option, "makenondscconformant") == 0) { + fprintf(stderr,"%s: simple: will make documents non DSC conformant\n", + myname); +#ifndef LWSRV8 + nodsc = TRUE; +#else /* LWSRV8 */ + return(T_NODSC); +#endif /* LWSRV8 */ + } else { + fprintf(stderr,"%s:simple: unknown Transcript compatibility option: %s\n", + myname, ioption); + return(-1); + } +#ifndef LWSRV8 + return(0); +#endif /* LWSRV8 */ +} + +#ifdef LWSRV8 +export +set_toptions(flg) +int flg; +{ + needquote = (flg & T_NEEDQUOTE); + crtolf = (flg & T_CRTOLF); + nodsc = (flg & T_NODSC); +} +#endif /* LWSRV8 */ + +/* + * establish tracefile if any + * establish fontfile name + * (prtname unused) + * establish procset/dictionary directory and scan for dictionaries + * + */ + +export int +#ifndef LWSRV8 +spool_setup(itracefile, fontfile, iprtname, idictdir) +char *itracefile; +char *fontfile; +#else /* LWSRV8 */ +spool_setup(iprtname, idictdir) +#endif /* LWSRV8 */ +char *iprtname; +char *idictdir; +{ +#ifndef LWSRV8 + int errs; + + tracefile = itracefile; +#endif /* not LWSRV8 */ + prtname = iprtname; + dictdir = idictdir; + + if (prtname == NULL) + prtname = "unknown"; + +#ifndef LWSRV8 + if (fontfile == NULL || !LoadFontList(fontfile)) { + fprintf(stderr,"lwsrv: simple: Bad FontList\n"); + return(FALSE); + } + fprintf(stderr,"lwsrv: simple: Font list from file %s\n",fontfile); + scandicts(dictdir); /* scan for dictionary files */ + return(TRUE); +#endif /* not LWSRV8 */ + +} + +/* + * validate/invalidate a token type for use + * +*/ +private void +validate_token_type(toktype) +int toktype; +{ + register struct atoken *tp; +#ifdef LWSRV8 + register int i; +#endif /* LWSRV8 */ + +#ifndef LWSRV8 + for (tp = toktbl; tp->token != NULL; tp++) +#else /* LWSRV8 */ + i = sizeof(toktbl) / sizeof(struct atoken); + for (tp = toktbl; i > 0; tp++, i--) +#endif /* LWSRV8 */ + if ((tp->tokval & TOK_VAL_MASK) == toktype) + tp->tokval &= (~TOK_INVALID); +} + +private void +invalidate_token_type(toktype) +int toktype; +{ + register struct atoken *tp; +#ifdef LWSRV8 + register int i; +#endif /* LWSRV8 */ + + /* can't invalidate these */ + if (toktype == TOK_UNK || toktype == TOK_EOF) + return; +#ifndef LWSRV8 + for (tp = toktbl; tp->token != NULL; tp++) +#else /* LWSRV8 */ + i = sizeof(toktbl) / sizeof(struct atoken); + for (tp = toktbl; i > 0; tp++, i--) +#endif /* LWSRV8 */ + if ((tp->tokval & TOK_VAL_MASK) == toktype) + tp->tokval |= TOK_INVALID; +} + +/* + * scan "str" for a token and return the token type. + * set ptr to the position after the token + * + */ + +private int +#ifndef LWSRV8 +tokval(str,ptr) +#else /* LWSRV8 */ +tokval(str, ptr, changeecho) +int *changeecho; +#endif /* LWSRV8 */ +char *str, **ptr; +{ + register char *p; +#ifdef LWSRV8 + register int i, low, high, cmp; +#endif /* LWSRV8 */ + register struct atoken *tp; + +#ifdef LWSRV8 + *changeecho = ECHO_UNCHANGED; +#endif /* LWSRV8 */ +#ifdef PASS_THRU + /* do we really want to process these tokens? */ +#ifndef LWSRV8 + if (simple_pass_thru == TRUE) +#else /* LWSRV8 */ + if (simple_pass_thru) +#endif /* LWSRV8 */ + return(TOK_UNK); +#endif PASS_THRU + /* all tokens start with "%?" or "%%" or "%!" */ + if (str[0] != '%') + return(TOK_UNK); + if (str[1] != '?' && str[1] != '%' && str[1] != '!') + return(TOK_UNK); + +#ifndef LWSRV8 + for (tp = toktbl; tp->token != NULL; tp++) { /* locate token value */ + if (tp->tokval & TOK_INVALID) + continue; + if (strncmp(str,tp->token,tp->toklen) == 0) { +#else /* LWSRV8 */ + /* binary search the token table */ + low = 0; + high = (sizeof(toktbl) / sizeof(struct atoken)) - 1; + while (low <= high) { + tp = toktbl + (i = (low + high) / 2); + if ((cmp = strncmp(str,tp->token,tp->toklen)) == 0) { + if (tp->tokval & TOK_INVALID) + return(TOK_UNK); +#endif /* LWSRV8 */ + p = &str[tp->toklen]; + if (*p == ':') /* skip ':' */ + p++; + /* skip leading white space */ + while (*p != '\0' && (*p == ' ' || *p == '\t')) + p++; + *ptr = p; /* set tokstr */ +#ifdef LWSRV8 + /* strip trailing white space */ + p += (i = strlen(p)) > 0 ? i - 1 : 0; + while (p >= *ptr && (*p == ' ' || *p == '\t')) + *p-- = 0; + *changeecho = tp->changeecho; +#endif /* LWSRV8 */ + return(tp->tokval); /* and return the value */ + } +#ifdef LWSRV8 + if (cmp < 0) + high = i - 1; + else + low = i + 1; +#endif /* LWSRV8 */ + } + return(TOK_UNK); +} + +#ifdef LWSRV8 +private void +dumpout(str, len, eol) +register byte *str; +register int len; +int eol; +{ + if (tracing || echo) + dumpoutfile(str, len, eol); + if (procsetfile) + dumpprocsetfile(str, len, eol); + if (qfp) + dumpquery(str, len, eol); +} + +private void +dumpone(c) +register unsigned c; +{ + if (tracing || echo) { + /* is it standard ascii? */ + if (needquote && (!isascii(c) || (!isprint(c) && !isspace(c)))) + fprintf(outfile, "\\%03o", c); + else + putc(c, outfile); + } + if (procsetfile) /* never quote */ + putc(c, procsetfile); + if (qfp) + putc(c, qfp); +} + +private void +dumpoutfile(str, len, eol) +register byte *str; +register int len; +int eol; +{ + register unsigned c; + + if (needquote) { + while (len-- > 0) { + c = *str++; + /* is it standard ascii? */ + if (!isascii(c) || (!isprint(c) && !isspace(c))) + fprintf(outfile, "\\%03o", c); + else + putc(c, outfile); + } + } else { + fwrite(str, 1, len, outfile); + } + if (eol) + putc(eol, outfile); +} + +private void +dumpprocsetfile(str, len, eol) +byte *str; +int len; +int eol; +{ + fwrite(str, 1, len, procsetfile); + if (eol) + putc(eol, procsetfile); +} + +private void +dumpquery(str, len, eol) +byte *str; +int len; +int eol; +{ + fwrite(str, 1, len, qfp); + if (eol) + putc(eol, qfp); +} + +private int +nextline(pf) +register PFILE *pf; +{ + register int c; + + while ((c = p_getc(pf)) != EOF) { + dumpone(c); + if (c == '\r' || c == '\n') + return(c); + } + return(c); +} + +#else /* LWSRV8 */ + +private void +dumpout(str,len) +char *str; +int len; +{ + int i,c; + + for (i=0; i < len; i++) { + c = str[i]; + if (crtolf && c == '\r') + c = '\n'; + if (needquote) { + if (isascii(c) && (isspace(c) || !iscntrl(c))) /* is it standard ascii? */ + putc(c,outfile); /* yes... so echo */ + else /* otherwise echo \ddd */ + fprintf(outfile,"\\%03o",(unsigned char) c); + } else + putc(c,outfile); + if (procsetfile) /* never quote */ + putc(c, procsetfile); + } + putc('\n',outfile); + if (procsetfile) + putc('\n', procsetfile); +} +#endif /* LWSRV8 */ + +/* + * int scantoken(PFILE *pf,int echo,char *tokstr) [lwsrv] + * int scantoken(PFILE *pf, char *tokstr) [lwsrv8] + * + * Read characters from the PAP connection specified by pf. + * Echo characters to stdout if echo is TRUE. Return token value + * and tokstr pointing past token characters. + * + * We assume all tokens begin with "%" + * + */ + +#ifdef LWSRV8 +private int +scantoken(pf, tptr) +register PFILE *pf; +char **tptr; +{ + register byte *cp; + register int i, c, tv; + int changeecho; +#ifdef LBUFFER + byte lbuffer[LBUFFER]; +#endif LBUFFER; +#ifdef PAGECOUNT + extern int pcopies; +#endif PAGECOUNT + + c = !EOF; + while (c != EOF) { + switch(c = p_getc(pf)) { + case EOF: + break; + case '%': + cp = tokstr; + *cp++ = '%'; + toklen = 0; + for ( ; ; ) { + /* + * Either the line is too long or there is a non-ascii character, + * both mean it can't be a real DSC comment line, so flush. + */ + if (++toklen >= MAXTOKSTR || !isascii(c)) { + dumpout(tokstr, toklen, 0); + c = nextline(pf); + break; + } + if ((c = p_getc(pf)) == EOF) { + dumpout(tokstr, toklen, 0); + break; + } + if (c == '\r' || c == '\n') { /* end of line */ + *cp = 0; + tv = tokval(tokstr, tptr, &changeecho); + tokeol = crtolf ? '\n' : c; + if (changeecho == ECHO_OFF) + echo = FALSE; + if (changeecho != ECHO_DROP) + dumpout(tokstr, toklen, tokeol); + if (changeecho == ECHO_ON) + echo = TRUE; + if (tv != TOK_UNK) + return(tv); + break; + } + *cp++ = c; + } + break; + case '\r': + case '\n': + dumpone(c); + break; + default: +#ifdef PAGECOUNT + cp = lbuffer; + i = 0; + tokeol = 0; + for ( ; ; ) { + if (c != '\r' && c != '\n') { + *cp++ = c; + i++; + } else + tokeol = crtolf ? '\n' : c; + if (i >= (LBUFFER - 1) || tokeol) { + *cp = 0; + if (strncmp(lbuffer, "userdict /#copies ", 18) == 0) + pcopies = atoi(&lbuffer[18]); + dumpout(lbuffer, i, tokeol); + if (!tokeol) + c = nextline(pf); + break; + } + if ((c = p_getc(pf)) == EOF) { + dumpout(lbuffer, i, 0); + break; + } + } +#else PAGECOUNT + dumpone(c); + c = nextline(pf); +#endif PAGECOUNT + break; + } + } + + /* found EOF */ + return(TOK_EOF); /* return now */ +} + +#else /* LWSRV8 */ + +#define MAXTOKSTR 1024 + +private int +scantoken(pf,echo,tptr) +PFILE *pf; +int echo; +char **tptr; +{ + static char tokstr[MAXTOKSTR]; + int atstart,i,c,tv,maybetoken; +#ifdef LBUFFER + static char lbuffer[LBUFFER]; + int l; +#endif LBUFFER; +#ifdef PAGECOUNT + extern int pcopies; +#endif PAGECOUNT + + i = 0; + atstart = TRUE; + maybetoken = FALSE; +#ifdef LBUFFER + l = 0; +#endif LBUFFER; + while ((c = p_getc(pf)) != EOF) { + if (c == '%' && atstart) + maybetoken = TRUE; +#ifdef DONT_PARSE + if (dont_parse) + maybetoken = FALSE; +#endif DONT_PARSE + atstart = (c == '\r' || c == '\n') ? TRUE : FALSE; + if (maybetoken) { + if (atstart) { /* last char is cr or lf */ + tokstr[i] = '\0'; /* tie off token */ + if ((tv = tokval(tokstr,tptr)) != TOK_UNK) { + if (tracefile != NULL) + dumpout(tokstr,i); + return(tv); + } else { + if ((tracefile != NULL) || echo) + dumpout(tokstr, i); + if (procsetfile != NULL) + fprintf(procsetfile, "%s%c", tokstr, crtolf ? c : '\n'); + } + i = 0; + maybetoken = FALSE; + continue; /* skip everything else */ + } + if (i < MAXTOKSTR) + tokstr[i++] = c; + continue; + } + /* papif handles it right, but others like transcript don't, so... */ + if ((tracefile != NULL) || echo) { /* do we want to echo this? */ + /* papif handles it right, but others like transcript don't, so... */ + /* make it a compilable option */ + if (crtolf && c == '\r') + c = '\n'; + if (needquote) { + if (isascii(c) && (isspace(c) || !iscntrl(c))) { /* standard ascii? */ + putc(c,outfile); /* yes... so echo */ +#ifdef LBUFFER + if (l < LBUFFER) + lbuffer[l++] = c; +#endif LBUFFER + } else { /* otherwise echo \ddd */ + fprintf(outfile,"\\%03o",(unsigned char) c); +#ifdef LBUFFER + if (l < LBUFFER) + lbuffer[l++] = '*'; +#endif LBUFFER + } + } else { + putc(c,outfile); +#ifdef LBUFFER + if (l < LBUFFER) + lbuffer[l++] = c; +#endif LBUFFER + } + } + if (crtolf && c == '\r') + c = '\n'; + if (procsetfile) + putc(c, procsetfile); +#ifdef LBUFFER + if (atstart) { +#ifdef PAGECOUNT + if (strncmp(lbuffer, "userdict /#copies ", 18) == 0) + pcopies = atoi(&lbuffer[18]); +#endif PAGECOUNT + l = 0; + } +#endif LBUFFER + } + + /* found EOF */ + + if ((tracefile != NULL) || echo) + putc('\n',outfile); /* yes... echo eol */ + if (procsetfile) + putc('\n', procsetfile); + return(TOK_EOF); /* return now */ +} +#endif /* LWSRV8 */ + +#ifdef LWSRV8 +/* + * Remove leading left and trailing right parenthesis + * (now part of LaserWriter 8.0 %%For: and %%Title: fields). + * + */ + +private char * +unparen(str) +char *str; +{ + register char *cp; + + if (*str != '(' || *(cp = str + strlen(str) - 1) != ')') + return(str); + *cp = 0; + return(str + 1); +} +#endif /* LWSRV8 */ + +/* + * handle an incoming job + * + */ + +#ifdef LWSRV8 +int +getjob(pf,of,actualprintout,doeof) +PFILE *pf; +FILE *of; +int *actualprintout,*doeof; +{ + char *ts,querybuf[MAXTOKSTR]; + char procname[256]; + int ltokval,tokval; + DictList *dl = NULL; /* current dictionary */ + DictList *dlnew = NULL; /* new dictionary */ + char buf[256]; + register void *vp; +#ifdef PROCSET_PATCH + int patchprocset = FALSE; +#endif PROCSET_PATCH + char *resp, *queryp; + char prefix[80], status[256]; + extern int pagecount; +#ifdef PAGECOUNT + extern int pcopies; +#endif PAGECOUNT + + outfile = of; + p_clreof(pf); /* clear EOF indicator */ + echo = TRUE; + *doeof = TRUE; + if (nodsc) { + fprintf(outfile, "%%!\n%% PostScript Document, but non-conformant\n"); + fprintf(outfile, "%% so psrv is not invoked\n"); + } + sprintf(status,"receiving job for %s", prtname); + + clearstack(); + while ((tokval = scantoken(pf, &ts)) != TOK_EOF) { + switch (tokval) { + case TOK_DROP: + break; + case TOK_BPS: + /* really smart would install into dict */ + /* remember to flush off "appledict" part */ + push(tokval); /* remember where */ + stripspaces(ts); /* clear off extra spaces */ + strcpy(procname, ts); + if ((dl = GetProcSet(procname)) != NULL) { + fprintf(stderr, "%s: simple: procset %s already known\n", myname, + procname); + break; + } + if (!capture) { +#ifdef PROCSET_PATCH + if (!patchprocset) { + echo = TRUE; + if (!tracing) + dumpoutfile(tokstr, toklen, tokeol); + } + patchprocset = FALSE; +#else PROCSET_PATCH + echo = TRUE; /* pass unknown procsets through */ + if (!tracing) + dumpoutfile(tokstr, toklen, tokeol); +#endif PROCSET_PATCH + break; + } + sleep(1); /* raise odds of unique timestamp */ +#ifdef xenix5 + sprintf(tmpstr, "%s/Found.%d",dictdir,time(0)); +#else xenix5 + sprintf(tmpstr, "%s/FoundProcSet.%d",dictdir,time(0)); +#endif xenix5 + fprintf(stderr, "%s: simple: Saving to %s BeginProcSet: %s\n", + myname, tmpstr, procname); + if (procsetfile != NULL) { + fprintf(stderr, "%s: simple: Already logging prep file!", myname); + break; + } + if ((procsetfile = fopen(tmpstr, "w+")) == NULL) + perror(tmpstr); + else { + dlnew = dl = CreateDict(); + dl->ad_fn = strdup(tmpstr+strlen(dictdir)+1); + dl->ad_sent = FALSE; + dumpprocsetfile(tokstr, toklen, tokeol); + } + /* copy from BPS to end into file or memory */ + /* tag given is new proc set */ + break; + case TOK_EPS: + if (pop() != TOK_BPS) { + fprintf(stderr,"%s: simple: Framing error on Begin/End Proc Set\n", + myname); + } + if (procsetfile) /* if remember file */ + fclose(procsetfile); /* close outfile */ + procsetfile = NULL; + if (dlnew) { + newdictionary(strdup(procname), dlnew); + dlnew = NULL; /* close off */ + } + if (dl && !dl->ad_sent) { + fprintf(stderr, + ((adobe_dsc2_conformant) ? + "%s: simple: dsc2: document supplied procset %s = %s\n" : + "%s: simple: non-dsc2: Including ProcSet %s = %s\n"), + myname, procname,dl->ad_fn); + fprintf(outfile,"%% ** Procset From File: %s **\n",dl->ad_fn); + ListProcSet(dl->ad_fn, dictdir, outfile, 0); + fprintf(outfile,"%% ** End of ProcSet **\n"); + /* only suppress second download of a procset if an AppleDict */ + if (strncmp("\"(AppleDict md)\"", procname, 16) == 0) + dl->ad_sent = TRUE; + } + dl = NULL; /* Close off */ + break; + case TOK_BEGIN: + push(tokval); + break; + case TOK_END: + if (pop() != TOK_BEGIN) + fprintf(stderr, "%s: simple: Framing error on TOK_BEGIN/END\n", myname); + break; + case TOK_BEGINR: + push(tokval); /* remember last token value */ + break; + case TOK_ENDR: + if (pop() != TOK_BEGINR) + fprintf(stderr,"%s: simple: Stack Frame error on TOK_BEGINR/ENDR\n", + myname); + strcat(ts, newline); + p_write(pf,ts,strlen(ts),FALSE); + break; +#ifdef PROCSET_PATCH + case TOK_PATCH: + patchprocset = TRUE; /* also skip next procset */ + break; +#endif PROCSET_PATCH + case TOK_PSA: + { + register int i; + + i = strlen(ts) - QUERYSTRLEN; + query3 = FALSE; + if (*actualprintout < 0) + *actualprintout = 0; + /* + * If we have !PS-Adobe x.x Query, then leave echo off and see if it + * is level 3 or higher. If not a query, turn on echo and output + * the line. + */ + if (i > 0 && strcmp(ts + i, querystr) == 0) /* Query */ + query3 = (atoi(ts) >= 3); + else { + echo = TRUE; + if (!tracing) + dumpoutfile(tokstr, toklen, tokeol); + } + break; + } + case TOK_TEOF: + *doeof = echo; + query3 = FALSE; + echo = TRUE; + break; + case TOK_BFOQ: /* BeginFontQuery */ + strcpy(querybuf,ts); /* remember arguments */ + /* drop through */ + case TOK_BFLQ: /* BeginFontListQuery */ + if (query3 && thequery) { + if ((vp = SearchKVTree(thequery, "font", strcmp)) == NULL) + logquery(); + } else + vp = NULL; + push(tokval); + strcpy(querybuf,ts); /* remember arguments */ + break; + case TOK_BFEQ: /* BeginFeatureQuery */ + if (query3 && thequery) { + strcpy(buf, "FeatureQuery\t"); + strcat(buf, ts); + if ((vp = SearchKVTree(thequery, buf, strcmp)) == NULL) + logquery(); + } else + vp = NULL; + push(tokval); + break; + case TOK_BVQ: /* BeginVMStatusQuery */ + if (query3 && thequery) { + strcpy(buf, "VMStatusQuery\t"); + strcat(buf, ts); + if ((vp = SearchKVTree(thequery, buf, strcmp)) == NULL) + logquery(); + } else + vp = NULL; + push(tokval); + break; + case TOK_BPRQ: /* BeginPrinterQuery */ + if (query3 && thequery) { + strcpy(buf, "PrinterQuery\t"); + strcat(buf, ts); + if ((vp = SearchKVTree(thequery, buf, strcmp)) == NULL) + logquery(); + } else + vp = NULL; + push(tokval); + break; + case TOK_BQU: /* BeginQuery */ + if (query3 && thequery) { + strcpy(buf, "Query\t"); + strcat(buf, ts); + if ((vp = SearchKVTree(thequery, buf, strcmp)) == NULL) + logquery(); + } else + vp = NULL; + push(tokval); + break; + case TOK_BFIQ: /* BeginFileQuery */ + if (query3 && thequery) { + if ((vp = SearchKVTree(thequery, "file", strcmp)) == NULL) + logquery(); + } else + vp = NULL; + push(tokval); + strcpy(querybuf,ts); /* remember arguments */ + break; + case TOK_BREQ: /* BeginResourceQuery */ + if (query3 && thequery) { + strcpy(querybuf, ts); + queryp = querybuf; + if (resp = nextoken(&queryp)) { + if ((vp = SearchKVTree(thequery, resp, strcmp)) == NULL) + logquery(); + } else + vp = NULL; + } else + vp = NULL; + push(tokval); + break; + case TOK_BRLQ: /* BeginResourceListQuery */ + if (query3 && thequery) { + if ((vp = SearchKVTree(thequery, ts, strcmp)) == NULL) + logquery(); + } else + vp = NULL; + push(tokval); + strcpy(querybuf,ts); /* remember arguments */ + break; + case TOK_BPSQ: /* BeginProcSetQuery */ + case TOK_ADV: /* appledict version */ + strcpy(querybuf,ts); /* remember arguments */ + /* drop through */ + case TOK_FLS: /* fontList */ + push(tokval); + break; + case TOK_FOR: /* For: */ + setusername(unparen(ts)); + break; + case TOK_TIT: /* Title: */ + setjobname(unparen(ts)); + break; + case TOK_ENC: /* EndComments */ + break; + case TOK_IPS: + if (!adobe_dsc2_conformant) + break; + stripspaces(ts); /* strip extra spaces */ + if ((dl = GetProcSet(ts)) != NULL) { + fprintf(stderr,"%s: simple: Including ProcSet %s = %s\n", + myname,ts,dl->ad_fn); + fprintf(outfile,"%% ** Include Procset From File: %s **\n",dl->ad_fn); + ListProcSet(dl->ad_fn, dictdir, outfile, 1); + fprintf(outfile,"%% ** End of ProcSet **\n"); + dl = NULL; /* close off */ + } else + fprintf(stderr,"%s: simple: Unknown ProcSet file for '%s'\n",myname,ts); + break; + case TOK_PAGE: /* Page: */ + queryp = ts; + if (nextoken(&queryp)) { + if (resp = nextoken(&queryp)) { /* got 2nd token */ + if (pagecount > 0) + sprintf(status,"receiving page %s of %d for %s", + resp, pagecount, prtname); + else + sprintf(status,"receiving page %s for %s", resp, prtname); + } else { /* no 2nd token, so use 1st */ + resp = nextoken(&ts); + if (pagecount > 0) + sprintf(status,"receiving page %s of %d for %s", + resp, pagecount, prtname); + else + sprintf(status,"receiving page %s for %s", resp, prtname); + } + } else /* no page count */ + sprintf(status,"receiving a page for %s", prtname); + NewStatus(status); /* update status */ + *actualprintout = 1; + break; + case TOK_PGS: /* Pages: */ + if (isdigit(*ts)) + pagecount = atoi(ts); + break; + case TOK_EPSQ: /* EndProcSetQuery */ + case TOK_EFLQ: /* EndFontListQuery */ + case TOK_EFOQ: /* EndFontQuery */ + case TOK_EFEQ: /* EndFeatureQuery */ + case TOK_EVQ: /* EndVMStatusQuery */ + case TOK_EPRQ: /* EndPrinterQuery */ + case TOK_EQU: /* EndQuery */ + case TOK_EFIQ: /* EndFileQuery */ + case TOK_EREQ: /* EndResourceQuery */ + case TOK_ERLQ: /* EndResourceListQuery */ + case TOK_ENDOLD: /* End */ + if (qfp) { + fclose(qfp); + qfp = NULL; + } + ltokval = pop(); + switch (ltokval) { /* according to last found token */ + case TOK_BPSQ: + case TOK_ADV: /* if last token was AppleDict */ + SendVAck(pf,querybuf); /* then handle appledict */ + break; + case TOK_BFLQ: + if (vp) { + SendResourceList(pf, (List *)vp, "/"); + break; + } + /* drop through for default action */ + case TOK_FLS: /* if last token was FontList */ + SendFontList(pf); /* then send out the fontlist */ + break; + case TOK_BFOQ: + if (vp) { + SendMatchedResources(pf, (List *)vp, "/", querybuf); + break; + } + strcat(ts, newline); + p_write(pf,ts,strlen(ts),FALSE); + break; + case TOK_BFEQ: + case TOK_BVQ: + case TOK_BPRQ: + case TOK_BQU: + if (vp) { + SendQueryResponse(pf, (List *)vp); + break; + } + strcat(ts, newline); + p_write(pf,ts,strlen(ts),FALSE); + break; + case TOK_BFIQ: + if (vp) { + SendMatchedResources(pf, (List *)vp, NULL, querybuf); + break; + } + strcat(ts, newline); + p_write(pf,ts,strlen(ts),FALSE); + break; + case TOK_BREQ: + if (vp) { + strcpy(prefix, resp); + if (islower(*prefix)) + *prefix = toupper(*prefix); + strcat(prefix, " "); + if (strcmp(resp, "procset") == 0) + SendMatchedKVTree(pf, (KVTree **)vp, prefix, queryp); + else { + if (strcmp(resp, "font") == 0) + strcat(prefix, "/"); + SendMatchedResources(pf, (List *)vp, prefix, queryp); + } + break; + } + strcat(ts, newline); + p_write(pf,ts,strlen(ts),FALSE); + break; + case TOK_BRLQ: + if (vp) { + strcpy(prefix, querybuf); + if (islower(*prefix)) + *prefix = toupper(*prefix); + strcat(prefix, " "); + if (strcmp(querybuf, "procset") == 0) + SendResourceKVTree(pf, (KVTree **)vp, prefix); + else { + if (strcmp(querybuf, "font") == 0) + strcat(prefix, "/"); + SendResourceList(pf, (List *)vp, prefix); + } + break; + } + strcat(ts, newline); + p_write(pf,ts,strlen(ts),FALSE); + break; + default: + fprintf(stderr, "%s: simple: Stack framing error: got %d\n", + myname, ltokval); + break; + } + break; + } + } + if (!isstackempty()) { + fprintf(stderr, "%s: simple: EOF and tokens still on stack!\n", myname); + dumpstack(); + } + if (procsetfile) { + fclose(procsetfile); + procsetfile = NULL; + if (dlnew) { + unlink(dlnew->ad_fn); + free(dlnew->ad_fn); /* free fn */ + free(dlnew); + } + } + if (p_isopn(pf)) /* if connection is still open */ + p_write(pf,"",0,TRUE); /* send EOF for this job */ + return(p_isopn(pf)); /* return open state */ +} + +private void +logquery() +{ + if (!queryfile) + return; + if (qfp = fopen(queryfile, "a")) + dumpquery(tokstr, toklen, tokeol); +} + +#else /* LWSRV8 */ + +int +getjob(pf,of) +PFILE *pf; +FILE *of; +{ + char *ts,adictver[80]; + int ltokval,tokval,echo; + DictList *dl = NULL; /* current dictionary */ + DictList *dlnew = NULL; /* new dictionary */ + extern int capture; +#ifdef PAGECOUNT + extern int pagecount; + extern int pcopies; +#endif PAGECOUNT +#ifdef PROCSET_PATCH + int patchprocset = FALSE; +#endif PROCSET_PATCH + + outfile = of; + p_clreof(pf); /* clear EOF indicator */ + echo = TRUE; + if (nodsc) { + fprintf(outfile, "%%!\n%% PostScript Document, but non-conformant\n"); + fprintf(outfile, "%% so psrv is not invoked\n"); + } + + clearstack(); + while ((tokval = scantoken(pf,echo,&ts)) != TOK_EOF) { + switch (tokval) { + case TOK_DROP: + break; + case TOK_BPS: + /* really smart would install into dict */ + /* remember to flush off "appledict" part */ + push(tokval); /* remember where */ + echo = FALSE; + stripspaces(ts); /* clear off extra spaces */ + if ((dl = GetProcSet(ts)) != NULL) { + fprintf(stderr, "lwsrv: simple: procset %s already known\n", ts); + break; + } + if (!capture) { +#ifdef PROCSET_PATCH + if (!patchprocset) + echo = TRUE; + patchprocset = FALSE; +#else PROCSET_PATCH + echo = TRUE; /* pass unknown procsets through */ +#endif PROCSET_PATCH + break; + } + sleep(1); /* raise odds of unique timestamp */ +#ifdef xenix5 + sprintf(tmpstr, "%s/Found.%d",dictdir,time(0)); +#else xenix5 + sprintf(tmpstr, "%s/FoundProcSet.%d",dictdir,time(0)); +#endif xenix5 + fprintf(stderr, "lwsrv: simple: Saving to %s BeginProcSet: %s\n", + tmpstr, ts); + if (procsetfile != NULL) { + fprintf(stderr, "lwsrv: simple: Already logging prep file!"); + break; + } + if ((procsetfile = fopen(tmpstr, "w+")) == NULL) { + procsetfile = NULL; + perror(tmpstr); + } else { + dlnew = dl = (DictList *)malloc(sizeof(DictList)); + dl->ad_ver = strdup(ts); + dl->ad_fn = strdup(tmpstr+strlen(dictdir)+1); + dl->ad_next = NULL; + dl->ad_sent = FALSE; + fprintf(procsetfile, "%%%%BeginProcSet: %s\n", ts); + } + /* copy from BPS to end into file or memory */ + /* tag given is new proc set */ + break; + case TOK_EPS: + if (pop() != TOK_BPS) { + fprintf(stderr,"lwsrv: simple: Framing error on Begin/End Proc Set\n"); + } + if (procsetfile) { /* if remember file */ + fprintf(procsetfile, "%%%%EndProcSet\n"); + fclose(procsetfile); /* close outfile */ + } + procsetfile = NULL; + echo = TRUE; + if (dlnew) { + newdictionary(dlnew); + dlnew = NULL; /* close off */ + } + if (dl && !dl->ad_sent) { + fprintf(stderr, + ((adobe_dsc2_conformant) ? + "lwsrv: simple: dsc2: document supplied procset %s = %s\n" : + "lwsrv: simple: non-dsc2: Including ProcSet %s = %s\n"), + dl->ad_ver,dl->ad_fn); + fprintf(outfile,"%% ** Procset From File: %s **\n",dl->ad_fn); + ListProcSet(dl->ad_fn, dictdir, outfile, 0); + fprintf(outfile,"%% ** End of ProcSet **\n"); + /* only suppress second download of a procset if an AppleDict */ + if (strncmp("\"(AppleDict md)\"", dl->ad_ver, 16) == 0) + dl->ad_sent = TRUE; + } + dl = NULL; /* Close off */ + break; + case TOK_BEGIN: + push(tokval); + echo = FALSE; + break; + case TOK_END: + if (pop() != TOK_BEGIN) + fprintf(stderr, "lwsrv: simple: Framing error on TOK_BEGIN/END\n"); + echo = TRUE; + break; + case TOK_BEGINR: + push(tokval); /* remember last token value */ + echo = FALSE; + break; + case TOK_ENDR: + if (pop() != TOK_BEGINR) + fprintf(stderr,"lwsrv: simple: Stack Frame error on TOK_BEGINR/ENDR\n"); + echo = TRUE; /* we are now echoing */ + p_write(pf,ts,strlen(ts),FALSE); + break; +#ifdef PROCSET_PATCH + case TOK_PATCH: + echo = FALSE; /* until EOF or valid section */ + patchprocset = TRUE; /* also skip next procset */ + break; +#endif PROCSET_PATCH + case TOK_BPSQ: + case TOK_BFLQ: + case TOK_FLS: /* fontList */ + case TOK_ADV: /* appledict version */ + push(tokval); + if (tracefile == NULL) /* unless tracing there is no */ + echo = FALSE; /* echoing between token and End */ + if (tokval == TOK_ADV || /* is this appledict query? */ + tokval == TOK_BPSQ) + strcpy(adictver,ts); /* yes, remember appledict version */ + break; + case TOK_FOR: /* For: */ + setusername(ts); + break; + case TOK_TIT: /* Title: */ + setjobname(ts); + break; + case TOK_ENC: /* EndComments */ + break; + case TOK_IPS: + if (!adobe_dsc2_conformant) + break; + stripspaces(ts); /* strip extra spaces */ + if ((dl = GetProcSet(ts)) != NULL) { + fprintf(stderr,"lwsrv: simple: Including ProcSet %s = %s\n", + dl->ad_ver,dl->ad_fn); + fprintf(outfile,"%% ** Include Procset From File: %s **\n",dl->ad_fn); + ListProcSet(dl->ad_fn, dictdir, outfile, 1); + fprintf(outfile,"%% ** End of ProcSet **\n"); + dl = NULL; /* close off */ + } else + fprintf(stderr,"lwsrv: simple: Unknown ProcSet file for '%s'\n",ts); + break; +#ifdef PAGECOUNT + case TOK_PGS: /* Pages: */ + if (isdigit(*ts)) + pagecount = atoi(ts); + break; +#endif PAGECOUNT + case TOK_EFLQ: + case TOK_EPSQ: + case TOK_ENDOLD: /* End */ + echo = TRUE; /* we are now echoing */ + ltokval = pop(); + switch (ltokval) { /* according to last found token */ + case TOK_BPSQ: + case TOK_ADV: /* if last token was AppleDict */ + SendVAck(pf,adictver); /* then handle appledict */ + break; + case TOK_BFLQ: + case TOK_FLS: /* if last token was FontList */ + SendFontList(pf); /* then send out the fontlist */ + break; + default: + fprintf(stderr, "lwsrv: simple: Stack framing error: got %d\n", + ltokval); + break; + } + } + } + if (!isstackempty()) { + fprintf(stderr, "lwsrv: simple: EOF and tokens still on stack!\n"); + dumpstack(); + } + if (procsetfile) { + fclose(procsetfile); + procsetfile = NULL; + if (dlnew) { + unlink(dlnew->ad_fn); + free(dlnew->ad_fn); /* free fn */ + free(dlnew); + } + } + if (p_isopn(pf)) /* if connection is still open */ + p_write(pf,"",0,TRUE); /* send EOF for this job */ + return(p_isopn(pf)); /* return open state */ +} +#endif /* LWSRV8 */ + +#define MD_UNK "0\n" /* Mac dictionary not loaded */ +#define MD_AVOK "1\n" /* AppleDict version ok (av) */ +#define MD_AVBAD "2\n" /* AppleDict version not ok */ + +#ifdef LWSRV8 +private char procset7[] = "\"(AppleDict md)\" 71"; +#endif /* LWSRV8 */ + +/* + * answer a query as to whether a particular procset is known or not + * + */ + +#ifdef LWSRV8 +private void +SendVAck(pf,ver) +PFILE *pf; +char *ver; +{ + DictList *dl; + char status[80]; + + stripspaces(ver); /* strip any extra spaces */ + + if (!capture && strncmp(ver, procset7, sizeof(procset7) - 1) == 0) { + p_write(pf,MD_AVOK,2,FALSE); /* respond yes to system 7 */ + fprintf(stderr,"%s: simple: Responding Yes to ProcSet %s\n", myname, ver); + return; + } + + dl = GetProcSet(ver); /* find any procset */ + + if (tracing || dl == (DictList *)NULL) { + p_write(pf,MD_UNK,2,FALSE); /* if tracing download the dict */ + fprintf(stderr,"%s: simple: Receiving AppleDict version %s\n", myname, ver); + if (tracing) + fprintf(outfile,"%% %s: simple: Receiving AppleDict version %s\n", + myname, ver); + /* won't do much good unless single fork */ + sprintf(status,"Receiving AppleDict version #%s",ver); + NewStatus(status); + return; /* by pass the prepend */ + } else + p_write(pf,MD_AVOK,2,FALSE); /* otherwise we use local file */ + + if (!adobe_dsc2_conformant) { + if (dl != (DictList *) NULL) { + if (!tracing) { + /* won't do much good unless single fork */ + sprintf(status,"prepending AppleDict version #%s",ver); + NewStatus(status); + fprintf(stderr,"%s: simple: Using ProcSet %s = %s\n", + myname,ver,dl->ad_fn); + fprintf(outfile,"%% ** Prepending ProcSet from: %s **\n",dl->ad_fn); + ListProcSet(dl->ad_fn, dictdir, outfile, 1); /* prepend appledict */ + fprintf(outfile,"%% ** End of ProcSet **\n"); + dl->ad_sent = TRUE; + } + } else { + fprintf(stderr,"%s: simple: Unknown ProcSet file for '%s'\n",myname,ver); + } + } +} + +tracewrite(str, len) +char *str; +int len; +{ + if (len > 0) { + fprintf(outfile, "--%s=>", myname); + fwrite(str, 1, len, outfile); + } else + fprintf(outfile, "--%s=|EOF|\n", myname); +} + +#else /* LWSRV8 */ + +private void +SendVAck(pf,ver) +PFILE *pf; +char *ver; +{ + DictList *dl; + char status[80]; + + stripspaces(ver); /* strip any extra spaces */ + + dl = GetProcSet(ver); /* find any procset */ + + if (tracefile != NULL || dl == (DictList *)NULL) { + p_write(pf,MD_UNK,2,FALSE); /* if tracing download the dict */ + fprintf(stderr,"lwsrv: simple: Receiving AppleDict version %s\n",ver); + /* won't do much good unless single fork */ + sprintf(status,"Receiving AppleDict version #%s",ver); + NewStatus(status); + return; /* by pass the prepend */ + } else + p_write(pf,MD_AVOK,2,FALSE); /* otherwise we use local file */ + + if (!adobe_dsc2_conformant) { + if (dl != (DictList *) NULL) { + if (tracefile == NULL) { + /* won't do much good unless single fork */ + sprintf(status,"prepending AppleDict version #%s",ver); + NewStatus(status); + fprintf(stderr,"lwsrv: simple: Using ProcSet %s = %s\n",ver,dl->ad_fn); + fprintf(outfile,"%% ** Prepending ProcSet from: %s **\n",dl->ad_fn); + ListProcSet(dl->ad_fn, dictdir, outfile, 1); /* prepend appledict */ + fprintf(outfile,"%% ** End of ProcSet **\n"); + dl->ad_sent = TRUE; + } + } else { + fprintf(stderr,"lwsrv: simple: Unknown ProcSet file for '%s'\n",ver); + } + } +} +#endif /* LWSRV8 */ diff --git a/applications/lwsrv/spmisc.c b/applications/lwsrv/spmisc.c new file mode 100644 index 0000000..62a72f7 --- /dev/null +++ b/applications/lwsrv/spmisc.c @@ -0,0 +1,117 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/08/28 11:10:14 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/lwsrv/RCS/spmisc.c,v 2.2 1995/08/28 11:10:14 djh Rel djh $"; +static char revision[] = "$Revision: 2.2 $"; + +/* + * spmisc - UNIX AppleTalk spooling program: act as a laserwriter + * some misc. functions useful for spooler + * (some should be put in abmisc) + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * Created Sept 5, 1987 by cck from lwsrv. + * + * + */ + +#include +#include +#ifndef _TYPES +/* assume included by param.h */ +# include /* don't include types if param.h */ +#endif /* _TYPES */ /* is included - probs on some */ +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else /* USESTRINGDOTH */ +# include +#endif /* USESTRINGDOTH */ +#include "spmisc.h" + +extern char *myname; + +/* + * string input string of extra spaces and any leading and trailing ones + * (spaces defined as space or tab) + * + */ + +void +stripspaces(inp) +char *inp; +{ + char *p, *p2; + + for (p = inp; *p == ' ' && *p != '\0'; p++) /* strip leading */ + /* NULL */; + for (p2 = p; *p2 != '\0'; p2++) /* convert tabs to space */ + if (*p2 == '\t') + *p2 = ' '; + while (*p != '\0') + if (*p == ' ' && (*(p+1) == ' ' || *(p+1) == '\0')) + p++; + else + *inp++ = *p++; + *inp = '\0'; +} + +/* + * Simple stack for tokens + * + * + */ + +#define STKSIZ 10 +int mystack[STKSIZ]; +int sp = 0; + +boolean +push(val) +int val; +{ + mystack[sp++] = val; + if (sp >= STKSIZ) { + fprintf(stderr, "%s: stack push: STACK OVERFLOW!\n", myname); + sp = STKSIZ-1; + return(FALSE); + } + return(TRUE); +} + +int +pop() +{ + sp--; /* decrement stack pointer */ + if (sp < 0) { + fprintf(stderr, "%s: stack pop: STACK UNDERFLOW\n", myname); + sp = 0; + return(-1); + } + return(mystack[sp]); +} + +boolean +isstackempty() +{ + return(sp == 0); +} + +void +clearstack() +{ + sp = 0; +} + +void +dumpstack() +{ + while (sp--) { + fprintf(stderr, "%d\n", mystack[sp]); + } +} diff --git a/applications/lwsrv/spmisc.h b/applications/lwsrv/spmisc.h new file mode 100644 index 0000000..faffdf4 --- /dev/null +++ b/applications/lwsrv/spmisc.h @@ -0,0 +1,27 @@ +/* "$Author: djh $ $Date: 91/02/15 21:15:59 $" */ +/* "$Header: spmisc.h,v 2.1 91/02/15 21:15:59 djh Rel $" */ +/* "$Revision: 2.1 $" */ + +/* + * spmisc - UNIX AppleTalk spooling program: act as a laserwriter + * some misc. functions useful for spooler + * (some should be put in abmisc) + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Created sept 5, 1987 by cck from lwsrv + * +*/ + +void stripspaces(); /* void stripspaces(char *) */ +boolean push(); /* boolean push(int) */ +int pop(); +boolean isstackempty(); +void clearstack(); +void dumpstack(); +char *strdup(); /* char *strdup(char *) */ diff --git a/applications/lwsrv/whatiswhat b/applications/lwsrv/whatiswhat new file mode 100644 index 0000000..7eeb826 --- /dev/null +++ b/applications/lwsrv/whatiswhat @@ -0,0 +1,12 @@ +lwsrv.c - driver for lw spooler (semi-generic) +fontlist.c - simple fontlist handling +fontlist.h +papstream.c - pap streams code +papstream.h +procset.c - simple proc set handler +procset.h +simple.c - simple spooler +spmisc.c - auxillary routines +spmisc.h +LWFonts,LWPlusFonts - font coordination lists for LaserWriter + and LaserWriter Plus with no added fonts diff --git a/applications/makefile b/applications/makefile new file mode 100644 index 0000000..f240518 --- /dev/null +++ b/applications/makefile @@ -0,0 +1,34 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 14:00:15 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp + + +all: + (cd lwsrv; make) + (cd papif; make) + (cd aufs; make) + +install: + (cd lwsrv; make install) + (cd papif; make install) + (cd aufs; make install) + +clean: + -(cd lwsrv; make clean) + -(cd papif; make clean) + -(cd aufs; make clean) + +dist: + @cat todist + @(cd papif; make dist) + @(cd lwsrv; make dist) + @(cd aufs; make dist) + diff --git a/applications/papif/Makefile.m4 b/applications/papif/Makefile.m4 new file mode 100644 index 0000000..92bbe78 --- /dev/null +++ b/applications/papif/Makefile.m4 @@ -0,0 +1,80 @@ +CFLAGS=cflags() specialcflags() +LWFLAGS=lwflags() +I=includedir() +CAPLIB=libcap() +DESTDIR=capsrvrdestdir() + +# for other libraries (like BSD on hpux) +SLIB=libspecial() + +# See README file for notes about defines +# Valid: SFLOWQ=[1,2,3,4,5,6,7,8] +# Valid: IDLESTUFF, NO_STRUCT, NOACCT, CAPPRINTERS=location +PAPFLAGS=papflags() ifdef([capprinters],[-DCAPPRINTERS=]capprinters()) +PAPBANNER=papbanner() + +# USEVPRINTF - use vprintf in logging +ifdef([usevprintf],[],[#])VPRINTF=-DUSEVPRINTF +# If you have Transcript from Adobe for your laserWriter and want to +# print text files, uncomment the next line and set the location properly +ifdef([pstextloc],[WPSTEXT="-DPSTEXT=pstextloc()"], + [# WPSTEXT=-DPSTEXT=\"/usr/local/lib/ps/pstext\"]) + +# This is if you have transcript and and want page reversal if possible +ifdef([psrevloc],[WPSREVERSE=-DPSREVERSE=psrevloc()], + [# WPSREVERSE=-DPSREVERSE=\"/usr/local/lib/ps/psrv\"]) + +ifdef([columbia],[all: pstest ps8 ps7 papof],[all: papif papof]) + +papif: papif.o $(O) + ${CC} ${LFLAGS} -o papif papif.o $(O) $(CAPLIB) ${SLIB} + +papif.o: papif.c + ${CC} ${CFLAGS} ${VPRINTF} ${PAPBANNER} ${PAPFLAGS} ${LWFLAGS} \ + ${WPSTEXT} ${WPSREVERSE} -c papif.c + +ifdef([columbia],[ +pstest: papif.c ${LIBCAP} + ${CC} -O ${VPRINTF} -DIDLESTUFF -DSFLOWQ=1 papif.c -o pstest -lcap + +ps7: papif.c ${LIBCAP} + ${CC} -O ${VPRINTF} -DIDLESTUFF -DNO_STRUCT -DSFLOWQ=1 -DPSTEXT=\"/usr/local/lib/ps/pstext\" -DPSREVERSE=\"/usr/local/lib/ps/psrev\" papif.c -o ps7 -lcap + +ps8: papif ${LIBCAP} + cp papif ps8 + strip ps8 +]) + +ifelse(os,[hpux],[ +papof: + echo "papof doesn't compile under hpux, but you don't really" + echo "need it anyway" + +],[ +papof.o: papof.c + ${CC} -c ${CFLAGS} ${PAPBANNER} papof.c + +papof: papof.o + ${CC} ${LFLAGS} -o papof papof.o ${SLIB} + +]) +clean: + -rm -f papif papof *.o + +spotless: + -rm -f papif papof *.o *.orig Makefile makefile + +ifelse(os,[hpux],[ +install: papif papof + -strip papif papof + ifdef([sysvinstall],[install -f $(DESTDIR) papif], + [${INSTALLER} papif $(DESTDIR)]) +],[ +install: papif papof + -strip papif papof + ifdef([sysvinstall],[install -f $(DESTDIR) papif papof], + [${INSTALLER} papif papof $(DESTDIR)]) +]) + +dist: + @cat todist diff --git a/applications/papif/README b/applications/papif/README new file mode 100644 index 0000000..6ffba0b --- /dev/null +++ b/applications/papif/README @@ -0,0 +1,222 @@ +PAPIF notes and installation + +papif is the formatted text "input" filter for the BSD lpd spooling +system. (It is assumed you have a basic knowledge of how this works). +You should examine the papif manual page in cap/man/papif.8 before +proceeding. + +A sample entry for the /etc/printcap file is in printcap.samp1. + +See the section "LP under A/UX" below for directions on using papif on +A/UX. + +papif has been redesigned since CAP distribution 4. One of the +advantages is that its behavior is much closer to that of "pscomm" -- +the Adobe Transript filter for serial line LaserWriters. You should +be able to use papif in place of pscomm without ill effects in the +"psif" shell script. + +In addition, papif no longer forks off a process to deal with the +status information. papif will run in a single fork unless the pstext +or psrv filters are used. If the pstext or psrv filters are used, +papif will normally place these filters in front of itself (in that +order) unless the input is not from a disk file. Since papif must +read the beginning of the file, it is necessary to be able to backup +in the input stream. To do this when the input is not "rewindable", +papif will fork off a "" process in front of itself to +"virtually" rewind the input. Though papif could do this internally +for itself, it cannot do this for the pstext and psrv filters. When +the "" process is invoked, pstext and psrv are run from the +"" process and place between "" and papif. + +papif now "selects" on its input to keep it from blocking when the +input is from a pipe. This is suspected as being one of the main +causes of the "idle bug". + +Note: you may wish to reset the LaserWriter "timeout" variable. The +"timeout" value specifies the amount of time to wait for input before +issuing a "timeout" error. If you have page reversal on, this could +be more than the default of 30 seconds. You can change it to 300 +seconds with the following postcript code: + %! + serverdict begin 0 exittoserver + statusdict begin + defaulttimeouts pop 300 setdefaulttimouts +This should permanently install the value into the eeprom. + + +DEFINES/SETTABLE OPTIONS +------------------------ + +Defines are used to set the "standard" behavior of papif. Most of the +defines are not "hard" in that they may be modified via the use of +environment variables (see manual page). + +Defines are best set by changing m4.setup and reconfiguring your +makefiles. See [psrevloc], [pstextloc], [papflags], [papbanner] in +m4.setup. + +In the following, a binary option is set: + in defines by defining the specified option + +TRANSCRIPT COMPATIBLITY +----------------------- + +A number of options have been installed to make papif compatible with +Transcript filters and scripts. + +* BANNERFIRST, BANNERLAST and CHARGEBANNER: define in psbanner + +BANNERFIRST says to printer the banner first and BANNERLAST to +print it after all other printing. The default is neither. + +CHARGEBANNER is a papif specific binary option. If set, banner pages +will be charged to the user if accounting is being done. The default +is off. + +Note if you wish to define banner pages as on, you must have an +"output" filter that presents a ".banner" file in PostScript format. +CAP includes a filter "psof" that accomplishes this. You should also +be able to use the standard Transcript filter for this as well. + +* VERBOSELOG: default is always on, cannot be defined, but may be +modified via the environment. + +VERBOSELOG tells papif to be verbose in its logging + +* REVERSE: set psrevloc in m4.setup or define WPSREVERSE in the makefile + +REVERSE contains the location of the page reversal filter. This is a +standard part of Transcript. The default is none. + +* PSTEXT: set psrevloc in m4.setup or define WPSREVERSE in the makefile + +PSTEXT contains the location of the text filter that translates plain +text to PostScript. This is a standard part of Transcript. The +default is none. + +* TRANS3: defined in m4.features + +TranScript 3.0 compatibility. Assumes that psdman will be used +instead of pstext, and so it automatically gives psdman the required +-P printer argument. Also assumes that RUTGERS is not defined. + +* DUPLEXMODE: defined in m4.features + +support full-duplex printing. This code was written and tested with +an HP LaserJet IIIsi with a PostScript card. What it does is if papif is +given the -2 switch, or if the printer name ends in -dup* (e.g. lw-duplex, +lj-dup, lpr-dupsomething), it will add the following bit of PostScript code +to the prolog: + + statusdict begin true setduplexmode end + +OTHER NON Transcript OPTIONS +---------------------------- + +* CAPPRINTERS: defined in papflags, m4 macro: capprinters + +defines the name of the cap.printers file. The default is +/etc/cap.printers. + +* NO_STRUCT: defined in papflags + +PAPIF has a minimal understanding of the Adobe Systems Document +Structuring Conventions (to wit, it understands the "%%EOF" marker). +If this cause problems, you can turn it off by defining NO_STRUCT when +compiling (in papflags). + +* NOACCT: defined in papflags + +Setting this turns accounting off by default. + +* IDLESTUFF: defined in papflags + +Some sites have reported seeing an "idle" bug where papif doesn't do +anything while the LaserWriter is idle. You can define "IDLESTUFF" +when compiling papif to enable some code to get around this problem. +It is unclear if this option works properly anymore. +Note: with the new release of papif, this problem shouldn't be there +anymore, but it is included just in case. + +* SFLOWQ: defined in papflags +* RFLOWQ: defined in papflags + +PAPIF has a settable "Send Flow Quantum multiplier" called SFLOWQ that +tells them how many pkts it should be sending in a single write. You +should set this to 1 if you have problems with the FastPath or +LaserWriter dropping packets. The odds are that you want it set to 1. +The default is 8. + +RFLOWQ is the same thing but in the other direction. You can probably +leave it set to the default of 8 (the maximum). + +* ATPRESPONSETIMEOUT: may be defined in papflags + +This defines the ATP response cache timeout in ticks (1/4 second +intervals). (Note: zero is infinity: it never times out). This is +used to extend the time that a response from the LaserWriter may take. +The default is 2 minutes. + +* WATCHTIME: may be defined in papflags + +This defines the minimum interval in seconds that will take place +before papif polls the LaserWriter for its status (and updates the +status file in the spool directory). The default is 10 seconds. + +* DEBUG: may be defined in papflags + +tells papif to print out debugging information. default is off. + +The following two options are considered dangerous because they may +interfere with binary data, etc. + +* STRIPCONTROLD: may be defined in papflags + +tells papif to strip control-d -- mainly for use with pcnfsd. Default +is off. + +* MAPCRTOLF: may be defined in papflags + +tells papif to map carriage return to line feed for input from a +Macintosh. Default is off. + + +USING PAPIF WITH LP ON A/UX (Release 1) +--------------------------------------- + +There is already some lp support for an AppleTalk LaserWriter (from +Adobe's TranScript for System V). It just needs some adjusting. If +you used Configure, then papif should have the proper parameters set +under A/UX. + +Most of these steps require you to run as root. + +The first step is to install a printer using the ADD_AT_LW script in +/usr/spool/lp. For example: + /usr/spool/lp/ADD_AT_LW ps8 +where ps8 is the printer name. + +After this, the interface file that derives from the atpsiface model +must be editted. With a printer name of ps8 as above, the interface +file can be found in /usr/spool/lp/interface/ps8. + +Do the following: + o set "send" to point a system copy of papif + o set "sendopts" to "-P $pr -n $2 -h " + [both -n and -h are optional, but it does make things] + [look better] + o change all (three) occurances of REVERSE to "DOREVERSE" + [REVERSE is used as flag here, but as a pointer to the + the name of the page reversal filter in the BSD version + of TranScript. Not doing this will just cause papif to + print out some unnecessary warning messages] + +The printer should now work! + +You might also want to remove the sections that send the banner file +and set "BANNER" (remembering to "export" it) to the banner file name +--papif will then send the banner file in the same job stream as the +print requests. This has the side effect that multiple copies will +result in a banner file per copy. + diff --git a/applications/papif/add_at_printer b/applications/papif/add_at_printer new file mode 100644 index 0000000..f2d58f7 --- /dev/null +++ b/applications/papif/add_at_printer @@ -0,0 +1,195 @@ +#!/bin/sh +# +# Copyright (C) September 1993 appro@fy.chalmers.se +# +# Script to add an AppleTalk printer on a Solaris 2.N host +# + +MYHOME=`dirname $0`; [ "$MYHOME" = "." ] && MYHOME=`pwd` +case $MYHOME in .*) + echo "Use absolute pathname for $0." + exit 1 + ;; +esac + +CAPHOME=${CAPHOME:-$MYHOME}; PAPIFHOME=${CAPHOME} +if [ ! -x $PAPIFHOME/papif ]; then + PAPIFHOME=${PAPIFHOME}/bin + if [ ! -x $PAPIFHOME/papif ]; then + echo "FATAL: can't find $PAPIFHOME/papif," + echo " make sure that $0 resides in the same directory as papif" + echo " or set CAPHOME variable properly." + exit + fi +fi +export PAPIFHOME + +PATH=$PATH:$CAPHOME:$CAPHOME/bin:$PAPIFHOME + +SUFFIX=`basename $0`.$$; export SUFFIX +trap 'rm /tmp/*.$SUFFIX > /dev/null 2>&1' 0 +trap 'exit' 1 2 3 + +echo "" +echo "Enter AppleTalk zone [*] > \c"; read ZONE +[ "$ZONE" ] || ZONE="*" + +echo "" +echo "Looking for =:LaserWriter@$ZONE ..." +$CAPHOME/bin/atlook -n "=:LaserWriter@$ZONE" | egrep -e '\[Net:[0-9]*' + +echo "" +echo "Enter AppleTalk printer name > \c"; read ATPRINTER +[ "$ATPRINTER" ] || exit +[ "`echo "$ATPRINTER" | awk -F@ '{ print $2 }'`" ] || + ATPRINTER="$ATPRINTER@$ZONE" +[ "`echo "$ATPRINTER" | awk -F: '{ print $2 }'`" ] || + ATPRINTER="`echo "$ATPRINTER" | awk -F@ '{ printf "%s:LaserWriter@%s",$1,$2; }'`" + +TEMP=`echo "$ATPRINTER" | awk -F: '{ print $1 }' | awk '{ for(i=1;i<=NF;i++) printf "%s",$i; }'` +echo "Enter Unix printer name [$TEMP] > \c"; read UXPRINTER +[ "$UXPRINTER" ] || UXPRINTER=$TEMP + +if lpstat -p $UXPRINTER >/dev/null 2>&1; then + echo "\07" + echo "WARNING: $UXPRINTER already exists," + echo " destination is \"`cat /etc/lp/printers/$UXPRINTER/comment`\"." + echo "Continue [Y/n]?\c"; read OK + case $OK in [Nn]*) exit; ;; + *) ;; + esac + disable -c -r "Redefining to $ATPRINTER" $UXPRINTER + reject $UXPRINTER +else + echo "\07" + echo "Defining \"$ATPRINTER\" AppleTalk printer as $UXPRINTER ..." + echo "Continue [Y/n]?\c"; read OK + case $OK in [Nn]*) exit; ;; + *) ;; + esac +fi +echo "" + +if [ ! -r /etc/lp/filter.table ]; then + echo "Initializing of default printer filters..." + for f in `ls /etc/lp/fd | sed 's/.fd$//'`; do + lpfilter -f $f -F/etc/lp/fd/$f.fd + done +fi + +if lpfilter -f atalkio -l > /dev/null 2>&1; then + echo "AppleTalk printer filter is already defined." +else + echo "Defining AppleTalk printer filter ..." + cat > /etc/lp/fd/atalkio.fd << EOF +#ident "@(#)atalkio.fd 1.0 (C) July 1993 appro@fy.chalmers.se" + +Input types: postscript +Output types: PS +Printer types: AppleTalk +Printers: any +Filter type: fast +Command: \$PAPIFHOME/papif 2>>\$ERRFILE +EOF + lpfilter -f atalkio -F /etc/lp/fd/atalkio.fd +fi + +if [ ! -f /usr/share/lib/terminfo/A/AppleTalk ]; then + echo "Defining terminfo for AppleTalk printers ..." + cat > /tmp/terminfo.$SUFFIX << EOF +AppleTalk, + cols#80, lines#66, + cpi=null, csnm=^D, lpi=null, scs=^D, slines=^D, u9=^D, +EOF + tic /tmp/terminfo.$SUFFIX +fi + +LOCKSDIR=/var/spool/lp/tmp/AppleTalk +if [ ! -d $LOCKSDIR ]; then + mkdir -m 0771 $LOCKSDIR + chown lp $LOCKSDIR + chgrp lp $LOCKSDIR +fi +PSEUDODEVICE=$LOCKSDIR/$UXPRINTER +touch $PSEUDODEVICE +chown lp $PSEUDODEVICE +chgrp lp $PSEUDODEVICE +chmod 0600 $PSEUDODEVICE + +echo $PAPIFHOME | sed -e 's/\//\\\//g' > /tmp/sed.$SUFFIX +sed -e "s/PAPIFHOMEMAGICTOKEN/`cat /tmp/sed.$SUFFIX`/g" \ + $MYHOME/papif.interface.template > /etc/lp/interfaces/$UXPRINTER +chown lp /etc/lp/interfaces/$UXPRINTER +chgrp lp /etc/lp/interfaces/$UXPRINTER +chmod 0775 /etc/lp/interfaces/$UXPRINTER + +echo "(Re)defining $UXPRINTER ..." +( lpadmin -p $UXPRINTER -D "$ATPRINTER" \ + -T AppleTalk \ + -v $PSEUDODEVICE \ + -I PS \ + -i /etc/lp/interfaces/$UXPRINTER \ + -A none && \ + accept $UXPRINTER && \ + enable $UXPRINTER \ +) || exit + +echo "\07" +echo "Do you want to make it available to network [Y/n]?\c"; read OK +case $OK in [Nn]*) exit; ;; + *) ;; +esac + +echo "" +if sacadm -l -p tcp > /dev/null; then + echo "tcp listener is already defined." +else + echo "Defining tcp listener ..." + sacadm -a -p tcp -t listen \ + -c "/usr/lib/saf/listen tcp" \ + -v `nlsadmin -V` \ + -n 9999 +fi + +ADDR_LPD="\\x`lpsystem -A`"; export ADDR_LPD +ADDR_0=`echo $ADDR_LPD | sed -e 's/\\x00020203/\\x00020ACE/g'`; export ADDR_0 + +if pmadm -l -p tcp -s 0 > /dev/null; then + echo "<0> service is already defined." +else + echo "Defining <0>/tcp service ..." + pmadm -a -p tcp -s 0 -i root \ + -m `nlsadmin -c /usr/lib/saf/nlps_server -A $ADDR_0` \ + -v `nlsadmin -V` +fi + +if pmadm -l -p tcp -s lp > /dev/null; then + echo " service is already defined." +else + echo "Defining /tcp service ..." + pmadm -a -p tcp -s lp -i root \ + -m `nlsadmin -o /var/spool/lp/fifos/listenS5` \ + -v `nlsadmin -V` +fi + +if pmadm -l -p tcp -s lpd > /dev/null; then + echo " service is already defined." +else + echo "Defining /tcp service ..." + pmadm -a -p tcp -s lpd -i root \ + -m `nlsadmin -o /var/spool/lp/fifos/listenBSD -A $ADDR_LPD` \ + -v `nlsadmin -V` +fi + +if nistest printers.org_dir; then + if nistest "[printer_name=$UXPRINTER]printers.org_dir"; then + nistbladm -m printer_host=`uname -n` \ + description="$ATPRINTER" \ + "[printer_name=$UXPRINTER]printers.org_dir" + else + nistbladm -a printer_name=$UXPRINTER \ + printer_host=`uname -n` \ + description="$ATPRINTER" \ + printers.org_dir + fi +fi diff --git a/applications/papif/cap.printers b/applications/papif/cap.printers new file mode 100644 index 0000000..e6a48af --- /dev/null +++ b/applications/papif/cap.printers @@ -0,0 +1,15 @@ +# +# Sample Unix Printer name to LaserWriter mapping file +# +# Form: unix printer name (no spaces) +# the token '=' +# laserwriter name terminated by lf +# + +# These are all the same printer +xmac=LaserWriter Plus:LaserWriter@* +PostScript=LaserWriter Plus:LaserWriter@* +ps=LaserWriter Plus:LaserWriter@* +# Another printer with aliases (as in /etc/printcap) +us=User Services:LaserWriter@* +elw=User Services:LaserWriter@* diff --git a/applications/papif/makefile b/applications/papif/makefile new file mode 100644 index 0000000..7b6a632 --- /dev/null +++ b/applications/papif/makefile @@ -0,0 +1,65 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 14:00:20 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +LWFLAGS=-DDOCNAME -DPAGECOUNT=512 +I=/usr/include +CAPLIB=-lcap +DESTDIR=/usr/local/cap + +# for other libraries (like BSD on hpux) +SLIB= + +# See README file for notes about defines +# Valid: SFLOWQ=[1,2,3,4,5,6,7,8] +# Valid: IDLESTUFF, NO_STRUCT, NOACCT, CAPPRINTERS=location +PAPFLAGS=-DMACUSER -DIDLESTUFF +PAPBANNER=-DCHARGEBANNER + +# USEVPRINTF - use vprintf in logging +VPRINTF=-DUSEVPRINTF +# If you have Transcript from Adobe for your laserWriter and want to +# print text files, uncomment the next line and set the location properly +# WPSTEXT="-DPSTEXT=\"/usr/local/lib/ps/pstext\" + +# This is if you have transcript and and want page reversal if possible +# WPSREVERSE=-DPSREVERSE=\"/usr/local/lib/ps/psrev\" + +all: papif papof + +papif: papif.o $(O) + ${CC} ${LFLAGS} -o papif papif.o $(O) $(CAPLIB) ${SLIB} + +papif.o: papif.c + ${CC} ${CFLAGS} ${VPRINTF} ${PAPBANNER} ${PAPFLAGS} ${LWFLAGS} \ + ${WPSTEXT} ${WPSREVERSE} -c papif.c + + + + +papof.o: papof.c + ${CC} -c ${CFLAGS} ${PAPBANNER} papof.c + +papof: papof.o + ${CC} ${LFLAGS} -o papof papof.o ${SLIB} + + +clean: + -rm -f papif papof *.o + + +install: papif papof + -strip papif papof + ${INSTALLER} papif papof $(DESTDIR) + + +dist: + @cat todist diff --git a/applications/papif/papif.c b/applications/papif/papif.c new file mode 100644 index 0000000..10a084b --- /dev/null +++ b/applications/papif/papif.c @@ -0,0 +1,1851 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/09/11 04:02:06 $"; +static char rcsident[] = "$Header: /mac/src/cap60/applications/papif/RCS/papif.c,v 2.27 1996/09/11 04:02:06 djh Rel djh $"; +static char revision[] = "$Revision: 2.27 $"; + +/* + * papif - UNIX AppleTalk test program: simple line printer input filter + * for LaserWriter + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * June 29, 1986 Schilit&CCKim Created. + * July 5, 1986 CCKim Clean up + * Aug 20, 1986 CCKim, Gave up on old version, use lwpr routines instead. + * Nov 1986 croft, restart after "status: idle", call pstext + * + */ + +char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#include +#ifndef _TYPES +# include /* in case param doesn't */ +#endif +#include +#include +#include +#include + +#include /* include appletalk definitions */ +#include +#include +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH +#ifdef NEEDFCNTLDOTH +# include +#endif NEEDFCNTLDOTH +#ifdef USEVPRINTF +# include +#endif USEVPRINTF +#ifdef xenix5 +# include +#endif xenix5 +#ifdef SOLARIS +# include +# include +#endif SOLARIS +#ifdef linux +#define SIGEMT SIGUNUSED +# include +#endif /* linux */ + +/* Configuration options */ + +#ifndef CAPPRINTERS +# define CAPPRINTERS "/etc/cap.printers" +#endif CAPPRINTERS + +/* + * status watch time: minimum time to wait before doing next status + * watch +*/ +#ifndef WATCHTIME +# define WATCHTIME 10 /* status watch every 10 seconds */ +#endif + +/* PAP defines */ +#ifndef RFLOWQ /* default read flow quantum to use */ +# define RFLOWQ atpMaxNum +#endif +#ifndef SFLOWQ /* default send flow quantum to use */ +# define SFLOWQ atpMaxNum +#endif +#define MAX_RFLOWQ atpMaxNum /* maximum read flow quantum */ +#define MAX_SFLOWQ atpMaxNum /* maximum send flow quantum */ + +/* buffers sizes for papread/writes - based on maximum flow quantum */ +#define R_BUFMAX PAPSegSize*MAX_RFLOWQ +#define S_BUFMAX PAPSegSize*MAX_SFLOWQ + +/* default for debugging output or not */ +#ifndef DEBUG +# define DEBUG 0 /* no */ +#endif + +/* are we running with Adobe Document Structuring parsing? */ +/* for now, do we look for %%EOF in input and send an EOF to remote */ +/* based on this */ +#ifndef NO_STRUCT +# define NO_STRUCT 0 /* yes */ +#endif + +/* default to doing accounting? */ +#ifndef NOACCT +# define NOACCT 0 /* yes, do it */ +#endif + +/* default to stripping out ^Ds? */ +#ifndef STRIPCONTROLD +# define STRIPCONTROLD 0 /* no */ +#endif + +/* default to mapping ^M to ^J for psrv, etc. */ +#ifndef MAPCRTOLF +# define MAPCRTOLF 0 /* no */ +#endif + +/* default to idle check? */ +#ifndef IDLESTUFF +# define IDLESTUFF 0 /* no */ +#endif + +/* default to charge banner? */ +#ifndef CHARGEBANNER +# define CHARGEBANNER 0 /* no */ +#endif + +#ifndef ATPRESPONSETIMEOUT +# define ATPRESPONSETIMEOUT sectotick(60*2) +#endif + +/* Transcript compatibility section */ + +/* allow psmagic */ +/* use pstext? */ +#ifndef PSTEXT +# define PSTEXT NULL /* no */ +#endif +/* use psreverse */ +#ifndef PSREVERSE +# define PSREVERSE NULL /* no */ +#endif +/* need to check magic */ +#ifndef PSMAGIC +# define PSMAGIC 0 /* no */ +#endif + +/* default to putting banner first? */ +#ifndef BANNERFIRST +# define BANNERFIRST 0 /* no */ +#endif + +/* default to putting banner last? */ +#ifndef BANNERLAST +# define BANNERLAST 0 /* no */ +#endif + +#ifndef BANNER +# define BANNER ".banner" +#endif + + +/* + * GLOBAL VARIABLES +*/ +int cno; /* pap connection number */ +int pid = -1; /* top fork's pid */ +/* don't know where I got 30 from */ +char host[30]; /* host name */ +char printer[30]; /* printer name */ +char user[30]; /* user name */ +#ifdef RUTGERS +char account[30]; /* RUTGERS accounting */ +#endif RUTGERS +#ifdef PLP +char *format = "l"; /* Format type */ +#endif PLP + +u_char *lwname; /* entity name */ + +/* for pap */ +int rflowq = RFLOWQ; /* flow quatum on local reads */ +int sflowq = SFLOWQ; /* flow q on remote reads (local writes) */ +int r_bufsiz; /* flow quantum translated to buffers size */ +int s_bufsiz; + +int debug = DEBUG; /* debugging output? */ +char *capprinters = CAPPRINTERS; /* location of cap.printers */ +char *bannerfile = BANNER; /* banner file name */ +int watchtime = WATCHTIME; /* status watch time */ +int dostruct = !NO_STRUCT; /* parse adobe structuring? */ +int doacct = !NOACCT; /* do accounting? */ +int doidlestuff = IDLESTUFF; /* handle idle watch? */ +int chargebanner = CHARGEBANNER; /* charge user for banner pages? */ +u_long atpresponsetimeout = ATPRESPONSETIMEOUT; /* atp resp. cache timeout */ +int strip_control_d = STRIPCONTROLD; +int map_crtolf = MAPCRTOLF; + +/* Transcript compatiblity */ +int neverreverse = FALSE; /* never page reverse */ +int verboselog = TRUE; /* default */ +char *pstext = PSTEXT; /* pstext location */ +char *psreverse = PSREVERSE; /* psrv location */ +int psmagic = PSMAGIC; /* will be reset if necessary by initenv */ +int bannerfirst = BANNERFIRST; /* banner comes first? */ +int bannerlast = BANNERLAST; /* banner comes last? */ +#ifdef RUTGERS +char *filtername = ""; /* check for dvi filter */ +int Rotate = FALSE; /* Rutgers - print in portrait mode */ +char rot[2]; /* Rutgers - option for pstext */ +#endif RUTGERS +#ifdef DUPLEXMODE +int duplex = FALSE; /* Print on both sides of the page? */ +#endif DUPLEXMODE +#ifdef PSJOBHEADER +char *jobheader = NULL; /* filename of PS jobheader */ +#endif PSJOBHEADER + +/* declarations */ +char *ptime(); + +/* Definitions */ + +/* yes or no depending on the value */ +#define boolvalstr(value) ((value) ? "yes" : "no") +/* string value if not null, "none" o.w. */ +#define strval(str) (((str) == NULL) ? "none" : (str)) + +#ifdef USESYSLOG +# include +#else USESYSLOG +/* logging levels */ +# define log_i dolog /* information */ +# define log_w dolog /* warning */ +# define log_d dolog /* log debugging */ +#endif USESYSLOG + +#define log_e dolog /* error */ +#define log_r dolog /* return from remote */ + +/* + * LPD Error tokens - what to return from exit + */ +#define lpd_OK 0 /* all is well? */ +#define lpd_REPRINT 1 /* forces a reprint */ +#define lpd_ERRORS 2 /* printed with errors */ + +#ifdef USESYSLOG +#ifdef __STDC__ +log_i(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +char *fmt; +ptrdiff_t a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af; +#else /* __STDC__ */ +log_i(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +char *fmt; +#endif /* __STDC__ */ +{ + syslog(LOG_INFO, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); +} + +#ifdef __STDC__ +log_w(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +char *fmt; +ptrdiff_t a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af; +#else /* __STDC__ */ +log_w(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +char *fmt; +#endif /* __STDC__ */ +{ + syslog(LOG_WARNING,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); +} + +#ifdef __STDC__ +log_d(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +char *fmt; +ptrdiff_t a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af; +#else /* __STDC__ */ +log_d(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +char *fmt; +#endif /* __STDC__ */ +{ + syslog(LOG_DEBUG, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); +} +#endif USESYSLOG + +/* + * quit gets called when we've been killed by lprm via SIGINT + * + */ + +void +quit() +{ + if (cno >= 0) + PAPClose(cno); + log_w("papif: Aborted job at %s\n",ptime()); + exit(lpd_OK); +} + +/* + * return true: if s is "y"* or "on"* or s is a non-zero number (string) + * return false: o.w. + * +*/ +int +getbval(s) +char *s; +{ + if (isdigit(s[0])) + return(atoi(s)); + if (s[0] == 'y' || (s[0] == 'o' && s[1] == 'n')) + return(TRUE); + return(FALSE); +} + +/* + * get environment variables set by Transcript and set on that basis + * +*/ +initenv() +{ + char *getenv(); + char *v; + + if ((v = getenv("JOBOUTPUT"))) /* Sys V lp Transcript compatibility */ + if (*v != '\0') + joboutput(v); + if ((v = getenv("DOACCT"))) /* papif specific */ + doacct = getbval(v); + if ((v = getenv("DEBUG"))) /* papif specific */ + debug = getbval(v); + if ((v = getenv("BANNERFIRST"))) + if (*v != '\0') + bannerfirst = atoi(v); + if ((v = getenv("BANNERLAST"))) + if (*v != '\0') + bannerlast = atoi(v); + if ((v = getenv("BANNER"))) /* papif specific */ + bannerfile = v; + if ((v = getenv("VERBOSELOG"))) + verboselog = getbval(v); + if ((v = getenv("ATPRESPONSETIMEOUT"))) /* papif specific */ + if ( *v != '\0') + if ((atpresponsetimeout = (unsigned) atol(v)) < 0) { + atpresponsetimeout = ATPRESPONSETIMEOUT; + log_w("papif: environment atpresponsetimeout too small, setting to %lu\n", + atpresponsetimeout); + } + if ((v = getenv("RFLOWQ"))) /* papif specific */ + if ( *v != '\0') + if ((rflowq = atoi(v)) <= 0) { + log_w("papif: environment RFLOWQ too small, setting to 1"); + rflowq = 1; + } + if (rflowq > atpMaxNum) { + rflowq = atpMaxNum; + log_w("papif: rflowq too large - setting to %d\n", rflowq); + } + if ((v = getenv("SFLOWQ"))) /* papif specific */ + if ( *v != '\0') + if ((sflowq = atoi(v)) <= 0) { + log_w("papif: environment SFLOWQ too small, setting to 1"); + sflowq = 1; + } + if (sflowq > atpMaxNum) { + sflowq = atpMaxNum; + log_w("papif: sflowq too large - setting to %d\n", sflowq); + } + if ((v = getenv("CHARGEBANNER"))) /* papif specific */ + chargebanner = getbval(v); + if ((v = getenv("WATCHTIME"))) /* papif specific */ + if ( *v != '\0') + if ((watchtime = atoi(v)) < 0) { + watchtime = WATCHTIME; + log_w("papif: environment watchtime too small, setting to %d\n", + watchtime); + } + if ((v = getenv("IDLESTUFF"))) /* papif specific */ + doidlestuff = getbval(v); + if ((v = getenv("ADOBE_STRUCTURED"))) /* papif specific */ + dostruct = getbval(v); + if ((v = getenv("MAPCRTOLF"))) /* papif specific */ + map_crtolf = getbval(v); + if ((v = getenv("STRIPCONTROLD"))) /* papif specific */ + strip_control_d = getbval(v); + if ((v = getenv("CAPPRINTERS"))) /* papif specific */ + if ( *v != '\0') + if (access(v, R_OK) == 0) + capprinters = v; + else + log_w("papif: user specified cap.printers: %s not found, ignoring\n",v); +#ifdef RUTGERS + if ((v = getenv("FILTERNAME"))) + if ( *v != '\0') + filtername = v; /* for getting around dvi filter problem */ +#endif RUTGERS + if ((v = getenv("REVERSE"))) { + if (*v == '\0') + psreverse = NULL; + else { + if (access(v, X_OK) == 0) + psreverse = v; /* good enough I guess */ + else + log_w("papif: specified psreverse filter %s not found, ignoring\n", v); + } + } + if ((v = getenv("PSTEXT"))) { + if ( *v == '\0') + pstext = NULL; + else { + if (access(v, X_OK) == 0) + pstext = v; + else + log_w("papif: specified pstext filter %s not found, ignoring\n", v); + } + } +#ifdef RUTGERS + if ((psreverse != NULL || pstext != NULL) && strcmp(filtername,"psdf") != 0) +#else RUTGERS + if (psreverse != NULL || pstext != NULL) +#endif RUTGERS + psmagic = 1; +#ifdef PSJOBHEADER + if ((jobheader = getenv("PSJOBHEADER")) != NULL) + if (*jobheader == '\0' || access(jobheader, R_OK) != 0) + jobheader == NULL; +#endif PSJOBHEADER + r_bufsiz = PAPSegSize*rflowq; + s_bufsiz = PAPSegSize*sflowq; +} + +/* + * log current environment + * +*/ +logenv() +{ + if (debug) { + log_d("papif: bannerfirst %s, bannerlast %s, verboselog %s\n", + boolvalstr(bannerfirst), boolvalstr(bannerlast), + boolvalstr(verboselog)); + log_d("papif: atpresponse timeout = %lu ticks (1/4 second units)\n", + atpresponsetimeout); + if (watchtime) + log_d("papif: status watch lower time limit %d seconds\n", watchtime); + else + log_d("papif: no status watch\n"); + log_d("papif: rflowq = %d packets, sflowq = %d packets\n", rflowq, sflowq); + log_d("papif: strip control d's %s, map carriage return to line feed %s\n", + boolvalstr(strip_control_d), boolvalstr(map_crtolf)); + if (doacct) + log_d("papif: accounting = %s, chargebanner %s", + boolvalstr(doacct), boolvalstr(chargebanner)); + else + log_d("papif: accounting = %s, ", boolvalstr(doacct)); + log_d(", idlestuff %s, structured %s\n", + boolvalstr(doidlestuff), boolvalstr(dostruct)); + log_d("papif: reverse = %s\n", strval(psreverse)); + log_d("papif: pstext = %s\n", strval(pstext)); + log_d("papif: banner file = %s\n", strval(bannerfile)); + log_d("papif: cap.printers = %s\n",strval(capprinters)); +#ifdef PSJOBHEADER + log_d("papif: job header = %s\n",strval(jobheader)); +#endif PSJOBHEADER + } +} + +/* + * parse arguments +*/ +getargs(argc, argv, printer, user, host, acctfile) +int argc; +char **argv; +char *printer, *user, *host, **acctfile; +{ + int i; + char *pgmname = NULL; + char *p; +#ifdef MACUSER + char *pp; + char *getenv(); +#endif MACUSER +#ifdef DUPLEXMODE + char *tmpdup; /* temporary var to check for "-dup*" */ +#endif DUPLEXMODE + extern boolean dochecksum; + + *printer = *user = *host = '\0'; /* init to nothing */ + *acctfile = NULL; + + for (i = 1; i < argc; i++) { + p = argv[i]; + if (p[0] == '-') { + switch (p[1]) { +#ifdef DUPLEXMODE + case '2': + duplex = TRUE; /* turn on 2-sided printing */ + break; +#endif DUPLEXMODE + case 'd': + dbugarg(p+2); + break; + case 'c': /* specifies "pass control characters" */ + /* (literal mode) */ + break; + case 'p': /* program name: pscomm compat */ + pgmname = argv[++i]; /* set program name */ + break; + case 'P': /* printer name: pscomm compatible */ + strcpy(printer,argv[++i]); + break; + case 'n': /* user name */ + strcpy(user,argv[++i]); + break; + case 'h': /* host name */ + strcpy(host,argv[++i]); + break; + case 'r': + neverreverse = TRUE; /* pscomm compat */ + break; + case 'k': /* no DDP checksum */ + dochecksum = 0; + break; +#ifdef PLP + case 'F': + format = p+2; +#endif PLP + case 'x': /* width in pixels */ + case 'y': /* length in pixels */ + case 'i': /* indentation size in chars */ + case 'l': /* length in lines */ + case 'w': /* width in chars */ +#ifdef PLP + case 'Z': + case 'C': + case 'J': + case 'R': +#endif PLP + if (p[2] == '\0') /* any more args for these? */ + ++i; /* yes, eat it */ + break; +#ifdef RUTGERS + case 'L': + Rotate = TRUE; /* landscape mode */ + break; + case 'A': /* account */ + case 'a': + strcpy(account,argv[++i]); + break; +#endif RUTGERS +#ifdef SOLARIS + case 'U': + strcpy(host, argv[++i]); + p = strchr(host, '!'); + strcpy(user, p+1); + *p = '\0'; + break; +#endif SOLARIS + default: + log_e("papif: Unknown argument %c\n",p[1]); + } + } else if (doacct) + *acctfile = argv[i]; + } + /* Get base of program name if specified with full path */ + if (pgmname == NULL) { + if ((pgmname = rindex(argv[0], '/')) != NULL) + pgmname++; /* else skip over slash */ + else + pgmname = argv[0]; + } + /* if program name isn't papif or psif and printer name isn't there */ + /* then use name of program (psif added for Transcript compatibility) */ + if (*printer == '\0') /* no printer name? */ + if (strcmp(pgmname, "papif") != 0 && strcmp(pgmname, "psif") != 0) + strcpy(printer, pgmname); + +#ifdef DUPLEXMODE + /* + * if the printer name ends in -dup* then we + * want to print on both sides of the paper. + * + */ + if (tmpdup = rindex(printer, '-')) + if (strncmp(tmpdup, "-dup", 4) == 0) + duplex = TRUE; +#endif DUPLEXMODE + +#ifdef MACUSER + if (strcmp(user, "root") == 0 && (p = getenv("LPD_JOB")) && + strncmp(p, "MacUser: ", 9) == 0) { + p += 9; + pp = p; + for ( ; ; ) { + if ((pp = index(pp, ' ')) == NULL) { + pp = p; + for ( ; ; ) { + if ((pp = index(pp, ' ')) == NULL) + break; + if (strncmp(pp, " Pages: ", 8) == 0) { + *pp = 0; + break; + } + pp++; + } + break; + } + if (strncmp(pp, " Job: ", 6) == 0) { + *pp = 0; + break; + } + pp++; + } + strcpy(user, p); + strcpy(host, "Macintosh"); + } +#endif MACUSER +} + + +/* + * our "input" filter - send input to laserwriter + * + * stdin - input + * stdout - points to devices, empty here + * stderr - points to log file on BSD4.2, Ultrix 1.0 through Ultrix 1.2 + * - points to "err" tmp file on BSD 4.3 + * + * Exit codes are listed above + * +*/ +main(argc,argv) +int argc; +char **argv; +{ + int pstatus(); + u_char *getlwname(); + char *acctfile = NULL; + int spc, epc; + void quit(); +#ifdef ISO_TRANSLATE + void cMac2ISO(), cISO2Mac(); +#endif ISO_TRANSLATE + + /* Initialize base vars */ + cno = -1; + pid = getpid(); /* mark as not there for now */ + +#ifdef USESYSLOG + openlog("papif", LOG_PID, LOG_LPR); +#endif USESYSLOG + + getargs(argc, argv, printer, user, host, &acctfile); + initenv(); /* Transcript compatibility */ + + lwname = getlwname(printer); /* based on this */ + + if (lwname == (u_char *)NULL) { + log_e("papif: Cannot map name %s to LaserWriter name\n", printer); + exit(lpd_REPRINT); + } + +#ifdef ISO_TRANSLATE + cISO2Mac(lwname); +#endif ISO_TRANSLATE + + /* init cap */ + abInit(FALSE); /* don't printout -- messes up with */ + nbpInit(); + PAPInit(); /* init PAP printer routines */ + ATPSetResponseTimeout(atpresponsetimeout); /* set to 2 minutes */ + + /* log message */ +#ifdef ISO_TRANSLATE + cMac2ISO(lwname); + log_i("papif: Starting job for %s@%s at %s on printer %s\n", + user, host, ptime(), (char *)lwname); + cISO2Mac(lwname); +#else ISO_TRANSLATE + log_i("papif: Starting job for %s@%s at %s on printer %s\n", + user, host, ptime(), (char *)lwname); +#endif ISO_TRANSLATE + + logenv(); + signal(SIGEMT, SIG_IGN); /* used by psrev, etc to signal */ + /* they are "ready" */ + signal(SIGINT,quit); /* this is what lprm sends us */ + signal(SIGTERM,quit); /* this is what disable sends us */ + + cno = openlw(lwname); + if (doacct && chargebanner) + spc = getpagecount(cno); /* get the page count */ + if (bannerfirst) + sendbanner(cno, host, user); + if (doacct && !chargebanner) + spc = getpagecount(cno); /* get the page count */ + + if (psmagic) + dopsmagic(cno); + +#ifdef PSJOBHEADER + if (jobheader != NULL && *jobheader != '\0') + sendheader(cno, host, user); /* send PSJOBHEADER file if in env */ +#endif PSJOBHEADER + + sendfile(cno,fileno(stdin),host,user);/* send file to laserwriter */ + + if (doacct && !chargebanner) + epc = getpagecount(cno); /* get the page count */ + if (bannerlast) + sendbanner(cno, host, user); + if (doacct && chargebanner) + epc = getpagecount(cno); /* get the page count */ + + PAPClose(cno); + + if (doacct && acctfile != NULL) +#ifdef RUTGERS + doaccounting(spc, epc, acctfile, user, host, account); +#else RUTGERS + doaccounting(spc, epc, acctfile, user, host); +#endif RUTGERS + log_i("papif: Finished job at %s\n", ptime ()); +#ifdef USESYSLOG + closelog(); +#endif USESYSLOG + exit(lpd_OK); /* exit okay */ +} + +/* + * send banner page: unlink afterwards so bannerfirst+bannerlast + * won't result in two banner pages + * +*/ +sendbanner(cno, host, user) +int cno; +char *host, *user; +{ + int bannerfd; + + if ((bannerfd = open(bannerfile, 0)) >= 0) { + sendbfile(cno, bannerfd, host, user); + close(bannerfd); + unlink(bannerfile); + } +} + +#ifdef PSJOBHEADER +/* + * send the file specified in env. variable PSJOBHEADER + * + */ + +sendheader(cno, host, user) +int cno; +char *host, *user; +{ + int fd; + + if ((fd = open(jobheader, O_RDONLY)) < 0) { + perror(jobheader); + return; + } + + sendbfile(cno, fd, host, user); + close(fd); +} +#endif PSJOBHEADER + +/* + * open laserwriter connection + * log errors every 5 minutes to stderr + * + */ + +int +openlw(lwname) +u_char *lwname; +{ + char message[80]; + int i, cno, ocomp, err; + PAPStatusRec status; +#ifdef ISO_TRANSLATE + void cMac2ISO(), cISO2Mac(); +#endif ISO_TRANSLATE + + i = 0; + /* Keep trying to open */ + while ((err = PAPOpen(&cno,(char *)lwname,rflowq,&status,&ocomp)) != noErr) { + if (err != -1) /* should be can't find lw.. */ + log_e("papif: PAPOpen returns %d\n",err); + else { + if ((i % 10) == 0) { /* waited 5 minutes? */ +#ifdef ISO_TRANSLATE + cMac2ISO(lwname); + sprintf((message+1), "papif: Problems finding %s\n", (char *)lwname); + cISO2Mac(lwname); +#else ISO_TRANSLATE + sprintf((message+1), "papif: Problems finding %s\n", (char *)lwname); +#endif ISO_TRANSLATE + log_e((char *)(message+1)); +#ifndef SOLARIS + message[0] = strlen((char *)(message+1)); + pstatus(message); +#endif /* SOLARIS */ + i = 1; + } else i++; + } + sleep(30); /* wait N seconds */ + } + do { + abSleep(16, TRUE); + if (watchtime != 0) /* a little bogus.... */ + pstatus(status.StatusStr); + } while (ocomp > 0); + return(cno); +} + +/* + * reaper (grim &) - deal with death of children + * + */ + +void +reaper() +{ + WSTATUS status; + register int i; + +#ifdef POSIX + if ((i = waitpid(-1, &status, WNOHANG)) == 0) { + log_w("papif: SIGCHLD but nothing from waitpid\n"); + return; + } +#else POSIX +#ifdef NOWAIT3 + if ((i = wait(&status)) == 0) { + log_w("papif: SIGCHLD but nothing from wait\n"); + return; + } +#else NOWAIT3 + if ((i = wait3(&status, WNOHANG, 0)) == 0) { + log_w("papif: SIGCHLD but nothing from wait3\n"); + return; + } +#endif NOWAIT3 +#endif POSIX + + if (WIFSTOPPED(status)) { + log_e("papif: Child %d stopped!\n", i); + exit(lpd_REPRINT); + } + + if (WIFSIGNALED(status)) { + i = W_TERMSIG(status); + if (i != SIGINT && i != SIGTERM) { + log_e("papif: Child killed by signal %d\n", i); + exit(lpd_REPRINT); + } + } else { + if (i = W_RETCODE(status)) { + log_w("papif: Finished job at %s with status %d\n", ptime(),i); + /* probably psrev complaining */ + exit(lpd_ERRORS); + } + /* nothing to do with zero exit code */ + } +} + +#define MAGICSIZE 11 +#define MAXFILTERS 2 +private char *filters[MAXFILTERS]; +private int numfilters = 0; + +/* + * check input stream for adobe magic + * +*/ +dopsmagic() +{ + char magic[MAGICSIZE]; + int cnt, i; + struct stat buf; + int diskfile = FALSE; + int retval; + int in_front = TRUE; /* fork filters in front of current fork */ + extern int errno; + void quit(); + + if (fstat(fileno(stdin), &buf) < 0) { + perror("psmagic setup: fstat"); + diskfile = FALSE; + } else { + /* is it a regular file? */ + diskfile = S_ISREG(buf.st_mode); + } + if (!diskfile) { + retval = fork_filter("", TRUE); + if (retval < 0) + quit(); + if (retval != 0) + return; + in_front = FALSE; + } + if ((cnt = read(fileno(stdin), magic, MAGICSIZE)) < 0) { + perror("psmagic setup: read"); + if (diskfile) + quit(); + else + exit(lpd_ERRORS); + } + if (diskfile) { + rewind(stdin); + lseek(fileno(stdin), 0L, 0); /* paranoia */ + } + if (cnt < 2) { /* nothing to do */ + log_w("papif: psmagic: only read %d, can't check magic\n", cnt); + if (diskfile) + return; + else + passalong(magic, cnt, MAGICSIZE); + } + + if (strncmp(magic, "%!", 2) != 0 + && strncmp(magic, "\004%!", 3) != 0) { + if (pstext) { + filters[numfilters++] = pstext; + if (psreverse && !neverreverse) + filters[numfilters++] = psreverse; + } + } + + /* check to see if follows Doc structuring 1.0 or better */ + /* note mutually exclusive of pstext */ + if (!neverreverse && psreverse && + cnt >= MAGICSIZE && strncmp(magic, "%!PS-Adobe-", MAGICSIZE) == 0) + filters[numfilters++] = psreverse; + + if (in_front) { + for (i = 0 ; i < numfilters; i++) + fork_filter(filters[i], in_front); + } else { /* must do in revse order */ + while (numfilters--) + fork_filter(filters[numfilters], in_front); + } + if (diskfile) + return; + passalong(magic, cnt, MAGICSIZE); +} + +/* + * duplicate stdin with prefixed buffer + * +*/ +passalong(prebuf, precnt, wantcnt) +char *prebuf; +int precnt; +int wantcnt; +{ + register int cnt; + register char *p; + register int i; + char buf[BUFSIZ]; + + + if (precnt < 0) + exit(lpd_ERRORS); + if (map_crtolf) { + for (i = 0; i < precnt; i++) + if (prebuf[i] == '\r') + prebuf[i] = '\n'; + } + if (write(fileno(stdout), prebuf, precnt) < 0) + exit(lpd_ERRORS); + if (precnt < wantcnt) + exit(lpd_OK); + while ((cnt = read(fileno(stdin), buf, sizeof(buf))) > 0) { + /* dangerous */ + if (map_crtolf) { + for (i = 0, p = buf; i < cnt; i++, p++) + if (*p == '\r') + *p = '\n'; + } + if (write(fileno(stdout), buf, cnt) < 0) + exit(lpd_ERRORS); + } + exit(cnt < 0 ? lpd_ERRORS : lpd_OK); +} + + +char *current_fork = "papif"; + +/* + * Run a filter program in a child process; diddle the descriptors so that + * the filter eats the parent process's former stdin, and pipes its output + * into the parent's new stdin. + * +*/ +fork_filter(fp, in_front) +char *fp; +{ + int fdpipe[2]; + int fpid; + char *fn; + int not_stdin_fork; + int mask; + void quit(); + void reaper(); + + not_stdin_fork = (fp != NULL) && (strcmp(fp, "") != 0); + if (debug) { + log_d("papif: Forking %s %s current fork %s\n", + not_stdin_fork ? fp : "", + in_front ? "in front of" : "behind", + current_fork); + } + if (pipe(fdpipe) != 0) { + perror("filter setup: pipe"); + quit(); + } + + if (fp) { + fn = rindex(fp, '/'); + if (fn == NULL || fn[1] == '\0') + fn = fp; + else + fn++; + } else fn = NULL; + if (!not_stdin_fork) + fn = ""; + + /* interlock */ +#ifdef NOSIGMASK + sighold(SIGCHLD); + sighold(SIGINT); + sighold(SIGTERM); +#else NOSIGMASK + mask = sigblock(sigmask(SIGCHLD)|sigmask(SIGINT)|sigmask(SIGTERM)); +#endif NOSIGMASK +#ifdef NOVFORK + fpid = fork(); +#else + fpid = not_stdin_fork ? vfork() : fork(); +#endif + switch (fpid) { + case 0: /* child */ + if (in_front) { + if (dup2(fdpipe[1], fileno(stdout)) == -1) { + perror("filter setup: child dup2"); + exit(lpd_ERRORS); + } + } else { + if (dup2(fdpipe[0], fileno(stdin)) == -1) { + perror("filter setup: child dup2"); + exit(lpd_ERRORS); + } + } + close(fdpipe[1]); /* ignore errs */ + close(fdpipe[0]); + if (not_stdin_fork) { +#ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGINT); + sigrelse(SIGTERM); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK +#ifdef RUTGERS + if (strcmp(fn, "pstext") == 0) { + sprintf(rot, "%d", Rotate); + execl(fp, rot, fn, 0); + } else + execl(fp, fn, 0); +#else RUTGERS +#ifdef TRANS3 + if (strcmp(fn, "psdman") == 0) + execl(fp, fn, "-P", printer, 0); + else + execl(fp, fn, 0); +#else TRANS3 + execl(fp, fn, 0); +#endif TRANS3 +#endif RUTGERS + /* if we are here again, then... */ + perror("filter setup: child exec"); + exit(1); + } else { + current_fork = fn; + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); +#ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGINT); + sigrelse(SIGTERM); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK + PAPShutdown(cno); /* make sure child doesn't have the */ + /* fd's, etc. */ + return(0); + } + break; + case -1: + perror("filter setup: fork"); + return(-1); + break; + default: /* parent continues */ + /* set up stdin to be pipe */ + signal(SIGCHLD, reaper); +#ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGINT); + sigrelse(SIGTERM); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK + if (in_front) { + if (dup2(fdpipe[0],fileno(stdin)) == -1) { + perror("filter setup: parent dup2"); + return(-1); + } + } else { + if (dup2(fdpipe[1],fileno(stdout)) == -1) { + perror("filter setup: parent dup2"); + return(-1); + } + } + close(fdpipe[1]); /* ignore errs */ + close(fdpipe[0]); + return(fpid); + } + return(0); +} + +#ifdef NODUP2 +#ifndef NOFILE +YOU MUST SET THIS TO THE MAXIMUM NUMBER OF FILE DESCRIPTORS AVAILABLE ON YOUR SYSTEM +#endif + +/* emulate dup2 (hopefully) */ +dup2(fdcur, fdwant) +int fdcur; +int fdwant; +{ + int fdarr[NOFILE]; + int i,j; + int fd; + + close(fdwant); + for (i = 0 ; i < NOFILE; i++) { + fd = dup(fdcur); /* duplicate */ + if (fd == fdwant) + break; + fdarr[i] = fd; /* remember so we can close off */ + } + if (i == NOFILE) + return(-1); + for (j = 0; j < i; j++) + close(fdarr[j]); + return(fdwant); +} +#endif + +private char ps_buf[S_BUFMAX+10]; +private char pr_buf[R_BUFMAX+10]; + +/* + * Send a file to the specified connection + */ +sendfile(cno,fd,host,user) +int cno; +int fd; +char *host; +char *user; +{ + int eof, wcomp, paperr, err, doeof = FALSE; + int sent_one_eof = FALSE; + char *bp = ps_buf; + wcomp = 0; + + + strcpy(ps_buf, "/statusdict where {pop statusdict /jobname ("); + strcat(ps_buf, host); + strcat(ps_buf, ":"); + strcat(ps_buf, user); + strcat(ps_buf, ") put} if\n"); +#ifdef DUPLEXMODE + if (duplex) { + strcat(ps_buf, "statusdict /setduplexmode known {"); + strcat(ps_buf, " statusdict begin true setduplexmode end"); + strcat(ps_buf, "} if"); + } +#endif DUPLEXMODE + if ((paperr=PAPWrite(cno, ps_buf,strlen(ps_buf), FALSE, &wcomp)) < 0) { + log_e("papif: sendfile: 1st PAPWrite: call error %d\n", paperr); + PAPClose(cno); + exit(lpd_REPRINT); + } + /* post initial read from LW */ + err = 1; + /* this is the main read/write loop */ + initgetbuf(); /* initialize getbuf */ + do { + if ((eof = handleread(cno))) + break; + if (wcomp <= 0) { + if (wcomp != noErr) { + log_e("papif: sendfile: PAPWrite completion error %d\n",wcomp); + PAPClose(cno); /* sigh... */ + exit(lpd_REPRINT); + } else { + err = getbuf(fd, ps_buf, s_bufsiz, &bp, &doeof); + /* err == 0 and no doeof means that we didn't see input */ + /* within a second or so, so we should try going ahead first */ + if ((!sent_one_eof) && (err || doeof)) { + if ((paperr=PAPWrite(cno, bp, ((err<0)?0 :err), doeof, &wcomp)) < 0) + break; + sent_one_eof = doeof; + } else err = 1; + } + } + + statuswatch(); + abSleep(4, TRUE); /* wait a bit */ + } while (err > 0 ); + + if (err < 0) /* this is a little overloaded */ + perror("read"); + if (paperr != noErr) { + log_e("papif: sendfile: PAPWrite call error %d\n",paperr); + wcomp = 0; + } + while (!eof || wcomp > 0) { /* wait for completion */ + statuswatch(); + abSleep(4,TRUE); + if (!eof) + eof = handleread(cno); + } +} + +/* + * Send a banner file to the specified connection (no eof) + */ +sendbfile(cno,fd,host,user) +int cno; +int fd; +char *host; +char *user; +{ + int eof, wcomp, paperr, err, doeof = FALSE; + char *bp = ps_buf; + wcomp = 0; + + + strcpy(ps_buf, "/statusdict where {pop statusdict /jobname ("); + strcat(ps_buf, host); + strcat(ps_buf, ":"); + strcat(ps_buf, user); + strcat(ps_buf, ") put} if\n"); + if ((paperr=PAPWrite(cno, ps_buf,strlen(ps_buf), FALSE, &wcomp)) < 0) { + log_e("papif: sendbfile: 1st PAPWrite: call error %d\n", paperr); + PAPClose(cno); + exit(lpd_REPRINT); + } + /* post initial read from LW */ + err = 1; + /* this is the main read/write loop */ + inithandleread(); /* initialze handleread */ + initgetbuf(); /* initialize getbuf */ + do { + if ((eof = handleread(cno))) + break; + if (wcomp <= 0) { + if (wcomp != noErr) { + log_e("papif: sendbfile: PAPWrite completion error %d\n",wcomp); + PAPClose(cno); /* sigh... */ + exit(lpd_REPRINT); + } else { + err = getbuf(fd, ps_buf, s_bufsiz, &bp, &doeof); + /* err == 0 and no doeof means that we didn't see input */ + /* within a second or so, so we should try going ahead first */ + if (err || doeof) { + if (err <= 0 || (paperr=PAPWrite(cno, bp, err, FALSE, &wcomp)) < 0) + break; + if (doeof) { + eof = handleread(cno); + break; + } + } else err = 1; + } + } + + statuswatch(); + abSleep(4, TRUE); /* wait a bit */ + } while (err > 0 ); + + if (err < 0) /* this is a little overloaded */ + perror("read"); + if (paperr != noErr) { + log_e("papif: sendbfile: PAPWrite call error %d\n",paperr); + wcomp = 0; + } + while (/* !eof || */ wcomp > 0) { /* wait for completion */ + statuswatch(); + abSleep(4,TRUE); + } +} + +/* + * return page count from a laserwriter + * +*/ +int +getpagecount(cno) +int cno; +{ + static char gpcstr[] = "statusdict begin pagecount (*) print == "; + char buf[100]; /* enough for page count! */ + char *p; + int err, wcomp, rlen, eof, rcomp, started; + + err = PAPWrite(cno, gpcstr, sizeof(gpcstr), TRUE, &wcomp); + if (err != noErr) + return(-1); + err = PAPRead(cno, pr_buf, &rlen, &eof, &rcomp); + if (err != noErr) + return(-1); + started = 0; + do { + if (rcomp <= 0) { + if (rcomp == noErr && rlen > 0) { + pr_buf[rlen] = '\0'; /* tie off string */ + if (!started) { + p = index(pr_buf, '*'); /* look for marker */ + if (p != NULL) { + strcpy(buf, p+1); + started = 1; + } + } else + strcat(buf, pr_buf); + } + if (eof) + break; + err = PAPRead(cno, pr_buf, &rlen, &eof, &rcomp); + if (err != noErr) + return(-1); + } + statuswatch(); + abSleep(4, TRUE); + } while (!eof); + return(atoi(buf)); +} + +/* + * write out accounting information + * +*/ +#ifdef RUTGERS +doaccounting(spc, epc, acctfile, user, host, account) +#else RUTGERS +doaccounting(spc, epc, acctfile, user, host) +#endif RUTGERS +int spc, epc; +#ifdef RUTGERS +char *acctfile, *user, *host, *account; +#else RUTGERS +char *acctfile, *user, *host; +#endif RUTGERS +{ + FILE *afd; +#ifdef RUTGERS + long clock; + extern char *gdate(); +#endif RUTGERS + + if (epc >= spc && epc > 0 && spc > 0) { + if (user[0] != '\0' && acctfile && access(acctfile, W_OK) >= 0 && + (afd = fopen(acctfile, "a")) != NULL) +#ifdef PLP + fprintf(afd,"%s %s %s %s %5d %s\n", host, user, printer, format, + epc-spc, ptime()); +#else PLP +#ifdef RUTGERS + fprintf(afd,"%s\t%7.2f\t%s:%s:%s\n",gdate()+4, (float)(epc-spc), + host, user, account); +#else RUTGERS + fprintf(afd,"%7.2f\t%s:%s\n", (float)(epc-spc), host, user); +#endif RUTGERS +#endif PLP + } else + log_w("papif: Problems getting pagecount: start %d, end %d\n",spc,epc); +} + +#ifdef RUTGERS +/* rutgers mod to get date so that messages can be time-stamped */ +/* currently returns a pointer to a string that has all of the date */ +/* except the year */ + +#include + +char *ctime(); +char *malloc(); + +char *gdate() + { + time_t now; + static char *temp = 0; + + if (temp == 0) + temp = malloc(26); + now = time((time_t *)0); + strcpy(temp,ctime(&now)); + temp[19] = '\0'; + return(temp); + } + +#endif RUTGERS + +/* + * output status message to disk so lpq et al. can show it + * Note: input string is a pascal string + * Keeps around status until we have written out the status file again + * so looks for status aren't there are minimized + * + */ + +pstatus(s) +char *s; +{ +#ifndef SOLARIS + int fd; + int okay = TRUE; + + unlink("newstatus"); + if ((fd = open("newstatus",O_CREAT|O_WRONLY,0664)) < 0) + return; + if (write(fd, s+1, *s) < 0) + okay = FALSE; + write(fd, "\n", 1); + close(fd); + if (okay) { + unlink("status"); + link("newstatus","status"); + unlink("newstatus"); + } +#else SOLARIS + static char status[256]; + + if (strncmp("status: idle", s+1, 12) == 0) + return; + if (strncmp(status, s+1, *s) != 0) { + strncpy(status, s+1, *s); + status[*s] = '\0'; + log_e("%s\n", status); + } +#endif SOLARIS +} + +/* + * returns nicely formated time string +*/ +char * +ptime() +{ + long clock; + char *timestr; + char *p; + static char time_buffer[BUFSIZ]; + + clock = time(0); + timestr = (char *)asctime(localtime(&clock)); + /* truncate after first linefeed */ + if ((p = index(timestr, '\n'))) + *p = '\0'; + strcpy(time_buffer,timestr); + return(&time_buffer[0]); +} + +/* + * get the laserwriter name of the unix spooled printer + * + * returns NULL if nothing found + * returns 'LaserWriter Plus' if printer is null + * + */ + +u_char * +getlwname(printer) +char *printer; +{ + FILE *fd; + static char buf[1024]; + char *getenv(); + u_char *ep; + + if (printer[0] == '\0') { /* no printer */ + /* try last resort */ + if ((printer = getenv("PRINTER")) == NULL) { + return((u_char *)"LaserWriter Plus:LaserWriter@*"); /* default */ + } + } + +#ifdef NOCAPDOTPRINTERS + sprintf(buf, "/etc/lp/printers/%s/comment", printer); + if ((fd = fopen(buf, "r")) == NULL) { + perror(buf); + return(NULL); + } + if (fgets(buf, sizeof(buf), fd) == NULL) + return(NULL); + buf[strlen(buf)-1] = '\0'; /* get rid of the lf */ + return(buf); +#else NOCAPDOTPRINTERS + if ((fd = fopen(capprinters,"r")) == NULL) { + perror(capprinters); + return((u_char *)NULL); + } + do { + if (fgets(buf, 256, fd) == NULL) + break; + buf[strlen(buf)-1] = '\0'; /* get rid of the lf */ + if (buf[0] == '#' || buf[0] == '\0') + continue; + if ((ep=(u_char *)index(buf,'=')) == NULL) /* find first = */ + continue; /* no = in string */ + *ep = '\0'; /* set = to null now */ + if (strcmp(buf,printer) == 0) { + if (strlen((char *)(ep+1)) == 0) /* no name */ + continue; + fclose(fd); + return(ep+1); /* return pointer to value */ + } + } while (1); + fclose(fd); + return((u_char *)NULL); +#endif NOCAPDOTPRINTERS +} + +/* MODULE: statuswatch EXPORTS(statuswatch) IMPORTS(lwname, pstatus) */ + +private time_t oldtime = -1; +private time_t newtime; +/* + * Listens for status messages from lw + * +*/ +statuswatch() +{ + AddrBlock addr; + PAPStatusRec status; + int i; + char retry[255]; + char tmpbuf[255]; + static long idletime = 0; + OSErr err; + + if (watchtime == 0) /* no status watch */ + return; + if (oldtime == -1) + time(&oldtime); + else { + time(&newtime); + if ((newtime - oldtime) < watchtime) /* delay this much */ + return; + } + addr.net = 0; /* sufficient */ + err = PAPStatus((char *)lwname, &status, &addr); + if (err < 0) { + if (err != reqFailed) /* no status message */ + return; + } else + oldtime = newtime; /* reset */ + if (doidlestuff) { + cpyp2cstr(tmpbuf, status.StatusStr); + /* idletimeout if 40 seconds of "status: idle" or if papifidletest is */ + /* set. Note: I have seen cases when the open reply pkt was lost */ + /* and never got back into open state, but only when running without */ + /* xo on the opens (a bug) */ + if (idletime == 0) + time(&idletime); + if ((strncmp("status: idle", tmpbuf, 12) == 0 && (time(0)-idletime)>40) || + access("/tmp/papifidletest", F_OK) == 0) { + log_e("papif: status: idle bug; restarting\n"); + fflush(stderr); + unlink("/tmp/papifidletest"); + for (i = 0 ; i < NSIG ; i++) + signal(i, SIG_IGN); +#ifdef sun +# ifdef SOLARIS + sprintf(retry, "(/usr/bin/sleep 2;/usr/bin/disable %s;/usr/bin/sleep 2;/usr/bin/enable %s)&", +# else SOLARIS + sprintf(retry, "(/usr/bin/sleep 2;/usr/etc/lpc abort %s;/usr/bin/sleep 2;/usr/etc/lpc start %s)&", +# endif SOLARIS +#else sun +# ifdef pyr + sprintf(retry, "(/usr/bin/sleep 2;/usr/etc/lpc abort %s;/usr/bin/sleep 2;/usr/etc/lpc start %s)&", +# else pyr + sprintf(retry, "(/bin/sleep 2;/etc/lpc abort %s;/bin/sleep 2;/etc/lpc start %s)&", +# endif pyr +#endif sun + printer, printer); + system(retry); + exit(lpd_REPRINT); + } + } + pstatus(status.StatusStr); +} +/* END MODULE */ + + +/* MODULE: getbuf EXPORTS(initgetbuf, getbuf) IMPORTS() */ +/* + * handle reads from file/pipe + * +*/ + +static int gb_bc = 0, gb_stage = -1; +static char *gb_bp; +static char *gb_eofstr = "%%EOF\n"; + +initgetbuf() +{ + gb_bc = 0; + gb_stage = -1; +} + +/* + * do a read, but don't let it hangup protocol if the read is blocking + * +*/ +private +dosread(fd, buf, length, nodata) +int fd; +char *buf; +int length; +int *nodata; +{ + int rdy; + struct timeval t; + gfd_set aset; + int j; + + t.tv_sec = 0; + t.tv_usec = 250; /* 1/4 second? */ + FD_ZERO(&aset); + FD_SET(fd, &aset); + rdy = -1, j = 0; + *nodata = FALSE; + while (rdy <= 0 && j < 4) { + FD_SET(fd, &aset); + rdy = select(fd+1, &aset, 0, 0, &t); + if (rdy > 0) + break; + abSleep(1, TRUE); + j++; + } + if (rdy <= 0) { + *nodata = TRUE; + return(0); + } + return(read(fd, buf, length)); +} + +/* + * Get a buffer looking for "%%EOF" at the start of a line if dostruct is set + * otherwise just return a buffer + * +*/ +getbuf(fd, buf, maxbufsiz, bstart, doeof) +int fd; +char *buf; +int maxbufsiz; +char **bstart; +int *doeof; +{ + register char *p; + register int c; + int i, j, nodata; + + if (!dostruct) { + i = dosread(fd, buf, maxbufsiz, &nodata); + if (nodata) + return(i); + if (i <= 0) + *doeof = TRUE; + if (strip_control_d) { + /* dangerous, but may be necessary in some cases */ + for (p = buf, j = 0 ; j < i; j++) + if (*p == '\004') + *p = '\n'; + } + return(i); + } + *doeof = FALSE; + if (gb_bc <= 0) { + /* feed in previously read here: maybe backup this way? */ + if ((gb_bc = dosread(fd, buf, maxbufsiz, &nodata)) <= 0) { + if (nodata) + return(gb_bc); + *doeof = TRUE; + return(gb_bc); + } + gb_bp = buf; + } + /* scan gb_stage */ + for (p = gb_bp, i = 0; i < gb_bc; i++, p++) { + if (strip_control_d) { + if (*p == '\004') + *p = '\n'; + } + if (gb_stage < 0) { + if (*p == '\r' || *p == '\n') + gb_stage = 0; + } else { + c = (*p == '\r') ? '\n' : *p; + if (c == gb_eofstr[gb_stage]) { + gb_stage++; + if (gb_eofstr[gb_stage] == '\0') { + p++; + i++; + *doeof = TRUE; + gb_stage = -1; + break; + } + } else gb_stage = -1; + } + } + *bstart = gb_bp; + if (i != gb_bc) + i++; /* okay, have offset to convert to count */ + gb_bp = p; + gb_bc -= i; /* decrement by count */ + return(i); /* return count */ +} +/* END MODULE: getbuf */ + +/* MODULE: handleread EXPORTS(inithandleread,handleread) */ +/* IMPORTS() */ +private int hr_rcomp = noErr; +private int hr_rlen = 0; +private char hr_rbuf[R_BUFMAX+10]; +private int hr_eof = 0; + +inithandleread() +{ + hr_rcomp = noErr; + hr_rlen = 0; + hr_eof = 0; +} + +/* + * handle the papread + * return: -1 paperr + * 0 ok + * 1 eof +*/ +handleread(cno) +{ + int paperr; + + if (hr_rcomp > 0) + return(0); + switch (hr_rcomp) { + case noErr: + break; + default: + log_e("papif: handleread: PAPRead error %d\n", hr_rcomp); + return(-1); + } + hr_rbuf[hr_rlen] = '\0'; + if (hr_rlen) { + log_r("%s", hr_rbuf); + } + if (hr_eof) { + return(1); + } + paperr = PAPRead(cno, hr_rbuf, &hr_rlen, &hr_eof, &hr_rcomp); + switch (paperr) { + case noErr: + break; + default: + log_e("papif: handleread: PAPRead error %d\n", paperr); + return(-1); + } + return(0); +} + +/* END MODULE: handleread */ + +/* BEGIN MODULE: log EXPORTS(log,joboutput) */ + +/* + * Setup this so we can be smarter about errors in future + * logging level are setup as: i - information, w - warning + * e - error, r - return from laserwriter, and d - for debugging + * +*/ + +private FILE *jobout; + +#ifndef USEVPRINTF +/* Bletch - gotta do it because pyramids don't work the other way */ +/* (using _doprnt and &args) and don't have vprintf */ +/* of course, there will be something that is just one arg larger :-) */ +/* VARARGS1 */ +dolog(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +char *fmt; +#else +dolog(va_alist) +va_dcl +#endif +{ +#ifdef USEVPRINTF + register char *fmt; + va_list args; + + va_start(args); + fmt = va_arg(args, char *); + if (jobout) + vfprintf(jobout, fmt, args); + vfprintf(stderr, fmt, args); + va_end(args); +#else + /* + * Keep buffers flushed to avoid double-output after fork(); + */ + if (jobout) { + fprintf(jobout, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); + fflush(jobout); + } + fprintf(stderr, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); + fflush(stderr); +#endif +} + +/* + * Open the jobout file if possible - used for lp model + * +*/ +joboutput(filename) +char *filename; +{ +#ifdef RUTGERS + if ((jobout = fopen(filename, "a"))) { +#else RUTGERS + if ((jobout = fopen(filename, "w"))) { +#endif + /* put a return at the start */ + putc('\n', jobout); + } +} +/* END MODULE: log */ diff --git a/applications/papif/papif.interface.template b/applications/papif/papif.interface.template new file mode 100644 index 0000000..5c3fe6d --- /dev/null +++ b/applications/papif/papif.interface.template @@ -0,0 +1,1046 @@ +#ident "@(#)papif.interface 1.1 93/09/06 appro@fy.chalmers.se" /* CAP6.0 */ + +########### +## +## AppleTalk printer interface program. +## +## For use with the Solaris 2.N print system, refer to the "Setting up +## Printers" manual. Used in conjunction with the add_at_printer script +## +########### + +##### +# +# Until we get to the point below where the printer port +# and physical printer are initialized, we can't do much +# except exit if the Spooler/Scheduler cancels us. +##### +trap 'exit' 15 + +##### +# +# We can be clever about getting a hangup or interrupt, though, at least +# until the filter runs. Do this early, even though $LPTELL +# isn't defined, so that we're covered. +##### +catch_hangup () { + if [ -n "${LPTELL}" ] + then + echo \ +"The connection to the printer dropped; perhaps the printer went off-line?" \ + | ${LPTELL} ${printer} + fi + return 0 +} +catch_interrupt () { + if [ -n "${LPTELL}" ] + then + echo \ +"Received an interrupt from the printer. The reason is unknown, +although a common cause is that the baud rate is too high." \ + | ${LPTELL} ${printer} + fi + return 0 +} +trap 'catch_hangup; exit_code=129 exit 129' 1 +trap 'catch_interrupt; exit_code=129 exit 129' 2 3 + +##### +# +# Most of the time we don't want the standard error to be captured +# by the Spooler, mainly to avoid "Terminated" messages that the +# shell puts out when we get a SIGTERM. We'll save the standard +# error channel under another number, so we can use it when it +# should be captured. +# +# Open another channel to the printer port, for use when the +# regular standard output won't be directed there, such as in +# command substitution (`cmd`). +##### +exec 5>&2 2>/dev/null 3>&1 + +##### +# +# Set some globally used variables and functions. +##### + +: ${TMPDIR:=/tmp} +: ${SPOOLDIR:=/usr/spool/lp} +: ${TERMINFO:=/usr/lib/terminfo} +: ${CHARSETDIR:=/usr/lib/charsets} + +: ${LOCALPATH:=${SPOOLDIR}/bin} +PATH="/bin:/usr/bin:${LOCALPATH}" + +MAX_COLS_SMALL_BANNER=40 + +#this ----vvvvvvvvvvvvvvvvvvv---- is changed by add_at_printer +PAPIFHOME=PAPIFHOMEMAGICTOKEN; export PAPIFHOME + +##### +# +# On the 3.2 release of the 386unix product, the parallel port does +# not support any ioctl calls. As a result, we cannot set the opost +# and onlcr attributes to have 's expanded to . This +# "filter" gets the job done for us. +##### +: ${FIX386BD:=${LOCALPATH}/386parallel} +if [ -n "${FIX386BD}" -a -x "${FIX386BD}" ] +then + FIX386BD="| ${FIX386BD}" +else + FIX386BD="" +fi + +##### +# Use ${TMPPREFIX} as the prefix for all temporary files, so +# that cleanup is easy. The prefix may be up to 13 characters +# long, so you only have space for one more character to make +# a file name. If necessary, make a directory using this prefix +# for better management of unique temporary file names. +##### +TMPPREFIX=${TMPDIR}/`uname -n`$$ + +##### +# Before exiting, set ${exit_code} to the value with which to exit. +# Otherwise, the exit from this script will be 0. +##### +trap 'rm -fr ${TMPPREFIX}*; exit ${exit_code}' 0 + +##### +# ${LPTELL} is the name of a program that will send its +# standard input to the Spooler. It is used to forward +# the description of a printer fault to the Spooler, +# which uses it in an alert to the administrator. +##### +if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ] +then + fake_lptell () { + header="no" + while read line + do + if [ "no" = "${header}" ] + then + errmsg ERROR ${E_IP_UNKNOWN} \ + "unknown printer/interface failure" \ + "consult your system administrator; + reasons for failure (if any) follow:" + header=yes + fi + echo "${line}" >&2 + done + return 1 + } + LPTELL=fake_lptell +fi + +##### +# ${DRAIN} is the name of a program that will wait +# long enough for data sent to the printer to print. +##### +if [ -x "${LOCALPATH}/drain.output" ] +then + DRAIN="${LOCALPATH}/drain.output 5" # wait only five seconds +else + DRAIN= +fi + +##### +# ${LPCAT} is the name of a program to use as a default +# filter. Minimally it should copy its standard input to +# the standard output, but it should also trap printer +# faults. The current LPCAT traps hangups (DCD dropping, SIGHUP), +# interrupts (SIGINT, SIGQUIT), broken pipe (SIGPIPE), and +# excess delays in sending data to the printer, interpreting all +# as printer faults. +##### +if [ ! -x "${LPCAT:=${LOCALPATH}/lp.cat}" ] +then + LPCAT="cat" +fi + +##### +# ${LPSET} is the name of a program that will set the +# character pitch, line pitch, page width, page length, +# and character set. It helps to have this in a single +# binary program so that (1) it's faster than calls +# to "tput"; and (2) it can access the new Terminfo +# capabilities for printers (on pre SVR3.2 machines, tput can't). +##### +if [ ! -x "${LPSET:=${LOCALPATH}/lp.set}" ] +then + fake_lpset () { + echo H V W L S >&2 + false + } + LPSET=fake_lpset +fi + +internal_lpset () { + ##### + # + # The funny business with the "2>&1 1>&3" is to let us capture + # the standard ERROR, not the standard OUTPUT as is the usual case + # with foo=`cmd`. The standard output will go to the printer. + ##### + [ -n "${stty1}" ] && stty ${stty1} 0<&1 + chk=`${LPSET} "$1" "$2" "$3" "$4" "$5" 2>&1 1>&3` + [ -n "${stty2}" ] && stty ${stty2} 0<&1 + + ##### + # + # The standard error of the delivered ${LPSET} program + # is a string of letters, H, V, W, L, S, which correspond + # to cpi, lpi, width, length, and character set. A letter + # is present only if the corresponding attribute could not + # be set. + ##### + for err in ${chk} + do + case ${err} in + H ) + errmsg WARNING ${E_IP_BADCPI} \ + "can't select the character pitch \"${cpi}\"" \ + "check the valid pitches for the printer, + or consult your system administrator; + printing continues" + ;; + V ) + errmsg WARNING ${E_IP_BADLPI} \ + "can't select the line pitch \"${lpi}\"" \ + "check the valid pitches for the printer, + or consult your system administrator; + printing continues" + ;; + W ) + width=${cols} + errmsg WARNING ${E_IP_BADWIDTH} \ + "can't select the page width \"${width}\"" \ + "check the valid widths for the printer, + or consult your system administrator; + printing continues" + ;; + L ) + length=${lines} + errmsg WARNING ${E_IP_BADLENGTH} \ + "can't select the page length \"${length}\"" \ + "check the valid lengths for the printer, + or consult your system administrator; + printing continues" + ;; + S ) + errmsg WARNING ${E_IP_BADCHARSET} \ + "can't select the character set \"${CHARSET}\"" \ + "check the name given in the -S option, + or consult your system administrator; + printing continues" + ;; + esac + done +} + + +##### +# ${TPUT} is "tput" IF it works. We'll disable it if we get an +# ugly error message the first time we use it. See the TERM variable +# later in the script. +# +# NOTE: The check we use to see if "tput" works is to use an OLD +# Terminfo capability, like "lines". If it works with that it may +# still fail with some of the newer capabilities like "init" (SVR3.0) +# or "swidm" (SVR3.2), because the version of "tput" we have on your +# machine is older. Thus, on some of the code where ${TPUT} is used +# you'll see "2>/dev/null" being used to avoid ugly error messages. +##### +TPUT=tput + +##### +# Error message formatter: +# +# Invoke as +# +# errmsg severity message-number problem help +# +# where severity is "ERROR" or "WARNING", message-number is +# a unique identifier, problem is a short description of the +# problem, and help is a short suggestion for fixing the problem. +##### + +LP_ERR_LABEL="UX:lp" + +E_IP_ARGS=1 +E_IP_OPTS=2 +#E_IP_FILTER=3 +E_IP_STTY=4 +E_IP_UNKNOWN=5 +E_IP_BADFILE=6 +E_IP_BADCHARSET=7 +E_IP_BADCPI=8 +E_IP_BADLPI=9 +E_IP_BADWIDTH=10 +E_IP_BADLENGTH=11 +E_IP_ERRORS=12 # (in slow.filter) + +errmsg () { + case $1 in + ERROR ) + sev=" ERROR"; + ;; + WARNING ) + sev="WARNING"; + ;; + esac +# tag=`expr "${LP_ERR_LABEL}" : "\(.*\):"``expr "${LP_ERR_LABEL}" : ".*:\(.*\)"` + echo "${LP_ERR_LABEL}: ${sev}: $3 + TO FIX: $4" >&5 +} + + +########### +## +## Check arguments +########### + +parse () { + echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`" +} + +##### +# +# This program is invoked as +# +# ${SPOOLDIR}/.../printer request-id user title copies options files... +# +# The first three arguments are simply reprinted on the banner page, +# the fourth (copies) is used to control the number of copies to print, +# the fifth (options) is a blank separated list (in a single argument) +# of user or Spooler supplied options (without the -o prefix), +# and the last arguments are the files to print. +##### + +if [ $# -lt 5 ] +then + errmsg ERROR ${E_IP_ARGS} \ + "wrong number of arguments to interface program" \ + "consult your system administrator" + exit 1 +fi + +printer=`basename $0` +request_id=$1 +user_name=$2 +title=$3 +copies=$4 +option_list=$5 + +shift 5 +files="$*" + +nobanner="yes" # enable banners here (set it to "no") +nofilebreak="no" +stty= + +inlist= +for i in ${option_list} +do + case "${inlist}${i}" in + + + nobanner ) + nobanner="yes" + ;; + + nofilebreak ) + nofilebreak="yes" + ;; + + ##### + # + # If you want to add simple options (e.g. -o simple) + # identify them here. + ##### +# simple ) +# simple="yes" +# ;; + + + cpi=pica ) + cpi=10 + ;; + cpi=elite ) + cpi=12 + ;; + cpi=* ) + cpi=`parse ${i}` + ;; + + lpi=* ) + lpi=`parse ${i}` + ;; + + length=* ) + length=`parse ${i}` + ;; + + width=* ) + width=`parse ${i}` + ;; + + ##### + # + # If you want to add simple-value options (e.g. -o value=a) + # identify them here. + ##### +# value=* ) +# value=`parse ${i}` +# ;; + + + ##### + # + # If you want to add options that, like "stty", + # take a list (e.g. -o lopt='a b c'), identify + # them here and below (look for LOPT). + ##### + stty=* | flist=* | lpd=* ) +#LOPT stty=* | flist=* | lpd=* | lopt=* ) + + inlist=`expr "${inlist}${i}" : "^\([^=]*=\)"` + case "${i}" in + ${inlist}\'*\' ) + item=`expr "${i}" : "^[^=]*='*\(.*\)'\$"` + ;; + ${inlist}\' ) + continue + ;; + ${inlist}\'* ) + item=`expr "${i}" : "^[^=]*='*\(.*\)\$"` + ;; + ${inlist}* ) + item=`expr "${i}" : "^[^=]*=\(.*\)\$"` + ;; + *\' ) + item=`expr "${i}" : "^\(.*\)'\$"` + ;; + * ) + item="${i}" + ;; + esac + + ##### + # + # We don't dare use "eval" because a clever user could + # put something in an option value that we'd end up + # exec'ing. + ##### + case "${inlist}" in + stty= ) + stty="${stty} ${item}" + ;; + flist= ) + flist="${flist} ${item}" + ;; + lpd= ) + lpd="${lpd} ${item}" + ;; +#LOPT lopt= ) +#LOPT lopt="${lopt} ${item}" +#LOPT ;; + esac + + case "${i}" in + ${inlist}\'*\' ) + inlist= + ;; + ${inlist}\'* ) + ;; + *\' | ${inlist}* ) + inlist= + ;; + esac + ;; + + * ) + errmsg WARNING ${E_IP_OPTS} \ + "unrecognized \"-o ${i}\" option" \ + "check the option, resubmit if necessary + printing continues" + ;; + esac +done + +##### +# +# Additional ``parameters'' are passed via Shell environment +# variables: +# +# TERM The printer type (used for Terminfo access) +# CHARSET The character set to choose +# FILTER The filter to run +##### + +##### +# Set defaults for unset variables. +##### + +: ${TERM:=unknown} +tput lines 1>/dev/null 2>&1 || TPUT=: + +: ${CHARSET:=cs0} + +if [ -z "${FILTER}" ] +then + ##### + # + # If no filter is being used, we have a little routine that + # will push the data to the printer. It traps hangups (loss + # of carrier) and checks for excessive delays in sending the + # data to the printer. The lesser of the print rate of the printer + # (obtained from Terminfo) or the baud rate is used to compute + # the expected delay. If neither of these is correct, you + # may be experiencing false alarms. If so, give the correct + # rate, in characters per second, as a single argument. + # An argument of 0 means don't check for delays. + # Give an -r option to get a printout of actual delays. + # (QUOTES ARE IMPORTANT!) + ##### + # FILTER="${LPCAT} 120" # e.g. 120 CPS + FILTER="${LPCAT} 0" # allow infinite delays + # FILTER="${LPCAT} -r 0 2>/tmp/delays" # check actual delays + # FILTER=${LPCAT} +fi + +########### +## +## Initialize the printer port +########### + +##### +# +# SERIAL PORTS: +# Initialize everything. +# +# PARALLEL PORTS: +# Don't initialize baud rate. +# +# It's not obvious how to tell if a port is parallel or serial. +# However, by splitting the initialization into two steps and letting +# the serial-only part fail nicely, it'll work. +# +# Another point: The output must be a ``tty'' device. If not, don't +# bother with any of this. +##### +stty1= stty2= +tty 0<&1 1>/dev/null 2>&1 && { + + ##### + # + # First set the default parameters, + # then the requested parameters. + ##### + + stty \ + 9600 \ + 0<&1 2>/dev/null 1>&2 + stty \ + cs8 -cstopb -parenb -parodd \ + ixon -ixany \ + opost -olcuc onlcr -ocrnl -onocr -onlret -ofill \ + nl0 cr0 tab0 bs0 vt0 ff0 \ + 0<&1 2>/dev/null 1>&2 + + if [ -n "${stty}" ] + then + if stty ${stty} 0<&1 1>/dev/null 2>&5 + then + : + else + errmsg ERROR ${E_IP_STTY} \ + "stty option list failed" \ + "check the \"-o stty\" option you used, + or consult your system administrator" + exit 1 + fi + fi + + ##### + # + # Here you may want to add other port initialization code. + # Some examples: + # + # estty # for printer needing hardware flow control (3B2/EPORTS) + # fctty # for printer needing hardware flow control (3B15,3B20) + ##### + #estty 0<&1 + #fctty 0<&1 + + + ########## + # + # Find out if we have to turn off opost before initializing the + # printer and on after. Likewise, check clocal. + # + # Turning OFF opost (output postprocessing) keeps the UNIX system + # from changing what we try to send to the printer. Turning ON + # clocal keeps the UNIX system from dropping what we are trying to + # send if the printer drops DTR. An example of the former is the + # AT&T 479, which wants to send a linefeed (ASCII 10) when a page + # width of 10 is set; with opost on, this COULD BE turned into a + # carriage-return/linefeed pair. An example of the latter is the + # AT&T 455, which momentarily drops DTR when it gets the + # initialization string, is2; with clocal off, the UNIX system + # stops sending the rest of the initialization sequence at that + # point. + # + # THIS CODE MUST FOLLOW THE REST OF THE PORT INITIALIZATION CODE. + ########## + cur_stty=`stty -a 0<&3` + expr "${cur_stty}" : '.*-opost' 1>/dev/null 2>&1 \ + || stty1="${stty1} -opost" stty2="${stty2} opost" + expr "${cur_stty}" : '.*-clocal' 1>/dev/null 2>&1 \ + && stty1="${stty1} clocal" stty2="${stty2} -clocal" + expr "${cur_stty}" : '.* opost.*' 1>/dev/null 2>&1 \ + || banner_filter=${FIX386BD} + +} + + +########### +## +## Initialize the physical printer (Part I). +## Here we bring the printer to a sane state and set the page size. +########### + +########## +# +# WARNING! The "echo" command will catch backslashes (\) and +# try to interpret the characters following it. Thus, using +# "echo" to print string values obtained from "tput" is dangerous. +########## + +##### +# We're confident that most printers don't have backslashes +# in the control sequences for carriage return and form-feed. +# We're also confident that these don't contain newlines. +# We're also confident that most printers have a linefeed +# in the control sequence for doing a newline (move to beginning +# of next line), but we can't capture it like we do the +# carriage return or form-feed. Thus we set it unconditionally. +# We don't set form-feed if it isn't defined, however, because +# maybe the printer doesn't have a formfeed. If not set, we're +# out of luck. +##### + +CR=`${TPUT} cr` +[ -z "${CR}" ] && CR="\r" + +FF=`${TPUT} ff` + +NL="${CR}\n" + +lines=`${TPUT} lines` +[ -z "${lines}" -o 0 -ge "${lines}" ] && lines=66 + +cols=`${TPUT} cols` +[ -z "${cols}" -o 0 -ge "${cols}" ] && cols=132 + +##### +# +# Basic initialization. The ``else'' clause is equivalent, +# but covers cases where old Terminal Information Utilities are present. +##### +[ -n "${stty1}" ] && stty ${stty1} 0<&1 +if ${TPUT} init 2>/dev/null +then + : +else + pgm=`${TPUT} iprog` + if [ -x "${pgm}" ] + then + eval ${pgm} + fi + + ${TPUT} is1 + ${TPUT} is2 + + tabset= + if [ "8" != "`${TPUT} it`" ] + then + stty tab3 0<&1 1>/dev/null 2>&1 + + elif `${TPUT} ht >/dev/null` + then + tabset="/usr/lib/tabset/${TERM}" + if [ -r ${tabset} ] + then + cat -s ${tabset} + fi + stty tab3 0<&1 1>/dev/null 2>&1 + fi + + file=`${TPUT} if` + if [ "${tabset}" != "${file}" -a -r "${file}" ] + then + cat -s "${file}" + fi + + ${TPUT} is3 + echo "${CR}\c" +fi +[ -n "${stty2}" ] && stty ${stty2} 0<&1 + +##### +# +# Set the page size and print spacing, but not the character set. +# We will be doing the character set later (after the header). +##### +internal_lpset "${cpi}" "${lpi}" "${width}" "${length}" "" + +##### +# +# The banner page (and cancellation page) will +# use double width characters if they're available. +##### +WIDE_CS=`${TPUT} swidm 2>/dev/null` && NORM_CS=`${TPUT} rwidm 2>/dev/null` +PAD="#####${NL}" + +##### +# +# Some printers need to have the banner page filtered. +##### +case "${TERM}" in + +PS | PSR ) + banner_filter="/usr/lib/lp/postscript/postprint | /usr/lib/lp/postscript/postio" + ;; + +AppleTalk ) + banner_filter="$PAPIFHOME/papof" + ;; + +esac +if [ -n "${banner_filter}" ] +then + banner_filter="| ${banner_filter}" +fi + +##### +# +# Now that the printer is ready for printing, we're able +# to record on paper a cancellation. +##### + +cancel_banner () { + echo "${PAD}${PAD}\c" + echo "#####${WIDE_CS} Job ${request_id}${NORM_CS}${NL}\c" + echo "#####${WIDE_CS} suspended or canceled${NORM_CS}${NL}\c" + echo "${PAD}${PAD}\c" +} + +canceled () { + ${TPUT} scs 0 2>/dev/null + echo "${CR}\c" + if [ "${width:-${cols}}" -lt "${MAX_COLS_SMALL_BANNER}" ] + then + WIDE_CS= NORM_CS= + fi + cancel_banner + if [ -n "${FF}" ] + then + echo "${CR}${FF}\c" + fi +} + +##### doesn't supported yet +#trap 'eval canceled ${banner_filter}; exit_code=0 exit' 15 +trap 'exit_code=0 exit' 15 + + +########### +## +## Print the banner page +########### + +BANNER="/tmp/banner.$$"; export BANNER +BANNERFIRST="yes"; export BANNERFIRST +#BANNERLAST="yes"; export BANNERLAST + +##### +# +# You may want to change the following code to get a custom banner. +##### + +regular_banner () { + echo "${CR}\c" + echo "${PAD}${PAD}${PAD}${PAD}${PAD}\c" + echo "#####${WIDE_CS} User: ${user_name}${NORM_CS}${NL}\c" + if [ -n "$ALIAS_USERNAME" ] + then + echo "${PAD}\c" + echo "#####${WIDE_CS} Alias: ${ALIAS_USERNAME}${NORM_CS}${NL}\c" + fi + if [ -n "${title}" ] + then + echo "${PAD}\c" + echo "#####${WIDE_CS} Title: ${title}${NORM_CS}${NL}\c" + fi + echo "${PAD}\c" + echo "#####${WIDE_CS} Printed: `date '+%a %H:%M %h %d, 19%y'`${NORM_CS}${NL}\c" + echo "${PAD}\c" + echo "#####${WIDE_CS} Job number: ${request_id}${NORM_CS}${NL}\c" + echo "${PAD}${PAD}${PAD}${PAD}${PAD}\c" + if [ -n "${FF}" ] + then + echo "${CR}${FF}\c" + fi +} + +small_banner () { + echo "${CR}\c" + echo "${PAD}\c" + echo "##### User: ${user_name}${NL}\c" + if [ -n "${title}" ] + then + echo "##### Title: ${title}${NL}\c" + fi + echo "##### Date: `date '+%a %H:%M %h %d, 19%y'`${NL}\c" + echo "##### Job: ${request_id}${NL}\c" + echo "${PAD}\c" + if [ -n "${FF}" ] + then + echo "${CR}${FF}\c" + fi +} + +if [ "${width:-${cols}}" -lt "${MAX_COLS_SMALL_BANNER}" ] +then + banner=small_banner +else + banner=regular_banner +fi + +if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" ] +then + eval "${banner} ${banner_filter}" +fi + + +########### +## +## Initialize the physical printer (Part II) +## Here we select the character set. +## One could argue that this should be done before the banner is printed, +## but we don't, to keep the banner page looking consistent for the +## operator. You can move this code before the banner code if you +## disagree. If you do, combine it with the other call to "internal_lpset" +## to do everything in one shot. +########### +internal_lpset "" "" "" "" "${CHARSET}" + +########### +## +## Print some copies of the file(s) +########### + +##### +# +# The protocol between the interface program and the Spooler +# is fairly simple: +# +# All standard error output is assumed to indicate a +# fault WITH THE REQUEST. The output is mailed to the +# user who submitted the print request and the print +# request is finished. +# +# If the interface program sets a zero exit code, +# it is assumed that the file printed correctly. +# If the interface program sets a non-zero exit code +# less than 128, it is assumed that the file did not +# print correctly, and the user will be notified. +# In either case the print request is finished. +# +# If the interface program sets an exit code greater +# than 128, it is assumed that the file did not print +# because of a printer fault. If an alert isn't already +# active (see below) one will be activated. (Exit code +# 128 should not be used at all. The shell, which executes +# this program, turns SIGTERM, used to kill this program +# for a cancellation or disabling, into exit 128. The +# Spooler thus interpretes 128 as SIGTERM.) +# +# A message sent to the standard input of the ${LPTELL} +# program is assumed to describe a fault WITH THE PRINTER. +# The output is used in an alert (if alerts are defined). +# If the fault recovery is "wait" or "begin", the printer +# is disabled (killing the interface program if need be), +# and the print request is left on the queue. +# If the fault recovery is "continue", the interface program +# is allowed to wait for the printer fault to be cleared so +# it can resume printing. +# +# This interface program relies on filters to detect printer faults. +# In absence of a filter provided by the customer, it uses a simple +# filter (${LPCAT}) to detect the class of faults that cause DCD +# (``carrier'') drop. The protocol between the interface program and +# the filter: +# +# The filter should exit with zero if printing was +# successful and non-zero if printing failed because +# of a printer fault. This interface program turns a +# non-zero exit of the filter into an "exit 129" from +# itself, thus telling the Spooler that a printer fault +# (still) exists. +# +# The filter should report printer faults via a message +# to its standard error. This interface program takes all +# standard error output from the filter and feeds it as +# standard input to the ${LPTELL} program. +# +# The filter should wait for a printer fault to clear, +# and should resume printing when the fault clears. +# Preferably it should resume at the top of the page +# that was being printed when the fault occurred. +# If it waits and finishes printing, it should exit +# with a 0 exit code. If it can't wait, it should exit +# with a non-zero exit code. +# +# The interface program expects that ANY message on the +# standard error from the filter indicates a printer fault. +# Therefore, a filter should not put user (input) error +# messages on the standard error, but on the standard output +# (where the user can read them when he or she examines +# the print-out). +# +##### + +##### build papif command options +FILTER="${FILTER} -P $printer -U $user_name" +##### + +badfileyet= +i=1 +while [ $i -le $copies ] +do + for file in ${files} + do + if [ -r "${file}" ] + then + ##### + # + # Here's where we set up the $LPTELL program to + # capture fault messages, and... + # + # Here's where we print the file. + # + # We set up a pipeline to $LPTELL, but play a trick + # to get the filter's standard ERROR piped instead of + # its standard OUTPUT: Divert the standard error (#2) to + # the standard output (#1) IN THE PIPELINE. The shell + # will have changed #1 to be the pipe, not the + # printer, so diverting #2 connects it to the pipe. + # We then change the filter's #1 to a copy of the real + # standard output (the printer port) made earlier, + # so that is connected back to the printer again. + # + # We do all this inside a parenthesized expression + # so that we can get the exit code; this is necessary + # because the exit code of a pipeline is the exit + # code of the right-most command, which isn't the + # filter. + # + # These two tricks could be avoided by using a named + # pipe to connect the standard error to $LPTELL. In + # fact an early prototype of this script did just + # that; however, the named pipe introduced a timing + # problem. The processes that open a named pipe hang + # until both ends of the pipe are opened. Cancelling + # a request or disabling the printer often killed one + # of the processes, causing the other process to hang + # forever waiting for the other end of the pipe to + # be opened. + ##### + EXIT_CODE=${TMPPREFIX}e + trap '' 1 # Let the filter handle a hangup + trap '' 2 3 # and interrupts + ( + ##### + # Put the 0<${file} before the "eval" to keep + # clever users from giving a file name that + # evaluates as something to execute. + ##### + 0<${file} eval ${FILTER} 2>&1 1>&3 + echo $? >${EXIT_CODE} + ) | ${LPTELL} ${printer} + trap 'catch_hangup; exit_code=129 exit 129' 1 + trap 'catch_interrupt; exit_code=129 exit 129' 2 3 + exit_code=`cat ${EXIT_CODE}` + + if [ -n "${exit_code}" -a 0 -ne "${exit_code}" ] + then + trap '' 15 # Avoid dying from disable + sleep 4 # Give $LPTELL a chance to tell + exit_code=129 exit 129 + fi + + if [ -n "${FF}" -a "no" = "${nofilebreak}" ] + then + echo "${CR}${FF}\c" + fi + + else + + ##### + # + # Don't complain about not being able to read + # a file on second and subsequent copies, unless + # we've not complained yet. This removes repeated + # messages about the same file yet reduces the + # chance that the user can remove a file and not + # know that we had trouble finding it. + ##### + if [ "${i}" -le 1 -o -z "${badfileyet}" ] + then + errmsg WARNING ${E_IP_BADFILE} \ + "cannot read file \"${file}\"" \ + "see if the file still exists and is readable, + or consult your system administrator; + printing continues" + badfileyet=yes + fi + + fi + + done + i=`expr $i + 1` + +done + +######## banner is already handled +#if [ "no" = "${nobanner}" -a "${TERM}" = "PSR" ] +#then +# eval "${banner} ${banner_filter}" +#fi +######## + +if [ -n "${exit_code}" -a 0 -ne "${exit_code}" ] +then + exit ${exit_code} +fi + +##### +# +# Always ensure the complete job ends with a ``formfeed'', to +# let the next job start on a new page. (If someone wants to +# concatenate files, they can give them in one job.) +# So, if we haven't been putting out a ``formfeed'' between files, +# it means we haven't followed the last file with a formfeed, +# so we do it here. +##### +if [ -n "${FF}" -a "yes" = "${nofilebreak}" ] +then + echo "${CR}${FF}\c" +fi + +${DRAIN} + +exit_code=0 exit 0 diff --git a/applications/papif/papof.c b/applications/papif/papof.c new file mode 100644 index 0000000..9e6af76 --- /dev/null +++ b/applications/papif/papof.c @@ -0,0 +1,275 @@ +/* + * Basic output filter for the 4.2 spooling system + * + * Write out the banner (the input) into .banner for the input filter. + * The input filter can then print it out if it wants. + * + * Note: Do a sigstop on self when we see ^Y^A which denotes end of job. + * exiting is the WRONG thing to do at this point. + * + * Copyright (c) 1985, 1987 by The Trustees of Columbia University in the City + * of New York + * + * Author: Charlie C. Kim + * + */ + +#include +#include +#include + +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#ifdef BANNERFIRST +# ifndef BANNER +# define BANNER +# endif +#endif + +#ifdef BANNERLAST +# ifndef BANNER +# define BANNER +# endif +#endif + +#ifdef CHARGEBANNER +# ifndef BANNER +# define BANNER +# endif +#endif + +#ifdef BANNERFILE +# ifndef BANNER +# define BANNER +# endif +#endif +#ifndef BANNERFILE +# define BANNERFILE ".banner" +#endif + +#ifdef BANNER +#ifdef PSBANNER +char bannerpro[] = ".banner.pro"; +#endif PSBANNER +#endif BANNER + +FILE *bannerfile; +char *bannerfname; + +char buf[BUFSIZ]; + +main() +{ + int c, cl, i; + int dosusp; +#ifdef BANNER + char *getenv(); +#ifdef PSBANNER + int psstart, dopsbanner; +#endif PSBANNER +#endif BANNER + + while (1) { +#ifdef BANNER + if ((bannerfname = getenv("BANNER")) == NULL) + bannerfname = BANNERFILE; + if ((bannerfile = fopen(bannerfname, "w")) == NULL) { + perror("Can't open .banner"); + exit(8); + } +#ifdef PSBANNER + psstart = dopsbanner = 0; +#else PSBANNER + psbannerstart(bannerfile); +#endif PSBANNER +#endif BANNER + cl = -1, c = -1, dosusp = 0; + do { +#ifdef BANNER + for ( i = 0; i < BUFSIZ ; i++) { +#endif + cl = c; + c = getchar(); +#ifdef BANNER + buf[i] = c; +#endif + if (cl == '\031' && c == '\01') { + dosusp = 1; + break; + } +#ifdef BANNER + if (c == EOF) + break; + if (c == '\n') + break; + } + buf[i] = '\0'; + if (dosusp) + break; +#ifdef PSBANNER + if (!psstart) { + psstart++; + if (!(dopsbanner = ps_banner(bannerfile, buf))) + psbannerstart(bannerfile); + } + if (!dopsbanner && (c != EOF || i != 0)) + psbannerline(bannerfile,buf); +#else PSBANNER + if (c != EOF || i != 0) + psbannerline(bannerfile,buf); +#endif PSBANNER +#endif BANNER + } while (c != EOF && !dosusp); +#ifdef BANNER +#ifdef PSBANNER + if (!dopsbanner) + psbannerend(bannerfile); +#else PSBANNER + psbannerend(bannerfile); +#endif PSBANNER + fclose(bannerfile); /* close off file here - end of job */ +#endif BANNER + if (c == EOF) + break; +#ifdef DEBUG + fprintf(stderr,"Waiting for next job..."); +#endif DEBUG +#ifdef SIGSTOP + kill(getpid(), SIGSTOP); +#else SIGSTOP + pause(); /* KLUDGE ALERT */ +#endif SIGSTOP + } +} + +#ifdef BANNER +psbannerstart(fd) +FILE *fd; +{ + fputs("%!\n", fd); + fputs("/fs 8 def\n", fd); + fputs("/Courier findfont fs scalefont setfont\n", fd); + fputs("/vpos 72 10 mul def\n", fd); /* at 10 inches .5 inch margin */ + fputs("/LS {36 vpos moveto show /vpos vpos fs sub def} def\n", fd); +} + +psbannerline(fd,line) +FILE *fd; +unsigned char *line; +{ + int l = strlen((char *)line); + static char spaces[] = " "; + int i, pos; + unsigned char c; + + if (line[0] == '\f') + return; + putc('(', fd); + for (i = 0,pos=0; i < l ; pos++, i++) { + c = *line++; + if (c != '\t') + if (c < ' ' || c > '\177') + c = '\267'; + switch (c) { + case '(': + fputs("\\(",fd); + break; + case ')': + fputs("\\)",fd); + break; + case '\\': + fputs("\\\\",fd); + break; + case '\t': + fputs((pos%8) ? spaces+(pos % 8) : spaces, fd); + pos += (8 - (pos % 8)) - 1; + break; + default: + putc(c, fd); + break; + } + } + fputs(") LS\n", fd); +} + +psbannerend(fd) +FILE *fd; +{ + fputs("showpage\n", fd); +} + +#ifdef PSBANNER +char * +topsstr(str) +register char *str; +{ + register char *cp; + static char psbuf[BUFSIZ]; + + for(cp = psbuf ; *str ; ) { + if(*str == '(' || *str == ')' || *str == '\\') + *cp++ = '\\'; + *cp++ = *str++; + } + *cp = 0; + return(psbuf); +} + +ps_banner(fd, cp) +FILE *fd; +char *cp; +{ + register char *up, *jp, *dp; + register FILE *pro; + register int i, n; + char buf[BUFSIZ]; + + if((pro = fopen(bannerpro, "r")) == NULL) + return(0); + jp = cp; + for( ; ; ) { + if((jp = index(jp, ' ')) == NULL) { + fclose(pro); + return(0); + } + if(strncmp(jp, " Job: ", 7) == 0) { + *jp = 0; + jp += 7; + break; + } + jp++; + } + if(up = rindex(cp, ':')) + *up++ = 0; + else { + up = cp; + cp = ""; + } + dp = jp; + for( ; ; ) { + if((dp = index(dp, ' ')) == NULL) { + fclose(pro); + return(0); + } + if(strncmp(dp, " Date: ", 8) == 0) { + *dp = 0; + dp += 8; + break; + } + dp++; + } + while((i = fread(buf, 1, BUFSIZ, pro)) > 0) + fwrite(buf, 1, i, fd); + fclose(pro); + fprintf(fd, "(%s)", topsstr(cp)); + fprintf(fd, "(%s)", topsstr(up)); + fprintf(fd, "(%s)", topsstr(jp)); + fprintf(fd, "(%s) P\n", topsstr(dp)); + return(1); +} +#endif PSBANNER +#endif BANNER diff --git a/applications/papif/printcap.samp1 b/applications/papif/printcap.samp1 new file mode 100644 index 0000000..41288fc --- /dev/null +++ b/applications/papif/printcap.samp1 @@ -0,0 +1,24 @@ +Use this as a prototype if you don't need the Adobe filters. If you +do use the Adobe shell scripts, then note that papif can be used in +place of pscomm and use the standard printcap entry formed by +Transcript and adjust the shell script accordingly. + +ps:LaserWriter:A sample LaserWriter printer:\ # printer name + :lp=/dev/ps:\ # spool device + :sd=/usr/spool/lpd/ps:\ # spool directory + :pl#72:pw#85:\ # page length and width + :sf:\ # suppress form feeds + :lf=/usr/adm/ps-errs:\ # log file + :af=/usr/adm/ps.acct:\ # accounting file + :if=/usr/local/cap/ps:\ # input filter + :of=/usr/local/cap/papof: # output filter + +lp - device to print to - use any dummy file name. (Don't use /dev/null + if your systems interlocks on it (e.g. opens exclusive)) +of - output filter +if - input filter +sd - spooling directory - should be unique! +lf - logging file (useful, you should use this) +af - accounting file + + diff --git a/applications/papif/printcap.samp2 b/applications/papif/printcap.samp2 new file mode 100644 index 0000000..7997793 --- /dev/null +++ b/applications/papif/printcap.samp2 @@ -0,0 +1,22 @@ +From croft@russell.stanford.edu Thu Dec 18 09:13:31 1986 +Date: Wed, 17 Dec 86 21:00:47 pst +From: Bill Croft +Subject: samples/printcap.sample +To: cck@cu20b + +Charlie, Please pick up this file!!! +--- + + +For use when you have PSTEXT turned on..... + + + +ps|xmac|PostScript|LaserWriter in Ventura-H5, via appletalk:\ + :lp=/dev/null:if=/etc/cap/papif:\ + :du#0:\ + :sd=/usr/spool/lw:\ + :lf=/usr/spool/lw/errs:\ + :af=/usr/spool/lw/lwacct:pl#72:pw#85: + + diff --git a/cap60.patches/asip1.patch b/cap60.patches/asip1.patch new file mode 100644 index 0000000..20c6255 --- /dev/null +++ b/cap60.patches/asip1.patch @@ -0,0 +1,3801 @@ +Patch #: none yet +Type: operational change +Priority: none +Modification: add support for AppleShareIP (AFP via TCP/IP) +Submitted: David Hornsby +IMPORTANT: +IMPORTANT: This patch assumes CAP at patch level 198 +IMPORTANT: This is an interim patch only. You will need to keep this +IMPORTANT: patch file in order to reverse the code changes before patch +IMPORTANT: 199 can be applied without error. When reversing this patch, +IMPORTANT: you also need to manually remove the following files: +IMPORTANT: rm cap60/applications/aufs/afpdsi.h +IMPORTANT: rm cap60/applications/aufs/afpdsi.c +IMPORTANT: rm cap60/etc/aufsIPFilter +IMPORTANT: +IMPORTANT: To use the TCP/IP functionality, you will require AppleShare +IMPORTANT: Client version 3.7 or later, obtainable from Apple web sites +IMPORTANT: +IMPORTANT: http://appleshareip.apple.com/appleshareip/text/downloads.html +IMPORTANT: +File: cap60/applications/aufs/afpdsi.h +File: cap60/applications/aufs/afpdsi.c +File: cap60/applications/aufs/afpdt.c +File: cap60/applications/aufs/afpfork.c +File: cap60/applications/aufs/afpos.c +File: cap60/applications/aufs/afps.h +File: cap60/applications/aufs/afpserver.c +File: cap60/applications/aufs/aufs.c +File: cap60/applications/aufs/Makefile.m4 +File: cap60/lib/afp/afppacks.c +File: cap60/lib/cap/abasp.c +File: cap60/lib/cap/absched.c +File: cap60/etc/aufsIPFilter +File: cap60/samples/ash.c +File: cap60/man/AUFS.8 +File: cap60/netat/afp.h +File: cap60/netat/afpcmd.h + + +*** applications/aufs/afpdsi.h.orig Mon Jul 14 12:20:23 1997 +--- applications/aufs/afpdsi.h Fri Jul 11 20:07:07 1997 +*************** +*** 0 **** +--- 1,108 ---- ++ /* ++ * $Author: djh $ $Date: 91/03/14 13:45:20 $ ++ * $Header: afpdsi.h,v 2.2 91/03/14 13:45:20 djh Exp $ ++ * $Revision: 2.2 $ ++ * ++ */ ++ ++ /* ++ * afpdsi.h - Data Stream Interface Includes ++ * ++ * AFP via a Transport Protocol (eg: TCP/IP) ++ * ++ * AppleTalk package for UNIX ++ * ++ * The following routines implement a lightweight extra ++ * layer between AFP (as embodied in the AUFS code), the ++ * original ASP via ATP layer, and delivery via other ++ * Transport Protocol layers, currently only TCP/IP. ++ * ++ * Refer: "AppleTalk Filing Protocol 2.2 & ++ * AFP over TCP/IP Specification" ++ * ++ * SSS == Server Session Socket ++ * SLS == Session Listening Socket ++ * WSS == Workstation Session Socket ++ * ++ * Copyright (c) 1997 The University of Melbourne ++ * David Hornsby ++ * ++ */ ++ ++ /* ++ * options ++ * ++ */ ++ #define DSI_OPT_REQQ 0x00 ++ #define DSI_OPT_ATTQ 0x01 ++ ++ #define DSI_OPT_REQLEN 4 ++ #define DSI_OPT_ATTLEN 4 ++ ++ /* ++ * quantum sizes ++ * ++ */ ++ #define DSI_ATTN_SIZ 2 ++ #define DSI_SRVR_CMD 1500 ++ #define DSI_SRVR_MAX 64*1024 ++ ++ /* ++ * the DSI header will be inserted in front of ++ * every AFP request or reply packet ++ * ++ */ ++ ++ struct dsi_hdr { ++ byte dsi_flags; /* used to determine packet type */ ++ #define DSI_REQ_FLAG 0x00 ++ #define DSI_REP_FLAG 0x01 ++ byte dsi_command; /* similar to ASP commands, except WrtCont */ ++ #define DSICloseSession 1 ++ #define DSICommand 2 ++ #define DSIGetStatus 3 ++ #define DSIOpenSession 4 ++ #define DSITickle 5 ++ #define DSIWrite 6 ++ #define DSIAttention 8 ++ word dsi_requestID; /* req ID on per-session basis, wraps */ ++ dword dsi_err_offset; /* error for reply, offset for write, else 0 */ ++ dword dsi_data_len; /* total data length following dsi_hdr */ ++ dword dsi_reserved; /* reserved for future, should be zero */ ++ }; ++ ++ /* ++ * per-session demux info ++ * ++ */ ++ struct dsi_sess { ++ int sesstype; ++ #define DSI_SESSION_ATALK 0x01 ++ #define DSI_SESSION_TCPIP 0x02 ++ int state; /* type of DSI data expected */ ++ #define DSI_STATE_HDR 0x01 ++ #define DSI_STATE_AFP 0x02 ++ #define DSI_STATE_DAT 0x03 ++ #define DSI_STATE_REP 0x04 ++ char *ptr; /* where we have to put incoming data */ ++ int lenleft; /* amount of data expected to arrive */ ++ int timeout; /* per-session tickle timer */ ++ #define DSI_TIMEOUT 5*4 ++ word sess_id_out; /* outgoing session ID (0-65535) */ ++ word sess_id_in; /* incoming session ID (0-65535) */ ++ struct dsi_hdr hdr; /* current incoming header (for reply) */ ++ ASPQE *aspqe; /* callback data for GetRequest etc. */ ++ }; ++ ++ /* ++ * IP filter list ++ * ++ */ ++ #define MAXIPFILTERS 100 ++ #define MAXIPFILTSIZ sizeof(struct ipFilter) ++ ++ struct ipFilter { ++ sword perm; ++ dword mask; ++ dword addr; ++ }; +*** applications/aufs/afpdsi.c.orig Mon Jul 14 14:04:28 1997 +--- applications/aufs/afpdsi.c Sun Apr 19 19:21:07 1998 +*************** +*** 0 **** +--- 1,2029 ---- ++ /* ++ * $Author: djh $ $Date: 91/03/14 13:45:20 $ ++ * $Header: afpdsi.c,v 2.2 91/03/14 13:45:20 djh Exp $ ++ * $Revision: 2.2 $ ++ * ++ */ ++ ++ /* ++ * afpdsi.c - Data Stream Interface ++ * ++ * AFP via a Transport Protocol (eg: TCP/IP) ++ * ++ * AppleTalk package for UNIX ++ * ++ * The following routines implement a lightweight extra ++ * layer between AFP (as embodied in the AUFS code), the ++ * original ASP via ATP layer, and delivery via other ++ * Transport Protocol layers, currently only TCP/IP. ++ * ++ * Refer: "AppleTalk Filing Protocol 2.2 & ++ * AFP over TCP/IP Specification" ++ * ++ * SSS == Server Session Socket ++ * SLS == Session Listening Socket ++ * WSS == Workstation Session Socket ++ * ++ * Copyright (c) 1997 The University of Melbourne ++ * David Hornsby ++ * ++ */ ++ ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include "../../lib/cap/abasp.h" /* urk */ ++ #include "afpdsi.h" ++ ++ #ifdef HAVE_WRITEV ++ #include ++ #endif /* HAVE_WRITEV */ ++ ++ /* ++ * aufs AFP routines ++ * ++ */ ++ int dsiInit(); ++ int dsiGetSession(); ++ int dsiFork(); ++ int dsiTickleUserRoutine(); ++ int dsiGetRequest(); ++ int dsiWrtContinue(); ++ int dsiWrtReply(); ++ int dsiCmdReply(); ++ int dsiAttention(); ++ int dsiGetNetworkInfo(); ++ int dsiGetParms(); ++ int dsiCloseSession(); ++ int dsiShutdown(); ++ int dsiTCPIPCloseSLS(); ++ ++ /* ++ * AppleTalk Session Protocol routines ++ * (including some formerly 'private') ++ * ++ */ ++ int SPInit(); ++ int SPGetSession(); ++ int SPFork(); ++ int SPTickleUserRoutine(); ++ int SPGetRequest(); ++ int SPWrtContinue(); ++ int SPWrtReply(); ++ int SPCmdReply(); ++ int SPAttention(); ++ int SPGetNetworkInfo(); ++ int SPGetParms(); ++ int SPCloseSession(); ++ int SPShutdown(); ++ ++ ASPSSkt *aspsskt_find_slsrefnum(); ++ ASPSkt *aspskt_find_sessrefnum(); ++ ASPSkt *aspskt_find_active(); ++ ASPQE *create_aq(); ++ ++ void stopasptickle(); ++ void stop_ttimer(); ++ void delete_aq(); ++ void Timeout(); ++ ++ /* ++ * local TCP/IP routines ++ * ++ */ ++ int dsiTCPIPInit(); ++ int dsiTCPIPFork(); ++ int dsiTCPIPGetRequest(); ++ int dsiTCPWrtContinue(); ++ int dsiTCPIPWrtReply(); ++ int dsiTCPIPCmdReply(); ++ int dsiTCPIPReply(); ++ int dsiTCPIPTickleUserRoutine(); ++ int dsiTCPIPCloseSession(); ++ int dsiTCPIPAttention(); ++ int dsiTCPIPGetNetworkInfo(); ++ int dsiTCPIPWrite(); ++ int dsiTCPIPCloseSLS(); ++ ++ /* ++ * globals ++ * ++ */ ++ #ifdef DEBUG_AFP_CMD ++ extern FILE *dbg; ++ #endif /* DEBUG_AFP_CMD */ ++ ++ extern int errno; ++ extern int asip_enable; ++ extern char *dsiTCPIPFilter; ++ private struct dsi_sess *dsi_session = NULL; ++ ++ /* ++ * DSI transport-layer demultiplexing ++ * ++ */ ++ ++ /* ++ * Set up a Server Listening Socket (SLS) for both ++ * ASP/ATP and TCP/IP. ++ * ++ * SLSEntityIdentifier - server AppleTalk address ++ * ServiceStatusBlock - pointer to ServerInfo data ++ * ServiceStatusBlockSize - ServerInfo data size ++ * SLSRefNum - return a session RefNum ++ * ++ */ ++ ++ int ++ dsiInit(SLSEntityIdentifier, ServiceStatusBlock, ++ ServiceStatusBlockSize, SLSRefNum) ++ AddrBlock *SLSEntityIdentifier; /* SLS Net id */ ++ char *ServiceStatusBlock; /* block with status info */ ++ int ServiceStatusBlockSize; /* size of status info */ ++ int *SLSRefNum; /* sls ref num return place */ ++ { ++ int result; ++ extern int numasp; ++ ++ /* ++ * allocate & initialise space for DSI session data ++ * ++ */ ++ if (numasp <= 0) ++ return(-1); ++ ++ if ((dsi_session = (struct dsi_sess *) ++ malloc(sizeof(struct dsi_sess)*numasp)) == NULL) ++ return(-1); ++ ++ bzero((char *)dsi_session, sizeof(struct dsi_sess)*numasp); ++ ++ /* ++ * allocate SLSRefNum, initialise AppleTalk Session Protocol SLS ++ * ++ */ ++ result = SPInit(SLSEntityIdentifier, ServiceStatusBlock, ++ ServiceStatusBlockSize, SLSRefNum); ++ ++ #ifdef DEBUG_AFP_CMD ++ if (dbg != NULL) { ++ fprintf(dbg, "** SPInit(): (PID %d)\n", getpid()); ++ fprintf(dbg, "\tSLSRefNum: %d\n", *SLSRefNum); ++ fprintf(dbg, "\tServiceStatusBlockSize: %d\n", ServiceStatusBlockSize); ++ fprintf(dbg, "\tresult: %d\n", result); ++ fprintf(dbg, "\n\n"); ++ fflush(dbg); ++ } ++ #endif /* DEBUG_AFP_CMD */ ++ ++ if (result != noErr) ++ return(result); ++ ++ /* ++ * if enabled, setup TCP/IP SLS (uses same SLSRefNum) ++ * ++ */ ++ if (asip_enable) ++ if (dsiTCPIPInit(SLSEntityIdentifier, ServiceStatusBlock, ++ ServiceStatusBlockSize, SLSRefNum) != noErr) ++ asip_enable = 0; ++ ++ return(noErr); ++ } ++ ++ /* ++ * set up to wait for a new session to start ++ * ++ * SLSRefNum - Session Listening Socket RefNum ++ * SessRefNum - returns new session reference number ++ * comp - completion flag/error ++ * ++ */ ++ ++ int ++ dsiGetSession(SLSRefNum, SessRefNum, comp) ++ int SLSRefNum; ++ int *SessRefNum; ++ int *comp; ++ { ++ int result; ++ ++ /* ++ * get a session reference number, ++ * ++ */ ++ result = SPGetSession(SLSRefNum, SessRefNum, comp); ++ ++ #ifdef DEBUG_AFP_CMD ++ if (dbg != NULL) { ++ fprintf(dbg, "** SPGetSession(): (PID %d)\n", getpid()); ++ fprintf(dbg, "\tSLSRefNum: %d\n", SLSRefNum); ++ fprintf(dbg, "\tSessRefNum: %d\n", *SessRefNum); ++ fprintf(dbg, "\tcomp: %d\n", *comp); ++ fprintf(dbg, "\tresult: %d\n", result); ++ fprintf(dbg, "\n\n"); ++ fflush(dbg); ++ } ++ #endif /* DEBUG_AFP_CMD */ ++ ++ if (result != noErr) ++ return(result); ++ ++ /* ++ * assume that this session is going to be ++ * AppleTalk, until we find out otherwise ++ * (this depends on what type of OpenSession ++ * packet actually arrives) ++ * ++ */ ++ dsi_session[*SessRefNum].sesstype = DSI_SESSION_ATALK; ++ ++ /* ++ * initialise data structure for DSI state ++ * ++ */ ++ dsi_session[*SessRefNum].timeout = 0; ++ dsi_session[*SessRefNum].aspqe = NULL; ++ dsi_session[*SessRefNum].sess_id_in = 0; ++ dsi_session[*SessRefNum].sess_id_out = 0; ++ dsi_session[*SessRefNum].state = DSI_STATE_HDR; ++ dsi_session[*SessRefNum].lenleft = sizeof(struct dsi_hdr); ++ dsi_session[*SessRefNum].ptr = (char *)&dsi_session[*SessRefNum].hdr; ++ ++ return(noErr); ++ } ++ ++ /* ++ * fork and create new process to handle session ++ * ++ * SessRefNum - session reference number ++ * stickle - want server tickle ++ * ctickle - want client tickle ++ * ++ */ ++ ++ int ++ dsiFork(SessRefNum, stickle, ctickle) ++ int SessRefNum; ++ int stickle; ++ int ctickle; ++ { ++ /* ++ * if AppleTalk, hand off to Session Protocol ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) ++ return(SPFork(SessRefNum, stickle, ctickle)); ++ ++ /* ++ * handle locally for TCP/IP ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) ++ return(dsiTCPIPFork(SessRefNum, stickle, ctickle)); ++ ++ return(ParamErr); ++ } ++ ++ /* ++ * set the user-timeout routine and argument ++ * ++ * this needs to be handled in the parent ++ * process for AppleTalk connections and in ++ * the child process for TCP/IP connections ++ * ++ * 'pid' was obtained from the approriate fork() ++ * ++ */ ++ ++ int ++ dsiTickleUserRoutine(SessRefNum, routine, pid) ++ int SessRefNum; ++ int (*routine)(); ++ int pid; ++ { ++ /* ++ * AppleTalk ++ * ++ */ ++ if (pid) ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) ++ return(SPTickleUserRoutine(SessRefNum, routine, pid)); ++ ++ /* ++ * TCP/IP ++ * ++ */ ++ if (!pid) ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) ++ return(dsiTCPIPTickleUserRoutine(SessRefNum, routine, getpid())); ++ ++ return(noErr); ++ } ++ ++ /* ++ * set up to wait for a request on SSS ++ * ++ * SessRefNum - session reference number ++ * ReqBuff - request command buffer ++ * ReqBuffSize - request command buffer size ++ * ReqRefNum - pointer to a special command block ++ * SPReqType - returns command request type ++ * ActRcvdReqLen - returns command length ++ * comp - completion flag/error ++ * ++ */ ++ ++ int ++ dsiGetRequest(SessRefNum, ReqBuff, ReqBuffSize, ++ ReqRefNum, SPReqType, ActRcvdReqLen, comp) ++ int SessRefNum; ++ char *ReqBuff; ++ int ReqBuffSize; ++ ASPQE **ReqRefNum; ++ int *SPReqType; ++ int *ActRcvdReqLen; ++ int *comp; ++ { ++ /* ++ * AppleTalk ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) ++ return(SPGetRequest(SessRefNum, ReqBuff, ReqBuffSize, ++ ReqRefNum, SPReqType, ActRcvdReqLen, comp)); ++ ++ /* ++ * TCP/IP ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) ++ return(dsiTCPIPGetRequest(SessRefNum, ReqBuff, ReqBuffSize, ++ ReqRefNum, SPReqType, ActRcvdReqLen, comp)); ++ ++ return(ParamErr); ++ } ++ ++ /* ++ * 'read' data sent by client ++ * ++ * SessRefNum - session reference number ++ * ReqRefNum - client connection details (addr, TID) ++ * Buffer - final location for data ++ * BufferSize - maximum amount of data we can handle ++ * ActLenRcvd - actual amount of date received ++ * atptimeout - ATP get data timeout ++ * comp - completion flag/error ++ * ++ */ ++ ++ int ++ dsiWrtContinue(SessRefNum, ReqRefNum, Buffer, BufferSize, ++ ActLenRcvd, atptimeout, comp) ++ int SessRefNum; ++ ASPQE *ReqRefNum; ++ char *Buffer; ++ int BufferSize; ++ int *ActLenRcvd; ++ int atptimeout; ++ int *comp; ++ { ++ /* ++ * AppleTalk ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) ++ return(SPWrtContinue(SessRefNum, ReqRefNum, Buffer, ++ BufferSize, ActLenRcvd, atptimeout, comp)); ++ ++ /* ++ * TCP/IP ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) ++ return(dsiTCPIPWrtContinue(SessRefNum, ReqRefNum, Buffer, ++ BufferSize, ActLenRcvd, atptimeout, comp)); ++ ++ return(ParamErr); ++ } ++ ++ /* ++ * reply to a write request sent to our SSS ++ * ++ * SessRefNum - session reference number ++ * ReqRefNum - client connection details (addr, TID) ++ * CmdResult - return result ++ * CmdReplyData - return data ++ * CmdReplyDataSize - return data size ++ * comp - completion flag/error ++ * ++ */ ++ ++ int ++ dsiWrtReply(SessRefNum, ReqRefNum, CmdResult, ++ CmdReplyData, CmdReplyDataSize, comp) ++ int SessRefNum; ++ ASPQE *ReqRefNum; ++ dword CmdResult; ++ char *CmdReplyData; ++ int CmdReplyDataSize; ++ int *comp; ++ { ++ /* ++ * AppleTalk ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) ++ return(SPWrtReply(SessRefNum, ReqRefNum, CmdResult, ++ CmdReplyData, CmdReplyDataSize, comp)); ++ ++ /* ++ * TCP/IP ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) ++ return(dsiTCPIPWrtReply(SessRefNum, ReqRefNum, CmdResult, ++ CmdReplyData, CmdReplyDataSize, comp)); ++ ++ return(ParamErr); ++ } ++ ++ /* ++ * Reply to a command request sent to our SSS ++ * ++ * SessRefNum - session reference number ++ * ReqRefNum - client connection details (addr, TID) ++ * CmdResult - return result ++ * CmdReplyData - return data ++ * CmdReplyDataSize - return data size ++ * comp - completion flag/error ++ * ++ */ ++ ++ int ++ dsiCmdReply(SessRefNum, ReqRefNum, CmdResult, ++ CmdReplyData, CmdReplyDataSize, comp) ++ int SessRefNum; ++ ASPQE *ReqRefNum; ++ dword CmdResult; ++ char *CmdReplyData; ++ int CmdReplyDataSize; ++ int *comp; ++ { ++ /* ++ * AppleTalk ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) ++ return(SPCmdReply(SessRefNum, ReqRefNum, CmdResult, ++ CmdReplyData, CmdReplyDataSize, comp)); ++ ++ /* ++ * TCP/IP ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) ++ return(dsiTCPIPCmdReply(SessRefNum, ReqRefNum, CmdResult, ++ CmdReplyData, CmdReplyDataSize, comp)); ++ ++ return(ParamErr); ++ } ++ ++ /* ++ * send an Attention signal to WSS ++ * ++ * SessRefNum - session reference number ++ * AttentionCode - attention message ++ * atpretries - ATP Retries ++ * atptimeout - ATP Timeout ++ * comp - completion falg/error ++ * ++ */ ++ ++ int ++ dsiAttention(SessRefNum, AttentionCode, atpretries, atptimeout, comp) ++ int SessRefNum; ++ word AttentionCode; ++ int atpretries; ++ int *comp; ++ { ++ /* ++ * AppleTalk ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) ++ return(SPAttention(SessRefNum, AttentionCode, ++ atpretries, atptimeout, comp)); ++ ++ /* ++ * TCP/IP ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) ++ return(dsiTCPIPAttention(SessRefNum, AttentionCode, ++ atpretries, atptimeout, comp)); ++ ++ return(ParamErr); ++ } ++ ++ /* ++ * return remote address of session client ++ * ++ */ ++ ++ int ++ dsiGetNetworkInfo(SessRefNum, addr) ++ int SessRefNum; ++ AddrBlock *addr; ++ { ++ /* ++ * AppleTalk ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) ++ return(SPGetNetworkInfo(SessRefNum, addr)); ++ ++ /* ++ * TCP/IP ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) ++ return(dsiTCPIPGetNetworkInfo(SessRefNum, addr)); ++ ++ return(ParamErr); ++ } ++ ++ /* ++ * Get server operating parameters (these numbers are used ++ * to malloc() the appropriate amount of buffer space). ++ * ++ * MaxCmdSize - maximum single packet size ++ * QuantumSize - maximum outstanding data ++ * ++ * For ASP/ATP: ++ * MaxCmdSize = atpMaxData (578) ++ * QuantumSize = atpMaxData*atpMaxNum (578*8) ++ * ++ * For TCP/IP: ++ * MaxCmdSize = AFP Command Size (1500) ++ * QuantumSize = Data Chunk Size (65536) ++ * ++ */ ++ ++ int ++ dsiGetParms(MaxCmdSize, QuantumSize) ++ int *MaxCmdSize; ++ int *QuantumSize; ++ { ++ if (asip_enable) { ++ *MaxCmdSize = DSI_SRVR_CMD; ++ *QuantumSize = DSI_SRVR_MAX; ++ return(noErr); ++ } ++ ++ return(SPGetParms(MaxCmdSize, QuantumSize)); ++ } ++ ++ /* ++ * Close down a SSS ++ * ++ * SessRefNum - Session reference number ++ * atpretries - ATP Retries ++ * atptimeout - ATP Timeout ++ * comp - completion flag/error ++ * ++ */ ++ ++ int ++ dsiCloseSession(SessRefNum, atpretries, atptimeout, comp) ++ int SessRefNum; ++ int atpretries; ++ int atptimeout; ++ int *comp; ++ { ++ /* ++ * AppleTalk ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_ATALK) ++ return(SPCloseSession(SessRefNum, atpretries, atptimeout, comp)); ++ ++ /* ++ * TCP/IP ++ * ++ */ ++ if (dsi_session[SessRefNum].sesstype == DSI_SESSION_TCPIP) ++ return(dsiTCPIPCloseSession(SessRefNum, atpretries, atptimeout, comp)); ++ ++ return(ParamErr); ++ } ++ ++ /* ++ * shutdown session ++ * ++ * SessRefNum - session reference number ++ * ++ */ ++ ++ int ++ dsiShutdown(SessRefNum) ++ int SessRefNum; ++ { ++ /* ++ * clean up our session data ++ * ++ */ ++ if (dsi_session[SessRefNum].aspqe != NULL) ++ delete_aspaqe(dsi_session[SessRefNum].aspqe); ++ ++ dsi_session[SessRefNum].timeout = 0; ++ dsi_session[SessRefNum].aspqe = NULL; ++ dsi_session[SessRefNum].sess_id_in = 0; ++ dsi_session[SessRefNum].sess_id_out = 0; ++ dsi_session[SessRefNum].state = DSI_STATE_HDR; ++ dsi_session[SessRefNum].sesstype = DSI_SESSION_ATALK; ++ dsi_session[SessRefNum].lenleft = sizeof(struct dsi_hdr); ++ dsi_session[SessRefNum].ptr = (char *)&dsi_session[SessRefNum].hdr; ++ ++ /* ++ * then clean up the ASP stuff ++ * ++ */ ++ return(SPShutdown(SessRefNum)); ++ } ++ ++ #ifdef DEBUG_AFP_CMD ++ /* ++ * return descriptive command name string ++ * ++ */ ++ char * ++ dsi_cmd(cmd) ++ int cmd; ++ { ++ switch (cmd) { ++ case DSIGetStatus: ++ return("DSIGetStatus"); ++ break; ++ case DSIOpenSession: ++ return("DSIOpenSession"); ++ break; ++ case DSICommand: ++ return("DSICommand"); ++ break; ++ case DSIWrite: ++ return("DSIWrite"); ++ break; ++ case DSIAttention: ++ return("DSIAttention"); ++ break; ++ case DSITickle: ++ return("DSITickle"); ++ break; ++ case DSICloseSession: ++ return("DSICloseSession"); ++ break; ++ } ++ return("UNKNOWN"); ++ } ++ #endif /* DEBUG_AFP_CMD */ ++ ++ /* ++ * TCP/IP related routines ++ * ++ */ ++ ++ /* ++ * open and initialise TCP/IP SLS port ++ * ++ * "The interface will register the AFP server on a well-known ++ * (static) data stream port. In case of TCP, it will be TCP ++ * port number 548". ++ * ++ */ ++ ++ private int slsskt = -1; ++ private struct sockaddr_in lsin; ++ ++ int ++ dsiTCPIPInit(SLSEntityIdentifier, ServiceStatusBlock, ++ ServiceStatusBlockSize, SLSRefNum) ++ AddrBlock *SLSEntityIdentifier; /* SLS Net id */ ++ char *ServiceStatusBlock; /* block with status info */ ++ int ServiceStatusBlockSize; /* size of status info */ ++ int *SLSRefNum; /* sls ref num return place */ ++ { ++ int aport, len; ++ extern u_int asip_addr; ++ extern u_short asip_port; ++ private int dsiTCPIPSLSListener(); ++ struct protoent *t, *getprotobyname(); ++ ++ /* ++ * open a stream socket ++ * ++ */ ++ if ((slsskt = socket(AF_INET, SOCK_STREAM, 0)) < 0) ++ return(slsskt); ++ ++ bzero((char *)&lsin, sizeof(lsin)); ++ ++ lsin.sin_family = AF_INET; ++ lsin.sin_port = htons(asip_port); ++ lsin.sin_addr.s_addr = htonl(asip_addr); ++ ++ /* ++ * want to send data as it becomes available ++ * ++ */ ++ len = 1; ++ t = getprotobyname("tcp"); ++ aport = (t == NULL) ? IPPROTO_TCP : t->p_proto; ++ setsockopt(slsskt, aport, TCP_NODELAY, (char *)&len, sizeof(int)); ++ ++ /* ++ * bind to ipaddr:port selected by AUFS -B option ++ * (defaults to INADDR_ANY:548) ++ * ++ */ ++ if (bind(slsskt, (struct sockaddr *)&lsin, sizeof(lsin)) != 0) { ++ close(slsskt); ++ return(-1); ++ } ++ ++ /* ++ * start listening for connection attempts ++ * ++ */ ++ if (listen(slsskt, 5) != 0) { ++ close(slsskt); ++ return(-1); ++ } ++ ++ /* ++ * register a callback routine to handle SLS connection requests ++ * ++ */ ++ fdlistener(slsskt, dsiTCPIPSLSListener, NULL, *SLSRefNum); ++ ++ return(noErr); ++ } ++ ++ /* ++ * fdlistener() callback routine for TCP/IP connection attempts ++ * ++ * accept() the connection and register a data listener for ++ * incoming connection/getstatus packets. ++ * ++ */ ++ ++ private int ++ dsiTCPIPSLSListener(fd, none, SLSRefNum) ++ int fd; ++ caddr_t none; ++ int SLSRefNum; ++ { ++ int len, acc; ++ int illegal = 0; ++ struct sockaddr_in rsin; ++ extern u_short asip_port; ++ private int dsiTCPIPIllegalIP(); ++ private int dsiTCPIPSSSListener(); ++ ++ len = sizeof(struct sockaddr_in); ++ if ((acc = accept(fd, (struct sockaddr *)&rsin, &len)) < 0) ++ return(acc); ++ ++ /* ++ * check our IP address filter for ++ * a disallowed source IP address ++ * ++ */ ++ if (!dsiTCPIPIllegalIP(&rsin)) ++ fdlistener(acc, dsiTCPIPSSSListener, NULL, SLSRefNum); ++ else ++ close(acc), illegal = 1; ++ ++ #ifdef DEBUG_AFP_CMD ++ if (dbg != NULL) { ++ fprintf(dbg, ++ "** AppleShareIP connection attempt to port %d from %s:%d (PID %d)\n", ++ asip_port, inet_ntoa(rsin.sin_addr), ntohs(rsin.sin_port), getpid()); ++ if (illegal) ++ fprintf(dbg, "** Rejected by IP address filter (%s)\n", dsiTCPIPFilter); ++ fprintf(dbg, "\n\n"); ++ fflush(dbg); ++ } ++ #endif /* DEBUG_AFP_CMD */ ++ ++ return(noErr); ++ } ++ ++ /* ++ * fdlistener() callback routine for incoming DSI requests ++ * ++ * "An AFP server expects two command types, that is, DSIOpenSession ++ * or DSIGetStatus after the data stream connection establishment. ++ * DSIOpenSession command confirms the clients commitment to open an ++ * actual DSI session. There is a 1-to-1 mapping between the data ++ * stream connection and a DSI session. DSIGetSTatus command replies ++ * the server status followed by the connection tear down by an AFP ++ * server". ++ * ++ * This handler is interested only in DSIGetStatus or DSIOpenSession ++ * requests. Once the session is open, we unregister the fd with this ++ * handler and re-register it with the generic session handler. ++ * ++ */ ++ ++ private int ++ dsiTCPIPSSSListener(fd, none, SLSRefNum) ++ int fd; ++ caddr_t none; ++ int SLSRefNum; ++ { ++ int len; ++ int optlen; ++ ASPSkt *as; ++ ASPSSkt *sas; ++ int SessRefNum; ++ char reply_opt[8]; ++ struct dsi_hdr hdr; ++ char *optptr, *reqst_opt; ++ private int dsiTCPIPSessListener(); ++ ++ /* ++ * hopefully there are at least sizeof(hdr) bytes available to read ++ * (of course, there may not be, but the extra trouble of keeping a ++ * per stream partial header for just DSIGetStatus and DSIOpenSession ++ * isn't really worth it). ++ * ++ */ ++ if ((len = read(fd, (char *)&hdr, sizeof(hdr))) != sizeof(hdr)) { ++ fdunlisten(fd); ++ close(fd); ++ return(-1); ++ } ++ ++ #ifdef DEBUG_AFP_CMD ++ if (dbg != NULL) { ++ char *dsi_cmd(); ++ fprintf(dbg, "<< AppleShareIP DSI header (PID %d, session startup):\n", ++ getpid()); ++ fprintf(dbg, "\tFlags: %02x (%s)\n", hdr.dsi_flags, ++ (hdr.dsi_flags == DSI_REQ_FLAG) ? "Request" : "REPLY!!"); ++ fprintf(dbg, "\tCommand: %02x (%s)\n", hdr.dsi_command, ++ dsi_cmd(hdr.dsi_command)); ++ fprintf(dbg, "\tRequestID: %d\n", ntohs(hdr.dsi_requestID)); ++ fprintf(dbg, "\tErrCode/DataOffset: %d\n", ntohl(hdr.dsi_err_offset)); ++ fprintf(dbg, "\tDataLength: %d\n", ntohl(hdr.dsi_data_len)); ++ fprintf(dbg, "\tReserved: %d\n\n\n", ntohl(hdr.dsi_reserved)); ++ fflush(dbg); ++ } ++ #endif /* DEBUG_AFP_CMD */ ++ ++ /* ++ * not interested in Replies ++ * (should be none) ++ * ++ */ ++ if (hdr.dsi_flags != DSI_REQ_FLAG) ++ return(noErr); ++ ++ /* ++ * process the request ++ * ++ */ ++ switch (hdr.dsi_command) { ++ case DSIGetStatus: ++ /* dig out saved server status info for this SLS */ ++ if ((sas = aspsskt_find_slsrefnum(SLSRefNum)) != NULL) { ++ hdr.dsi_flags = DSI_REP_FLAG; ++ hdr.dsi_err_offset = htonl(noErr); ++ hdr.dsi_data_len = htonl(sas->ssbl); ++ hdr.dsi_reserved = htonl(0x00000000); ++ /* send server status information */ ++ dsiTCPIPWrite(fd, &hdr, (char *)sas->ssb, sas->ssbl); ++ } ++ /* fall through */ ++ default: /* tear down connection */ ++ fdunlisten(fd); ++ close(fd); ++ break; ++ case DSIOpenSession: ++ /* search for SLS next waiting session */ ++ if ((as = aspskt_find_active(SLSRefNum)) == NULL) { ++ hdr.dsi_flags = DSI_REP_FLAG; ++ hdr.dsi_err_offset = htonl(ServerBusy); ++ hdr.dsi_data_len = htonl(0x00000000); ++ hdr.dsi_reserved = htonl(0x00000000); ++ dsiTCPIPWrite(fd, &hdr, NULL, 0); ++ fdunlisten(fd); ++ close(fd); ++ break; ++ } ++ /* check for incoming OpenSession options */ ++ if ((optlen = ntohl(hdr.dsi_data_len)) > 0) { ++ if ((reqst_opt = (char *)malloc(optlen)) != NULL) { ++ optptr = reqst_opt; ++ while (optlen > 0) { ++ if ((len = read(fd, optptr, optlen)) < 0) { ++ fdunlisten(fd); ++ close(fd); ++ break; ++ } ++ optlen -= len; ++ optptr += len; ++ } ++ /* ++ * one day we might actually care ++ * ++ dsi_parse_option(optptr); ++ * ++ */ ++ free(reqst_opt); ++ } ++ } ++ /* start session */ ++ as->ss = fd; ++ as->state = SP_STARTED; ++ SessRefNum = as->SessRefNum; ++ /* mark this session as type TCP/IP */ ++ dsi_session[SessRefNum].sesstype = DSI_SESSION_TCPIP; ++ /* set up state for this session */ ++ dsi_session[SessRefNum].state = DSI_STATE_HDR; ++ dsi_session[SessRefNum].lenleft = sizeof(struct dsi_hdr); ++ dsi_session[SessRefNum].ptr = (char *)&dsi_session[SessRefNum].hdr; ++ dsi_session[SessRefNum].sess_id_in = ntohs(hdr.dsi_requestID)+1; ++ dsi_session[SessRefNum].sess_id_out = 0; ++ dsi_session[SessRefNum].aspqe = NULL; ++ dsi_session[SessRefNum].timeout = 0; ++ /* set OpenSession reply option */ ++ optlen = DSI_OPT_REQLEN+2; ++ reply_opt[0] = DSI_OPT_REQQ; ++ reply_opt[1] = DSI_OPT_REQLEN; ++ reply_opt[2] = (DSI_SRVR_MAX >> 24) & 0xff; ++ reply_opt[3] = (DSI_SRVR_MAX >> 16) & 0xff; ++ reply_opt[4] = (DSI_SRVR_MAX >> 8) & 0xff; ++ reply_opt[5] = (DSI_SRVR_MAX) & 0xff; ++ /* setup response header */ ++ hdr.dsi_flags = DSI_REP_FLAG; ++ hdr.dsi_err_offset = htonl(noErr); ++ hdr.dsi_data_len = htonl(optlen); ++ hdr.dsi_reserved = htonl(0x00000000); ++ /* send OpenSession Reply */ ++ dsiTCPIPWrite(fd, &hdr, reply_opt, optlen); ++ /* move fd to session handler */ ++ fdunlisten(fd); ++ fdlistener(fd, dsiTCPIPSessListener, (caddr_t)as, SessRefNum); ++ *as->comp = noErr; ++ return(noErr); ++ break; ++ } ++ ++ return(noErr); ++ } ++ ++ /* ++ * data listener for opened sessions ++ * ++ * At any time the data listener can be in one of four states, ++ * waiting until the expected amount of data has arrived, or a ++ * reply has been sent, freeing the header for re-use: ++ * ++ * DSI_STATE_HDR - reading the 16-byte DSI header ++ * DSI_STATE_AFP - reading the AFP command data ++ * DSI_STATE_DAT - reading the DSIWrite data ++ * DSI_STATE_REP - waiting until reply is sent ++ * ++ */ ++ ++ private int ++ dsiTCPIPSessListener(fd, as, SessRefNum) ++ int fd; ++ ASPSkt *as; ++ int SessRefNum; ++ { ++ int len; ++ int comp; ++ char *ptr; ++ ASPQE *aspqe; ++ atpProto *ap; ++ struct dsi_hdr *hdr; ++ ++ /* ++ * better have a waiting request ++ * ++ */ ++ if ((aspqe = dsi_session[SessRefNum].aspqe) == NULL) { ++ logit(0, "Incoming TCP/IP data but no pending request"); ++ dsiTCPIPCloseSession(SessRefNum, 0, 0, &comp); ++ return(-1); ++ } ++ ++ /* ++ * ignore available data until reply is sent ++ * (reply uses the sessionID in DSI header) or ++ * dsiTCPIPWrtContinue() changes the state to ++ * DSI_STATE_DAT ++ * ++ */ ++ if (dsi_session[SessRefNum].state == DSI_STATE_REP) ++ return(noErr); ++ ++ /* ++ * read DSI header or data from the ++ * tcp/ip stream as it comes in ++ * ++ */ ++ len = dsi_session[SessRefNum].lenleft; ++ ptr = dsi_session[SessRefNum].ptr; ++ ++ if ((len = read(fd, ptr, len)) < 0) { ++ logit(0, "TCP/IP read() returned %d (errno %d)", len, errno); ++ dsiTCPIPCloseSession(SessRefNum, 0, 0, &comp); ++ *aspqe->comp = SessClosed; ++ return(len); ++ } ++ ++ dsi_session[SessRefNum].lenleft -= len; ++ dsi_session[SessRefNum].ptr += len; ++ ++ if (dsi_session[SessRefNum].lenleft > 0) ++ return(noErr); ++ ++ /* ++ * sanity check ++ * ++ */ ++ if (dsi_session[SessRefNum].lenleft < 0) { ++ logit(0, "mismatch in expected amount of read data"); ++ dsiTCPIPCloseSession(SessRefNum, 0, 0, &comp); ++ *aspqe->comp = SessClosed; ++ return(-1); ++ } ++ ++ hdr = &dsi_session[SessRefNum].hdr; ++ ++ /* ++ * finished reading something, deal with it ++ * ++ */ ++ switch (dsi_session[SessRefNum].state) { ++ case DSI_STATE_HDR: ++ /* now have a complete DSI hdr */ ++ if (ntohl(hdr->dsi_data_len) > 0) { ++ /* and AFP hdr to follow */ ++ ap = &aspqe->abr.proto.atp; ++ dsi_session[SessRefNum].ptr = ap->atpDataPtr; ++ if (hdr->dsi_command == DSIWrite && ntohl(hdr->dsi_err_offset) != 0) ++ dsi_session[SessRefNum].lenleft = ntohl(hdr->dsi_err_offset); ++ else ++ dsi_session[SessRefNum].lenleft = ntohl(hdr->dsi_data_len); ++ dsi_session[SessRefNum].state = DSI_STATE_AFP; ++ return(noErr); ++ break; ++ } ++ /* fall through */ ++ case DSI_STATE_AFP: ++ /* have DSI hdr and optional AFP header */ ++ dsi_session[SessRefNum].ptr = (char *)hdr; ++ dsi_session[SessRefNum].lenleft = sizeof(struct dsi_hdr); ++ if (hdr->dsi_flags == DSI_REQ_FLAG) ++ dsi_session[SessRefNum].state = DSI_STATE_REP; ++ else ++ dsi_session[SessRefNum].state = DSI_STATE_HDR; ++ break; ++ case DSI_STATE_DAT: ++ /* have all DSIWrite data, reset state, tell client */ ++ dsi_session[SessRefNum].aspqe = NULL; ++ dsi_session[SessRefNum].ptr = (char *)hdr; ++ dsi_session[SessRefNum].lenleft = sizeof(struct dsi_hdr); ++ dsi_session[SessRefNum].state = DSI_STATE_REP; ++ *aspqe->ActRcvdReqLen = ntohl(hdr->dsi_data_len); ++ *aspqe->ActRcvdReqLen -= ntohl(hdr->dsi_err_offset); ++ *aspqe->comp = noErr; ++ delete_aspaqe(aspqe); ++ return(noErr); ++ break; ++ default: ++ /* huh ? */ ++ break; ++ } ++ ++ /* ++ * process DSI header and optional AFP data ++ * ++ */ ++ ++ #ifdef DEBUG_AFP_CMD ++ if (dbg != NULL) { ++ char *dsi_cmd(); ++ fprintf(dbg, "<< AppleShareIP DSI header (PID %d, session #%d):\n", ++ getpid(), SessRefNum); ++ fprintf(dbg, "\tFlags: %02x (%s)\n", hdr->dsi_flags, ++ (hdr->dsi_flags == DSI_REQ_FLAG) ? "Request" : "Reply"); ++ fprintf(dbg, "\tCommand: %02x (%s)\n", hdr->dsi_command, ++ dsi_cmd(hdr->dsi_command)); ++ fprintf(dbg, "\tRequestID: %d\n", ntohs(hdr->dsi_requestID)); ++ fprintf(dbg, "\tErrCode/DataOffset: %d\n", ntohl(hdr->dsi_err_offset)); ++ fprintf(dbg, "\tDataLength: %d\n", ntohl(hdr->dsi_data_len)); ++ fprintf(dbg, "\tReserved: %d\n\n\n", ntohl(hdr->dsi_reserved)); ++ fflush(dbg); ++ } ++ #endif /* DEBUG_AFP_CMD */ ++ ++ /* ++ * reset tickle timer ++ * ++ */ ++ dsi_session[SessRefNum].timeout = 0; ++ ++ /* ++ * ignore packet replies, rely on TCP to ++ * deliver in-order, that's what it's for. ++ * ++ */ ++ if (hdr->dsi_flags == DSI_REP_FLAG) ++ return(noErr); ++ ++ /* ++ * must be request, check if incoming ++ * session ID is what we are expecting ++ * ++ */ ++ if (ntohs(hdr->dsi_requestID) != dsi_session[SessRefNum].sess_id_in) { ++ logit(0, "unexpected incoming TCP/IP session ID"); ++ *aspqe->comp = ParamErr; ++ return(-1); ++ } ++ dsi_session[SessRefNum].sess_id_in++; ++ ++ /* ++ * only 3 valid commands to pass on to client ++ * handle DSITickle locally, 'cause it's simple ++ * ++ */ ++ switch (hdr->dsi_command) { ++ case DSICommand: ++ case DSIWrite: ++ *aspqe->comp = noErr; ++ break; ++ case DSICloseSession: ++ *aspqe->comp = SessClosed; ++ break; ++ case DSITickle: ++ hdr->dsi_flags = DSI_REP_FLAG; ++ dsiTCPIPWrite(fd, hdr, NULL, 0); ++ dsi_session[SessRefNum].state = DSI_STATE_HDR; ++ return(noErr); ++ break; ++ default: ++ logit(0, "unexpected incoming DSI cond (%d)", hdr->dsi_command); ++ *aspqe->comp = ParamErr; ++ break; ++ } ++ ++ /* ++ * tell the client how much data ++ * came in and the command type ++ * ++ */ ++ if (hdr->dsi_command == DSIWrite && ntohl(hdr->dsi_err_offset) != 0) ++ *aspqe->ActRcvdReqLen = ntohl(hdr->dsi_err_offset); ++ else ++ *aspqe->ActRcvdReqLen = ntohl(hdr->dsi_data_len); ++ *aspqe->SPReqType = hdr->dsi_command; ++ *aspqe->ReqRefNum = aspqe; ++ ++ /* ++ * free previous GetRequest aspqe ++ * ++ */ ++ delete_aspaqe(aspqe); ++ dsi_session[SessRefNum].aspqe = NULL; ++ ++ return(noErr); ++ } ++ ++ /* ++ * fork and create new process to handle TCP/IP session ++ * ++ * SessRefNum - session reference number ++ * stickle - want server tickle ++ * ctickle - want client tickle ++ * ++ * In the server code (parent) close all but SLS ++ * In the child code forget about SLS, just listen ++ * for SSS requests ++ * ++ */ ++ ++ int ++ dsiTCPIPFork(SessRefNum, stickle, ctickle) ++ int SessRefNum; ++ int stickle; ++ int ctickle; ++ { ++ int i, pid; ++ ASPSSkt *sas; ++ extern int sqs; ++ ASPSkt *as, *bs; ++ extern int numasp; ++ private void dsiTCPIPTimer(); ++ ++ if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) ++ return(-1); ++ ++ if (as->state != SP_STARTED) ++ return(-1); ++ ++ /* ++ * make a new process ++ * ++ */ ++ if ((pid = fork()) < 0) ++ return(pid); ++ ++ /* ++ * if in parent process: ++ * close SSS ++ * ++ * if in child process: ++ * close both SLS (AppleTalk and TCP/IP) ++ * start tickle timer for our client session ++ * stop tickle timers for sibling ATALK sessions ++ * ++ */ ++ if (pid) { ++ /* close SSS */ ++ if (as->ss != -1) { ++ fdunlisten(as->ss); ++ close(as->ss); ++ as->ss = -1; ++ } ++ as->state = SP_HALFCLOSED; ++ } else { /* in child */ ++ if (as->type != SP_SERVER) ++ return(noErr); ++ /* close TCP/IP SLS */ ++ dsiTCPIPCloseSLS(); ++ /* kill sibling AT timeouts */ ++ for (i = 0; i < numasp; i++) { ++ if (i != SessRefNum) { ++ if ((bs = aspskt_find_sessrefnum(i)) != NULL) { ++ if (bs->tickling) ++ stopasptickle(bs); ++ stop_ttimer(bs); ++ } ++ } ++ } ++ /* close AppleTalk SLS */ ++ if ((sas = aspsskt_find_slsrefnum(as->SLSRefNum)) != NULL) ++ ATPCloseSocket(sas->addr.skt); ++ /* set a new read quantum */ ++ sqs = DSI_SRVR_MAX; ++ /* start our tickle timer */ ++ Timeout(dsiTCPIPTimer, numasp, DSI_TIMEOUT); ++ } ++ ++ return(pid); ++ } ++ ++ /* ++ * set up to wait for a request on TCP/IP SSS ++ * ++ * SessRefNum - session reference number ++ * ReqBuff - request command buffer ++ * ReqBuffSize - request command buffer size ++ * ReqRefNum - pointer to a special command block ++ * SPReqType - returns command request type ++ * ActRcvdReqLen - returns command length ++ * comp - completion flag/error ++ * ++ */ ++ ++ int ++ dsiTCPIPGetRequest(SessRefNum, ReqBuff, ReqBuffSize, ++ ReqRefNum, SPReqType, ActRcvdReqLen, comp) ++ int SessRefNum; ++ char *ReqBuff; ++ int ReqBuffSize; ++ ASPQE **ReqRefNum; ++ int *SPReqType; ++ int *ActRcvdReqLen; ++ int *comp; ++ { ++ ASPSkt *as; ++ atpProto *ap; ++ ASPQE *aspqe; ++ ++ /* ++ * check state of connection ++ * and validity of descriptor ++ * ++ */ ++ if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ if (as->state != SP_STARTED) { ++ *comp = SessClosed; ++ return(SessClosed); ++ } ++ if (as->ss == -1) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ ++ /* ++ * subsequent GetRequests from TREL_TIMEOUT code ++ * on this SessRefNum will never get a callback ++ * (because we don't need them for TCP/IP use) ++ * ++ */ ++ if (dsi_session[SessRefNum].aspqe != NULL) { ++ *comp = 1; ++ return(noErr); ++ } ++ ++ /* ++ * save GetRequest args for data arrival ++ * ++ */ ++ aspqe = create_aspaqe(); ++ ++ aspqe->type = tSPGetRequest; ++ aspqe->SessRefNum = SessRefNum; ++ aspqe->ReqRefNum = ReqRefNum; ++ aspqe->SPReqType = SPReqType; ++ aspqe->ActRcvdReqLen = ActRcvdReqLen; ++ aspqe->comp = comp; ++ ++ ap = &aspqe->abr.proto.atp; ++ ap->atpReqCount = ReqBuffSize; ++ ap->atpDataPtr = ReqBuff; ++ ++ dsi_session[SessRefNum].aspqe = aspqe; ++ ++ *comp = 1; ++ return(noErr); ++ } ++ ++ /* ++ * arrange to put the 'read' data into Buffer ++ * ++ * SessRefNum - session reference number ++ * ReqRefNum - client connection details (addr, TID) ++ * Buffer - final location for data ++ * BufferSize - maximum amount of data we can handle ++ * ActLenRcvd - actual amount of date received ++ * atptimeout - ATP get data timeout ++ * comp - completion flag/error ++ * ++ */ ++ ++ dsiTCPIPWrtContinue(SessRefNum, ReqRefNum, Buffer, ++ BufferSize, ActLenRcvd, atptimeout, comp) ++ int SessRefNum; ++ ASPQE *ReqRefNum; ++ char *Buffer; ++ int BufferSize; ++ int *ActLenRcvd; ++ int atptimeout; ++ int *comp; ++ { ++ ASPSkt *as; ++ ASPQE *aspqe; ++ struct dsi_hdr *hdr; ++ ++ /* ++ * sanity checks ++ * ++ */ ++ if (BufferSize < 0) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ if (as->state != SP_STARTED) { ++ *comp = SessClosed; ++ return(SessClosed); ++ } ++ if (as->ss == -1) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ ++ /* ++ * save WrtContinue args for ++ * completion of data arrival ++ * ++ */ ++ aspqe = create_aspaqe(); ++ ++ aspqe->type = tSPWrtContinue; ++ aspqe->SessRefNum = SessRefNum; ++ aspqe->ActRcvdReqLen = ActLenRcvd; ++ aspqe->comp = comp; ++ ++ /* ++ * reset state & continue data reads ++ * ++ */ ++ hdr = &dsi_session[SessRefNum].hdr; ++ dsi_session[SessRefNum].aspqe = aspqe; ++ dsi_session[SessRefNum].state = DSI_STATE_DAT; ++ dsi_session[SessRefNum].lenleft = ntohl(hdr->dsi_data_len); ++ dsi_session[SessRefNum].lenleft -= ntohl(hdr->dsi_err_offset); ++ dsi_session[SessRefNum].ptr = Buffer; ++ ++ *comp = 1; ++ return(noErr); ++ } ++ ++ /* ++ * reply to a write request sent to our TCP/IP SSS ++ * ++ * SessRefNum - session reference number ++ * ReqRefNum - client connection details (addr, TID) ++ * CmdResult - return result ++ * CmdReplyData - return data ++ * CmdReplyDataSize - return data size ++ * comp - completion flag/error ++ * ++ */ ++ ++ int ++ dsiTCPIPWrtReply(SessRefNum, ReqRefNum, CmdResult, ++ CmdReplyData, CmdReplyDataSize, comp) ++ int SessRefNum; ++ ASPQE *ReqRefNum; ++ dword CmdResult; ++ char *CmdReplyData; ++ int CmdReplyDataSize; ++ int *comp; ++ { ++ return(dsiTCPIPReply(DSIWrite, SessRefNum, ReqRefNum, ++ CmdResult, CmdReplyData, CmdReplyDataSize, comp)); ++ } ++ ++ /* ++ * Reply to a command request sent to our TCP/IP SSS ++ * ++ * SessRefNum - session reference number ++ * ReqRefNum - client connection details (addr, TID) ++ * CmdResult - return result ++ * CmdReplyData - return data ++ * CmdReplyDataSize - return data size ++ * comp - completion flag/error ++ * ++ */ ++ ++ int ++ dsiTCPIPCmdReply(SessRefNum, ReqRefNum, CmdResult, ++ CmdReplyData, CmdReplyDataSize, comp) ++ int SessRefNum; ++ ASPQE *ReqRefNum; ++ dword CmdResult; ++ char *CmdReplyData; ++ int CmdReplyDataSize; ++ int *comp; ++ { ++ return(dsiTCPIPReply(DSICommand, SessRefNum, ReqRefNum, ++ CmdResult, CmdReplyData, CmdReplyDataSize, comp)); ++ } ++ ++ /* ++ * common reply code ++ * ++ */ ++ ++ int ++ dsiTCPIPReply(dsiType, SessRefNum, ReqRefNum, CmdResult, ++ CmdReplyData, CmdReplyDataSize, comp) ++ int dsiType; ++ int SessRefNum; ++ ASPQE *ReqRefNum; ++ dword CmdResult; ++ char *CmdReplyData; ++ int CmdReplyDataSize; ++ int *comp; ++ { ++ ASPSkt *as; ++ struct dsi_hdr hdr; ++ ++ /* ++ * some sanity checking ++ * ++ */ ++ if (CmdReplyDataSize < 0) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ if (CmdReplyDataSize > DSI_SRVR_MAX) { ++ *comp = SizeErr; ++ return(SizeErr); ++ } ++ if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ if (as->state != SP_STARTED) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ if (as->ss == -1) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ ++ /* ++ * setup DSI response header ++ * (the requestID is already ++ * in network byte order) ++ * ++ */ ++ hdr.dsi_flags = DSI_REP_FLAG; ++ hdr.dsi_command = dsiType; ++ hdr.dsi_requestID = dsi_session[SessRefNum].hdr.dsi_requestID; ++ hdr.dsi_err_offset = htonl(CmdResult); ++ hdr.dsi_data_len = htonl(CmdReplyDataSize); ++ hdr.dsi_reserved = htonl(0x00000000); ++ ++ /* ++ * session hdr can be re-used now ++ * ++ */ ++ dsi_session[SessRefNum].state = DSI_STATE_HDR; ++ ++ /* ++ * send it ... ++ * ++ */ ++ if (dsiTCPIPWrite(as->ss, &hdr, CmdReplyData, CmdReplyDataSize) < 0) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ ++ *comp = noErr; ++ return(noErr); ++ } ++ ++ /* ++ * setup tickle timeout callback ++ * ++ */ ++ ++ int ++ dsiTCPIPTickleUserRoutine(SessRefNum, routine, arg) ++ int SessRefNum; ++ int (*routine)(); ++ int arg; ++ { ++ ASPSkt *as; ++ ++ if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) ++ return(ParamErr); ++ ++ as->tickle_timeout_user = routine; ++ as->ttu_arg = arg; ++ ++ return(noErr); ++ } ++ ++ /* ++ * Close down a TCP/IP Service Socket socket ++ * ++ * SessRefNum - Session reference number ++ * atpretries - ATP Retries ++ * atptimeout - ATP Timeout ++ * comp - completion flag/error ++ * ++ */ ++ ++ private struct dsi_hdr shut_hdr; ++ ++ int ++ dsiTCPIPCloseSession(SessRefNum, atpretries, atptimeout, comp) ++ int SessRefNum; ++ int atpretries; ++ int atptimeout; ++ int *comp; ++ { ++ ASPSkt *as; ++ ++ if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ ++ switch (as->state) { ++ case SP_STARTED: ++ break; ++ case SP_HALFCLOSED: ++ break; ++ default: ++ as->active = FALSE; /* aspskt_free(as); */ ++ return(noErr); ++ break; ++ } ++ ++ /* ++ * set up the DSI header ++ * ++ */ ++ shut_hdr.dsi_flags = DSI_REQ_FLAG; ++ shut_hdr.dsi_command = DSICloseSession; ++ shut_hdr.dsi_requestID = htons(dsi_session[SessRefNum].sess_id_out++); ++ shut_hdr.dsi_err_offset = htonl(0x00000000); ++ shut_hdr.dsi_data_len = htonl(0x00000000); ++ shut_hdr.dsi_reserved = htonl(0x00000000); ++ ++ /* ++ * and send it ... ++ * ++ */ ++ if (dsiTCPIPWrite(as->ss, &shut_hdr, NULL, 0) < 0) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ ++ as->state = SP_INACTIVE; ++ as->active = FALSE; /* aspskt_free(as); */ ++ ++ if (as->ss != -1) { ++ fdunlisten(as->ss); ++ close(as->ss); ++ as->ss = -1; ++ } ++ ++ *comp = noErr; ++ return(noErr); ++ } ++ ++ /* ++ * send a TCP/IP Attention signal to WSS ++ * ++ * SessRefNum - session reference number ++ * AttentionCode - attention message ++ * atpretries - ATP Retries ++ * atptimeout - ATP Timeout ++ * comp - completion falg/error ++ * ++ */ ++ ++ private struct dsi_hdr attn_hdr; ++ ++ int ++ dsiTCPIPAttention(SessRefNum, AttentionCode, atpretries, atptimeout, comp) ++ int SessRefNum; ++ word AttentionCode; ++ int atpretries; ++ int *comp; ++ { ++ ASPSkt *as; ++ char attn[2]; ++ ++ /* ++ * some sanity checking ++ * ++ */ ++ if (AttentionCode == 0x00) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ if (as->state == SP_STARTING) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ ++ /* ++ * set up the DSI attention header, ++ * ++ */ ++ attn_hdr.dsi_flags = DSI_REQ_FLAG; ++ attn_hdr.dsi_command = DSIAttention; ++ attn_hdr.dsi_requestID = htons(dsi_session[SessRefNum].sess_id_out++); ++ attn_hdr.dsi_err_offset = htonl(0x00000000); ++ attn_hdr.dsi_data_len = htonl(sizeof(attn)); ++ attn_hdr.dsi_reserved = htonl(0x00000000); ++ ++ /* ++ * the attention field ++ * ++ */ ++ attn[0] = AttentionCode >> 8; ++ attn[1] = AttentionCode & 0xff; ++ ++ /* ++ * and send it ... ++ * ++ */ ++ if (dsiTCPIPWrite(as->ss, &attn_hdr, attn, sizeof(attn)) < 0) { ++ *comp = ParamErr; ++ return(ParamErr); ++ } ++ ++ *comp = noErr; ++ return(noErr); ++ } ++ ++ /* ++ * return peer name of session client ++ * ++ * (NB: function return value is positive TCP/IP port number, ++ * to distinguish this from a real AppleTalk GetNetworkInfo ++ * call which returns noErr. The IP address is returned in ++ * the four bytes of the AddrBlock) ++ * ++ */ ++ ++ int ++ dsiTCPIPGetNetworkInfo(SessRefNum, addr) ++ int SessRefNum; ++ AddrBlock *addr; ++ { ++ ASPSkt *as; ++ struct sockaddr_in name; ++ int len = sizeof(struct sockaddr); ++ ++ if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) ++ return(ParamErr); ++ ++ if (as->ss == -1) ++ return(ParamErr); ++ ++ if (getpeername(as->ss, (struct sockaddr *)&name, &len) != 0) ++ return(ParamErr); ++ ++ if (name.sin_family != AF_INET) ++ return(ParamErr); ++ ++ name.sin_addr.s_addr = ntohl(name.sin_addr.s_addr); ++ addr->net = ((name.sin_addr.s_addr & 0xff000000) >> 16); ++ addr->net |= ((name.sin_addr.s_addr & 0x00ff0000) >> 16); ++ addr->node = ((name.sin_addr.s_addr & 0x0000ff00) >> 8); ++ addr->skt = (name.sin_addr.s_addr & 0x000000ff); ++ ++ return(ntohs(name.sin_port)); ++ } ++ ++ /* ++ * write data to client via TCP/IP stream ++ * ++ * We deliberately don't use non-blocking I/O ++ * because the majority of the large data transfers ++ * happen in a process dedicated to a single client. ++ * ++ */ ++ ++ int ++ dsiTCPIPWrite(fd, hdr, data, len) ++ int fd; ++ struct dsi_hdr *hdr; ++ char *data; ++ int len; ++ { ++ int cc, cd; ++ ++ #ifdef DEBUG_AFP_CMD ++ if (dbg != NULL) { ++ char *dsi_cmd(); ++ fprintf(dbg, ">> AppleShareIP DSI header (PID %d)\n", getpid()); ++ fprintf(dbg, "\tFlags: %02x (%s)\n", hdr->dsi_flags, ++ (hdr->dsi_flags == DSI_REQ_FLAG) ? "Request" : "Reply"); ++ fprintf(dbg, "\tCommand: %02x (%s)\n", hdr->dsi_command, ++ dsi_cmd(hdr->dsi_command)); ++ fprintf(dbg, "\tRequestID: %d\n", ntohs(hdr->dsi_requestID)); ++ fprintf(dbg, "\tErrCode/DataOffset: %d\n", ntohl(hdr->dsi_err_offset)); ++ fprintf(dbg, "\tDataLength: %d\n", ntohl(hdr->dsi_data_len)); ++ fprintf(dbg, "\tReserved: %d\n", ntohl(hdr->dsi_reserved)); ++ fflush(dbg); ++ } ++ #endif /* DEBUG_AFP_CMD */ ++ ++ /* ++ * writev() is more efficient but ++ * is less portable than write() ++ * ++ */ ++ #ifdef HAVE_WRITEV ++ { struct iovec iov[2]; ++ iov[0].iov_base = (caddr_t)hdr; ++ iov[0].iov_len = sizeof(struct dsi_hdr); ++ iov[1].iov_base = (caddr_t)data; ++ iov[1].iov_len = len; ++ cc = writev(fd, iov, (data == NULL) ? 1 : 2); ++ } ++ #else /* HAVE_WRITEV */ ++ if ((cc = write(fd, (char *)hdr, sizeof(struct dsi_hdr))) >= 0) { ++ if (data != NULL) { ++ if ((cd = write(fd, data, len)) >= 0) ++ cc += cd; ++ else ++ cc = cd; ++ } ++ } ++ #endif /* HAVE_WRITEV */ ++ ++ #ifdef DEBUG_AFP_CMD ++ if (dbg != NULL) { ++ extern int errno; ++ if (cc < 0) ++ fprintf(dbg, "** dsiTCPIPWrite(): %d bytes returns %d (errno %d)", ++ len+sizeof(struct dsi_hdr), cc, errno); ++ fprintf(dbg, "\n\n\n"); ++ fflush(dbg); ++ } ++ #endif /* DEBUG_AFP_CMD */ ++ ++ return(cc); ++ } ++ ++ /* ++ * Tickle Timeout timer ++ * ++ */ ++ ++ private void ++ dsiTCPIPTimer(numsess) ++ int numsess; ++ { ++ int i; ++ ASPSkt *as; ++ static int inited = 0; ++ static struct dsi_hdr tick_hdr; ++ void Timeout(); ++ ++ /* ++ * set-up the invariant ++ * fields of the tickle hdr ++ * ++ */ ++ if (!inited) { ++ tick_hdr.dsi_flags = DSI_REQ_FLAG; ++ tick_hdr.dsi_command = DSITickle; ++ tick_hdr.dsi_err_offset = htonl(0x00000000); ++ tick_hdr.dsi_data_len = htonl(0x00000000); ++ tick_hdr.dsi_reserved = htonl(0x00000000); ++ inited = 1; ++ } ++ ++ /* ++ * check for idle TCP/IP sessions ++ * ++ */ ++ for (i = 0; i < numsess; i++) { ++ if (dsi_session[i].sesstype == DSI_SESSION_TCPIP) { ++ dsi_session[i].timeout += DSI_TIMEOUT; ++ if (dsi_session[i].timeout >= ASPCONNECTIONTIMEOUT) { ++ if ((as = aspskt_find_sessrefnum(i)) != NULL) { ++ if (as->tickle_timeout_user != NULL) ++ (*as->tickle_timeout_user)(i, as->ttu_arg); ++ else { /* no user routine */ ++ as->state = SP_INACTIVE; ++ as->active = FALSE; /* aspskt_free(as); */ ++ dsiShutdown(i); ++ } ++ } ++ } else { /* not timed out, but time to send a tickle ? */ ++ if ((dsi_session[i].timeout % (ASPTICKLETIMEOUT)) == 0) { ++ if ((as = aspskt_find_sessrefnum(i)) != NULL) { ++ tick_hdr.dsi_requestID = htons(dsi_session[i].sess_id_out++); ++ dsiTCPIPWrite(as->ss, &tick_hdr, NULL, 0); ++ } ++ } ++ } ++ } ++ } ++ ++ Timeout(dsiTCPIPTimer, numsess, DSI_TIMEOUT); ++ ++ return; ++ } ++ ++ /* ++ * close the SLS (called from either the ++ * AppleTalk or TCP/IP child processes) ++ * ++ */ ++ ++ int ++ dsiTCPIPCloseSLS() ++ { ++ if (slsskt != -1) { ++ fdunlisten(slsskt); ++ close(slsskt); ++ slsskt = -1; ++ } ++ ++ return(noErr); ++ } ++ ++ /* ++ * IP address filter ++ * ++ * compatible with, and stolen from, ++ * the ARNS remote access package ++ * ++ * http://www.cs.mu.OZ.AU/appletalk/atalk.html ++ * ++ */ ++ ++ private int ipFilters = 0; ++ private struct ipFilter *ipFilter = NULL; ++ ++ /* ++ * read the specified IP address filter file ++ * ++ */ ++ ++ private void ++ dsiTCPIPBuildFilterList() ++ { ++ FILE *fp; ++ char line[160]; ++ char *mask, *addr; ++ unsigned long inet_addr(); ++ ++ ipFilters = 0; ++ ++ if (dsiTCPIPFilter != NULL) { ++ if (ipFilter == NULL) ++ if ((ipFilter = ++ (struct ipFilter *)malloc(MAXIPFILTSIZ*MAXIPFILTERS)) == NULL) ++ return; ++ if ((fp = fopen(dsiTCPIPFilter, "r")) != NULL) { ++ while (fgets(line, sizeof(line), fp) != NULL) { ++ if (line[0] == '#') ++ continue; ++ if ((mask = (char *)index(line, '\n')) != NULL) ++ *mask = '\0'; ++ mask = line+1; ++ while (*mask != '\0' && isspace(*mask)) ++ mask++; /* skip spaces */ ++ addr = mask; ++ while (*addr != '\0' && !isspace(*addr)) ++ addr++; /* skip mask */ ++ while (*addr != '\0' && isspace(*addr)) ++ addr++; /* skip spaces */ ++ if (line[0] == '+' || line[0] == '*' || line[0] == '-') { ++ ipFilter[ipFilters].perm = line[0]; ++ ipFilter[ipFilters].addr = (*addr == '\0') ? 0L : inet_addr(addr); ++ ipFilter[ipFilters].mask = (*mask == '\0') ? 0L : inet_addr(mask); ++ if (++ipFilters >= MAXIPFILTERS) ++ break; ++ } ++ } ++ (void)fclose(fp); ++ } ++ } ++ ++ return; ++ } ++ ++ /* ++ * check the IP address filter, if any ++ * ++ */ ++ ++ private int ++ dsiTCPIPIllegalIP(from) ++ struct sockaddr_in *from; ++ { ++ int i; ++ u_long addr; ++ ++ dsiTCPIPBuildFilterList(); ++ ++ if (ipFilters == 0 ++ || ipFilter == NULL) ++ return(0); ++ ++ addr = from->sin_addr.s_addr; ++ ++ for (i = 0 ; i < ipFilters ; i++) { ++ if (ipFilter[i].addr != 0L) { ++ if ((addr & ipFilter[i].mask) == ipFilter[i].addr) ++ return(ipFilter[i].perm == '-'); ++ } else { ++ if ((addr & ipFilter[i].mask) == addr) ++ return(ipFilter[i].perm == '-'); ++ } ++ } ++ ++ return(0); ++ } +*** applications/aufs/afpdt.c.orig Wed Sep 25 00:10:20 1996 +--- applications/aufs/afpdt.c Mon Jul 7 17:01:14 1997 +*************** +*** 1170,1176 **** + PrintIconInfo(adi.adi_fcreator,adi.adi_ftype); + } + +! err = SPWrtContinue(cno,reqref,icon,adi.adi_iconsize,&rcvlen,-1,&comp); + if (err != noErr) { + free((char *)icon); + return(err); +--- 1170,1176 ---- + PrintIconInfo(adi.adi_fcreator,adi.adi_ftype); + } + +! err = dsiWrtContinue(cno,reqref,icon,adi.adi_iconsize,&rcvlen,-1,&comp); + if (err != noErr) { + free((char *)icon); + return(err); +*** applications/aufs/afpfork.c.orig Wed Sep 25 00:09:33 1996 +--- applications/aufs/afpfork.c Mon Jul 7 17:43:35 1997 +*************** +*** 585,591 **** + return(aeAccessDenied); + } + +! err = SPWrtContinue(cno,reqref,r,n_rrpkts*atpMaxData,&rcvlen,-1,&comp); + if (err != noErr) + return(err); + do { abSleep(4,TRUE); } while (comp > 0); +--- 585,591 ---- + return(aeAccessDenied); + } + +! err = dsiWrtContinue(cno,reqref,r,n_rrpkts*atpMaxData,&rcvlen,-1,&comp); + if (err != noErr) + return(err); + do { abSleep(4,TRUE); } while (comp > 0); +*** applications/aufs/afpos.c.orig Wed Sep 25 00:10:24 1996 +--- applications/aufs/afpos.c Wed Jul 9 11:39:02 1997 +*************** +*** 766,772 **** +--- 766,776 ---- + msg[i] = '\r'; + } + close(fd); ++ return(noErr); + } ++ ++ sprintf(msg, ""); ++ + return(noErr); + } + +*** applications/aufs/afps.h.orig Wed Sep 25 00:10:19 1996 +--- applications/aufs/afps.h Mon Mar 3 22:14:05 1997 +*************** +*** 247,252 **** +--- 247,253 ---- + #define AFPVersion1DOT1 110 + #define AFPVersion2DOT0 200 + #define AFPVersion2DOT1 210 ++ #define AFPVersion2DOT2 220 + + #ifdef APPLICATION_MANAGER + struct flist { +*** applications/aufs/afpserver.c.orig Wed Sep 25 00:10:23 1996 +--- applications/aufs/afpserver.c Wed Jul 9 10:44:28 1997 +*************** +*** 47,52 **** +--- 47,54 ---- + /* assume included by param.h */ + # include + #endif ++ #include ++ #include + #include + #include + #include +*************** +*** 97,103 **** + + private char *afpmtyp = "Unix"; + +! #define AFPVERSZ (5*16) /* room for IndStr hold all versions */ + /* define 1 more than needed just in case */ + struct afpversionstruct { + char *version_name; +--- 99,105 ---- + + private char *afpmtyp = "Unix"; + +! #define AFPVERSZ (6*16) /* room for IndStr hold all versions */ + /* define 1 more than needed just in case */ + struct afpversionstruct { + char *version_name; +*************** +*** 107,112 **** +--- 109,115 ---- + {"AFPVersion 1.1", AFPVersion1DOT1}, + {"AFPVersion 2.0", AFPVersion2DOT0}, + {"AFPVersion 2.1", AFPVersion2DOT1}, ++ {"AFP2.2", AFPVersion2DOT2}, + {NULL, AFPVersionUnknown} + }; + +*************** +*** 216,221 **** +--- 219,229 ---- + + private DumpBuf(), clockstart(), clockend(); + ++ private struct sig { ++ byte filler[8]; ++ struct timeval s_time; ++ } sig; ++ + #ifdef LOGIN_AUTH_PROG + extern char *srvrname; /* NBP registered name of server */ + extern char *login_auth_prog; /* name of authorization program */ +*************** +*** 254,259 **** +--- 262,271 ---- + #ifdef STAT_CACHE + OSStatInit(); /* init stat cache */ + #endif STAT_CACHE ++ ++ /* init server signature */ ++ bzero((char *)&sig, sizeof(struct sig)); ++ gettimeofday(&sig.s_time, NULL); + } + + /* +*************** +*** 968,974 **** + fprintf(dbg, "\tMsgTyp: %04x\t", smrp.msgr_typ); + fprintf(dbg, "(%s)\n", (smrp.msgr_typ == 0) ? "Login" : "Server"); + fprintf(dbg, "\tMsgBMp: %04x\n", smrp.msgr_bitmap); +! dbg_print_name("\tMsgStr:", smrp.msgr_data); + fflush(dbg); + } + #endif /* DEBUG_AFP_CMD */ +--- 980,986 ---- + fprintf(dbg, "\tMsgTyp: %04x\t", smrp.msgr_typ); + fprintf(dbg, "(%s)\n", (smrp.msgr_typ == 0) ? "Login" : "Server"); + fprintf(dbg, "\tMsgBMp: %04x\n", smrp.msgr_bitmap); +! fprintf(dbg, "\tMsgStr: %s\n", smrp.msgr_data); + fflush(dbg); + } + #endif /* DEBUG_AFP_CMD */ +*************** +*** 1035,1040 **** +--- 1047,1055 ---- + } + #endif + ++ #define MAXNADSIZ 8 /* tag #2, IP addr & port */ ++ #define NUMNAD 5 /* no more than 5 IP addr/host */ ++ + int + GetSrvrInfo(r,sname,icon,iconsize) + byte *r; +*************** +*** 1042,1068 **** + byte icon[]; + int iconsize; + { + int i,len; + extern int nopwdsave; + GetSrvrInfoReplyPkt sr; + byte avobuf[AFPVERSZ],uamobuf[AFPUAMSZ]; +! OPTRType avo,uamo,vicono; + +! strcpy(sr.sr_machtype,afpmtyp); +! cpyc2pstr(sr.sr_servername,sname); +! /* set server capabilities */ + sr.sr_flags = SupportsFPCopyFile; + #ifdef DISTRIB_PASSWDS + sr.sr_flags |= SupportsChgPwd; + #endif DISTRIB_PASSWDS + sr.sr_flags |= SupportsServerMsgs; + if (nopwdsave) + sr.sr_flags |= DontAllowSavePwd; + + vicono.optr_loc = icon; + vicono.optr_len = iconsize; + sr.sr_vicono = (char *) &vicono; + + IniIndStr(avobuf); + for (i = 0; afpversions[i].version_name != NULL; i++) + AddIndStr(afpversions[i].version_name, avobuf); +--- 1057,1109 ---- + byte icon[]; + int iconsize; + { ++ byte *q; + int i,len; + extern int nopwdsave; + GetSrvrInfoReplyPkt sr; ++ OPTRType avo,uamo,vicono,sigo,nado; + byte avobuf[AFPVERSZ],uamobuf[AFPUAMSZ]; +! byte nadbuf[MAXNADSIZ*NUMNAD+1]; +! extern u_short asip_port; +! extern u_int asip_addr; +! extern int asip_enable; +! struct hostent *he; +! char hostname[128]; + +! /* +! * set server capabilities +! * +! */ + sr.sr_flags = SupportsFPCopyFile; + #ifdef DISTRIB_PASSWDS + sr.sr_flags |= SupportsChgPwd; + #endif DISTRIB_PASSWDS + sr.sr_flags |= SupportsServerMsgs; ++ sr.sr_flags |= SupportsServerSig; ++ if (asip_enable) ++ sr.sr_flags |= SupportsTCPIP; + if (nopwdsave) + sr.sr_flags |= DontAllowSavePwd; + ++ /* ++ * set Server Name & Machine Type ++ * ++ */ ++ strcpy(sr.sr_machtype,afpmtyp); ++ cpyc2pstr(sr.sr_servername,sname); ++ ++ /* ++ * set Volume Icon & Mask ++ * ++ */ + vicono.optr_loc = icon; + vicono.optr_len = iconsize; + sr.sr_vicono = (char *) &vicono; + ++ /* ++ * set AFP Versions ++ * ++ */ + IniIndStr(avobuf); + for (i = 0; afpversions[i].version_name != NULL; i++) + AddIndStr(afpversions[i].version_name, avobuf); +*************** +*** 1070,1075 **** +--- 1111,1120 ---- + avo.optr_loc = avobuf; + sr.sr_avo = (byte *) &avo; + ++ /* ++ * set UAMs ++ * ++ */ + IniIndStr(uamobuf); + for (i=0 ; i < numuam; i++) + AddIndStr(afpuams[i].uamname, uamobuf); +*************** +*** 1077,1082 **** +--- 1122,1191 ---- + uamo.optr_loc = uamobuf; + sr.sr_uamo = (byte *) &uamo; + ++ /* ++ * set server signature ++ * ++ */ ++ sigo.optr_len = 16; ++ sigo.optr_loc = (byte *)&sig; ++ sr.sr_sigo = (byte *)&sigo; ++ ++ /* ++ * set network address(es) ++ * use single bound address (-B ) ++ * or all known addresses for this machine ++ * ++ */ ++ q = nadbuf+1; ++ nadbuf[0] = 0; ++ if (asip_enable) { ++ if (asip_addr) { ++ nadbuf[0] = 1; ++ q[2] = (asip_addr >> 24) & 0xff; ++ q[3] = (asip_addr >> 16) & 0xff; ++ q[4] = (asip_addr >> 8) & 0xff; ++ q[5] = (asip_addr & 0xff); ++ if (asip_port == ASIP_PORT) { ++ q[0] = 0x06; /* len */ ++ q[1] = 0x01; /* tag */ ++ q += 6; ++ } else { ++ q[0] = 0x08; /* len */ ++ q[1] = 0x02; /* tag */ ++ q[6] = asip_port >> 8; ++ q[7] = asip_port & 0xff; ++ q += 8; ++ } ++ } else { /* list all known addresses */ ++ if (gethostname(hostname, sizeof(hostname)) == 0) { ++ if ((he = gethostbyname(hostname)) != NULL) { ++ for (i = 0; he->h_addr_list[i] && i < NUMNAD; i++) { ++ bcopy(he->h_addr_list[i], q+2, 4); /* copy IP */ ++ if (asip_port == ASIP_PORT) { ++ q[0] = 0x06; /* len */ ++ q[1] = 0x01; /* tag */ ++ q += 6; ++ } else { ++ q[0] = 0x08; /* len */ ++ q[1] = 0x02; /* tag */ ++ q[6] = asip_port >> 8; ++ q[7] = asip_port & 0xff; ++ q += 8; ++ } ++ } ++ nadbuf[0] = i; ++ } ++ } ++ } ++ } ++ nado.optr_len = q-nadbuf; ++ nado.optr_loc = nadbuf; ++ sr.sr_naddro = (byte *)&nado; ++ ++ /* ++ * pack data for sending ++ * ++ */ + len = htonPackX(ProtoSRP,(byte *) &sr,r); + + #ifdef DEBUG_AFP_CMD +*************** +*** 1389,1395 **** + int i; + byte *q = r; + u_short srvrflags; +! short machoff, afpoff, uamoff, vicnoff; + void dbg_print_name(); + void dbg_print_sflg(); + void dbg_print_icon(); +--- 1498,1505 ---- + int i; + byte *q = r; + u_short srvrflags; +! short machoff, afpoff, uamoff; +! short vicnoff, sigoff, nadoff; + void dbg_print_name(); + void dbg_print_sflg(); + void dbg_print_icon(); +*************** +*** 1402,1410 **** + fprintf(dbg, "\tUAMOff: %d\n", uamoff); + vicnoff = get2(q); q += 2; + fprintf(dbg, "\tICNOff: %d\n", vicnoff); +! fprintf(dbg, "\tVolFlg: %04x\t", (srvrflags = get2(q))); q += 2; + dbg_print_sflg(srvrflags); + dbg_print_name("\tSrvrNm:", q); + if (machoff != 0) + dbg_print_name("\tMchTyp:", r+machoff); + if (afpoff != 0) { +--- 1512,1528 ---- + fprintf(dbg, "\tUAMOff: %d\n", uamoff); + vicnoff = get2(q); q += 2; + fprintf(dbg, "\tICNOff: %d\n", vicnoff); +! srvrflags = get2(q); q += 2; +! fprintf(dbg, "\tVolFlg: %04x\t", srvrflags); + dbg_print_sflg(srvrflags); + dbg_print_name("\tSrvrNm:", q); ++ q += ((*q)+1); ++ if ((u_long)q & 0x01) ++ q++; /* even */ ++ sigoff = get2(q); q += 2; ++ fprintf(dbg, "\tSIGOff: %d\n", sigoff); ++ nadoff = get2(q); q += 2; ++ fprintf(dbg, "\tNADOff: %d\n", nadoff); + if (machoff != 0) + dbg_print_name("\tMchTyp:", r+machoff); + if (afpoff != 0) { +*************** +*** 1412,1418 **** + fprintf(dbg, "\tVerCnt: %d\n", (int)(*q)); + for (i = *q++; i > 0; i--) { + dbg_print_name("\tAFPVer:", q); +! q += ((*q) + 1); + } + } else + fprintf(dbg, "\t\n"); +--- 1530,1536 ---- + fprintf(dbg, "\tVerCnt: %d\n", (int)(*q)); + for (i = *q++; i > 0; i--) { + dbg_print_name("\tAFPVer:", q); +! q += ((*q) + 1); + } + } else + fprintf(dbg, "\t\n"); +*************** +*** 1431,1436 **** +--- 1549,1588 ---- + fprintf(dbg, "\n"); + } else + fprintf(dbg, "\t\n"); ++ if (sigoff != 0) { ++ q = r + sigoff; ++ fprintf(dbg, "\tSrvSIG: "); ++ for (i = 0; i < 16; i++) ++ fprintf(dbg, "%02x ", *(q+i)); ++ fprintf(dbg, "\n"); ++ } else ++ fprintf(dbg, "\t\n"); ++ if (nadoff != 0) { ++ q = r + nadoff; ++ fprintf(dbg, "\tNADCnt: %d\n", (int)(*q)); ++ for (i = *q++; i > 0; i--) { ++ fprintf(dbg, "\tAFPNAD: len %d tag %d ", q[0], q[1]); ++ switch (q[1]) { ++ case 0x01: ++ fprintf(dbg, "IP %d.%d.%d.%d\n", ++ q[2], q[3], q[4], q[5]); ++ break; ++ case 0x02: ++ fprintf(dbg, "IP %d.%d.%d.%d Port %d\n", ++ q[2], q[3], q[4], q[5], (q[6] << 8) | q[7]); ++ break; ++ case 0x03: ++ fprintf(dbg, "DDP net %d.%d node %d skt %d\n", ++ q[2], q[3], q[4], q[5]); ++ break; ++ default: ++ fprintf(dbg, "\n"); ++ break; ++ } ++ q += q[0]; ++ } ++ } else ++ fprintf(dbg, "\t\n"); + + return; + } +*************** +*** 1475,1485 **** + bmap &= ~(0x0001 << i); + switch (i) { + case 0: +! fprintf(dbg, "SuppCopyFile"); + j++; + break; + case 1: +! fprintf(dbg, "SuppChngPass"); + j++; + break; + case 2: +--- 1627,1637 ---- + bmap &= ~(0x0001 << i); + switch (i) { + case 0: +! fprintf(dbg, "CopyFile"); + j++; + break; + case 1: +! fprintf(dbg, "ChngPass"); + j++; + break; + case 2: +*************** +*** 1487,1497 **** + j++; + break; + case 3: +! fprintf(dbg, "SuppSrvrMesg"); + j++; + break; + case 15: +! fprintf(dbg, "SuppMGetReqs"); + j++; + break; + default: +--- 1639,1661 ---- + j++; + break; + case 3: +! fprintf(dbg, "SrvrMesg"); +! j++; +! break; +! case 4: +! fprintf(dbg, "SrvrSig"); +! j++; +! break; +! case 5: +! fprintf(dbg, "TCP/IP"); +! j++; +! break; +! case 6: +! fprintf(dbg, "SrvrNotf"); + j++; + break; + case 15: +! fprintf(dbg, "MGetReqs"); + j++; + break; + default: +*** applications/aufs/aufs.c.orig Wed Sep 25 00:10:19 1996 +--- applications/aufs/aufs.c Fri Jul 11 19:29:43 1997 +*************** +*** 91,96 **** +--- 91,100 ---- + export u_char *srvrtype = (u_char *)AFSTYPE; /* NBP registered type */ + export char *messagefile = NULL; /* AFP2.1 GetSrvrMsg srvr msg filename */ + export char *motdfile = NULL; /* AFP2.1 GetSrvrMsg login msg filename */ ++ export char *dsiTCPIPFilter = NULL; /* AFP2.2 AppleShareIP address filter */ ++ export u_int asip_addr = INADDR_ANY; /* AFP2.2 AppleShare over TCP/IP */ ++ export u_short asip_port = ASIP_PORT; /* AFP2.2 AppleShare TCP/IP port */ ++ export int asip_enable = FALSE; /* AFP2.2 AppleShare TCP/IP default off */ + + private char *sysvolfile = NULL; /* system afpvols file */ + private char *passwdlookaside = NULL; /* local password file??? */ +*************** +*** 240,245 **** +--- 244,252 ---- + fprintf(stderr,"\t-V VolsFile for server wide afp volumes\n"); + fprintf(stderr,"\t-G to set guest id for logins\n"); + fprintf(stderr,"\t-P LookAsidePasswordFile for scrambled transactions\n"); ++ fprintf(stderr,"\t-T enable AFP connections via TCP/IP (default addr)\n"); ++ fprintf(stderr,"\t-B enable AFP over TCP/IP & set address\n"); ++ fprintf(stderr,"\t-f set the AFP over TCP-IP address filter\n"); + fprintf(stderr,"\t-U to allow sessions\n"); + fprintf(stderr,"\t-m|M specifies login or server message file\n"); + #ifndef STAT_CACHE +*************** +*** 297,307 **** + char **argv; + { + int c; + u_char *parsename(); + extern char *optarg; + extern int optind; + extern boolean dochecksum; +! static char optlist[64] = "a:d:D:n:N:t:kpsuV:U:G:P:c:l:z:S:R:M:m:"; + #ifdef ISO_TRANSLATE + void cISO2Mac(); + #endif ISO_TRANSLATE +--- 304,315 ---- + char **argv; + { + int c; ++ char *p; + u_char *parsename(); + extern char *optarg; + extern int optind; + extern boolean dochecksum; +! static char optlist[100] = "a:B:d:f:D:n:N:t:kpsTuV:U:G:P:c:l:z:S:R:M:m:"; + #ifdef ISO_TRANSLATE + void cISO2Mac(); + #endif ISO_TRANSLATE +*************** +*** 388,393 **** +--- 396,417 ---- + if (!SetPktTrace(optarg)) + usage(argv[0]); + break; ++ case 'f': /* AppleShareIP IP address filter */ ++ dsiTCPIPFilter = optarg; ++ break; ++ case 'B': /* Bind AppleShare TCP/IP address */ ++ if ((p = (char *)index(optarg, ':')) != NULL) { ++ asip_port = atoi(p+1); ++ *p = '\0'; ++ } ++ if ((asip_addr = (u_int)ntohl(inet_addr(optarg))) == -1) ++ asip_addr = INADDR_ANY; ++ if (p != NULL) ++ *p = ':'; ++ /* fall through */ ++ case 'T': /* Enable AppleShare TCP/IP */ ++ asip_enable = TRUE; ++ break; + case 'V': /* system afpvols file */ + sysvolfile = optarg; + break; +*************** +*** 688,693 **** +--- 712,722 ---- + if ((ctp_stack = (int *)malloc(sizeof(int)*maxsess)) == NULL) { + logit(0,"couldn't malloc stack for pid recording, fatal!"); + } ++ if (asip_enable) ++ if (asip_addr != INADDR_ANY) ++ logit(0,"AFP over TCP/IP enabled (IP %08x Port %d)",asip_addr,asip_port); ++ else ++ logit(0,"AFP over TCP/IP enabled (IP INADDR_ANY Port %d)", asip_port); + #ifdef LWSRV_AUFS_SECURITY + if (userlogindir != NULL) { /* budd... */ + logit(0,"Aufs: user login database in %s", userlogindir); +*************** +*** 725,731 **** + if (n_rrpkts < atpMaxNum) + logit(0,"remote limited to %d packet%s in a response", n_rrpkts, + n_rrpkts > 1 ? "s" : ""); +! SPGetParms(&mcs, &qs); + if (DBDEB) + printf("Command buffer size is %d, Quantum size is %d\n", mcs, qs); + buf = (byte *)malloc(mcs); +--- 754,760 ---- + if (n_rrpkts < atpMaxNum) + logit(0,"remote limited to %d packet%s in a response", n_rrpkts, + n_rrpkts > 1 ? "s" : ""); +! dsiGetParms(&mcs, &qs); + if (DBDEB) + printf("Command buffer size is %d, Quantum size is %d\n", mcs, qs); + buf = (byte *)malloc(mcs); +*************** +*** 783,798 **** + PrtSrvrInfo(srvinfo,srvinfolen); + + /* Init asp */ +! err = SPInit(&addr,srvinfo,srvinfolen,&slsref); + if (err != noErr) { +! logit(0,"SPInit failed with code %d, fatal",err); + exit(0); + } + + logit(0,"Aufs Starting (%s)",srvrname); + if (sysvolfile) + logit(0,"System vols in '%s'",sysvolfile); +! logit(0,"SPInit Completed. Waiting for connection..."); + + #ifndef NOSHUTDOWNCODE + # ifndef NOPGRP +--- 812,827 ---- + PrtSrvrInfo(srvinfo,srvinfolen); + + /* Init asp */ +! err = dsiInit(&addr,srvinfo,srvinfolen,&slsref); + if (err != noErr) { +! logit(0,"dsiInit failed with code %d, fatal",err); + exit(0); + } + + logit(0,"Aufs Starting (%s)",srvrname); + if (sysvolfile) + logit(0,"System vols in '%s'",sysvolfile); +! logit(0,"dsiInit Completed. Waiting for connection..."); + + #ifndef NOSHUTDOWNCODE + # ifndef NOPGRP +*************** +*** 819,825 **** + + do { + pid = -1; /* make sure zero at start */ +! SPGetSession(slsref,&cno,&comp); + if (comp > 0) + logit(0,"Waiting for session %d to activate", cno); + /* won't wait if we set comp above */ +--- 848,854 ---- + + do { + pid = -1; /* make sure zero at start */ +! dsiGetSession(slsref,&cno,&comp); + if (comp > 0) + logit(0,"Waiting for session %d to activate", cno); + /* won't wait if we set comp above */ +*************** +*** 848,855 **** + sesscount++; + logit(0,"New session %d started on server socket %d, count %d", + cno,slsref,sesscount); +! if ((err = SPGetNetworkInfo(cno, &addr)) != noErr) { +! logit(0,"Get Network info failed with error %d", err); + } else { + #ifdef AUTHENTICATE + err = (authenticate(ntohs(addr.net), addr.node)) ? noErr : ~noErr; +--- 877,889 ---- + sesscount++; + logit(0,"New session %d started on server socket %d, count %d", + cno,slsref,sesscount); +! if ((err = dsiGetNetworkInfo(cno, &addr)) != noErr) { +! if (err > 0) { /* AppleShareIP session */ +! logit(0,"Session %d from [IP addr %d.%d.%d.%d, port %d]", +! cno, addr.net>>8, addr.net&0xff, addr.node, addr.skt, err); +! err = noErr; +! } else +! logit(0,"Get Network info failed with error %d", err); + } else { + #ifdef AUTHENTICATE + err = (authenticate(ntohs(addr.net), addr.node)) ? noErr : ~noErr; +*************** +*** 864,870 **** + } + #ifdef AUTHENTICATE + if(err != noErr) { +! SPCloseSession(cno, 1, 1, &comp2); + continue; + } + #endif AUTHENTICATE +--- 898,904 ---- + } + #ifdef AUTHENTICATE + if(err != noErr) { +! dsiCloseSession(cno, 1, 1, &comp2); + continue; + } + #endif AUTHENTICATE +*************** +*** 889,898 **** + # endif NOSHUTDOWNCODE; + #endif NOSIGMASK + /* fork on connection - only tickle from parent */ +! if ((pid = SPFork(cno, TRUE, FALSE)) < 0) { +! logit(0,"SPFork failed on session %d, last system error %d",cno, errno); + /* try to close, but don't worry too much */ +! SPCloseSession(cno, 1, 1, &comp2); + #ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGHUP); +--- 923,932 ---- + # endif NOSHUTDOWNCODE; + #endif NOSIGMASK + /* fork on connection - only tickle from parent */ +! if ((pid = dsiFork(cno, TRUE, FALSE)) < 0) { +! logit(0,"dsiFork failed on session %d, last system error %d",cno,errno); + /* try to close, but don't worry too much */ +! dsiCloseSession(cno, 1, 1, &comp2); + #ifdef NOSIGMASK + sigrelse(SIGCHLD); + sigrelse(SIGHUP); +*************** +*** 905,912 **** + #endif NOSIGMASK + continue; + } + if (pid) { +- SPTickleUserRoutine(cno, timedout, pid); + logit(0,"pid %d starting for session %d",pid, cno); + addinferior(cno, pid); /* addinferior scans (phew) */ + } else { +--- 939,946 ---- + #endif NOSIGMASK + continue; + } ++ dsiTickleUserRoutine(cno, timedout, pid); + if (pid) { + logit(0,"pid %d starting for session %d",pid, cno); + addinferior(cno, pid); /* addinferior scans (phew) */ + } else { +*************** +*** 997,1003 **** + umask(0); /* file creates have explict modes */ + for (;;) { + #ifndef TREL_TIMEOUT +! SPGetRequest(cno,buf,mcs,&reqref,&type,&rlen,&comp); + while (comp > 0) { + abSleep(sectotick(60),TRUE); + #ifdef AUFS_IDLE_TIMEOUT +--- 1031,1037 ---- + umask(0); /* file creates have explict modes */ + for (;;) { + #ifndef TREL_TIMEOUT +! dsiGetRequest(cno,buf,mcs,&reqref,&type,&rlen,&comp); + while (comp > 0) { + abSleep(sectotick(60),TRUE); + #ifdef AUFS_IDLE_TIMEOUT +*************** +*** 1016,1022 **** + } + + if (comp1 <= 0) { +! SPGetRequest(cno,buf1,mcs,&reqref1,&type1,&rlen1,&comp1); + while (comp1 > 0) { + abSleep(sectotick(60),TRUE); + #ifdef AUFS_IDLE_TIMEOUT +--- 1050,1056 ---- + } + + if (comp1 <= 0) { +! dsiGetRequest(cno,buf1,mcs,&reqref1,&type1,&rlen1,&comp1); + while (comp1 > 0) { + abSleep(sectotick(60),TRUE); + #ifdef AUFS_IDLE_TIMEOUT +*************** +*** 1034,1043 **** + } + #ifndef TREL_TIMEOUT + if (comp < 0) { +! logit(0,"SPGetRequest failed %d",comp); + #else TREL_TIMEOUT + if (comp1 < 0) { +! logit(0,"SPGetRequest failed %d",comp1); + #endif TREL_TIMEOUT + continue; + } +--- 1068,1077 ---- + } + #ifndef TREL_TIMEOUT + if (comp < 0) { +! logit(0,"dsiGetRequest failed %d",comp); + #else TREL_TIMEOUT + if (comp1 < 0) { +! logit(0,"dsiGetRequest failed %d",comp1); + #endif TREL_TIMEOUT + continue; + } +*************** +*** 1079,1108 **** + } + #ifndef TREL_TIMEOUT + if (type == aspWrite) +! SPWrtReply(cno,reqref,(dword) err,rspbuf,rsplen,&comp); + #else TREL_TIMEOUT + if (type1 == aspWrite) +! SPWrtReply(cno,reqref1,(dword) err,rspbuf1,rsplen1,&comp1); + #endif TREL_TIMEOUT + else + #ifndef TREL_TIMEOUT +! SPCmdReply(cno,reqref,(dword) err,rspbuf,rsplen,&comp); + while (comp > 0) { + abSleep(sectotick(60),TRUE); + } + if (DBSRV) + printf("done\n"); + #else TREL_TIMEOUT +! SPCmdReply(cno,reqref1,(dword) err,rspbuf1,rsplen1,&comp1); + #endif TREL_TIMEOUT + break; + case aspCloseSession: + logit(0,"Closing ASP Session..."); + #ifndef TREL_TIMEOUT +! SPCloseSession(cno,10,3,&comp); /* 5 times, .75 seconds */ + while (comp > 0) + #else TREL_TIMEOUT +! SPCloseSession(cno,10,3,&comp1); /* 5 times, .75 seconds */ + while (comp1 > 0) + #endif TREL_TIMEOUT + abSleep(1, TRUE); +--- 1113,1142 ---- + } + #ifndef TREL_TIMEOUT + if (type == aspWrite) +! dsiWrtReply(cno,reqref,(dword) err,rspbuf,rsplen,&comp); + #else TREL_TIMEOUT + if (type1 == aspWrite) +! dsiWrtReply(cno,reqref1,(dword) err,rspbuf1,rsplen1,&comp1); + #endif TREL_TIMEOUT + else + #ifndef TREL_TIMEOUT +! dsiCmdReply(cno,reqref,(dword) err,rspbuf,rsplen,&comp); + while (comp > 0) { + abSleep(sectotick(60),TRUE); + } + if (DBSRV) + printf("done\n"); + #else TREL_TIMEOUT +! dsiCmdReply(cno,reqref1,(dword) err,rspbuf1,rsplen1,&comp1); + #endif TREL_TIMEOUT + break; + case aspCloseSession: + logit(0,"Closing ASP Session..."); + #ifndef TREL_TIMEOUT +! dsiCloseSession(cno,10,3,&comp); /* 5 times, .75 seconds */ + while (comp > 0) + #else TREL_TIMEOUT +! dsiCloseSession(cno,10,3,&comp1); /* 5 times, .75 seconds */ + while (comp1 > 0) + #endif TREL_TIMEOUT + abSleep(1, TRUE); +*************** +*** 1133,1139 **** + #endif NOSHUTDOWNCODE; + #ifdef TREL_TIMEOUT + } else { /* comp2 */ +! SPGetRequest(cno,buf2,mcs,&reqref2,&type2,&rlen2,&comp2); + while (comp2 > 0) { + abSleep(sectotick(60),TRUE); + #ifdef AUFS_IDLE_TIMEOUT +--- 1167,1173 ---- + #endif NOSHUTDOWNCODE; + #ifdef TREL_TIMEOUT + } else { /* comp2 */ +! dsiGetRequest(cno,buf2,mcs,&reqref2,&type2,&rlen2,&comp2); + while (comp2 > 0) { + abSleep(sectotick(60),TRUE); + #ifdef AUFS_IDLE_TIMEOUT +*************** +*** 1149,1155 **** + return; + } + if (comp2 < 0) { +! logit(0,"SPGetRequest failed %d",comp2); + continue; + } + if (rlen2 == 0) +--- 1183,1189 ---- + return; + } + if (comp2 < 0) { +! logit(0,"dsiGetRequest failed %d",comp2); + continue; + } + if (rlen2 == 0) +*************** +*** 1173,1185 **** + fflush(stdout); /* force out */ + } + if (type2 == aspWrite) +! SPWrtReply(cno,reqref2,(dword) err,rspbuf2,rsplen2,&comp2); + else +! SPCmdReply(cno,reqref2,(dword) err,rspbuf2,rsplen2,&comp2); + break; + case aspCloseSession: + logit(0,"Closing ASP Session..."); +! SPCloseSession(cno,10,3,&comp2); /* 5 times, .75 seconds */ + while (comp2 > 0) + abSleep(1, TRUE); + #ifndef NOSHUTDOWNCODE +--- 1207,1219 ---- + fflush(stdout); /* force out */ + } + if (type2 == aspWrite) +! dsiWrtReply(cno,reqref2,(dword) err,rspbuf2,rsplen2,&comp2); + else +! dsiCmdReply(cno,reqref2,(dword) err,rspbuf2,rsplen2,&comp2); + break; + case aspCloseSession: + logit(0,"Closing ASP Session..."); +! dsiCloseSession(cno,10,3,&comp2); /* 5 times, .75 seconds */ + while (comp2 > 0) + abSleep(1, TRUE); + #ifndef NOSHUTDOWNCODE +*************** +*** 1300,1308 **** + logit(0,"process %d, session %d was suspended! gads what is happening?", + cp->pid, srn); + } else if (WIFSIGNALED(cp->status)) { +! SPAttention(srn, AFPSHUTDOWNNOW, 1, -1, &comp); + while (comp > 0) {abSleep(30, TRUE);} /* ignore error */ +! SPCloseSession(srn, 3, 2, &comp); /* try 3 times every .5 seconds */ + while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */ + logit(0,"process %d, session %d was terminated on signal %d", + cp->pid, srn, W_TERMSIG(cp->status)); +--- 1334,1342 ---- + logit(0,"process %d, session %d was suspended! gads what is happening?", + cp->pid, srn); + } else if (WIFSIGNALED(cp->status)) { +! dsiAttention(srn, AFPSHUTDOWNNOW, 1, -1, &comp); + while (comp > 0) {abSleep(30, TRUE);} /* ignore error */ +! dsiCloseSession(srn, 3, 2, &comp); /* try 3 times every .5 seconds */ + while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */ + logit(0,"process %d, session %d was terminated on signal %d", + cp->pid, srn, W_TERMSIG(cp->status)); +*************** +*** 1321,1327 **** + logit(0,"process %d, session %d terminated with exit code %d", + cp->pid, srn, W_RETCODE(cp->status)); + } +! SPShutdown(srn); + nomoresessions = FALSE; /* if this was set, unset now */ + #ifdef DORUSAGE + logit(0,"%d messages out, %d in, CPU %.2f user %.2f system", +--- 1355,1361 ---- + logit(0,"process %d, session %d terminated with exit code %d", + cp->pid, srn, W_RETCODE(cp->status)); + } +! dsiShutdown(srn); + nomoresessions = FALSE; /* if this was set, unset now */ + #ifdef DORUSAGE + logit(0,"%d messages out, %d in, CPU %.2f user %.2f system", +*************** +*** 1385,1391 **** + /* assume sigchild interlocked here */ + if (ctp_tab[srn].state & CP_RUNNING) + ctp_tab[srn].state |= CP_TIMEDOUT; +! SPShutdown(srn); /* ignore errors */ + kill(pid, SIGHUP); /* hangup inferior */ + } + +--- 1419,1425 ---- + /* assume sigchild interlocked here */ + if (ctp_tab[srn].state & CP_RUNNING) + ctp_tab[srn].state |= CP_TIMEDOUT; +! dsiShutdown(srn); /* ignore errors */ + kill(pid, SIGHUP); /* hangup inferior */ + } + +*************** +*** 1399,1408 **** + /* The following shouldn't really do anything since remote should be gone */ + /* be in case it really isn't, let's go through this rigamorle */ + /* Tell remote we are shutting down */ +! SPAttention(cno, AFPSHUTDOWNNOW, 1, -1, &comp); + while (comp > 0) {abSleep(30, TRUE);} /* ignore error */ + /* Try closing just in case */ +! SPCloseSession(cno, 3, 2, &comp); /* 3 times, .5 seconds */ + while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */ + #ifdef LWSRV_AUFS_SECURITY + clearuserlogin(); /* gtw: delete auth-file entry for dead users */ +--- 1433,1442 ---- + /* The following shouldn't really do anything since remote should be gone */ + /* be in case it really isn't, let's go through this rigamorle */ + /* Tell remote we are shutting down */ +! dsiAttention(cno, AFPSHUTDOWNNOW, 1, -1, &comp); + while (comp > 0) {abSleep(30, TRUE);} /* ignore error */ + /* Try closing just in case */ +! dsiCloseSession(cno, 3, 2, &comp); /* 3 times, .5 seconds */ + while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */ + #ifdef LWSRV_AUFS_SECURITY + clearuserlogin(); /* gtw: delete auth-file entry for dead users */ +*************** +*** 1431,1437 **** + /* Tell remote we are shutting down */ + if (minutes_to_shutdown % 2) { /* all odd minutes */ + /* there is a potential race condition here */ +! SPAttention(cno, AFPSHUTDOWNTIME(minutes_to_shutdown), 1, -1, &comp); + while (comp > 0) {abSleep(30, TRUE);} /* ignore error */ + } + minutes_to_shutdown--; +--- 1465,1471 ---- + /* Tell remote we are shutting down */ + if (minutes_to_shutdown % 2) { /* all odd minutes */ + /* there is a potential race condition here */ +! dsiAttention(cno, AFPSHUTDOWNTIME(minutes_to_shutdown), 1, -1, &comp); + while (comp > 0) {abSleep(30, TRUE);} /* ignore error */ + } + minutes_to_shutdown--; +*************** +*** 1465,1471 **** + int comp; + + signal(SIGURG, SIG_IGN); +! SPAttention(cno, AFPSERVERMESG, 1, -1, &comp); + while (comp > 0) + abSleep(30, TRUE); + signal(SIGURG, msgavail); +--- 1499,1505 ---- + int comp; + + signal(SIGURG, SIG_IGN); +! dsiAttention(cno, AFPSERVERMESG, 1, -1, &comp); + while (comp > 0) + abSleep(30, TRUE); + signal(SIGURG, msgavail); +*************** +*** 1893,1899 **** + if (cmp == 0 && *buf != 17) { /* periodic GetVolParms AFP call */ + if (sentshutdown) { + logit(0, "Session %d: Aborting Idle Timeout", cno); +! SPAttention(cno, AFPSHUTDOWNCANCEL, 1, -1, &comp); + while (comp > 0) { abSleep(30, TRUE); } /* ignore error */ + sentshutdown = 0; + } +--- 1927,1933 ---- + if (cmp == 0 && *buf != 17) { /* periodic GetVolParms AFP call */ + if (sentshutdown) { + logit(0, "Session %d: Aborting Idle Timeout", cno); +! dsiAttention(cno, AFPSHUTDOWNCANCEL, 1, -1, &comp); + while (comp > 0) { abSleep(30, TRUE); } /* ignore error */ + sentshutdown = 0; + } +*************** +*** 1922,1937 **** + case 12: /* shutdown in 3 min */ + case 24: /* shutdown in 1 min */ + logit(0, "Session %d: sending %d minute idle timeout warning",cno,5-i/6); +! SPAttention(cno, AFPSHUTDOWNTIME(5-i/6), 1, -1, &comp); + while (comp > 0) { abSleep(30, TRUE); } /* ignore error */ + sentshutdown++; + return; + break; + case 30: /* shutdown now */ + logit(0, "Session %d: Idle Timeout Shutdown", cno); +! SPAttention(cno, AFPSHUTDOWNNOW, 1, -1, &comp); + while (comp > 0) { abSleep(30, TRUE); } /* ignore error */ +! SPCloseSession(cno, 3, 2, &comp); /* 3 times, .5 seconds */ + while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */ + #ifdef LWSRV_AUFS_SECURITY + clearuserlogin(); /* gtw: delete auth-file entry for dead users */ +--- 1956,1971 ---- + case 12: /* shutdown in 3 min */ + case 24: /* shutdown in 1 min */ + logit(0, "Session %d: sending %d minute idle timeout warning",cno,5-i/6); +! dsiAttention(cno, AFPSHUTDOWNTIME(5-i/6), 1, -1, &comp); + while (comp > 0) { abSleep(30, TRUE); } /* ignore error */ + sentshutdown++; + return; + break; + case 30: /* shutdown now */ + logit(0, "Session %d: Idle Timeout Shutdown", cno); +! dsiAttention(cno, AFPSHUTDOWNNOW, 1, -1, &comp); + while (comp > 0) { abSleep(30, TRUE); } /* ignore error */ +! dsiCloseSession(cno, 3, 2, &comp); /* 3 times, .5 seconds */ + while (comp > 0) { abSleep(30, TRUE); } /* close down if we can */ + #ifdef LWSRV_AUFS_SECURITY + clearuserlogin(); /* gtw: delete auth-file entry for dead users */ +*** applications/aufs/Makefile.m4.orig Wed Sep 25 00:09:57 1996 +--- applications/aufs/Makefile.m4 Mon Jul 7 02:03:26 1997 +*************** +*** 48,59 **** + afpmisc.c afpserver.c aufsicon.c abmisc2.c \ + afpdt.c afpdid.c afposenum.c afpavl.c \ + afposfi.c afpgc.c afppasswd.c afposlock.c aufsv.c \ +! afpudb.c afposncs.c afpspd.c afpfid.c + OBJS=afpos.o afpvols.o afpfile.o \ + afpmisc.o afpserver.o aufsicon.o abmisc2.o \ + afpdt.o afpdir.o afpfork.o afpdid.o afposenum.o afpavl.o \ + afposfi.o afpgc.o afppasswd.o aufsv.o \ +! afpudb.o afposncs.o afpspd.o afpfid.o + SYMLINKS=att_getopt.c + + all: aufs sizeserver afpidsrvr afpidlist afpidtool +--- 48,59 ---- + afpmisc.c afpserver.c aufsicon.c abmisc2.c \ + afpdt.c afpdid.c afposenum.c afpavl.c \ + afposfi.c afpgc.c afppasswd.c afposlock.c aufsv.c \ +! afpudb.c afposncs.c afpspd.c afpfid.c afpdsi.c + OBJS=afpos.o afpvols.o afpfile.o \ + afpmisc.o afpserver.o aufsicon.o abmisc2.o \ + afpdt.o afpdir.o afpfork.o afpdid.o afposenum.o afpavl.o \ + afposfi.o afpgc.o afppasswd.o aufsv.o \ +! afpudb.o afposncs.o afpspd.o afpfid.o afpdsi.o + SYMLINKS=att_getopt.c + + all: aufs sizeserver afpidsrvr afpidlist afpidtool +*************** +*** 189,191 **** +--- 189,193 ---- + afppasswd.o: afppasswd.c $I/netat/sysvcompat.h afppasswd.h + afposncs.o: afposncs.c $I/netat/appletalk.h $I/netat/afp.h \ + afposncs.h afps.h ++ afpdsi.o: afpdsi.c $I/netat/appletalk.h ../../lib/cap/abasp.h \ ++ afpdsi.h +*** lib/afp/afppacks.c.orig Wed Sep 25 00:10:07 1996 +--- lib/afp/afppacks.c Mon Mar 3 17:34:40 1997 +*************** +*** 615,620 **** +--- 615,622 ---- + PAKB(GVPRPPtr,P_DWRD,gvpr_free,VP_FREE), /* free bytes */ + PAKB(GVPRPPtr,P_DWRD,gvpr_size,VP_SIZE), /* size in bytes */ + PKSB(GVPRPPtr,P_OSTR,gvpr_name,VP_NAME), /* name of volume */ ++ PKSB(GVPRPPtr,P_BYTS,gvpr_efree,VP_EFREE), /* extended free bytes */ ++ PKSB(GVPRPPtr,P_BYTS,gvpr_esize,VP_ESIZE), /* extended total bytes */ + PACKEND() + }; + +*************** +*** 663,668 **** +--- 665,673 ---- + PACK(GSIRPPtr, P_OPTR, sr_vicono), + PACK(GSIRPPtr, P_WORD, sr_flags), + PAKS(GSIRPPtr, P_PATH, sr_servername), ++ PACKEVEN(), ++ PACK(GSIRPPtr, P_OPTR, sr_sigo), ++ PACK(GSIRPPtr, P_OPTR, sr_naddro), + PACKEND() + }; + +*** lib/cap/abasp.c.orig Thu Mar 14 13:47:51 1991 +--- lib/cap/abasp.c Fri Jul 11 18:20:13 1997 +*************** +*** 18,25 **** + * Aug 4, 1986 CCKim Verified: level 0 + */ + +- /* PATCH: Moy@Berkeley/abasp.c.diff, djh@munnari.OZ.AU, 17/11/90 */ +- + #include + #include + #include +--- 18,23 ---- +*************** +*** 61,78 **** + private void start_client_aspskt(); + private void shutdown_aspskt(); + +! private ASPQE *create_aq(); + private ASPQE *get_aq(); +- private void delete_aq(); + private boolean match_aspwe(); + private ASPQE *find_aspawe(); + + private void startasptickle(); +! private void stopasptickle(); + private void ttimeout(); + private void start_ttimer(); + private void reset_ttimer(); +! private void stop_ttimer(); + + int SPFork(); + OSErr SPShutdown(); +--- 59,76 ---- + private void start_client_aspskt(); + private void shutdown_aspskt(); + +! void delete_aq(); +! ASPQE *create_aq(); + private ASPQE *get_aq(); + private boolean match_aspwe(); + private ASPQE *find_aspawe(); + + private void startasptickle(); +! void stopasptickle(); + private void ttimeout(); + private void start_ttimer(); + private void reset_ttimer(); +! void stop_ttimer(); + + int SPFork(); + OSErr SPShutdown(); +*************** +*** 89,98 **** + #ifdef ASPPID + private ASPSkt *aspskt_find_pid(); + #endif +- private ASPSkt *aspskt_find_active(); +- private ASPSkt *aspskt_find_sessrefnum(); + private OSErr aspsskt_new(); +! private ASPSSkt *aspsskt_find_slsrefnum(); + private boolean aspsskt_isactive(); + + private void sizeof_abr_bds_and_req(); +--- 87,96 ---- + #ifdef ASPPID + private ASPSkt *aspskt_find_pid(); + #endif + private OSErr aspsskt_new(); +! ASPSkt *aspskt_find_active(); +! ASPSkt *aspskt_find_sessrefnum(); +! ASPSSkt *aspsskt_find_slsrefnum(); + private boolean aspsskt_isactive(); + + private void sizeof_abr_bds_and_req(); +*************** +*** 1896,1901 **** +--- 1894,1900 ---- + stopasptickle(as); + } + ATPCloseSocket(sas->addr.skt); /* close down server listener here */ ++ dsiTCPIPCloseSLS(); /* and the AppleShareIP SLS */ + } + return(pid); + } +*************** +*** 2093,2099 **** + } + #endif + +! private ASPSkt * + aspskt_find_active(SLSRefNum) + int SLSRefNum; + { +--- 2092,2098 ---- + } + #endif + +! ASPSkt * + aspskt_find_active(SLSRefNum) + int SLSRefNum; + { +*************** +*** 2106,2112 **** + return(NULL); + } + +! private ASPSkt * + aspskt_find_sessrefnum(srn) + int srn; + { +--- 2105,2111 ---- + return(NULL); + } + +! ASPSkt * + aspskt_find_sessrefnum(srn) + int srn; + { +*************** +*** 2137,2143 **** + } + + +! private ASPSSkt * + aspsskt_find_slsrefnum(sls) + int sls; + { +--- 2136,2148 ---- + } + + +! /* +! * locate SLSRefNum structure +! * (non-private for DSI access) +! * +! */ +! +! ASPSSkt * + aspsskt_find_slsrefnum(sls) + int sls; + { +*************** +*** 2212,2218 **** + * stopasptickle - cancel the tickle on the specified connection + * + */ +! private void + stopasptickle(as) + ASPSkt *as; + { +--- 2217,2223 ---- + * stopasptickle - cancel the tickle on the specified connection + * + */ +! void + stopasptickle(as) + ASPSkt *as; + { +*************** +*** 2277,2283 **** + * cancel the remote tickle timeout + * + */ +! private void + stop_ttimer(as) + ASPSkt *as; + { +--- 2282,2288 ---- + * cancel the remote tickle timeout + * + */ +! void + stop_ttimer(as) + ASPSkt *as; + { +*************** +*** 2291,2297 **** + private ASPQE *aspqe_list; + private QElemPtr aspqe_free; + +! private ASPQE * + create_aq(which, as) + int which; + ASPSkt *as; +--- 2296,2302 ---- + private ASPQE *aspqe_list; + private QElemPtr aspqe_free; + +! ASPQE * + create_aq(which, as) + int which; + ASPSkt *as; +*************** +*** 2320,2326 **** + return(aspqe); + } + +! private void + delete_aq(aspqe, which, as) + ASPQE *aspqe; + int which; +--- 2325,2331 ---- + return(aspqe); + } + +! void + delete_aq(aspqe, which, as) + ASPQE *aspqe; + int which; +*** lib/cap/absched.c.orig Wed Sep 25 00:10:17 1996 +--- lib/cap/absched.c Wed Jul 9 16:07:49 1997 +*************** +*** 704,709 **** +--- 704,711 ---- + } + if (dbug.db_skd) + fprintf(stderr,"%d ", rdy); ++ if (rdy < 0) ++ return(rdy); + if (rdy > 0) { + /* rdy should be # of set file descriptors in the masks */ + /* since we only pass it the "read" bits, this loop */ +*** etc/aufsIPFilter.orig Mon Jul 14 13:46:44 1997 +--- etc/aufsIPFilter Mon Jul 14 13:49:11 1997 +*************** +*** 0 **** +--- 1,26 ---- ++ # ++ # aufs/AppleShareIP Address Access Filter List ++ # ++ # NB: The filter file format is compatible with that used by the ARNS ++ # Remote Access package (http://www.cs.mu.OZ.AU/appletalk/atalk.html) ++ # ++ # The filter list consists of a single character mode, an IP mask and ++ # optional IP address. If the latter is included, the mask is applied ++ # to the incoming IP address and tested against the provided address. ++ # Otherwise the incoming IP address must be unchanged by the mask. ++ # ++ # Modes: ++ # ++ # * IP_MASK [ IP_ADDR ] permit access ++ # + IP_MASK [ IP_ADDR ] permit access ++ # - IP_MASK [ IP_ADDR ] deny access ++ # ++ # ++ # any mac on a specific subnet ++ + 255.255.255.0 192.43.207.0 ++ # connections from ariel ++ * 128.250.255.255 128.250.20.3 ++ # anybody on campus ++ + 128.243.255.255 ++ # nobody else ++ - 255.255.255.255 +*** samples/ash.c.orig Wed Sep 25 00:10:10 1996 +--- samples/ash.c Mon Jul 14 14:27:33 1997 +*************** +*** 1090,1092 **** +--- 1090,1102 ---- + *bp++ = '\0'; + return(buf); + } ++ ++ /* ++ * this is a dummy routine for abasp.c ++ * ++ */ ++ ++ dsiTCPIPCloseSLS() ++ { ++ return(noErr); ++ } +*** man/AUFS.8.orig Wed Sep 25 00:09:56 1996 +--- man/AUFS.8 Mon Jul 14 13:53:02 1997 +*************** +*** 21,26 **** +--- 21,30 ---- + ] [ + .BI \-F " " + ] [ ++ .BI \-B " " ++ ] [ ++ .BI \-f " " ++ ] [ + .BI \-[i|I] " " + ] [ + .BI \-c " " +*************** +*** 45,50 **** +--- 49,56 ---- + ] [ + .BI \-u + ] [ ++ .BI \-T ++ ] [ + .BI \-d " " + ] [ + .BI \-a " " +*************** +*** 57,71 **** + ] + .SH DESCRIPTION + .I aufs +! implements a file server on a UNIX host connected +! to an AppleTalk network, for client computers on AppleTalk that support AFP. +! Specifically, it works as a file server for Macintosh computers with +! the AppleShare client code. + This manual entry describes how to run the UNIX server daemon process. + See AUFS(1) for information about how to use the server. + .PP + .I aufs +! is normally started at boot time via a command in start-cap-servers (whic + is usually run from /etc/rc.local). + The CAP name information server daemon + .I atis +--- 63,77 ---- + ] + .SH DESCRIPTION + .I aufs +! implements a file server on a UNIX host for client computers on AppleTalk +! that support AFP, +! or Macintoshes on the internet that support AFP via TCP/IP using AppleShare +! client 3.7 or later. + This manual entry describes how to run the UNIX server daemon process. + See AUFS(1) for information about how to use the server. + .PP + .I aufs +! is normally started at boot time via a command in start-cap-servers (which + is usually run from /etc/rc.local). + The CAP name information server daemon + .I atis +*************** +*** 148,153 **** +--- 154,186 ---- + user may over-ride these mappings by having a .afpfile (or afpfile) file + in their home directory. + .TP 10 ++ .BI \-T ++ enables AppleShareIP (AFP via TCP/IP) support in ++ .I aufs. ++ Note that the first ++ .I aufs ++ process defaults to the well-know port number 548. Subsequent incarnations ++ of ++ .I aufs ++ will require that the TCP/IP port number be specified using the \-B option. ++ Clients needing to connect to these servers, that are not also connected to ++ the same network via AppleTalk, must specify the port number in ++ the dialog box obtained by clicking on the Chooser "Server IP Address..." ++ button. The format is the same as for the \-B option, ie: 128.250.1.21:2169 ++ to use port number 2169 on host 128.250.1.21. ++ .TP 10 ++ .BI \-B " " ++ tells ++ .I aufs ++ to listen for TCP/IP connections at the specified IP address and optional ++ port (defaults to any available interface address and port number 548). ++ This option implies \-T. ++ .TP 10 ++ .BI \-f " " ++ specifies the pathname of a file containing yes/no permissions for client ++ IP numbers or subnets wishing to connect using AFP via TCP/IP. See the file ++ cap60/etc/aufsIPFilter for details. ++ .TP 10 + .BI \-c " " + specifies a directory where + .I aufs +*************** +*** 341,354 **** + .PP + Notes and warnings pertaining to client use and file system implementation + are documented in AUFS(1). +- .PP +- AUFS Version 3, released post 2/88, has a different .finderinfo and +- desktop format than previous releases of AUFS. Old format desktop +- files are automatically discarded and old format .finderinfo files are +- rewritten on sight (if possible). You should consider rebuilding your +- desktop if you had a volume created with AUFS Version 2 or previous to +- regain the applications mappings and to ensure that all .finderinfo +- files are rewritten. + .SH AUTHOR + AUFS was written by Bill Schilit, Computer Science Deparment and + Charlie C. Kim, User Services, Columbia University. +--- 374,379 ---- +*** netat/afp.h.orig Wed Sep 25 00:09:55 1996 +--- netat/afp.h Fri Aug 7 12:18:33 1998 +*************** +*** 192,207 **** + + /* Volume Params */ + +! #define VP_ATTR 0001 /* attributes */ +! #define VP_SIG 0002 /* signature byte */ +! #define VP_CDATE 0004 /* creation date */ +! #define VP_MDATE 0010 /* modification date */ +! #define VP_BDATE 0020 /* backup date */ +! #define VP_VOLID 0040 /* volume id */ +! #define VP_FREE 0100 /* free bytes */ +! #define VP_SIZE 0200 /* size in bytes */ +! #define VP_NAME 0400 /* volume name */ +! #define VP_ALL (0777) + + #define VOL_VAR_DIRID 0x03 /* volume has variable dirids */ + #define VOL_FIXED_DIRID 0x02 /* volume has fixed dirids */ +--- 192,210 ---- + + /* Volume Params */ + +! #define VP_ATTR 00001 /* attributes */ +! #define VP_SIG 00002 /* signature byte */ +! #define VP_CDATE 00004 /* creation date */ +! #define VP_MDATE 00010 /* modification date */ +! #define VP_BDATE 00020 /* backup date */ +! #define VP_VOLID 00040 /* volume id */ +! #define VP_FREE 00100 /* free bytes */ +! #define VP_SIZE 00200 /* size in bytes */ +! #define VP_NAME 00400 /* volume name */ +! #define VP_EFREE 01000 /* AFP2.2: extended free bytes */ +! #define VP_ESIZE 02000 /* AFP2.2: extended total bytes */ +! #define VP_ALLOC 04000 /* AFP2.2: allocation block size */ +! #define VP_ALL (07777) + + #define VOL_VAR_DIRID 0x03 /* volume has variable dirids */ + #define VOL_FIXED_DIRID 0x02 /* volume has fixed dirids */ +*************** +*** 278,282 **** +--- 281,287 ---- + #define UIP_PRIMARY_GID 0x2 /* primary group (dword) */ + + #define AFSTYPE "AFPServer" /* NBP type for AFS */ ++ ++ #define ASIP_PORT 548 /* AppleShare over TCP/IP well-known port */ + + char *afperr(); /* in afperr.c */ +*** netat/afpcmd.h.orig Wed Sep 25 00:09:56 1996 +--- netat/afpcmd.h Tue Mar 4 13:22:33 1997 +*************** +*** 279,285 **** +--- 279,290 ---- + #define SupportsChgPwd 0x02 /* AFP2.0: can do change password */ + #define DontAllowSavePwd 0x04 /* AFP2.1: user can't save password */ + #define SupportsServerMsgs 0x08 /* AFP2.1: can send server messages */ ++ #define SupportsServerSig 0x10 /* AFP2.2: can supply unique signature */ ++ #define SupportsTCPIP 0x20 /* AFP2.2: AFP commands via TCP/IP stream */ ++ #define SupportsSrvrNotif 0x40 /* AFP2.2: server to client messages */ + byte sr_servername[33]; /* server name */ ++ byte *sr_sigo; /* AFP2.2: offset to signature */ ++ byte *sr_naddro; /* AFP2.2: offset to network address count */ + } GetSrvrInfoReplyPkt, *GSIRPPtr; + + typedef struct { /* FPGetSrvrParms */ +*************** +*** 319,324 **** +--- 324,331 ---- + sdword gvpr_size; /* size of volume in bytes */ + sdword gvpr_free; /* free bytes on volume */ + byte gvpr_name[MAXVLEN]; /* advertised name */ ++ byte gvpr_esize[8]; /* extended volume size */ ++ byte gvpr_efree[8]; /* extended bytes free */ + } GetVolParmsReplyPkt, *GVPRPPtr; + + diff --git a/cap60.patches/cicon.patch b/cap60.patches/cicon.patch new file mode 100644 index 0000000..f890deb --- /dev/null +++ b/cap60.patches/cicon.patch @@ -0,0 +1,2605 @@ +Patch #: none yet +Type: operational change +Priority: none +Modification: add support for AUFS color volume icons +IMPORTANT: +IMPORTANT: This patch assumes CAP at patch level 198 plus asip.patch +IMPORTANT: This is an interim patch only. You will need to keep this +IMPORTANT: patch file in order to reverse the code changes before patch +IMPORTANT: 199 can be applied without error. When reversing this patch, +IMPORTANT: you also need to manually remove the following file: +IMPORTANT: rm cap60/applications/aufs/aufscicon.c +IMPORTANT: +File: cap60/man/AUFS.1 +File: cap60/netat/macfile.h +File: cap60/applications/aufs/afpdir.c +File: cap60/applications/aufs/afpos.c +File: cap60/applications/aufs/afpvols.c +File: cap60/applications/aufs/aufscicon.c +File: cap60/applications/aufs/Makefile.m4 + + +*** man/AUFS.1.orig Fri Aug 8 00:12:46 1997 +--- man/AUFS.1 Fri Aug 8 01:09:00 1997 +*************** +*** 293,298 **** +--- 293,317 ---- + the Macintosh. + .PP + .B ++ AUFS Color Volume Icons ++ .PP ++ Color icons for AppleShare volumes (and, in fact, any directory) are stored ++ in an invisible Macintosh file named "Icon^M". The ^M is a carriage return ++ character. Under AUFS this file is renamed to the UNIX file "Icon:0d". When ++ the AUFS volume owner (or any user with write permission) first mounts the ++ volume, AUFS creates an approriate color icon file - if none already exists. ++ When configuring CAP, you can define USE_HOST_ICON to have the volume icon ++ associate with the underlying UNIX hardware or operating system. ++ .PP ++ There are two methods for creating a new color icon file. Using the ++ Macintosh utility 'ResEdit' (make sure that the file contains resources ++ 'icl4', 'icl8', ICN#', 'ics#', 'ics4' and 'ics8' and that all of the ++ resource IDs are set to -16455. The 'Invisible' bit should also be set). ++ You can also paste a new icon into the 'Get Info' window of an AUFS ++ directory and then move the three forks of the "Icon:0d" file into the ++ root of the AUFS volume. ++ .PP ++ .B + Macintosh Volumes vs. UNIX volumes under AUFS + .PP + AUFS maintains a distinction between "Macintosh" volumes and "UNIX" +*** netat/macfile.h.orig Sun Mar 8 23:44:21 1992 +--- netat/macfile.h Tue Aug 5 19:25:18 1997 +*************** +*** 74,79 **** +--- 74,80 ---- + + /* Flags */ + #define FNDR_fOnDesk 0x1 ++ #define FNDR_fHasCustomIcon 0x0400 + #define FNDR_fHasBundle 0x2000 + #define FNDR_fInvisible 0x4000 + /* locations */ +*************** +*** 106,114 **** + /* extended finder information */ + word frScroll[2]; /* (Point) Scroll position [20] */ + dword frOpenChain; /* dir id chain of open folders [24] */ +! word frUnused; /* Unused [26] */ + word frComment; /* Comment id [28] */ +! word frPutAway; /* home directory id [32] */ + } dirFinderInfo; + + typedef union { +--- 107,116 ---- + /* extended finder information */ + word frScroll[2]; /* (Point) Scroll position [20] */ + dword frOpenChain; /* dir id chain of open folders [24] */ +! byte frScript; /* script flag and code [26] */ +! byte frXFlags; /* reserved [27] */ + word frComment; /* Comment id [28] */ +! dword frPutAway; /* home directory id [32] */ + } dirFinderInfo; + + typedef union { +*** applications/aufs/afpdir.c.orig Wed Sep 25 00:10:20 1996 +--- applications/aufs/afpdir.c Tue Aug 5 19:32:58 1997 +*************** +*** 969,975 **** + fprintf(dbg, "\t DiRect: %04x %04x %04x %04x\n", + get2(f), get2(f+2), get2(f+4), get2(f+6)); + f += 8; +! fprintf(dbg, "\t FdrFlg: %04x %04x\n", get2(f), get2(f+2)); + f += 4; + fprintf(dbg, "\t FdView: %04x\n", get2(f)); + f += 2; +--- 969,977 ---- + fprintf(dbg, "\t DiRect: %04x %04x %04x %04x\n", + get2(f), get2(f+2), get2(f+4), get2(f+6)); + f += 8; +! fprintf(dbg, "\t FdrFlg: %04x\n", get2(f)); +! f += 2; +! fprintf(dbg, "\t Locatn: %04x %04x\n", get2(f), get2(f+2)); + f += 4; + fprintf(dbg, "\t FdView: %04x\n", get2(f)); + f += 2; +*************** +*** 977,983 **** + f += 4; + fprintf(dbg, "\t DChain: %08x\n", get4(f)); + f += 4; +! f += 2; /* unused */ + fprintf(dbg, "\t CommID: %04x\n", get2(f)); + f += 2; + fprintf(dbg, "\t HomeID: %08x\n", get4(f)); +--- 979,987 ---- + f += 4; + fprintf(dbg, "\t DChain: %08x\n", get4(f)); + f += 4; +! fprintf(dbg, "\t Script: %02x\n", *f); +! fprintf(dbg, "\t XFlags: %02x\n", *(f+1)); +! f += 2; + fprintf(dbg, "\t CommID: %04x\n", get2(f)); + f += 2; + fprintf(dbg, "\t HomeID: %08x\n", get4(f)); +*** applications/aufs/afpos.c.orig Mon Jul 14 14:04:29 1997 +--- applications/aufs/afpos.c Tue Aug 5 19:42:26 1997 +*************** +*** 1605,1610 **** +--- 1605,1612 ---- + { + IDirP idirid; + dword ItoEAccess(); ++ char path[MAXPATHLEN]; ++ dirFinderInfo *dfi; + word bm; + int nchild; + extern int sessvers; +*************** +*** 1620,1627 **** + if (bm & DP_ATTR) /* skip attr if not requested */ + OSGetAttr(ipdir,fn,&fdp->fdp_attr); + +! if (bm & (DP_FINFO|DP_PDOS)) /* skip finfo if not requested */ + OSGetFNDR(ipdir,fn,fdp->fdp_finfo); + + if (bm & DP_PDOS) /* generate some ProDOS info. */ + mapFNDR2PDOS(fdp); +--- 1622,1637 ---- + if (bm & DP_ATTR) /* skip attr if not requested */ + OSGetAttr(ipdir,fn,&fdp->fdp_attr); + +! if (bm & (DP_FINFO|DP_PDOS)) { /* skip finfo if not requested */ + OSGetFNDR(ipdir,fn,fdp->fdp_finfo); ++ dfi = (dirFinderInfo *)fdp->fdp_finfo; ++ OSfname(path, ipdir, fn, F_DATA); ++ strcat(path, "/Icon:0d"); ++ if (access(path, R_OK) == 0) ++ dfi->frFlags |= htons(FNDR_fHasCustomIcon); /* has custom ICON */ ++ else ++ dfi->frFlags &= htons(~FNDR_fHasCustomIcon); /* no custom ICON */ ++ } + + if (bm & DP_PDOS) /* generate some ProDOS info. */ + mapFNDR2PDOS(fdp); +*** applications/aufs/afpvols.c.orig Wed Sep 25 00:10:24 1996 +--- applications/aufs/afpvols.c Thu Aug 7 17:18:37 1997 +*************** +*** 129,134 **** +--- 129,140 ---- + /* Okay, stick it into the table */ + if (vp->v_rootd != NILDIR) /* avoid NULL handles */ + VolTbl[VolCnt++] = vp; /* set volume record */ ++ ++ /* check for color volume icon */ ++ if (!icon_exists(path)) ++ icon_create(path); ++ ++ return; + } + + private char * +*** applications/aufs/aufscicon.c.orig Thu Aug 7 18:17:32 1997 +--- applications/aufs/aufscicon.c Mon Aug 11 13:22:02 1997 +*************** +*** 0 **** +--- 1,2389 ---- ++ /* ++ * $Author: djh $ $Date: 1996/06/18 10:49:40 $ ++ * $Header: /mac/src/cap60/applications/aufs/RCS/aufsicon.c,v 2.9 1996/06/18 10:49:40 djh Rel djh $ ++ * $Revision: 2.9 $ ++ */ ++ ++ /* ++ * aufscicon.c - aufs color icon. ++ * ++ * Set up to display a color CAP volume ICON, and where ++ * possible represent the underlying hardware platform. ++ * ++ * AppleTalk package for UNIX (4.2 BSD). ++ * ++ * Copyright (c) 1986, 1987 by The Trustees of Columbia University in ++ * the City of New York. ++ * ++ */ ++ ++ #include ++ #include ++ #ifndef _TYPES ++ /* assume included by param.h */ ++ # include ++ #endif ++ #ifdef SOLARIS ++ # include ++ #endif /* SOLARIS */ ++ #ifdef linux ++ # include ++ #endif /* linux */ ++ #include ++ #include ++ #include ++ #include ++ #include ++ #ifdef NEEDFCNTLDOTH ++ #include ++ #endif /* NEEDFCNTLDOTH */ ++ #include "afps.h" ++ ++ /* ++ * The Icons in this file are intended for non-commercial CAP use, ++ * they are provided to visually link the CAP server and host type. ++ * Components of the images are copyright by the respective hardware ++ * manufacturers. ++ * ++ * BSD Daemon Copyright 1988 Marshall Kirk McKusick. All Rights Reserved. ++ * Penguin With Scarf Copyright 1996 David Hornsby. All Rights Reserved. ++ * Used with permisson. ++ * ++ * To enable the automatic host ICON selection, define USE_HOST_ICON in ++ * the m4.features file. ++ * ++ * The color_cap_icon[] array mirrors the resource fork of a ResEdit file ++ * that contains 'icl4', 'icl8', 'ICN#', 'ics#', 'ics4' & 'ics8' resources ++ * (resource IDs -16455). If you edit an Icon, or add resource names etc. ++ * ensure that the array length is accurate. ++ * ++ */ ++ ++ #define ICON_FNDR_WAS 286 ++ #define ICON_FNDR_LEN 300 ++ #define ICON_RSRC_LEN 2670 ++ #define ICON_NAME "Icon:0d" ++ #define INFO_MESSAGE1 "BSD Daemon Copyright 1988 Marshall Kirk McKusick - \ ++ All Rights Reserved.\r\rhttp://www.cs.mu.OZ.AU/appletalk/cap.html\r" ++ #define INFO_MESSAGE2 "http://www.cs.mu.OZ.AU/appletalk/cap.html\r" ++ ++ /* Automatic host/ICON selection */ ++ ++ #ifdef USE_HOST_ICON ++ ++ #ifdef __NetBSD__ ++ #define BSD_ICON 1 ++ #endif /* __NetBSD__ */ ++ #ifdef __386BSD__ ++ #define BSD_ICON 1 ++ #endif /* __386BSD__ */ ++ #ifdef __FreeBSD__ ++ #define BSD_ICON 1 ++ #endif /* __FreeBSD__ */ ++ #ifdef __bsdi__ ++ #define BSD_ICON 1 ++ #endif /* __bsdi__ */ ++ ++ #ifdef BSD_ICON ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* BSD2icon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x00,0x00,0x80,0x00,0xc0,0xab,0x00,0x08,0x66,0x08,0x30,0x3c, ++ 0xf8,0xc2,0x60,0x00,0x00,0x88,0x20,0x3c,0x00,0x00,0x80,0x00,0xc0,0xab, ++ 0x00,0x08,0x66,0x5a,0x20,0x3c,0x09,0x42,0x53,0x44,0x5f,0x50,0x69,0x63, ++ 0x6f,0x6e,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, ++ 0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x3c,0xdf,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x4e,0xba,0x0c,0x44,0x26,0x40,0x4f,0xef,0x00,0x14,0x20,0x3c,0x00,0x00, ++ 0x80,0x00,0xc0,0xab,0x00,0x08,0x66,0x06,0x30,0x3c,0xf8,0xc2,0x60,0x0e, ++ 0x20,0x6e,0x00,0x18,0x20,0x8b,0x20,0x6e,0x00,0x14,0x20,0x8a,0x70,0x00, ++ 0x4c,0xee,0x1c,0xe0,0xff,0xe2,0x4e,0x5e,0x4e,0x75,0x4e,0x56,0xff,0xee, ++ 0x48,0xe7,0x0f,0x18,0x28,0x2e,0x00,0x1c,0x2a,0x2e,0x00,0x18,0x20,0x6e, ++ 0x00,0x08,0x26,0x68,0x00,0x40,0x4a,0xae,0x00,0x14,0x67,0x14,0x00,0x84, ++ 0x00,0x00,0x80,0x00,0x2e,0x05,0x08,0x07,0x00,0x00,0x67,0x0a,0x20,0x07, ++ 0x52,0x87,0x60,0x04,0x7e,0x00,0x2a,0x07,0x2f,0x0b,0x2f,0x2e,0x00,0x0c, ++ 0x4e,0xba,0xf6,0x8e,0x2c,0x00,0x50,0x4f,0x66,0x7e,0x20,0x53,0x2c,0x10, ++ 0x20,0x4b,0x22,0x06,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, ++ 0xd8,0xff,0x00,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0xff,0xd8,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0xff,0xd8,0xd8, ++ 0xff,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff, ++ 0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00, ++ 0xff,0xff,0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8, ++ 0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff, ++ 0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff, ++ 0xff,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, ++ 0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff, ++ 0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00, ++ 0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xfc,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xd8,0xff,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00, ++ 0xfc,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xff,0x00,0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xff, ++ 0xff,0xff,0xd8,0xd8,0xff,0x00,0x00,0x00,0xfc,0xfc,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, ++ 0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xfc,0xfc,0x00, ++ 0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0xfc,0xfc, ++ 0xfc,0x00,0x00,0xfc,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0xff,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff, ++ 0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xfc,0xff,0xd8,0xd8,0xd8,0xff, ++ 0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff, ++ 0xff,0xd8,0xff,0x00,0x00,0x00,0xff,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8, ++ 0xff,0xff,0xff,0xd8,0xff,0x00,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xd8,0xff,0xd8,0xff,0xff,0xff,0xff,0xd8,0xff,0xff,0xd8, ++ 0xd8,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0xff,0x00,0x00,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xff,0xff,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xc0,0xc0, ++ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xfc,0xc0,0xc0,0xc0, ++ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xff,0x00, ++ 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0xff,0xff,0xff,0xfd, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd, ++ 0xff,0xff,0xff,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xf9, ++ 0xfb,0xfb,0xfb,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb, ++ 0xf9,0xf9,0xf7,0xf7,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0xf6,0xf6,0xf9,0xf9,0xfb,0xfb,0xfb,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0x00,0x00, ++ 0x00,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x02,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00,0xff,0xff,0xf0,0x00, ++ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x3f,0x0f,0x33,0x33, ++ 0x3f,0x0f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0xf3, ++ 0x3f,0xff,0x33,0xf3,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0x0f,0xff,0x33,0xf3,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x0f,0x00,0xff,0x00,0xf3,0x3f,0x33,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xf0,0x00,0xf0,0x00,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0xff,0xf0,0x0f,0x3f,0x33,0x3f,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0xff,0xf0,0x0f,0x3f,0x33, ++ 0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0xff,0xf0,0x0f, ++ 0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0xf0, ++ 0x00,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0, ++ 0x00,0xf0,0x00,0xf3,0x3f,0x33,0x3f,0x00,0x0e,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x0f,0xff,0xff,0xff,0x3f,0xf3,0x33,0x3f,0x00,0xe0,0x0e,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xf3,0x33,0xf3,0x33,0x33,0x33,0x3f,0x00,0xe0,0xe0, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xf3,0x33,0xf3,0x3f,0xff,0x33,0xf0,0x00, ++ 0xee,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0x33,0x33,0xf3,0x33, ++ 0xf0,0x0e,0xe0,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf3,0x33,0x3f, ++ 0x33,0x3f,0xee,0xe0,0x0e,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f, ++ 0xff,0xff,0xff,0xf3,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xf3,0x33,0x33,0xf3,0x33,0xf0,0x00,0xff,0xf0,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xf3,0x3f,0x33,0x33,0xf0,0x0f,0x00,0x0f,0x00,0x00, ++ 0xf0,0xff,0x00,0x00,0x0f,0x33,0x3f,0xef,0x33,0x3f,0xf0,0x0f,0x0f,0x0f, ++ 0x00,0x00,0xff,0xf3,0xf0,0x00,0xff,0x33,0x3f,0x33,0xff,0xf3,0xf0,0x0f, ++ 0x0f,0xf0,0x00,0x00,0xf3,0xf3,0xff,0xff,0x3f,0xf3,0x3f,0x3f,0x33,0x33, ++ 0xf0,0xf0,0x0f,0xff,0x00,0x00,0xf3,0xf3,0x33,0x33,0x33,0xff,0xf3,0x33, ++ 0x33,0x33,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0x77,0x77,0x77,0x77,0x77, ++ 0x77,0xe7,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0xf0,0x00,0xff,0xff,0xff, ++ 0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x0a,0xff,0xfa,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xff,0xfa,0x00,0x00, ++ 0x00,0x00,0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab,0xba,0xbb, ++ 0xaa,0xae,0xee,0xdd,0xcc,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb, ++ 0xa0,0xab,0xbb,0xbb,0xbb,0xcc,0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa, ++ 0xaa,0xaa,0x00,0x0a,0xaa,0xae,0xee,0xdd,0x00,0x00,0x01,0x00,0x00,0x0c, ++ 0x01,0x80,0x00,0x18,0xf8,0xc0,0x00,0x15,0x05,0x40,0x00,0x12,0x72,0x40, ++ 0x00,0x0d,0xc9,0x40,0x00,0x13,0x24,0xc0,0x00,0x22,0x14,0x40,0x00,0x3b, ++ 0x94,0x40,0x00,0x3b,0x94,0x40,0x00,0x3b,0x94,0x40,0x00,0x22,0x14,0x40, ++ 0x00,0x22,0x24,0x44,0x00,0x1f,0xd8,0x49,0x00,0x08,0x80,0x4a,0x00,0x08, ++ 0x9c,0x8c,0x00,0x07,0x08,0x99,0x00,0x02,0x11,0xe6,0x00,0x01,0xfe,0x80, ++ 0x00,0x02,0x08,0x8e,0x00,0x03,0x90,0x91,0x0b,0x04,0x71,0x95,0x0e,0x8c, ++ 0x4e,0x96,0x0a,0xf6,0x50,0xa7,0x0a,0x03,0x80,0xc0,0xff,0xff,0xff,0xff, ++ 0x40,0x02,0x00,0x02,0x3f,0xff,0xff,0xfc,0x00,0x08,0x1f,0x00,0x00,0x00, ++ 0x1f,0x00,0xff,0xff,0xe4,0xff,0x00,0x00,0x0a,0x00,0xff,0xff,0xf1,0xff, ++ 0x00,0x0c,0x01,0x80,0x00,0x18,0xf8,0xc0,0x00,0x1d,0xfd,0xc0,0x00,0x1f, ++ 0xff,0xc0,0x00,0x0f,0xff,0xc0,0x00,0x1f,0xff,0xc0,0x00,0x3f,0xff,0xc0, ++ 0x00,0x3f,0xff,0xc0,0x00,0x3f,0xff,0xc0,0x00,0x3f,0xff,0xc0,0x00,0x3f, ++ 0xff,0xc0,0x00,0x3f,0xff,0xc4,0x00,0x1f,0xff,0xc9,0x00,0x0f,0xff,0xca, ++ 0x00,0x0f,0xff,0x8c,0x00,0x07,0xff,0x99,0x00,0x03,0xff,0xe6,0x00,0x01, ++ 0xff,0x80,0x00,0x03,0xff,0x8e,0x00,0x03,0xff,0x91,0x0b,0x07,0xff,0x95, ++ 0x0f,0x8f,0xff,0x96,0x0f,0xff,0xff,0xa7,0x0f,0xff,0xff,0xc0,0xff,0xff, ++ 0xff,0xff,0x7f,0xff,0xff,0xfe,0x3f,0xff,0xff,0xfc,0x00,0x08,0x1f,0x00, ++ 0x00,0x00,0x1f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff, ++ 0xf1,0xff,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00, ++ 0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, ++ 0x00,0x00,0xff,0xff,0x00,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xd8,0xd8,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xd8,0xd8,0xff,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xd8,0xff,0xff,0xff,0xd8, ++ 0xd8,0xd8,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xff,0xd8,0xd8,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xd8,0x00, ++ 0xd8,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff,0x00,0x00, ++ 0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xff, ++ 0x00,0x00,0xff,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, ++ 0xc0,0xc0,0xc0,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x80, ++ 0x00,0x00,0x0f,0xf0,0x00,0xff,0x00,0x00,0x00,0x00,0x0f,0x00,0xff,0x03, ++ 0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x0f,0x0f, ++ 0x0f,0x33,0xf0,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x33,0xf0,0x00,0x00,0x00, ++ 0x03,0x33,0x33,0x33,0xf0,0x0f,0x00,0x00,0x03,0xff,0xf3,0x33,0xf0,0xf0, ++ 0x00,0x00,0x00,0x33,0x33,0x3f,0x00,0xff,0x00,0x00,0x00,0x0f,0xf3,0x3f, ++ 0xff,0x00,0x00,0x30,0x30,0x00,0xf3,0x33,0xf0,0x0f,0x00,0x3f,0x33,0x33, ++ 0x33,0xf3,0xff,0xff,0x00,0x03,0x33,0x3f,0x33,0x33,0x3f,0x00,0xf7,0x77, ++ 0x77,0x77,0x77,0x77,0x77,0x7f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xf0, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, ++ 0xaa,0xaa,0x00,0x00,0x00,0x40,0x06,0x30,0x04,0xd0,0x07,0xf0,0x05,0x78, ++ 0x05,0x78,0x07,0xf9,0x07,0xfa,0x03,0xf3,0x01,0xfc,0x28,0xf9,0x29,0xff, ++ 0x1f,0xfc,0xff,0xff,0x7f,0xfe,0x00,0x30,0xff,0xff,0x06,0x30,0x04,0xf0, ++ 0x07,0xf0,0x07,0xf8,0x07,0xf8,0x07,0xf9,0x07,0xfa,0x03,0xf3,0x01,0xfc, ++ 0x28,0xf9,0x3f,0xff,0x1f,0xfc,0xff,0xff,0x7f,0xfe,0x00,0x30,0xff,0xff, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x03,0x71,0xee,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x38,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x34, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x38,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x23,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x03,0x71,0xf3,0x30,0xbf,0xb9,0xff,0xff,0x00,0x00,0x04,0x04, ++ 0x03,0x71,0xea,0xd8,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, ++ 0xf5,0xd4,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xef,0x78, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x08,0x10,0x03,0x70,0xf8,0xc0,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x08,0x94,0x03,0x71,0xf4,0xf0 ++ }; ++ #endif /* BSD_ICON */ ++ ++ #ifndef HOST_ICON ++ #ifdef sun ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* SUNicon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x08,0xc8, ++ 0x09,0x99,0x08,0xc8,0x09,0xca,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x07,0x53,0x55,0x4e,0x69,0x63,0x6f,0x6e, ++ 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00, ++ 0x00,0x00,0x00,0x81,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0x00,0x00,0x00,0x81,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x3b,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x00,0x00,0x00,0x53, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x01,0x02, ++ 0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04, ++ 0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02, ++ 0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04,0x01,0x02,0x03,0x04, ++ 0x00,0x07,0x07,0x43,0x6f,0x75,0x72,0x69,0x65,0x72,0x02,0x05,0x06,0x02, ++ 0x05,0x07,0x03,0x05,0x06,0x07,0x01,0x2d,0x04,0x42,0x6f,0x6c,0x64,0x07, ++ 0x4f,0x62,0x6c,0x69,0x71,0x75,0x65,0x00,0x00,0x00,0x01,0x00,0x00,0x02, ++ 0x1a,0xf6,0x00,0x02,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f, ++ 0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00, ++ 0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f, ++ 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, ++ 0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f, ++ 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f, ++ 0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f, ++ 0x7f,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00, ++ 0x7f,0x7f,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f, ++ 0x7f,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00, ++ 0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, ++ 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f, ++ 0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x7f,0x7f, ++ 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0xab, ++ 0xab,0xab,0xab,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x00, ++ 0x00,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f, ++ 0x7f,0x00,0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x7f,0x7f,0x7f,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x7f,0x7f, ++ 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, ++ 0xab,0xab,0xab,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, ++ 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0xab,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x7f, ++ 0x7f,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00, ++ 0xab,0x2a,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00, ++ 0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, ++ 0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, ++ 0xab,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00, ++ 0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff, ++ 0x2a,0xff,0x2a,0x2a,0xab,0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f, ++ 0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x7f,0x7f,0x7f,0x00, ++ 0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00, ++ 0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00, ++ 0x00,0x00,0x00,0x00,0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f,0x00, ++ 0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, ++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x7f, ++ 0x00,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x7f,0x7f, ++ 0x7f,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x7f,0x7f,0x7f,0x00,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd, ++ 0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb, ++ 0xfb,0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb, ++ 0xf9,0xf9,0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x02,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x55,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x55,0x50, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55, ++ 0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x50,0x55,0x50,0x55,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x50,0x55,0x50,0x55,0x50,0x55,0x50,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x55,0x50,0x55,0x50,0x55,0x50,0x50, ++ 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x55,0x05,0x55,0x05,0x55,0x05, ++ 0x55,0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x50,0x50,0x55,0x50, ++ 0x55,0x50,0x50,0x55,0x50,0x50,0x00,0x00,0x00,0x00,0x05,0x55,0x05,0x55, ++ 0x05,0x55,0x05,0x50,0x05,0x55,0x05,0x55,0x00,0x00,0x00,0x00,0x55,0x50, ++ 0x55,0x50,0x00,0x55,0x55,0x50,0x55,0x50,0x55,0x50,0x00,0x00,0x00,0x05, ++ 0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00, ++ 0x00,0x00,0x00,0x55,0x50,0x55,0x55,0x50,0x00,0x55,0x50,0x55,0x50,0x55, ++ 0x55,0x50,0x0e,0xee,0xe0,0x55,0x05,0x55,0x05,0x50,0x00,0x55,0x05,0x55, ++ 0x05,0x55,0x05,0x50,0xe7,0x77,0x7e,0x00,0x00,0x00,0x55,0x50,0x00,0x55, ++ 0x55,0x50,0x55,0x50,0x55,0x50,0xee,0xee,0xee,0xee,0xee,0xe0,0x55,0x05, ++ 0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0, ++ 0x50,0x55,0x55,0x50,0x00,0x55,0x50,0x55,0x50,0x00,0xe7,0x77,0xf7,0x77, ++ 0x77,0xe0,0x00,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0x00,0xe7,0x7f, ++ 0x77,0x77,0x77,0xe0,0x50,0x55,0x50,0x55,0x50,0x50,0x55,0x50,0x00,0x00, ++ 0xe7,0xf7,0x7f,0x7f,0x77,0xe0,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00, ++ 0x00,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x55,0x50,0x55,0x50,0x55,0x50, ++ 0x50,0x00,0x00,0x00,0xee,0xee,0xee,0xee,0xee,0xe0,0x05,0x55,0x05,0x55, ++ 0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0xad,0xa0,0x00,0x00,0x50,0x55, ++ 0x50,0x55,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x05, ++ 0x55,0x05,0x55,0x05,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f, ++ 0x00,0x00,0x55,0x50,0x55,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x0f, ++ 0xff,0xff,0x00,0x00,0x05,0x55,0x05,0x50,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x0a,0xda,0xda,0x00,0x00,0x00,0x55,0x55,0x50,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xde,0xad,0xda,0xdd,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, ++ 0xaa,0xae,0xee,0xdd,0xcd,0xdd,0xa0,0xad,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd, ++ 0xdd,0xdd,0xdd,0xdd,0xdd,0xcc,0xde,0xaa,0x00,0x0a,0xaa,0xaa,0xaa,0xaa, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0x00,0x00,0x01,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x01,0xc0,0x00,0x00,0x03,0xe0,0x00,0x00,0x03,0x70,0x00, ++ 0x00,0x0b,0xb8,0x00,0x00,0x1d,0xdc,0x00,0x00,0x2e,0xee,0x00,0x00,0x77, ++ 0x77,0x00,0x00,0xbb,0xba,0x80,0x01,0xdd,0xdd,0xc0,0x03,0xae,0xeb,0xa0, ++ 0x07,0x77,0x67,0x70,0x0e,0xe3,0xee,0xe0,0x1d,0xdd,0xdd,0xdc,0x03,0xbe, ++ 0x3b,0xbe,0x7b,0x76,0x37,0x76,0x84,0x0e,0x3e,0xee,0xff,0xed,0xdd,0xdc, ++ 0x80,0x2b,0xe3,0xb8,0x88,0x23,0x77,0x70,0x90,0x2b,0xba,0xe0,0xa5,0x2d, ++ 0xdd,0xc0,0x80,0x2e,0xee,0x80,0xff,0xe7,0x77,0x00,0x0a,0x0b,0xba,0x00, ++ 0x1f,0x1d,0xdc,0x00,0x11,0x0e,0xe8,0x00,0x1f,0x07,0x60,0x00,0x15,0x03, ++ 0xe0,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x03,0xe0,0x00,0x00,0x07,0xf0,0x00,0x00,0x0b, ++ 0xf8,0x00,0x00,0x1f,0xfc,0x00,0x00,0x3f,0xfe,0x00,0x00,0x7f,0xff,0x00, ++ 0x00,0xff,0xff,0x80,0x01,0xff,0xff,0xc0,0x03,0xff,0xff,0xe0,0x07,0xff, ++ 0xff,0xf0,0x0f,0xff,0xef,0xf8,0x1f,0xf7,0xff,0xf4,0x3f,0xff,0xff,0xfe, ++ 0x03,0xff,0xff,0xfe,0x7b,0xff,0xff,0xfe,0xfc,0x0f,0xff,0xfe,0xff,0xef, ++ 0xff,0xfe,0xff,0xef,0xf7,0xfc,0xff,0xeb,0xff,0xf8,0xff,0xef,0xff,0xf0, ++ 0xff,0xef,0xff,0xe0,0xff,0xef,0xff,0xc0,0xff,0xef,0xff,0x80,0x0e,0x0f, ++ 0xff,0x00,0x1f,0x3f,0xfe,0x00,0x1f,0x1f,0xfc,0x00,0x1f,0x0f,0xe8,0x00, ++ 0x1f,0x07,0xf0,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, ++ 0xff,0xff,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f, ++ 0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00, ++ 0x00,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, ++ 0x7f,0x7f,0x00,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, ++ 0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0xab,0xab,0xab,0x7f,0x7f,0x7f,0x7f,0x7f, ++ 0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0xab,0xab,0xab,0xab,0xab,0xab, ++ 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0xab,0x2a,0xff,0x2a, ++ 0x2a,0xab,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0xab,0xff, ++ 0xff,0xff,0x2a,0xab,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00, ++ 0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x7f,0x7f,0x7f,0x7f, ++ 0x7f,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9,0x00,0x00,0x00,0x80, ++ 0x00,0x00,0x00,0x05,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x55,0x00, ++ 0x00,0x00,0x00,0x00,0x05,0x55,0x55,0x50,0x00,0x00,0x00,0x00,0x55,0x55, ++ 0x55,0x55,0x00,0x00,0x00,0x05,0x55,0x55,0x55,0x55,0x50,0x00,0x00,0x55, ++ 0x55,0x55,0x55,0x55,0x55,0x00,0x05,0x55,0x55,0x55,0x55,0x55,0x55,0x50, ++ 0xee,0xe5,0x55,0x55,0x05,0x55,0x55,0x55,0xee,0xee,0xee,0x55,0x55,0x55, ++ 0x55,0x55,0xe7,0xf7,0x7e,0x55,0x55,0x55,0x55,0x50,0xef,0xff,0x7e,0x55, ++ 0x55,0x55,0x55,0x00,0xee,0xee,0xee,0x55,0x55,0x55,0x50,0x00,0x0f,0xff, ++ 0x05,0x55,0x55,0x55,0x00,0x00,0x0f,0xff,0x00,0x55,0x55,0x50,0x00,0x00, ++ 0xed,0xdd,0xaa,0xaa,0xaa,0xaa,0xae,0xed,0xed,0xad,0xdd,0xdd,0xdd,0xdd, ++ 0xde,0xed,0x00,0x00,0x00,0x40,0x01,0x80,0x03,0xc0,0x07,0xe0,0x0f,0xf0, ++ 0x1f,0xf8,0x3f,0xfc,0x7f,0xfe,0xff,0x7f,0xff,0xff,0xa7,0xfe,0xf7,0xfc, ++ 0xff,0xf8,0x77,0xf0,0x73,0xe0,0xff,0xff,0xff,0xff,0x01,0xc0,0x03,0xe0, ++ 0x07,0xf0,0x0f,0xf8,0x1f,0xfc,0x3f,0xfe,0x7f,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xfe,0xff,0xfc,0xff,0xf8,0x7f,0xf0,0x7f,0xe0,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x03,0x71,0xee,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x38,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x34, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x38,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x23,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x03,0x71,0xf1,0xbc,0xbf,0xb9,0xff,0xff,0x00,0x00,0x04,0x04, ++ 0x03,0x71,0xf2,0xd4,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, ++ 0xf1,0x7c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf1,0x74, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x08,0x10,0x03,0x71,0xf5,0x4c,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x08,0x94,0x03,0x71,0xf4,0xac ++ }; ++ #endif /* sun */ ++ #endif /* HOST_ICON */ ++ ++ #ifndef HOST_ICON ++ #ifdef NeXT ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* NeXTicon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x6e,0x00,0x08,0x32,0x2e,0x00,0x12,0x60,0x06,0x12,0xc2,0x30, ++ 0x01,0x53,0x41,0x4a,0x41,0x66,0xf6,0x4e,0x5e,0x4e,0x75,0x4e,0x56,0xff, ++ 0xfc,0x48,0xe7,0x03,0x08,0x42,0x08,0x4e,0x45,0x58,0x54,0x69,0x63,0x6f, ++ 0x6e,0x58,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, ++ 0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x36,0xfb,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x6f,0x00,0x04,0x70,0x00,0xa0,0xac,0x20,0x5f,0x58,0x8f,0x60,0x0c,0x30, ++ 0x6f,0x00,0x04,0x70,0x01,0xa0,0xac,0x20,0x5f,0x54,0x8f,0x3e,0x80,0x4e, ++ 0xd0,0x48,0xe7,0x18,0x00,0x20,0x6f,0x00,0x0c,0x36,0x2f,0x00,0x10,0x38, ++ 0x2f,0x00,0x12,0x70,0x05,0xa0,0xac,0x4c,0xdf,0x00,0x18,0x20,0x5f,0x50, ++ 0x8f,0x60,0xde,0x48,0xe7,0x18,0x00,0x20,0x6f,0x00,0x10,0x30,0x10,0x20, ++ 0x6f,0x00,0x0c,0x31,0x40,0x00,0x04,0x36,0x2f,0x00,0x14,0x38,0x2f,0x00, ++ 0x16,0x70,0x02,0xa0,0xac,0x32,0x04,0x4c,0xdf,0x00,0x18,0x4a,0x40,0x66, ++ 0x06,0x20,0x6f,0x00,0x08,0x30,0x81,0x20,0x5f,0xde,0xfc,0x00,0x0c,0x60, ++ 0xa8,0x2f,0x03,0x20,0x6f,0x00,0x08,0x36,0x2f,0x00,0x0c,0x70,0x06,0xa0, ++ 0xac,0x26,0x1f,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0xf1,0xf0,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x1f,0xff,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0xf1,0xff, ++ 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, ++ 0x1f,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff, ++ 0xff,0xf1,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0xff,0xff,0x1f,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x0f,0xff,0xff,0xf1,0xff,0xff,0xf1,0x11,0xff,0xff,0xf0,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xff,0xff,0x1f,0xff,0xff,0xf1,0x1f,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0xf1,0xff,0xff,0xff,0xf1,0xff,0x1f, ++ 0xff,0xff,0xf0,0x00,0x00,0x00,0xff,0xff,0xff,0x1f,0xff,0xff,0x1f,0xff, ++ 0x11,0xff,0x1f,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0xf1,0xff,0xff,0xff, ++ 0xf1,0xff,0xff,0xf1,0xff,0xff,0xff,0xf0,0x00,0x0f,0xff,0xff,0x1f,0xff, ++ 0xff,0x11,0x11,0x1f,0xff,0x1f,0x1f,0xff,0xff,0xff,0x0e,0xee,0xef,0xff, ++ 0xf1,0xff,0xff,0xf1,0xff,0xff,0xff,0xff,0xf1,0xff,0xff,0xf0,0xe7,0x77, ++ 0x7e,0xff,0xff,0x1f,0xff,0xff,0x1f,0xff,0x1f,0xff,0xff,0xff,0xff,0x00, ++ 0xee,0xee,0xee,0xee,0xee,0xe1,0xff,0xff,0xff,0xff,0x1f,0xff,0xff,0xff, ++ 0xf0,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x1f,0xff,0xff,0x11,0x11,0x1f, ++ 0xff,0xff,0x00,0x00,0xe7,0x77,0xf7,0x77,0x77,0xe0,0xf1,0xff,0xff,0xff, ++ 0x1f,0xff,0xff,0xf0,0x00,0x00,0xe7,0x7f,0x77,0x77,0x77,0xe0,0xff,0x1f, ++ 0xff,0xff,0x1f,0xff,0xff,0x00,0x00,0x00,0xe7,0xf7,0x7f,0x7f,0x77,0xe0, ++ 0xff,0xf1,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0xe7,0x77,0x77,0x77, ++ 0x77,0xe0,0xff,0xff,0x1f,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xee,0xee, ++ 0xee,0xee,0xee,0xe0,0xff,0xff,0xf1,0xff,0xff,0xf0,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xad,0xa0,0x00,0x00,0xff,0xff,0xff,0x1f,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0xf1,0xf0,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f,0x00,0x00,0x00,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x0f, ++ 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xde,0xeb,0xba,0xbb, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, ++ 0xa0,0xab,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, ++ 0xde,0xea,0x00,0x0a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, ++ 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05, ++ 0xff,0xff,0xff,0xff,0xff,0x05,0x05,0x05,0xff,0xff,0xff,0xff,0xff,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0x05,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x05, ++ 0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff, ++ 0x05,0xff,0xff,0xff,0x05,0x05,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, ++ 0x05,0xff,0xff,0xff,0xff,0xff,0x05,0x05,0x05,0x05,0x05,0xff,0xff,0xff, ++ 0x05,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xab,0xab,0xab, ++ 0xab,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x00, ++ 0xab,0x54,0x54,0x54,0x54,0xab,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff, ++ 0xff,0xff,0x05,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, ++ 0xab,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x05,0xff,0xff,0xff,0xff,0xff,0x05,0x05, ++ 0x05,0x05,0x05,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xab,0x2a, ++ 0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xff,0x05,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00, ++ 0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff,0x2a,0xff, ++ 0x2a,0x2a,0xab,0x00,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xff,0xff,0xff,0xff,0x05,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xff,0xff, ++ 0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x05, ++ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0x33, ++ 0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, ++ 0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xf0,0x00,0x00,0x07,0xe8,0x00, ++ 0x00,0x0f,0xdc,0x00,0x00,0x1f,0xbe,0x00,0x00,0x3f,0x7f,0x00,0x00,0x7e, ++ 0xff,0x80,0x00,0xfd,0xff,0xc0,0x01,0xfb,0xe3,0xe0,0x03,0xf7,0xe7,0xf0, ++ 0x07,0xef,0xed,0xf8,0x0f,0xdf,0x73,0x7c,0x1f,0xbf,0xbe,0xfe,0x1f,0x7c, ++ 0x1d,0x7f,0x7f,0xbe,0xff,0xbe,0x87,0xdf,0x77,0xfc,0xff,0xef,0xf7,0xf8, ++ 0x80,0x27,0xc1,0xf0,0x88,0x2b,0xf7,0xe0,0x90,0x2d,0xf7,0xc0,0xa5,0x2e, ++ 0xff,0x80,0x80,0x2f,0x7f,0x00,0xff,0xef,0xbe,0x00,0x0a,0x0f,0xdc,0x00, ++ 0x1f,0x07,0xe8,0x00,0x11,0x03,0xf0,0x00,0x1f,0x01,0xe0,0x00,0x15,0x00, ++ 0x00,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xf0,0x00,0x00,0x07, ++ 0xf8,0x00,0x00,0x0f,0xfc,0x00,0x00,0x1f,0xfe,0x00,0x00,0x3f,0xff,0x00, ++ 0x00,0x7f,0xff,0x80,0x00,0xff,0xff,0xc0,0x01,0xff,0xff,0xe0,0x03,0xff, ++ 0xff,0xf0,0x07,0xff,0xff,0xf8,0x0f,0xff,0xff,0xfc,0x1f,0xff,0xff,0xfe, ++ 0x1f,0xff,0xff,0xff,0x7f,0xff,0xff,0xfe,0xff,0xff,0xff,0xfc,0xff,0xff, ++ 0xff,0xf8,0xff,0xef,0xff,0xf0,0xff,0xff,0xff,0xe0,0xff,0xef,0xff,0xc0, ++ 0xff,0xff,0xff,0x80,0xff,0xef,0xff,0x00,0xff,0xff,0xfe,0x00,0x0e,0x0f, ++ 0xfc,0x00,0x1f,0x07,0xf8,0x00,0x1f,0x03,0xf0,0x00,0x1f,0x01,0xe0,0x00, ++ 0x1f,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x40,0x00,0x00,0x03,0xe0,0x07,0x70,0x0e,0xf8, ++ 0x1d,0x9c,0x3b,0xfe,0x76,0x67,0xfb,0xff,0xfd,0x9e,0xa6,0xfc,0xf7,0x78, ++ 0xff,0xb0,0x73,0xe0,0x71,0xc0,0xff,0xfe,0xff,0xfe,0x00,0x00,0x03,0xe0, ++ 0x07,0xf0,0x0f,0xf8,0x1f,0xfc,0x3f,0xfe,0x7f,0xff,0xff,0xff,0xff,0xfe, ++ 0xff,0xfc,0xff,0xf8,0xff,0xf0,0x7f,0xe0,0x7f,0xc0,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x0f,0xff,0x1f,0xff,0x00,0x00, ++ 0x00,0x00,0xff,0xf1,0xff,0xff,0xf0,0x00,0x00,0x0f,0xff,0x1f,0xf1,0x1f, ++ 0xff,0x00,0x00,0xff,0xf1,0xff,0xff,0xff,0xff,0xf0,0x0f,0xff,0x1f,0xf1, ++ 0x1f,0xf1,0x1f,0xff,0xef,0xff,0xf1,0xff,0xff,0xff,0xff,0xff,0xee,0xef, ++ 0xff,0x1f,0xf1,0x1f,0xff,0xf0,0xec,0xfc,0xce,0xf1,0xff,0xff,0xff,0x00, ++ 0xef,0xff,0xce,0xff,0x1f,0xff,0xf0,0x00,0xee,0xee,0xee,0xff,0xf1,0xff, ++ 0x00,0x00,0x0f,0xff,0x00,0xff,0xff,0xf0,0x00,0x00,0x0f,0xff,0x00,0x0f, ++ 0xff,0x00,0x00,0x00,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0xea,0xaa, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x05,0xff,0xff,0xff,0xff, ++ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x05,0xff,0xff,0x05, ++ 0x05,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x05,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0x05,0xff, ++ 0xff,0x05,0x05,0xff,0xff,0x05,0x05,0xff,0xff,0xff,0xab,0xff,0xff,0xff, ++ 0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xab,0xab, ++ 0xab,0xff,0xff,0xff,0x05,0xff,0xff,0x05,0x05,0xff,0xff,0xff,0xff,0x00, ++ 0xab,0x2a,0xff,0x2a,0x2a,0xab,0xff,0x05,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0xab,0xff,0xff,0xff,0x2a,0xab,0xff,0xff,0x05,0xff,0xff,0xff, ++ 0xff,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xff,0xff,0xff,0x05, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x03,0x71,0xf1,0xe4,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, ++ 0x03,0x71,0xee,0xb0,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, ++ 0xf3,0xdc,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf5,0x84, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf0,0x2c,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf0,0xe4 ++ }; ++ #endif /* NeXT */ ++ #endif /* HOST_ICON */ ++ ++ #ifndef HOST_ICON ++ #ifdef AIX ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* IBMicon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x20,0x03,0x9d, ++ 0x1f,0x00,0x00,0x00,0x00,0xf0,0x20,0xff,0xff,0x00,0x03,0x9d,0x27,0x00, ++ 0x00,0x00,0x00,0xf0,0x37,0xff,0x07,0x49,0x42,0x4d,0x69,0x63,0x6f,0x6e, ++ 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00, ++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x1d,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x37,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x00,0x00,0x00,0xf0,0x3e,0xff,0xff,0x00,0x03,0x9f,0x1c,0x00,0x00,0x00, ++ 0x00,0xf0,0x3c,0xff,0xff,0x00,0x03,0x9f,0x84,0x00,0x00,0x00,0x00,0xf0, ++ 0x30,0xff,0xff,0x00,0x03,0xa0,0x12,0x00,0x00,0x00,0x00,0xf0,0x32,0xff, ++ 0xff,0x00,0x03,0xa5,0x06,0x00,0x00,0x00,0x00,0xf0,0x20,0xff,0xff,0x00, ++ 0x03,0xa6,0x0e,0x00,0x00,0x00,0x00,0xf0,0x3f,0xff,0xff,0x00,0x03,0xaf, ++ 0x41,0x00,0x00,0x00,0x00,0xf0,0x37,0xff,0xff,0x00,0x03,0xf6,0x52,0x00, ++ 0x00,0x00,0x00,0xf0,0x20,0xff,0xff,0x00,0x03,0xaf,0x5f,0x00,0x00,0x00, ++ 0x00,0xf0,0x20,0xff,0xff,0x00,0x03,0xaf,0x81,0x00,0x00,0x00,0x00,0xf0, ++ 0x37,0xff,0xff,0x00,0x03,0xb0,0x85,0x00,0x00,0x00,0x00,0xf0,0x38,0x01, ++ 0x8d,0x00,0x03,0xb1,0x00,0x00,0x02,0x00,0x06,0x66,0x60,0x06,0x66,0x66, ++ 0x66,0x00,0x00,0x66,0x66,0x60,0x00,0x66,0x66,0x60,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x66, ++ 0x60,0x06,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x60,0x00,0x66,0x66,0x60, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x66,0x00,0x00,0x66,0x00,0x00,0x66,0x60,0x06,0x66,0x60, ++ 0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x00,0x00,0x66,0x66,0x66,0x66, ++ 0x00,0x06,0x66,0x60,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x00,0x00, ++ 0x66,0x66,0x66,0x66,0x00,0x06,0x60,0x66,0x06,0x60,0x66,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x66,0x00,0x00,0x66,0x00,0x00,0x66,0x60,0x06,0x60,0x66,0x06,0x60, ++ 0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x06,0x66,0x60,0x06,0x66,0x66,0x66,0x66,0x00,0x66, ++ 0x60,0x06,0x66,0x00,0x66,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x66,0x60,0x06,0x66,0x66, ++ 0x66,0x00,0x00,0x66,0x60,0x00,0x60,0x00,0x66,0x60,0x0e,0xee,0xe0,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77, ++ 0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xee,0xee,0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xe7,0x77,0xf7,0x77,0x77,0xe0,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x7f,0x77,0x77,0x77,0xe0,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0xf7,0x7f,0x7f,0x77,0xe0, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77,0x77,0x77, ++ 0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xee,0xee, ++ 0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xab,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xde,0xeb,0xba,0xbb, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, ++ 0xa0,0xab,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, ++ 0xde,0xea,0x00,0x0a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, ++ 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0xec, ++ 0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec, ++ 0xec,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xec,0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec, ++ 0xec,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0xec,0xec, ++ 0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x00,0x00, ++ 0x00,0x00,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xec, ++ 0xec,0xec,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec, ++ 0xec,0xec,0xec,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0x00, ++ 0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec, ++ 0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00, ++ 0x00,0xec,0xec,0x00,0xec,0xec,0x00,0xec,0xec,0x00,0xec,0xec,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec, ++ 0x00,0x00,0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xec,0xec,0x00,0xec,0xec, ++ 0x00,0xec,0xec,0x00,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, ++ 0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec, ++ 0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xec,0xec, ++ 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0xec, ++ 0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0x00, ++ 0x00,0x00,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0x00,0x00,0xab,0xab,0xab, ++ 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, ++ 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a, ++ 0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff,0x2a,0xff, ++ 0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0x33, ++ 0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, ++ 0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x79,0xfc, ++ 0x3e,0x3e,0x00,0x00,0x00,0x00,0x79,0xff,0x3e,0x3e,0x00,0x00,0x00,0x00, ++ 0x30,0xc3,0x9e,0x3c,0x00,0x00,0x00,0x00,0x30,0xff,0x1e,0x3c,0x00,0x00, ++ 0x00,0x00,0x30,0xff,0x1b,0x6c,0x00,0x00,0x00,0x00,0x30,0xc3,0x9b,0x6c, ++ 0x00,0x00,0x00,0x00,0x79,0xff,0x39,0xce,0x00,0x00,0x00,0x00,0x79,0xfc, ++ 0x38,0x8e,0x78,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0xff,0xe0,0x00,0x00, ++ 0x80,0x20,0x00,0x00,0x88,0x20,0x00,0x00,0x90,0x20,0x00,0x00,0xa5,0x20, ++ 0x00,0x00,0x80,0x20,0x00,0x00,0xff,0xe0,0x00,0x00,0x0a,0x00,0x00,0x00, ++ 0x1f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x15,0x00, ++ 0x00,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, ++ 0x79,0xfc,0x3e,0x3e,0x79,0xfc,0x3e,0x3e,0x79,0xff,0x3e,0x3e,0x30,0xc3, ++ 0x1e,0x3c,0x30,0xc3,0x9e,0x3c,0x30,0xc3,0x1e,0x3c,0x30,0xff,0x1e,0x3c, ++ 0x30,0xfe,0x1a,0x2c,0x30,0xff,0x1b,0x6c,0x30,0xc3,0x1b,0x6c,0x30,0xc3, ++ 0x9b,0x6c,0x30,0xc3,0x19,0xcc,0x79,0xff,0x39,0xce,0x79,0xfc,0x38,0x8e, ++ 0x79,0xfc,0x38,0x8e,0x78,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0xff,0xe0, ++ 0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00, ++ 0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0x0e,0x00, ++ 0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, ++ 0x1f,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x40,0xfe,0x77,0xff,0x77,0x49,0xf6,0x4f,0x76, ++ 0x4f,0x7e,0x49,0xfe,0xff,0x7b,0xfe,0x6b,0xfc,0x00,0xa4,0x00,0xf4,0x00, ++ 0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff,0xfe,0x77,0xff,0x77, ++ 0x49,0xf6,0x4f,0x76,0x4f,0x7e,0x49,0xfe,0xff,0x7b,0xfe,0x6b,0xfc,0x00, ++ 0xfc,0x00,0xfc,0x00,0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x80,0x66,0x66,0x66,0x60,0x06,0x66,0x06,0x66,0x66,0x66, ++ 0x66,0x66,0x06,0x66,0x06,0x66,0x06,0x00,0x60,0x06,0x66,0x66,0x06,0x60, ++ 0x06,0x00,0x66,0x66,0x06,0x66,0x06,0x60,0x06,0x00,0x66,0x66,0x06,0x66, ++ 0x66,0x60,0x06,0x00,0x60,0x06,0x66,0x66,0x66,0x60,0x66,0x66,0x66,0x66, ++ 0x06,0x66,0x60,0x66,0xee,0xe6,0x66,0x60,0x06,0x60,0x60,0x66,0xee,0xee, ++ 0xee,0x00,0x00,0x00,0x00,0x00,0xe7,0xf7,0x7e,0x00,0x00,0x00,0x00,0x00, ++ 0xef,0xff,0x7e,0x00,0x00,0x00,0x00,0x00,0xee,0xee,0xee,0x00,0x00,0x00, ++ 0x00,0x00,0x0f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xee,0xbb,0xaa,0xaa,0xaa,0xaa,0xae,0xed,0xee,0xab, ++ 0xbb,0xbb,0xbb,0xbb,0xbe,0xed,0x00,0x00,0x01,0x00,0xec,0xec,0xec,0xec, ++ 0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec, ++ 0xec,0xec,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0x00,0xec,0xec,0xec, ++ 0x00,0xec,0x00,0x00,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0xec, ++ 0xec,0x00,0x00,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec, ++ 0x00,0xec,0xec,0x00,0x00,0xec,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0xec, ++ 0xec,0xec,0xec,0xec,0xec,0x00,0x00,0xec,0x00,0x00,0xec,0x00,0x00,0xec, ++ 0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec, ++ 0xec,0xec,0x00,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec, ++ 0xec,0xec,0xec,0x00,0x00,0xec,0xec,0x00,0xec,0x00,0xec,0xec,0xab,0xab, ++ 0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xab,0x2a,0xff,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xab,0xff,0xff,0xff,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x03,0x71,0xf1,0x94,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, ++ 0x03,0x71,0xee,0xb8,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, ++ 0xf1,0x8c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xe0,0xa8, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf5,0x58,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf5,0x64 ++ }; ++ #endif /* AIX */ ++ #endif /* HOST_ICON */ ++ ++ #ifndef HOST_ICON ++ #if defined(ultrix) || defined(vax) || defined(__alpha) ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* DIGITALicon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0xff,0xff,0xff,0xff,0x00,0x9c,0x00,0xd2,0x11,0x01,0xa0,0x00, ++ 0x82,0xa0,0x00,0x8c,0x01,0x00,0x0a,0xff,0xff,0xff,0xff,0x00,0x9c,0x00, ++ 0xd2,0x09,0x00,0x00,0x00,0x00,0x0b,0x44,0x49,0x47,0x49,0x54,0x41,0x4c, ++ 0x69,0x63,0x6f,0x6e,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53, ++ 0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x37,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x00,0x09,0x00,0x03,0x06,0x47,0x65,0x6e,0x65,0x76,0x61,0x03,0x00,0x03, ++ 0x0d,0x00,0x0c,0x2b,0x0e,0x40,0x1e,0x54,0x68,0x65,0x20,0x42,0x75,0x69, ++ 0x6c,0x74,0x2d,0x49,0x6e,0x20,0x44,0x69,0x67,0x69,0x74,0x69,0x7a,0x65, ++ 0x72,0x20,0x63,0x61,0x6e,0x6e,0x6f,0x74,0x20,0x2b,0x0a,0x10,0x1b,0x64, ++ 0x69,0x73,0x70,0x6c,0x61,0x79,0x20,0x76,0x69,0x64,0x65,0x6f,0x20,0x77, ++ 0x68,0x69,0x6c,0x65,0x20,0x69,0x6e,0x20,0x74,0x68,0x65,0x20,0x2b,0x02, ++ 0x10,0x19,0x63,0x75,0x72,0x72,0x65,0x6e,0x74,0x20,0x6e,0x75,0x6d,0x62, ++ 0x65,0x72,0x20,0x6f,0x66,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x73,0x2e,0xa0, ++ 0x00,0x97,0xa0,0x00,0x8d,0xa0,0x00,0x83,0xff,0x00,0x00,0x00,0xe4,0x00, ++ 0xe4,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x33,0x33,0x30,0x33,0x30,0x33, ++ 0x33,0x30,0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x33,0x33,0x33,0x30,0x33, ++ 0x30,0x33,0x33,0x30,0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x33,0x33,0x30, ++ 0x30,0x33,0x30,0x33,0x33,0x30,0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x03, ++ 0x33,0x30,0x30,0x30,0x30,0x33,0x33,0x30,0x30,0x30,0x30,0x30,0x33,0x33, ++ 0x03,0x03,0x33,0x30,0x30,0x33,0x30,0x33,0x33,0x30,0x33,0x30,0x30,0x30, ++ 0x30,0x03,0x03,0x03,0x30,0x00,0x30,0x30,0x30,0x30,0x00,0x30,0x30,0x30, ++ 0x00,0x00,0x33,0x30,0x03,0x03,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, ++ 0x30,0x30,0x30,0x30,0x30,0x00,0x03,0x03,0x30,0x30,0x30,0x30,0x30,0x30, ++ 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x03,0x03,0x30,0x00,0x30,0x30, ++ 0x30,0x30,0x00,0x30,0x30,0x30,0x33,0x00,0x30,0x00,0x03,0x03,0x33,0x33, ++ 0x30,0x33,0x30,0x33,0x30,0x30,0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x33, ++ 0x33,0x33,0x30,0x33,0x30,0x33,0x30,0x30,0x33,0x30,0x33,0x30,0x33,0x33, ++ 0x03,0x33,0x33,0x33,0x30,0x33,0x30,0x30,0x03,0x30,0x33,0x30,0x33,0x30, ++ 0x33,0x33,0x03,0x33,0x33,0x33,0x30,0x33,0x30,0x33,0x33,0x30,0x33,0x30, ++ 0x33,0x30,0x33,0x33,0x03,0x33,0x33,0x33,0x30,0x33,0x30,0x33,0x33,0x30, ++ 0x33,0x30,0x33,0x30,0x33,0x33,0x03,0x33,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0xee,0xe0,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77, ++ 0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xee,0xee,0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xe7,0x77,0xf7,0x77,0x77,0xe0,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x7f,0x77,0x77,0x77,0xe0,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0xf7,0x7f,0x7f,0x77,0xe0, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77,0x77,0x77, ++ 0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xee,0xee, ++ 0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xad,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xde,0xeb,0xba,0xbb, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, ++ 0xa0,0xab,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, ++ 0xde,0xea,0x00,0x0a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, ++ 0xee,0xdd,0x00,0x00,0x04,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, ++ 0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, ++ 0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a, ++ 0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, ++ 0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a, ++ 0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a, ++ 0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a, ++ 0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, ++ 0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, ++ 0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00, ++ 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x00, ++ 0x00,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x00,0x6a,0x00, ++ 0x6a,0x00,0x6a,0x00,0x00,0x00,0x00,0x00,0x6a,0x6a,0x6a,0x00,0x00,0x6a, ++ 0x00,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, ++ 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, ++ 0x00,0x00,0x00,0x6a,0x00,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, ++ 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00, ++ 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a,0x6a,0x00,0x00,0x00, ++ 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x00,0x6a,0x00,0x6a,0x00, ++ 0x6a,0x00,0x6a,0x6a,0x00,0x00,0x6a,0x00,0x00,0x00,0x00,0x6a,0x00,0x6a, ++ 0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a, ++ 0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a, ++ 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a, ++ 0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, ++ 0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x6a,0x6a, ++ 0x6a,0x6a,0x00,0x6a,0x6a,0x6a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0xab,0xab, ++ 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, ++ 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a, ++ 0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff,0x2a,0xff, ++ 0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0x33, ++ 0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, ++ 0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0xfb,0xbe, ++ 0xee,0xf7,0xfb,0xbe,0xee,0xf7,0xeb,0xbe,0xee,0xf5,0xea,0xbe,0xaa,0xf5, ++ 0xeb,0xbe,0xea,0x95,0x8a,0xa2,0xa0,0xe5,0xaa,0xaa,0xaa,0x85,0xaa,0xaa, ++ 0xaa,0xa5,0x8a,0xa2,0xac,0x85,0xfb,0xba,0xee,0xf7,0xfb,0xba,0xee,0xf7, ++ 0xfb,0xa6,0xee,0xf7,0xfb,0xbe,0xee,0xf7,0xfb,0xbe,0xee,0xf7,0x00,0x00, ++ 0x00,0x00,0x78,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0xff,0xe0,0x00,0x00, ++ 0x80,0x20,0x00,0x00,0x88,0x20,0x00,0x00,0x90,0x20,0x00,0x00,0xa5,0x20, ++ 0x00,0x00,0x80,0x20,0x00,0x00,0xff,0xe0,0x00,0x00,0x0a,0x00,0x00,0x00, ++ 0x1f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x15,0x00, ++ 0x00,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0xff,0xe0, ++ 0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00, ++ 0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0x0e,0x00, ++ 0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, ++ 0x1f,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x40,0xff,0xff,0xaf,0x5e,0xbf,0xce,0xa9,0x52, ++ 0x29,0x52,0xfd,0xff,0xfb,0xff,0xe0,0x00,0xfc,0x00,0xa4,0x00,0xf4,0x00, ++ 0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xfe,0xff,0xfe,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x00,0xfc,0x00, ++ 0xfc,0x00,0xfc,0x00,0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x80,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x30, ++ 0x33,0x33,0x03,0x03,0x33,0x30,0x30,0x33,0x33,0x33,0x33,0x00,0x33,0x30, ++ 0x30,0x30,0x30,0x03,0x03,0x03,0x00,0x30,0x00,0x30,0x30,0x03,0x03,0x03, ++ 0x00,0x30,0x33,0x33,0x33,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x33, ++ 0x33,0x33,0x33,0x33,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0xee,0xee, ++ 0xee,0x00,0x00,0x00,0x00,0x00,0xec,0xfc,0xce,0x00,0x00,0x00,0x00,0x00, ++ 0xef,0xff,0xce,0x00,0x00,0x00,0x00,0x00,0xee,0xee,0xee,0x00,0x00,0x00, ++ 0x00,0x00,0x0f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0xea,0xaa, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0x00,0x00,0x01,0x00,0x6a,0x6a,0x6a,0x6a, ++ 0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x00,0x6a,0x6a, ++ 0x6a,0x00,0x6a,0x00,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a,0x00,0x6a, ++ 0x00,0x00,0x6a,0x00,0x00,0x00,0x6a,0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a, ++ 0x00,0x6a,0x00,0x00,0x6a,0x00,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00,0x6a, ++ 0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x00, ++ 0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0xab,0xab,0xab,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0xab, ++ 0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xab,0x2a,0xff,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xab,0xff,0xff,0xff,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x03,0x71,0xf0,0x9c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, ++ 0x03,0x71,0xf3,0x28,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, ++ 0xf4,0xfc,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf0,0x80, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf5,0x78,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf0,0x08 ++ }; ++ #endif /* ultrix || vax || __alpha */ ++ #endif /* HOST_ICON */ ++ ++ #ifndef HOST_ICON ++ #ifdef aux ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* AUXicon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x41,0x55,0x58,0x69,0x63,0x6f,0x6e, ++ 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00, ++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x38,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x08,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x80,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x88,0x80,0x08,0x80,0x08,0x88,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x88,0x88,0x08,0x80,0x88, ++ 0x88,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x88,0x88,0x88,0x88, ++ 0x88,0x88,0x88,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x11,0x11, ++ 0x11,0x11,0x11,0x11,0x11,0x11,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x11, ++ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x10,0x00,0x00,0x00,0x00,0x00, ++ 0x01,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x10,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x02,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x02,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x22,0x22,0x22,0x22,0x22,0x22,0x22, ++ 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x00,0x0e,0xee,0xe0,0x00, ++ 0x00,0x04,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x40,0x00,0xe7,0x77, ++ 0x7e,0x00,0x00,0x04,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x40,0x00, ++ 0xee,0xee,0xee,0xee,0xee,0xe0,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44, ++ 0x00,0x00,0xe7,0x77,0x77,0x77,0x77,0xe0,0x00,0x66,0x66,0x66,0x66,0x66, ++ 0x66,0x60,0x00,0x00,0xe7,0x77,0xf7,0x77,0x77,0xe0,0x00,0x06,0x66,0x60, ++ 0x66,0x66,0x66,0x00,0x00,0x00,0xe7,0x7f,0x77,0x77,0x77,0xe0,0x00,0x00, ++ 0x66,0x00,0x06,0x66,0x00,0x00,0x00,0x00,0xe7,0xf7,0x7f,0x7f,0x77,0xe0, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x77,0x77,0x77, ++ 0x77,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xee,0xee, ++ 0xee,0xee,0xee,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xad,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xde,0xeb,0xba,0xbb, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, ++ 0xa0,0xab,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, ++ 0xde,0xea,0x00,0x0a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, ++ 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3, ++ 0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0x00,0x00,0xe3,0xe3,0x00,0x00,0xe3, ++ 0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0x00,0xe3, ++ 0xe3,0x00,0xe3,0xe3,0xe3,0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0xe3, ++ 0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x05, ++ 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, ++ 0x05,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, ++ 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, ++ 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0x17,0x17,0x17, ++ 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17, ++ 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, ++ 0x17,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, ++ 0x17,0x17,0x17,0x17,0x17,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0xab,0xab,0xab, ++ 0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20, ++ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00, ++ 0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20, ++ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, ++ 0x20,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, ++ 0xab,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, ++ 0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec, ++ 0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0xab,0x2a, ++ 0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0xec, ++ 0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00, ++ 0x00,0x00,0x00,0x00,0xec,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0xff,0x2a,0x2a,0xff,0x2a,0xff, ++ 0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0x33, ++ 0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, ++ 0xf7,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x00, ++ 0x06,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x18,0x00,0x00,0x07,0x99,0xc0, ++ 0x00,0x0f,0xdb,0xe0,0x00,0x1f,0xff,0xf0,0x00,0x3f,0xff,0xf8,0x00,0x3f, ++ 0xff,0xf8,0x00,0x7f,0xff,0xe0,0x00,0x7f,0xff,0xc0,0x00,0x7f,0xff,0xc0, ++ 0x00,0x7f,0xff,0xc0,0x00,0x7f,0xff,0xc0,0x00,0x3f,0xff,0xe0,0x00,0x3f, ++ 0xff,0xf8,0x78,0x1f,0xff,0xf8,0x84,0x1f,0xff,0xf8,0xff,0xef,0xff,0xf0, ++ 0x80,0x23,0xff,0xe0,0x88,0x21,0xef,0xc0,0x90,0x20,0xc7,0x00,0xa5,0x20, ++ 0x00,0x00,0x80,0x20,0x00,0x00,0xff,0xe0,0x00,0x00,0x0a,0x00,0x00,0x00, ++ 0x1f,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x15,0x00, ++ 0x00,0x00,0xe4,0xff,0xff,0xff,0x0a,0x00,0x00,0x00,0xf1,0xff,0xff,0xff, ++ 0x00,0x00,0x06,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x18,0x00,0x00,0x07, ++ 0x99,0xc0,0x00,0x0f,0xdb,0xe0,0x00,0x1f,0xff,0xf0,0x00,0x3f,0xff,0xf8, ++ 0x00,0x3f,0xff,0xf8,0x00,0x7f,0xff,0xe0,0x00,0x7f,0xff,0xc0,0x00,0x7f, ++ 0xff,0xc0,0x00,0x7f,0xff,0xc0,0x00,0x7f,0xff,0xc0,0x00,0x3f,0xff,0xe0, ++ 0x00,0x3f,0xff,0xf8,0x78,0x1f,0xff,0xf8,0xfc,0x0f,0xff,0xf8,0xff,0xe7, ++ 0xff,0xf0,0xff,0xe3,0xff,0xe0,0xff,0xe1,0xef,0xc0,0xff,0xe0,0xc7,0x00, ++ 0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0xff,0xe0,0x00,0x00,0x0e,0x00, ++ 0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, ++ 0x1f,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xf1,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x40,0x00,0x30,0x03,0xf8,0x07,0xfc,0x07,0xfe, ++ 0x0f,0xfc,0x0f,0xf8,0x0f,0xfc,0xe7,0xfe,0xff,0xfe,0xa5,0xfc,0xf4,0xb0, ++ 0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff,0x00,0x30,0x03,0xf8, ++ 0x07,0xfc,0x07,0xfe,0x0f,0xfc,0x0f,0xf8,0x0f,0xfc,0xe7,0xfe,0xff,0xfe, ++ 0xfd,0xfc,0xfc,0xb0,0xfc,0x00,0x70,0x00,0x70,0x00,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x00, ++ 0x00,0x88,0x88,0x88,0x80,0x00,0x00,0x00,0x08,0x88,0x88,0x88,0x88,0x00, ++ 0x00,0x00,0x01,0x11,0x11,0x11,0x11,0x10,0x00,0x00,0x22,0x22,0x22,0x22, ++ 0x21,0x00,0x00,0x00,0x22,0x22,0x22,0x22,0x20,0x00,0x00,0x00,0x33,0x33, ++ 0x33,0x33,0x33,0x00,0xee,0xe0,0x04,0x44,0x44,0x44,0x44,0x40,0xee,0xee, ++ 0xee,0x44,0x44,0x44,0x44,0x40,0xe7,0xf7,0x7e,0x06,0x66,0x66,0x66,0x00, ++ 0xef,0xff,0x7e,0x00,0x60,0x66,0x00,0x00,0xee,0xee,0xee,0x00,0x00,0x00, ++ 0x00,0x00,0x0f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xee,0xbb,0xaa,0xaa,0xaa,0xaa,0xae,0xed,0xee,0xab, ++ 0xbb,0xbb,0xbb,0xbb,0xbe,0xed,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x05,0x05,0x05,0x05,0x05,0x05, ++ 0x05,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0x17,0x17,0x17,0x17, ++ 0x17,0x17,0x17,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0x17,0x17, ++ 0x17,0x17,0x17,0x17,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xab,0xab,0xab,0x00, ++ 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0xab,0xab, ++ 0xab,0xab,0xab,0xab,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00, ++ 0xab,0x2a,0xff,0x2a,0x2a,0xab,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec, ++ 0x00,0x00,0xab,0xff,0xff,0xff,0x2a,0xab,0x00,0x00,0xec,0x00,0xec,0xec, ++ 0x00,0x00,0x00,0x00,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xfb,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x03,0x71,0xd6,0x90,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, ++ 0x03,0x71,0xf2,0x24,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, ++ 0xf4,0xd8,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf6,0x48, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf5,0x2c,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xef,0xbc ++ }; ++ #endif /* aux */ ++ #endif /* HOST_ICON */ ++ ++ #ifndef HOST_ICON ++ #ifdef sgi ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* SGIicon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x08,0x00,0x09,0x37,0x07,0xe7,0x0b,0x68,0x0d,0x2b,0x08,0xc8, ++ 0x04,0x62,0x05,0x53,0x05,0x53,0x0c,0x49,0x0b,0xce,0x08,0xe5,0x07,0x1a, ++ 0x05,0x53,0x09,0xb2,0x08,0xc8,0x07,0x53,0x47,0x49,0x69,0x63,0x6f,0x6e, ++ 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00, ++ 0x00,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00,0x00,0x68,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x38,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x06,0x39,0x06,0x39,0x06,0x39,0x0d,0x53,0x0d,0x53,0x0c,0xa3,0x0d,0x53, ++ 0x0c,0x72,0x0c,0x72,0x0c,0x72,0x05,0x53,0x05,0x53,0x05,0x53,0x05,0x53, ++ 0x05,0x53,0x05,0x53,0x08,0xe5,0x05,0x53,0x05,0x53,0x05,0x53,0x05,0x53, ++ 0x04,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03, ++ 0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05, ++ 0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03,0x04,0x05,0x02,0x03, ++ 0x04,0x05,0x02,0x03,0x04,0x05,0x00,0x09,0x08,0x50,0x61,0x6c,0x61,0x74, ++ 0x69,0x6e,0x6f,0x02,0x06,0x07,0x02,0x06,0x08,0x02,0x06,0x09,0x03,0x06, ++ 0x08,0x09,0x01,0x2d,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x55,0x50,0x00,0x55,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x05,0x55,0x55,0x05,0x55,0x55,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x55,0x50,0x55,0x05,0x50,0x55,0x50,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x55,0x00,0x55,0x05,0x50,0x05,0x55, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x50,0x00,0x55,0x05,0x50, ++ 0x00,0x55,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x00,0x00,0x55, ++ 0x05,0x50,0x00,0x05,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x55,0x50, ++ 0x00,0x55,0x05,0x50,0x00,0x55,0x50,0x50,0x00,0x00,0x00,0x00,0x05,0x55, ++ 0x05,0x55,0x00,0x55,0x05,0x50,0x05,0x55,0x05,0x55,0x00,0x00,0x00,0x00, ++ 0x05,0x55,0x50,0x55,0x50,0x55,0x05,0x50,0x55,0x50,0x55,0x55,0x00,0x00, ++ 0x00,0x00,0x05,0x55,0x55,0x05,0x55,0x05,0x05,0x05,0x55,0x05,0x55,0x55, ++ 0x00,0x00,0x00,0x00,0x05,0x50,0x55,0x50,0x55,0x50,0x00,0x55,0x50,0x55, ++ 0x50,0x55,0x00,0x00,0x00,0x00,0x05,0x50,0x05,0x55,0x05,0x55,0x05,0x55, ++ 0x05,0x55,0x00,0x55,0x00,0x00,0x00,0x00,0x05,0x50,0x00,0x55,0x50,0x55, ++ 0x55,0x50,0x55,0x50,0x00,0x55,0x0e,0xee,0xe0,0x00,0x05,0x50,0x00,0x05, ++ 0x55,0x05,0x55,0x05,0x55,0x00,0x00,0x55,0xe7,0x77,0x7e,0x00,0x05,0x50, ++ 0x00,0x50,0x55,0x50,0x50,0x55,0x50,0x50,0x00,0x55,0xee,0xee,0xee,0xee, ++ 0xee,0xee,0x05,0x55,0x05,0x55,0x05,0x55,0x05,0x55,0x00,0x55,0xe7,0x77, ++ 0x77,0x77,0x77,0x7e,0x55,0x50,0x50,0x55,0x05,0x50,0x50,0x55,0x50,0x55, ++ 0xe7,0x77,0x77,0x7f,0x77,0x7e,0x55,0x05,0x55,0x55,0x05,0x55,0x55,0x05, ++ 0x55,0x55,0xe7,0x77,0x77,0xf7,0x77,0x7e,0x50,0x55,0x50,0x55,0x05,0x50, ++ 0x55,0x50,0x55,0x55,0xe7,0x77,0x7f,0x77,0x77,0x7e,0x05,0x55,0x00,0x55, ++ 0x05,0x50,0x05,0x55,0x05,0x55,0xe7,0x77,0xf7,0x77,0x77,0x7e,0x55,0x50, ++ 0x00,0x55,0x05,0x50,0x00,0x55,0x50,0x50,0xe7,0x7f,0x77,0xf7,0xf7,0x7e, ++ 0x55,0x00,0x00,0x55,0x05,0x50,0x00,0x05,0x50,0x00,0xe7,0x77,0x77,0x77, ++ 0x77,0x7e,0x55,0x50,0x00,0x55,0x05,0x50,0x00,0x55,0x50,0x00,0xee,0xee, ++ 0xee,0xee,0xee,0xee,0x05,0x55,0x00,0x55,0x05,0x50,0x05,0x55,0x00,0x00, ++ 0x00,0x00,0x0a,0xca,0x00,0x00,0x00,0x55,0x50,0x55,0x05,0x50,0x55,0x50, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x05,0x55,0x55,0x05,0x55, ++ 0x55,0x00,0x00,0x00,0x00,0x00,0xf3,0x33,0xf0,0x00,0x00,0x00,0x55,0x50, ++ 0x00,0x55,0x50,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0xab,0xa0,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0xee,0xbb,0xab, ++ 0xba,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcc,0xbb, ++ 0xba,0x0a,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, ++ 0xdd,0xee,0xa0,0x00,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, ++ 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00, ++ 0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0, ++ 0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00, ++ 0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0, ++ 0x00,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00,0x00, ++ 0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x00, ++ 0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00, ++ 0xb0,0xb0,0xb0,0x00,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0x00,0xb0, ++ 0xb0,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00, ++ 0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0, ++ 0x00,0xb0,0xb0,0xb0,0x00,0xb0,0x00,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0, ++ 0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0, ++ 0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0, ++ 0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, ++ 0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0, ++ 0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00, ++ 0xb0,0xb0,0x00,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00, ++ 0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, ++ 0x00,0x00,0x00,0x00,0xb0,0xb0,0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00, ++ 0x00,0xb0,0xb0,0x00,0x00,0x00,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0x00, ++ 0xb0,0xb0,0xb0,0x00,0xb0,0x00,0x00,0x00,0xb0,0xb0,0xab,0xab,0xab,0xab, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xb0,0xb0,0xb0,0x00,0xb0, ++ 0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0x00,0xb0,0xb0, ++ 0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0xb0,0xb0, ++ 0xb0,0x00,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0x00,0xb0,0xb0, ++ 0xb0,0x00,0xb0,0xb0,0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xfe,0x2a,0x2a, ++ 0x2a,0xab,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, ++ 0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, ++ 0xfe,0x2a,0x2a,0x2a,0x2a,0xab,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0, ++ 0x00,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xab,0x2a, ++ 0x2a,0x2a,0x2a,0xfe,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xb0,0xb0,0xb0, ++ 0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0, ++ 0xb0,0xb0,0xab,0x2a,0x2a,0x2a,0xfe,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab, ++ 0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00, ++ 0xb0,0xb0,0xb0,0x00,0xb0,0x00,0xab,0x2a,0x2a,0xfe,0x2a,0x2a,0xfe,0x2a, ++ 0xfe,0x2a,0x2a,0xab,0xb0,0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00,0xb0, ++ 0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0xb0,0xb0,0xb0,0x00,0x00,0x00, ++ 0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xb0, ++ 0xb0,0xb0,0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0xb0,0xb0,0xb0, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0xf8,0xfd,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00, ++ 0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0, ++ 0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xb0,0xb0,0xb0,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33, ++ 0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xf9,0xfb,0xfb, ++ 0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, ++ 0xf6,0xf6,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0xf6,0xf6,0xf9,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x00, ++ 0xe3,0x80,0x00,0x01,0xf7,0xc0,0x00,0x03,0xb6,0xe0,0x00,0x07,0x36,0x70, ++ 0x00,0x0e,0x36,0x38,0x00,0x0c,0x36,0x18,0x00,0x2e,0x36,0x3a,0x00,0x77, ++ 0x36,0x77,0x00,0x7b,0xb6,0xef,0x00,0x7d,0xd5,0xdf,0x00,0x6e,0xe3,0xbb, ++ 0x00,0x67,0x77,0x73,0x00,0x63,0xbe,0xe3,0x78,0x61,0xdd,0xc3,0x84,0x62, ++ 0xeb,0xa3,0xff,0xf7,0x77,0x73,0x80,0x1e,0xb6,0xbb,0x81,0x1d,0xf7,0xdf, ++ 0x82,0x1b,0xb6,0xef,0x84,0x17,0x36,0x77,0x88,0x1e,0x36,0x3a,0x92,0x9c, ++ 0x36,0x18,0x80,0x1e,0x36,0x38,0xff,0xf7,0x36,0x70,0x05,0x03,0xb6,0xe0, ++ 0x0f,0x81,0xf7,0xc0,0x0f,0x80,0xe3,0x80,0x0f,0x80,0x00,0x00,0x0a,0x80, ++ 0x00,0x00,0x32,0x7f,0xff,0xfc,0x05,0x00,0x00,0x00,0x38,0xff,0xff,0xfc, ++ 0x00,0x00,0xe3,0x80,0x00,0x01,0xf7,0xc0,0x00,0x03,0xb6,0xe0,0x00,0x07, ++ 0x36,0x70,0x00,0x0e,0x36,0x38,0x00,0x0c,0x36,0x18,0x00,0x2e,0x36,0x3a, ++ 0x00,0x77,0x36,0x77,0x00,0x7b,0xb6,0xef,0x00,0x7d,0xd5,0xdf,0x00,0x6e, ++ 0xe3,0xbb,0x00,0x67,0x77,0x73,0x00,0x63,0xbe,0xe3,0x78,0x61,0xdd,0xc3, ++ 0xfc,0x62,0xeb,0xa3,0xff,0xf7,0x77,0x73,0xff,0xfe,0xb6,0xbb,0xff,0xfd, ++ 0xf7,0xdf,0xff,0xfb,0xb6,0xef,0xff,0xf7,0x36,0x77,0xff,0xfe,0x36,0x3a, ++ 0xff,0xfc,0x36,0x18,0xff,0xfe,0x36,0x38,0xff,0xf7,0x36,0x70,0x07,0x03, ++ 0xb6,0xe0,0x0f,0x81,0xf7,0xc0,0x0f,0x80,0xe3,0x80,0x0f,0x80,0x00,0x00, ++ 0x0f,0x80,0x00,0x00,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xf8,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x40,0x01,0xf8,0x03,0xfc,0x07,0x76,0x0f,0x77, ++ 0x0f,0xff,0x0f,0xff,0xed,0xdd,0xfd,0xfd,0x97,0xff,0xb7,0xff,0xff,0x77, ++ 0xff,0x76,0x39,0xfc,0x38,0xd8,0x7f,0xfe,0x7f,0xfe,0x01,0xf8,0x03,0xfc, ++ 0x03,0xfe,0x0f,0xff,0x0f,0xff,0x0f,0xff,0xef,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xfe,0x3f,0xfc,0x3f,0xf8,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x05,0x55,0x55,0x50,0x00,0x00,0x00, ++ 0x00,0x55,0x55,0x55,0x55,0x00,0x00,0x00,0x05,0x55,0x05,0x55,0x05,0x50, ++ 0x00,0x00,0x55,0x55,0x05,0x55,0x05,0x55,0x00,0x00,0x55,0x55,0x55,0x55, ++ 0x55,0x55,0x00,0x00,0x55,0x55,0x55,0x55,0x55,0x55,0xee,0xe0,0x55,0x05, ++ 0x55,0x05,0x55,0x05,0xee,0xee,0x55,0x05,0x55,0x55,0x55,0x05,0xec,0xcf, ++ 0xce,0x55,0x55,0x55,0x55,0x55,0xec,0xff,0xce,0x55,0x55,0x55,0x55,0x55, ++ 0xef,0xff,0xfe,0x55,0x05,0x55,0x05,0x55,0xee,0xee,0xee,0x55,0x05,0x55, ++ 0x05,0x50,0x00,0xff,0xf0,0x05,0x55,0x55,0x55,0x00,0x00,0xff,0xf0,0x00, ++ 0x55,0x05,0x50,0x00,0xde,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0xde,0xaa, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xed,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0, ++ 0xb0,0x00,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, ++ 0x00,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0, ++ 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0xb0,0xb0,0xb0,0xb0, ++ 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xab,0xab,0xab,0x00,0xb0,0xb0, ++ 0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0x00,0xb0,0xab,0xab,0xab,0xab, ++ 0xb0,0xb0,0x00,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0xb0,0xab,0x2a, ++ 0x2a,0xfe,0x2a,0xab,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0, ++ 0xab,0x2a,0xfe,0xfe,0x2a,0xab,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0, ++ 0xb0,0xb0,0xab,0xfe,0xfe,0xfe,0xfe,0xab,0xb0,0xb0,0x00,0xb0,0xb0,0xb0, ++ 0x00,0xb0,0xb0,0xb0,0xab,0xab,0xab,0xab,0xab,0xab,0xb0,0xb0,0x00,0xb0, ++ 0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0xb0, ++ 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00, ++ 0x00,0x00,0xb0,0xb0,0x00,0xb0,0xb0,0x00,0x00,0x00,0xf9,0xfb,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0xf9,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x00,0xec,0xa7,0x58,0x1f,0x38,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, ++ 0x00,0xec,0xb2,0x44,0xbf,0xb9,0xff,0xff,0x00,0x00,0x06,0x08,0x00,0xec, ++ 0xa7,0xa0,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x00,0x00,0x00,0x00, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x00,0x00,0x00,0x00,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x07,0xd4,0x00,0x00,0x00,0x00 ++ }; ++ #endif /* sgi */ ++ #endif /* HOST_ICON */ ++ ++ #ifndef HOST_ICON ++ #ifdef hpux ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* HPicon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x66,0x66,0x00,0x5e,0x99,0x99,0x66,0x66,0x33,0x33,0x00,0x5f, ++ 0x99,0x99,0x66,0x66,0x00,0x00,0x00,0x60,0x99,0x99,0x33,0x33,0xff,0xff, ++ 0x00,0x61,0x99,0x99,0x33,0x33,0x06,0x48,0x50,0x69,0x63,0x6f,0x6e,0x48, ++ 0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00, ++ 0x00,0xd0,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00,0x00,0xd0,0x00,0x80,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x6d,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x00,0x00,0x00,0x00,0x00,0x6c,0x66,0x66,0xff,0xff,0xff,0xff,0x00,0x6d, ++ 0x66,0x66,0xff,0xff,0xcc,0xcc,0x00,0x6e,0x66,0x66,0xff,0xff,0x99,0x99, ++ 0x00,0x6f,0x66,0x66,0xff,0xff,0x66,0x66,0x00,0x70,0x66,0x66,0xff,0xff, ++ 0x33,0x33,0x00,0x71,0x66,0x66,0xff,0xff,0x00,0x00,0x00,0x72,0x66,0x66, ++ 0xcc,0xcc,0xff,0xff,0x00,0x73,0x66,0x66,0xcc,0xcc,0xcc,0xcc,0x00,0x74, ++ 0x66,0x66,0xcc,0xcc,0x99,0x99,0x00,0x75,0x66,0x66,0xcc,0xcc,0x66,0x66, ++ 0x00,0x76,0x66,0x66,0xcc,0xcc,0x33,0x33,0x00,0x77,0x66,0x66,0xcc,0xcc, ++ 0x00,0x00,0x00,0x78,0x66,0x66,0x99,0x99,0xff,0xff,0x00,0x79,0x66,0x66, ++ 0x99,0x99,0xcc,0xcc,0x00,0x7a,0x66,0x66,0x99,0x99,0x99,0x99,0x00,0x7b, ++ 0x66,0x66,0x99,0x99,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0xec,0xec,0xec,0xec,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, ++ 0x7f,0xec,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, ++ 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0xec,0x7f,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0x7f,0x7f,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0xec, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0x00, ++ 0xec,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f, ++ 0xec,0xec,0xec,0xec,0x7f,0x7f,0x7f,0xec,0x00,0x00,0x00,0x00,0x00,0xec, ++ 0xec,0x7f,0x7f,0xec,0xec,0x00,0xec,0xec,0x7f,0x7f,0xec,0xec,0x00,0xec, ++ 0xec,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0xec,0xec,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0xec,0xec,0xec,0xec, ++ 0xec,0x00,0x00,0xec,0x7f,0x7f,0x7f,0xec,0xec,0xec,0xec,0xec,0xec,0xec, ++ 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0xec,0xec,0xfd,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f, ++ 0xec,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xec,0x7f,0x7f,0xec,0xfd,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0xec,0x7f,0x7f,0xec,0xfd,0x33,0xfd,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x7f,0x7f,0xec,0xff,0xff,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0x7f,0x7f, ++ 0xec,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xec,0xec,0xec,0xec,0xec,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd, ++ 0x33,0xfd,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xf9, ++ 0xfb,0xfb,0xfb,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb, ++ 0xf9,0xf9,0xf7,0xf7,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0xf7,0xf7,0xf9,0xf9,0xfb,0xfb,0xfb,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0x00,0x00, ++ 0x00,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x02,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf5,0x55,0xf0,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf5,0x5f,0xf0,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x55,0x5f, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, ++ 0x55,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xf5,0x55,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x0f,0xf5,0x5f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x0f,0x55,0x5f,0xff,0xff,0x00,0x00,0x0f,0xff,0xff, ++ 0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x55,0x55,0x55,0x5f,0x00,0x00,0xff, ++ 0x55,0x55,0x55,0xf0,0x00,0x00,0x00,0x00,0xf5,0x55,0xff,0x55,0x55,0xf0, ++ 0x00,0xf5,0x55,0x55,0x55,0x5f,0x00,0x00,0x00,0x0f,0xf5,0x5f,0xf0,0xf5, ++ 0x55,0xf0,0x0f,0xf5,0x5f,0xff,0x55,0x5f,0x00,0x00,0x00,0x0f,0x55,0x5f, ++ 0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f,0x00,0xf5,0x5f,0x00,0x00,0x00,0xff, ++ 0x55,0xff,0x0f,0xf5,0x5f,0xf0,0xff,0x55,0xff,0x00,0xf5,0x5f,0x00,0x00, ++ 0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f,0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f, ++ 0x00,0x00,0x0f,0xf5,0x5f,0xf0,0xff,0x55,0xff,0x0f,0xf5,0x5f,0xf0,0xff, ++ 0x55,0xff,0x00,0x00,0x0f,0x55,0x5f,0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f, ++ 0x00,0xf5,0x55,0xf0,0x00,0x00,0xff,0x55,0xff,0x0f,0xf5,0x5f,0xf0,0xff, ++ 0x55,0xff,0x0f,0xf5,0x5f,0xf0,0x00,0x00,0xf5,0x55,0xf0,0x0f,0x55,0x5f, ++ 0x00,0xf5,0x55,0xff,0xff,0x55,0x5f,0x00,0x00,0x0f,0xf5,0x5f,0xf0,0xff, ++ 0x55,0xff,0x0f,0xf5,0x55,0x55,0x55,0x55,0xff,0x00,0x00,0x0f,0xff,0xff, ++ 0x00,0xff,0xff,0xf0,0x0f,0x55,0x5f,0xff,0xff,0xff,0xf0,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x55,0xff,0xa0,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x55,0xfb,0xa0,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf5,0x5f,0xab,0xa0, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf5,0x5f, ++ 0xab,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f, ++ 0x55,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0x55,0xff,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xff,0xff,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xba,0xba,0x00,0x00, ++ 0x00,0x00,0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab,0xba,0xbb, ++ 0xaa,0xae,0xee,0xdd,0xcc,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb, ++ 0xa0,0xab,0xbb,0xbb,0xbb,0xcc,0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa, ++ 0xaa,0xaa,0x00,0x0a,0xaa,0xae,0xee,0xdd,0x00,0x00,0x01,0x00,0x00,0x0f, ++ 0x80,0x00,0x00,0x08,0x80,0x00,0x00,0x19,0x80,0x00,0x00,0x11,0x00,0x00, ++ 0x00,0x33,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x47, ++ 0xc1,0xfc,0x00,0xc0,0x43,0x02,0x00,0x8c,0x22,0x01,0x01,0x9a,0x26,0x71, ++ 0x01,0x12,0x24,0x49,0x03,0x36,0x6c,0xc9,0x02,0x24,0x48,0x91,0x06,0x6c, ++ 0xd9,0xb3,0x04,0x48,0x91,0x22,0x0c,0xd9,0xb3,0x66,0x08,0x91,0x23,0xc4, ++ 0x19,0xb3,0x60,0x0c,0x1f,0x3e,0x47,0xf8,0x00,0x00,0x4e,0x00,0x00,0x00, ++ 0xca,0x00,0x00,0x00,0x9a,0x00,0x00,0x01,0x9a,0x00,0x00,0x01,0x3f,0x00, ++ 0x00,0x03,0x31,0x00,0x00,0x03,0xf1,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, ++ 0x15,0x00,0xbf,0xff,0xe4,0xfd,0x00,0x00,0x0a,0x00,0xbf,0xff,0xf1,0xfd, ++ 0x00,0x0f,0x80,0x00,0x00,0x0f,0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f, ++ 0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x7e,0x00,0x00, ++ 0x00,0x7f,0xc1,0xfc,0x00,0xff,0xc3,0xfe,0x00,0xff,0xe3,0xff,0x01,0xfb, ++ 0xe7,0xff,0x01,0xf3,0xe7,0xcf,0x03,0xf7,0xef,0xcf,0x03,0xe7,0xcf,0x9f, ++ 0x07,0xef,0xdf,0xbf,0x07,0xcf,0x9f,0x3e,0x0f,0xdf,0xbf,0x7e,0x0f,0x9f, ++ 0x3f,0xfc,0x1f,0xbf,0x7f,0xfc,0x1f,0x3e,0x7f,0xf8,0x00,0x00,0x7e,0x00, ++ 0x00,0x00,0xfe,0x00,0x00,0x00,0xfe,0x00,0x00,0x01,0xfe,0x00,0x00,0x01, ++ 0xff,0x00,0x00,0x03,0xff,0x00,0x00,0x03,0xff,0x00,0x00,0x00,0x1f,0x00, ++ 0x00,0x00,0x1f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff, ++ 0xf1,0xff,0x00,0x00,0x00,0x40,0x03,0x80,0x07,0x80,0x05,0x00,0x0f,0x9e, ++ 0x0a,0xd1,0x1f,0x7f,0x17,0xef,0x3e,0xfd,0x2f,0xdf,0x7f,0xfe,0x00,0xb0, ++ 0x01,0xf0,0x01,0x70,0x01,0xf0,0xff,0xff,0xff,0xff,0x03,0x80,0x07,0x80, ++ 0x07,0x00,0x0f,0x9e,0x0f,0xdf,0x1f,0xff,0x1f,0xff,0x3f,0xff,0x3f,0xff, ++ 0x7f,0xfe,0x00,0xf0,0x01,0xf0,0x01,0xf0,0x01,0xf0,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x66,0x60,0x00,0x00,0x00,0x00,0x00, ++ 0x06,0x66,0x60,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x66,0x66,0x60,0x06,0x66,0x60,0x00,0x00,0x60,0x60,0x66,0x06, ++ 0x00,0x06,0x00,0x06,0x66,0x66,0x06,0x66,0x66,0x66,0x00,0x06,0x06,0x66, ++ 0x66,0x60,0x66,0x66,0x00,0x66,0x66,0x60,0x66,0x66,0x66,0x06,0x00,0x60, ++ 0x66,0x66,0x66,0x06,0x66,0x66,0x06,0x66,0x66,0x66,0x66,0x66,0x66,0x60, ++ 0x00,0x00,0x00,0x00,0x60,0x6f,0x00,0x00,0x00,0x00,0x00,0x06,0x66,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x06,0x06,0xff,0x00,0x00,0x00,0x00,0x00,0x06, ++ 0x66,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xec,0x00,0xec,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0xec, ++ 0xec,0xec,0xec,0x00,0x00,0x00,0x00,0x00,0xec,0x00,0xec,0x00,0xec,0xec, ++ 0x00,0xec,0x00,0x00,0x00,0xec,0x00,0x00,0x00,0xec,0xec,0xec,0xec,0xec, ++ 0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0x00,0x00,0xec,0x00,0xec, ++ 0xec,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0x00,0x00,0xec,0xec, ++ 0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0xec,0x00,0x00, ++ 0xec,0x00,0xec,0xec,0xec,0xec,0xec,0xec,0x00,0xec,0xec,0xec,0xec,0xec, ++ 0x00,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec, ++ 0xec,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0x00,0xec,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec,0xec,0xec, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xec, ++ 0x00,0xec,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xec,0xec,0xec,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x00,0xec,0xa7,0x58,0x1f,0x96,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x38,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x34, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0x9e,0x9c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x04,0x04, ++ 0x00,0x00,0x00,0x00,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x00,0xec, ++ 0x94,0x14,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x00,0x00,0x00,0x00, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x00,0x00,0x00,0x00,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x07,0xd4,0x00,0x00,0x00,0x00 ++ }; ++ #endif /* hpux */ ++ #endif /* HOST_ICON */ ++ ++ #ifndef HOST_ICON ++ #ifdef linux ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* LINUXicon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x66,0xff,0x0c,0x0c,0x0c,0x0f,0x00,0x00,0x00,0x0f,0xc0,0xc0, ++ 0xc0,0xcf,0x60,0x66,0x00,0x0f,0xc0,0xc0,0xc0,0xcf,0x00,0x00,0x00,0x0f, ++ 0x0c,0x0c,0x0c,0x0f,0x66,0x00,0x09,0x4c,0x49,0x4e,0x55,0x58,0x69,0x63, ++ 0x6f,0x6e,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, ++ 0x05,0x00,0x00,0x68,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x05,0x00,0x00,0x68,0x01,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x39,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0xcc,0xcc,0xcc,0xc3,0xcf,0xc0,0xc0,0xcf,0x00,0x00,0x00,0x0f,0x0c,0x0c, ++ 0x0f,0xcc,0xcc,0xcc,0xcc,0xcc,0xcf,0x0c,0x0c,0x0f,0x00,0x00,0x00,0x0f, ++ 0xc0,0xc0,0xcf,0xff,0xff,0xff,0xff,0xff,0xff,0xc0,0xc0,0xcf,0x00,0x00, ++ 0x00,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f, ++ 0x00,0x00,0x00,0x0f,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, ++ 0xc0,0xcf,0x00,0x00,0x00,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, ++ 0x0c,0x0c,0x0c,0x0f,0x00,0x00,0x00,0x0f,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, ++ 0xc0,0xc0,0xc0,0xc0,0xc0,0xcf,0x00,0x00,0x00,0x0f,0x0c,0x0c,0x0c,0x0c, ++ 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0f,0x00,0x00,0x00,0x0f,0xc0,0xc0, ++ 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xf0,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x22,0xff,0xfc,0xcc,0xcf, ++ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0xff,0xcc, ++ 0xff,0xcc,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0xcc,0xff,0xcc,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xfc,0xcc,0xcf,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xfc,0xcc,0xcf,0xff,0xff,0xf0,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xcf,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, ++ 0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf3,0x78,0xf3, ++ 0x78,0xf3,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x0e,0xee,0xe0,0x00,0x00,0x00,0x00,0x00, ++ 0x0f,0xcc,0xff,0xff,0xff,0xf7,0xf8,0xf0,0xe7,0x77,0x7e,0x00,0x00,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xee,0xee,0xee,0xee, ++ 0xee,0xee,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0xff,0xe7,0x77, ++ 0x77,0x77,0x77,0x7e,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0, ++ 0xe7,0x77,0x77,0x7f,0x77,0x7e,0x00,0x0f,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xf0,0x0f,0xe7,0x77,0x77,0xf7,0x77,0x7e,0x00,0x00,0xfc,0xcc,0xff,0xff, ++ 0xff,0xff,0xf0,0x00,0xe7,0x77,0x7f,0x77,0x77,0x7e,0x00,0x00,0xfc,0xcc, ++ 0xff,0xff,0xff,0xff,0xf0,0x00,0xe7,0x77,0xf7,0x77,0x77,0x7e,0x00,0x00, ++ 0xfc,0xcc,0xff,0xff,0xff,0xff,0xf0,0x00,0xe7,0x7f,0x77,0xf7,0xf7,0x7e, ++ 0x00,0x00,0xfc,0xcc,0xff,0xff,0xff,0xff,0xf0,0x00,0xe7,0x77,0x77,0x77, ++ 0x77,0x7e,0x00,0x00,0xfc,0xcc,0xff,0xff,0xff,0xff,0xf0,0x00,0xee,0xee, ++ 0xee,0xee,0xee,0xee,0x00,0x00,0xfc,0xcc,0xff,0xff,0xff,0xff,0xf0,0x00, ++ 0x00,0x00,0x0a,0xba,0x00,0x00,0x00,0x00,0x0f,0xcc,0xff,0xff,0xff,0xff, ++ 0xf0,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00,0x0f,0xcc,0xcf,0xff, ++ 0xff,0xff,0xf0,0x00,0x00,0x00,0xf3,0x33,0xf0,0x00,0x00,0x00,0x00,0xfc, ++ 0xcc,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00, ++ 0x0f,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0xab,0xab,0xa0,0x00, ++ 0x00,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xde,0xea,0xbb,0xab, ++ 0xba,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcb,0xbb, ++ 0xba,0x0a,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, ++ 0xde,0xea,0xa0,0x00,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, ++ 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0x17,0xff,0xff,0xff,0x2b, ++ 0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17, ++ 0xff,0xff,0x2b,0x2b,0xff,0xff,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0x2b,0x2b,0xff,0xff,0x2b,0x2b,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0x2b, ++ 0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0x2b,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8, ++ 0xc0,0xe3,0xff,0xd8,0xc0,0xe3,0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xc0,0xff,0xe3,0xff,0x00,0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00, ++ 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xab,0xab,0xab,0xab, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff, ++ 0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0x00,0xff,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xff,0x2a,0x2a, ++ 0x2a,0xab,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, ++ 0xff,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0x2b, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xab,0x2a, ++ 0x2a,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00, ++ 0xff,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, ++ 0x00,0x00,0xab,0x2a,0x2a,0x2a,0xff,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab, ++ 0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0xab,0x2a,0x2a,0xff,0x2a,0x2a,0xff,0x2a, ++ 0xff,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0x2b,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0x00,0x00,0xff,0x2b, ++ 0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00, ++ 0x00,0x00,0xff,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x2b,0x2b, ++ 0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0x2b,0x2b,0x2b,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33, ++ 0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xf9,0xfb,0xfb,0xfd, ++ 0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, ++ 0xf7,0x33,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0xf7,0xf7,0xf9,0xfb,0xfb,0xfd,0xfd,0x00,0x00,0x00,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x0f,0xc0,0x00,0x00,0x3f,0xe0,0x00,0x00,0xff,0xf0, ++ 0x00,0x07,0xe1,0xf0,0x00,0x03,0xcc,0xf8,0x00,0x00,0xcc,0xf8,0x00,0x00, ++ 0x21,0xf8,0x00,0x00,0x21,0xf8,0x00,0x00,0x17,0xf0,0x00,0x00,0x3f,0xf8, ++ 0x00,0x00,0x62,0x28,0x00,0x00,0x3f,0xfc,0x78,0x00,0x4f,0xea,0x84,0x3f, ++ 0xff,0xfe,0xff,0xff,0xff,0xf7,0x80,0x17,0xff,0xfa,0x81,0x11,0xff,0xf9, ++ 0x82,0x10,0x8f,0xf8,0x84,0x10,0x8f,0xf8,0x88,0x10,0x8f,0xf8,0x92,0x90, ++ 0x8f,0xf8,0x80,0x10,0x8f,0xf8,0xff,0xf0,0x8f,0xf8,0x05,0x00,0x4f,0xf8, ++ 0x0f,0x80,0x47,0xf8,0x08,0x80,0x23,0xf0,0x0f,0x80,0x7f,0xe0,0x0a,0x81, ++ 0xff,0xf0,0xf2,0x7f,0xff,0xff,0x05,0x00,0x00,0x00,0xf8,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xc0,0x00,0x00,0x3f,0xe0,0x00,0x00, ++ 0xff,0xf0,0x00,0x07,0xff,0xf0,0x00,0x03,0xff,0xf8,0x00,0x00,0xff,0xf8, ++ 0x00,0x00,0x3f,0xf8,0x00,0x00,0x3f,0xf8,0x00,0x00,0x1f,0xf0,0x00,0x00, ++ 0x3f,0xf8,0x00,0x00,0x7f,0xf8,0x00,0x00,0x3f,0xfc,0x78,0x00,0x7f,0xfe, ++ 0xfc,0x3f,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xf7,0xff,0xfa,0xff,0xf1, ++ 0xff,0xf9,0xff,0xf0,0xff,0xf8,0xff,0xf0,0xff,0xf8,0xff,0xf0,0xff,0xf8, ++ 0xff,0xf0,0xff,0xf8,0xff,0xf0,0xff,0xf8,0xff,0xf0,0xff,0xf8,0x07,0x00, ++ 0x7f,0xf8,0x0f,0x80,0x7f,0xf8,0x0f,0x80,0x3f,0xf0,0x0f,0x80,0x7f,0xe0, ++ 0x0f,0x81,0xff,0xf0,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xf8,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x40,0x00,0x38,0x00,0xfc,0x03,0xde,0x00,0xfe, ++ 0x00,0x7e,0x00,0xd6,0xe0,0xff,0xff,0xff,0xff,0xff,0xfc,0xbe,0xfc,0xbe, ++ 0xfc,0xbe,0x38,0xbe,0x38,0xfc,0xff,0xfe,0xff,0xfe,0x00,0x38,0x00,0xfc, ++ 0x03,0xfe,0x00,0xfe,0x00,0x7e,0x00,0xfe,0xe0,0xff,0xff,0xff,0xff,0xff, ++ 0xfc,0xfe,0xfc,0xfe,0xfc,0xfe,0x38,0xfe,0x38,0xfc,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x22,0xff,0x0f,0xff,0xf0, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x0f,0xff, ++ 0xff,0xf0,0x00,0x00,0x00,0x00,0x38,0x73,0x87,0xf0,0xee,0xe0,0x00,0x00, ++ 0xff,0xff,0xff,0xf8,0xee,0xee,0xef,0xff,0xff,0xff,0xff,0xf3,0xe7,0x7f, ++ 0x7e,0xff,0xff,0xff,0xff,0xf7,0xe7,0xff,0x7e,0x00,0xfc,0xff,0xff,0xf0, ++ 0xef,0xff,0xfe,0x00,0xfc,0xff,0xff,0xf0,0xee,0xee,0xee,0x00,0xfc,0xff, ++ 0xff,0xf0,0x00,0xff,0xf0,0x00,0xfc,0xff,0xff,0xf0,0x00,0xff,0xf0,0x00, ++ 0xff,0xff,0xff,0x00,0xee,0xbb,0xba,0xaf,0xff,0xff,0xff,0xed,0xee,0xba, ++ 0xbb,0xbb,0xbb,0xbb,0xbe,0xed,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x17,0xff,0xff,0x00,0xff,0xff,0xff, ++ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xd8,0xe3,0xc0,0xd8,0xe3,0xc0,0xff,0x00,0xfc,0xfc,0xfc,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe3,0xfc,0xfc,0xfc,0xfc, ++ 0xfc,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd8,0xfc,0xc0, ++ 0xc0,0xff,0xc0,0xfc,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc0, ++ 0xfc,0xc0,0xff,0xff,0xc0,0xfc,0x00,0x00,0xff,0x2b,0xff,0xff,0xff,0xff, ++ 0xff,0x00,0xfc,0xff,0xff,0xff,0xff,0xfc,0x00,0x00,0xff,0x2b,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0x00,0x00,0xff,0x2b, ++ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00, ++ 0xff,0x2b,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00, ++ 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xfc,0xfc,0x5e,0x5e, ++ 0x5e,0x89,0x89,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0xf9,0xfc,0xfc, ++ 0x5e,0x89,0x5e,0x5e,0x5e,0x5e,0x5e,0x5e,0x5e,0x5e,0x5e,0xfc,0xfc,0xf9, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x00,0xec,0xb8,0x74,0x1b,0x8c,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0xec,0xb6,0x4c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, ++ 0x00,0xec,0xb6,0xc4,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x00,0x00, ++ 0x00,0x00,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x00,0x00,0x00,0x00, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x00,0xec,0xb6,0x80,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x07,0xd4,0x00,0xec,0xb6,0x3c ++ }; ++ #endif /* linux */ ++ #endif /* HOST_ICON */ ++ #endif /* USE_HOST_ICON */ ++ ++ /* default for System V */ ++ ++ #ifndef HOST_ICON ++ #ifdef SYSTYPE_SYSV ++ #define HOST_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* AT&Ticon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x08,0x41,0x54,0x26,0x54,0x69,0x63,0x6f, ++ 0x6e,0x26,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, ++ 0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x39,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x06,0x66,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99,0x09,0x99, ++ 0x09,0x99,0x08,0xc8,0x09,0x99,0x09,0x99,0x0b,0x68,0x08,0xc8,0x08,0xc8, ++ 0x08,0xc8,0x09,0x99,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x03,0x33, ++ 0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03, ++ 0x30,0x00,0x00,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x33,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x33,0x33,0x33,0x30,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33, ++ 0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x00, ++ 0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x00,0x00,0x00,0x03,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x03,0x30, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x30,0x00,0x00, ++ 0x33,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33, ++ 0x00,0x00,0x30,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x00,0x00, ++ 0x00,0x03,0x00,0x00,0x33,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03, ++ 0x33,0x33,0x33,0x33,0x00,0x00,0x33,0x33,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x30,0x00,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x33,0x33,0x30,0x00, ++ 0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x33,0x33, ++ 0x33,0x30,0x00,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00, ++ 0x33,0x00,0x00,0x03,0x33,0x33,0x33,0x30,0x00,0x00,0x00,0x00,0x00,0x33, ++ 0x00,0x00,0x33,0x33,0x33,0x33,0x30,0x00,0x03,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x3a,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x3a,0x00,0x00,0x03,0x30,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x03,0xda,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xda,0x00,0x00,0x00,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x3a,0xda,0x00,0x00,0x00,0x00, ++ 0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0a,0xda,0x00,0x00, ++ 0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x30,0x0a,0xda, ++ 0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x00, ++ 0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x03,0x33,0x00,0x00,0x00,0x03,0x33, ++ 0x00,0x00,0xf3,0x33,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x33,0x33, ++ 0x30,0x00,0x00,0x00,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xab,0xab,0xa0,0x00,0xdd,0xee,0xea,0xaa, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xbb,0xab,0xbe,0xed,0xcc,0xbb, ++ 0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xba,0x0a,0xbb,0xbc, ++ 0xdd,0xee,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xa0,0x00, ++ 0xae,0xed,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, ++ 0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0x00,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8, ++ 0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00, ++ 0x00,0x00,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0x00,0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xfd,0x00,0x00,0x00,0x00,0x00,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xfd,0x00,0x00, ++ 0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8, ++ 0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xfd,0x33,0xfd,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0x00,0xfd,0x33,0xfd, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, ++ 0x00,0xfd,0x33,0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xfd,0x33,0xfd,0x33,0xfd,0x00,0x00,0x00,0xf9,0xf9,0xfb,0xfb, ++ 0xfb,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0x33,0x33,0xfd,0x33,0x33,0xfb,0xfb,0xf9, ++ 0xf7,0xf7,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xfd,0x00,0xfd, ++ 0x33,0x33,0x33,0xf7,0xf9,0xf9,0xfb,0xfb,0xfb,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0x00,0x00,0x00,0xfd,0xfb,0xfb,0xf9,0x00,0x00,0x01,0x00,0x00,0x7f, ++ 0xc0,0x00,0x01,0x83,0xf0,0x00,0x03,0xff,0x0c,0x00,0x04,0x00,0xfe,0x00, ++ 0x08,0x00,0x7f,0x00,0x1f,0xff,0xc0,0x80,0x30,0x00,0x3f,0xc0,0x20,0x00, ++ 0x1f,0xc0,0x7f,0xff,0xe0,0x20,0x60,0x00,0x1f,0xe0,0xe0,0x00,0x1f,0xf0, ++ 0xbf,0xff,0xe0,0x10,0xe0,0x00,0x1f,0xf0,0xf0,0x00,0x3f,0xf0,0x8f,0xff, ++ 0xc0,0x10,0xf8,0x00,0x7f,0xf0,0xfe,0x00,0xff,0xf0,0xc1,0xfe,0x00,0x30, ++ 0xff,0x87,0xff,0xf0,0x7f,0xff,0xff,0xf0,0x60,0x00,0x00,0x50,0x3f,0xff, ++ 0xff,0xd0,0x3f,0xff,0xff,0xd0,0x08,0x00,0x01,0x50,0x07,0xff,0xfe,0x50, ++ 0x07,0xff,0xfc,0xf8,0x01,0xc0,0x70,0x88,0x00,0x3f,0x80,0xf8,0x00,0x00, ++ 0x00,0xa8,0xff,0xff,0xff,0x27,0x00,0x00,0x00,0x50,0xff,0xff,0xff,0x8f, ++ 0x00,0x7f,0xc0,0x00,0x01,0xff,0xf0,0x00,0x03,0xff,0xfc,0x00,0x07,0xff, ++ 0xfe,0x00,0x0f,0xff,0xff,0x00,0x1f,0xff,0xff,0x80,0x3f,0xff,0xff,0xc0, ++ 0x3f,0xff,0xff,0xc0,0x7f,0xff,0xff,0xe0,0x7f,0xff,0xff,0xe0,0xff,0xff, ++ 0xff,0xf0,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xf0, ++ 0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xf0,0xff,0xff,0xff,0xf0,0xff,0xff, ++ 0xff,0xf0,0xff,0xff,0xff,0xf0,0x7f,0xff,0xff,0xf0,0x7f,0xff,0xff,0xf0, ++ 0x3f,0xff,0xff,0xf0,0x3f,0xff,0xff,0xf0,0x1f,0xff,0xfe,0x70,0x0f,0xff, ++ 0xfe,0x70,0x07,0xff,0xfc,0x70,0x03,0xff,0xf0,0xf8,0x00,0x3f,0x80,0xf8, ++ 0x00,0x00,0x00,0xf8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xff,0xff, ++ 0xff,0x8f,0x00,0x00,0x00,0x40,0x1f,0xc0,0x3b,0xf0,0x7f,0xf8,0x60,0xf8, ++ 0xff,0xfc,0xc0,0x7c,0xff,0xfc,0xe0,0xfc,0xff,0xfc,0xfb,0xfc,0xff,0xfc, ++ 0x7f,0xfc,0x3f,0xfe,0x1f,0xce,0x7f,0xff,0x7f,0xff,0x1f,0xc0,0x3f,0xf0, ++ 0x7f,0xf8,0x7f,0xf8,0xff,0xfc,0xff,0xfc,0xff,0xfc,0xff,0xfc,0xff,0xfc, ++ 0xff,0xfc,0xff,0xfc,0x7f,0xfc,0x3f,0xfc,0x1f,0xfe,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x80,0x00,0x03,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x33, ++ 0x30,0x33,0x33,0x33,0x00,0x00,0x03,0x33,0x33,0x33,0x33,0x33,0x30,0x00, ++ 0x03,0x30,0x00,0x00,0x33,0x33,0x30,0x00,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x00,0x33,0x00,0x00,0x00,0x03,0x33,0x33,0x00,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x00,0x33,0x30,0x00,0x00,0x33,0x33,0x33,0x00,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x00,0x33,0x33,0x30,0x33,0x33,0x33,0x3a,0x00, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x3a,0x00,0x03,0x33,0x33,0x33,0x33,0x33, ++ 0xaa,0x00,0x00,0x33,0x33,0x33,0x33,0x33,0xff,0xf0,0x00,0x03,0x33,0x33, ++ 0x33,0x00,0xff,0xf0,0xde,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xde,0xaa, ++ 0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xd8,0xd8,0xd8,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0x00,0x00, ++ 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00, ++ 0x00,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xd8,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xd8,0xd8,0xd8,0x00, ++ 0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xfd, ++ 0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xfd,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xfd,0xfd,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xff,0xff,0xff,0x00,0xf9,0xfb,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xf9,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xfb, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x03,0x71,0xef,0x04,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, ++ 0x03,0x71,0xea,0xe4,0xbf,0xb9,0xff,0xff,0x20,0x00,0x06,0x08,0x03,0x71, ++ 0xf5,0xdc,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf1,0xc4, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf3,0x8c,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf2,0x90 ++ }; ++ #endif /* SYSTYPE_SYSV */ ++ #endif /* HOST_ICON */ ++ ++ /* default */ ++ ++ #ifndef HOST_ICON ++ #define BSD_ICON 1 ++ unsigned char color_cap_icon[ICON_RSRC_LEN] = { /* BSDicon */ ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x00,0x60,0x00,0x03,0x00,0x70,0x00,0x30,0x06,0x4a,0x80,0x66, ++ 0x06,0x70,0x01,0x60,0x00,0x02,0xf2,0x24,0x6b,0x1d,0x8e,0x2d,0x6b,0x00, ++ 0x20,0xff,0xf4,0x42,0x44,0x42,0x08,0x53,0x55,0x4e,0x20,0x69,0x63,0x6f, ++ 0x6e,0x4e,0x02,0x00,0x00,0x00,0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44, ++ 0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x73, ++ 0x72,0x63,0x52,0x53,0x45,0x44,0x01,0x00,0xff,0xff,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xb0,0x0e,0x36,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x6e, ++ 0x00,0x1c,0x14,0x70,0x00,0x10,0x06,0x0c,0x40,0x00,0x2e,0x67,0x00,0x00, ++ 0x84,0x70,0x00,0x10,0x06,0x0c,0x40,0x00,0xe5,0x67,0x78,0x70,0x00,0x10, ++ 0x06,0x4a,0x80,0x67,0x7e,0x70,0x10,0xc0,0x2c,0x00,0x0b,0x67,0x34,0x4a, ++ 0x44,0x67,0x06,0x70,0x00,0x60,0x00,0x02,0x64,0x48,0x7a,0x02,0x76,0x2f, ++ 0x0c,0x4e,0xba,0x8b,0x20,0x4a,0x00,0x50,0x4f,0x67,0x14,0x70,0x00,0x10, ++ 0x2c,0x00,0x1b,0xe1,0x88,0x72,0x00,0x12,0x2c,0x00,0x1a,0x38,0x01,0xd8, ++ 0x40,0x60,0x3a,0x70,0x00,0x60,0x00,0x02,0x3a,0x4a,0x43,0x67,0x06,0x70, ++ 0x00,0x60,0x00,0x02,0x30,0x48,0x7a,0x02,0x36,0x2f,0x0c,0x4e,0xba,0x8a, ++ 0xec,0x4a,0x00,0x50,0x4f,0x67,0x14,0x70,0x00,0x10,0x2c,0x00,0x1b,0xe1, ++ 0x88,0x72,0x00,0x12,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0x00,0x00,0x00,0x00,0x0f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x0f,0xf0,0x00,0xff,0xff,0xf0,0x00,0xff,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x0f,0x3f,0x0f,0x33,0x33,0x3f,0x0f,0x3f,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x0f,0x33,0xf3,0x3f,0xff,0x33,0xf3,0x3f,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x3f,0xff,0x33,0xf3,0x3f, ++ 0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0xff,0x00,0xf3, ++ 0x3f,0x33,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0xf0, ++ 0x00,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, ++ 0xf0,0xff,0xf0,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xf0,0xff,0xf0,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0xf0,0xff,0xf0,0x0f,0x3f,0x33,0x3f,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0xf0,0x00,0x0f,0x3f,0x33,0x3f,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0xf0,0x00,0xf3,0x3f,0x33, ++ 0x3f,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,0xff,0xff,0x3f, ++ 0xf3,0x33,0x3f,0x00,0xe0,0x0e,0x0e,0xee,0xe0,0x00,0x00,0x00,0xf3,0x33, ++ 0xf3,0x33,0x33,0x33,0x3f,0x00,0xe0,0xe0,0xe7,0x77,0x7e,0x00,0x00,0x00, ++ 0xf3,0x33,0xf3,0x3f,0xff,0x33,0xf0,0x00,0xee,0x00,0xee,0xee,0xee,0xee, ++ 0xee,0xee,0x0f,0xff,0x33,0x33,0xf3,0x33,0xf0,0x0e,0xe0,0x0e,0xe7,0x77, ++ 0x77,0x77,0x77,0x7e,0x00,0xf3,0x33,0x3f,0x33,0x3f,0xee,0xe0,0x0e,0xe0, ++ 0xe7,0x77,0x77,0x7f,0x77,0x7e,0x00,0x0f,0xff,0xff,0xff,0xf3,0xf0,0x00, ++ 0x00,0x00,0xe7,0x77,0x77,0xf7,0x77,0x7e,0x00,0xf3,0x33,0x33,0xf3,0x33, ++ 0xf0,0x00,0x0f,0xf0,0xe7,0x77,0x7f,0x77,0x77,0x7e,0x00,0xff,0xf3,0x3f, ++ 0x33,0x33,0xf0,0x00,0xf0,0x0f,0xe7,0x77,0xf7,0x77,0x77,0x7e,0x0f,0x33, ++ 0x3f,0xef,0x33,0x3f,0xf0,0x00,0xf0,0x0f,0xe7,0x7f,0x77,0xf7,0xf7,0x7e, ++ 0x0f,0x33,0x3f,0x33,0xff,0xf3,0xf0,0x0f,0x00,0xf0,0xe7,0x77,0x77,0x77, ++ 0x77,0x7e,0x0f,0xf3,0x3f,0x33,0x33,0x33,0xf0,0xf0,0x0f,0x00,0xee,0xee, ++ 0xee,0xee,0xee,0xee,0x00,0xef,0xf3,0x3f,0x33,0x33,0xff,0x00,0x0f,0x00, ++ 0x00,0x00,0x0a,0xca,0x00,0x00,0x0e,0xf3,0x33,0x33,0x33,0x3f,0xf0,0x00, ++ 0xf0,0x00,0x00,0x00,0xaa,0xaa,0xa0,0x00,0xe0,0x0f,0x33,0x33,0x33,0x3f, ++ 0x00,0xf0,0xf0,0xf0,0x00,0x00,0xa3,0x33,0xa0,0x00,0x00,0x00,0xf3,0x3f, ++ 0x33,0x3f,0x00,0x0f,0xff,0x00,0x00,0x00,0xaa,0xaa,0xa0,0x00,0x00,0x00, ++ 0xf3,0x3f,0x33,0x3f,0x00,0x00,0xf0,0x00,0x00,0x00,0xab,0xab,0xa0,0x00, ++ 0x00,0x00,0xf3,0x3f,0x33,0x3f,0x00,0x00,0x00,0x00,0xdd,0xee,0xbb,0xab, ++ 0xba,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xee,0xdd,0xcc,0xbb, ++ 0xba,0x0a,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xcc, ++ 0xdd,0xee,0xa0,0x00,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae, ++ 0xee,0xdd,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xd8,0xff, ++ 0x00,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0xff,0xd8,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0xff,0xd8,0xd8,0xff,0xd8, ++ 0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0xff,0xff,0xff,0xd8,0xd8, ++ 0xff,0xd8,0xd8,0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff, ++ 0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00, ++ 0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xfe,0xfe,0x00,0xff,0xfe,0xfe,0x00,0x00,0xff,0xd8,0xff, ++ 0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xfe,0xfe,0x00,0xff,0xfe,0xfe,0x00, ++ 0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xfe,0xfe,0x00, ++ 0xff,0xfe,0xfe,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xd8,0xff,0xd8,0xd8, ++ 0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xd8, ++ 0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xd8,0xff,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xfc,0x00, ++ 0x00,0xfc,0x00,0xab,0xab,0xab,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff, ++ 0x00,0x00,0xfc,0x00,0xfc,0x00,0xab,0x54,0x54,0x54,0x54,0xab,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff,0xff, ++ 0xd8,0xd8,0xff,0x00,0x00,0x00,0xfc,0xfc,0x00,0x00,0xab,0xab,0xab,0xab, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xff,0xff,0xff,0xd8,0xd8, ++ 0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xfc,0xfc,0x00,0x00,0xfc, ++ 0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00, ++ 0xff,0xd8,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xfc,0xfc,0xfc,0xfc,0x00, ++ 0x00,0xfc,0xfc,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xfe,0x2a,0x2a, ++ 0x2a,0xab,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd8, ++ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x2a,0x2a,0x2a,0x2a,0x2a, ++ 0xfe,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xd8,0xd8, ++ 0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0xab,0x2a, ++ 0x2a,0x2a,0x2a,0xfe,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0x00,0xff,0xff, ++ 0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xff,0x00, ++ 0x00,0xff,0xab,0x2a,0x2a,0x2a,0xfe,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab, ++ 0x00,0xff,0xd8,0xd8,0xd8,0xff,0xfc,0xff,0xd8,0xd8,0xd8,0xff,0xff,0x00, ++ 0x00,0x00,0xff,0x00,0x00,0xff,0xab,0x2a,0x2a,0xfe,0x2a,0x2a,0xfe,0x2a, ++ 0xfe,0x2a,0x2a,0xab,0x00,0xff,0xd8,0xd8,0xd8,0xff,0xd8,0xd8,0xff,0xff, ++ 0xff,0xd8,0xff,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0xab,0x2a,0x2a,0x2a, ++ 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0xab,0x00,0xff,0xff,0xd8,0xd8,0xff, ++ 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00, ++ 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0x00, ++ 0xfc,0xff,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xd8,0xff,0xff,0x00,0x00, ++ 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0xf8,0xfd,0x00,0x00, ++ 0x00,0x00,0x00,0xfc,0xff,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xff, ++ 0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, ++ 0xff,0xff,0xff,0x00,0x00,0x00,0xfc,0x00,0x00,0xff,0xd8,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00, ++ 0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfd,0x33,0xfd,0x33, ++ 0xfd,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xd8,0xd8,0xff,0xd8,0xd8, ++ 0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf9,0xf9,0xfb,0xfb, ++ 0x33,0x33,0xfd,0x33,0x33,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9, ++ 0xf6,0xf6,0x33,0x33,0x33,0xfd,0x00,0xfd,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, ++ 0x33,0x33,0xf6,0xf6,0xf9,0xf9,0xfb,0xfb,0xfd,0x00,0x00,0x00,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfb,0xfb,0xfb,0xf9,0xf9,0x00,0x00,0x01,0x00,0x00,0x0c, ++ 0x01,0x80,0x00,0x18,0xf8,0xc0,0x00,0x15,0x05,0x40,0x00,0x12,0x72,0x40, ++ 0x00,0x0d,0xc9,0x40,0x00,0x13,0x24,0xc0,0x00,0x22,0x14,0x40,0x00,0x3b, ++ 0x94,0x40,0x00,0x3b,0x94,0x40,0x00,0x3b,0x94,0x40,0x00,0x22,0x14,0x40, ++ 0x00,0x22,0x24,0x44,0x00,0x1f,0xd8,0x49,0x78,0x08,0x80,0x4a,0x84,0x08, ++ 0x9c,0x8c,0xff,0xf7,0x08,0x99,0x80,0x12,0x11,0xe6,0x81,0x11,0xfe,0x80, ++ 0x82,0x12,0x08,0x86,0x84,0x13,0x90,0x89,0x88,0x14,0x71,0x89,0x92,0x94, ++ 0x4e,0x92,0x80,0x16,0x40,0xa4,0xff,0xf3,0x90,0xc4,0x05,0x06,0x01,0x88, ++ 0x0f,0x89,0x01,0x2a,0x08,0x80,0x91,0x1c,0x0f,0x80,0x91,0x08,0x0a,0x80, ++ 0x91,0x00,0xf2,0x7f,0xff,0xff,0x05,0x00,0x00,0x00,0xf8,0xff,0xff,0xff, ++ 0x00,0x0c,0x01,0x80,0x00,0x18,0xf8,0xc0,0x00,0x1d,0xfd,0xc0,0x00,0x1f, ++ 0xff,0xc0,0x00,0x0f,0xff,0xc0,0x00,0x1f,0xff,0xc0,0x00,0x3f,0xff,0xc0, ++ 0x00,0x3f,0xff,0xc0,0x00,0x3f,0xff,0xc0,0x00,0x3f,0xff,0xc0,0x00,0x3f, ++ 0xff,0xc0,0x00,0x3f,0xff,0xc4,0x00,0x1f,0xff,0xc9,0x78,0x0f,0xff,0xca, ++ 0xfc,0x0f,0xff,0x8c,0xff,0xf7,0xff,0x99,0xff,0xf3,0xff,0xe6,0xff,0xf1, ++ 0xff,0x80,0xff,0xf3,0xff,0x86,0xff,0xf3,0xff,0x89,0xff,0xf7,0xff,0x89, ++ 0xff,0xf7,0xff,0x92,0xff,0xf7,0xff,0xa4,0xff,0xf3,0xff,0xc4,0x07,0x07, ++ 0xff,0x88,0x0f,0x89,0xff,0x2a,0x0f,0x80,0xff,0x1c,0x0f,0x80,0xff,0x08, ++ 0x0f,0x80,0xff,0x00,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xf8,0xff, ++ 0xff,0xff,0x00,0x00,0x00,0x40,0x06,0x30,0x04,0xd0,0x07,0xf0,0x05,0x58, ++ 0x05,0x58,0x07,0xd8,0xe6,0xf9,0xff,0xf2,0x95,0xff,0xb5,0x68,0xfd,0x59, ++ 0xfd,0xff,0x39,0xf8,0x38,0xf0,0x7f,0xf8,0x00,0x00,0x06,0x30,0x04,0xf0, ++ 0x07,0xf0,0x07,0xf8,0x07,0xf8,0x07,0xf8,0xe7,0xf9,0xff,0xf2,0xff,0xff, ++ 0xff,0xf8,0xff,0xf9,0xff,0xff,0x3f,0xf8,0x3f,0xf0,0xff,0xff,0xff,0xff, ++ 0x00,0x00,0x00,0x80,0x00,0x00,0x0f,0xf0,0x00,0xff,0x00,0x00,0x00,0x00, ++ 0x0f,0x00,0xff,0x0f,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0x00,0x00, ++ 0x00,0x00,0x0f,0x0f,0x0f,0x33,0xf0,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x33, ++ 0xf0,0x00,0x00,0x00,0x03,0x33,0x33,0x33,0xf0,0x00,0xee,0xe0,0x03,0xff, ++ 0xf3,0x33,0xf0,0x0f,0xee,0xee,0xee,0x33,0x33,0x33,0x00,0xf0,0xec,0xcf, ++ 0xce,0x03,0x33,0xff,0xff,0xff,0xec,0xff,0xce,0x03,0x3f,0xf3,0xf0,0x00, ++ 0xef,0xff,0xfe,0x03,0x3f,0xff,0xf0,0x0f,0xee,0xee,0xee,0x0f,0xff,0x33, ++ 0xff,0xff,0x00,0xaa,0x30,0x0f,0x33,0x3f,0xf0,0x00,0x00,0x33,0x30,0x00, ++ 0xff,0x3f,0x00,0x00,0xda,0xaa,0xaa,0xaa,0xff,0xaf,0xad,0xdc,0xdd,0xdd, ++ 0xdd,0xdd,0xdd,0xdd,0xdd,0xdc,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xd8,0xd8, ++ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff, ++ 0xd8,0xd8,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8, ++ 0xd8,0xd8,0xd8,0xd8,0xff,0x00,0x00,0x00,0xab,0xab,0xab,0x00,0x00,0xd8, ++ 0xff,0xff,0xff,0xd8,0xd8,0xd8,0xff,0x00,0x00,0xff,0xab,0xab,0xab,0xab, ++ 0xab,0xab,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x00,0x00,0xff,0x00,0xab,0x54, ++ 0x54,0xfe,0x54,0xab,0x00,0xd8,0xd8,0xd8,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xab,0x54,0xfe,0xfe,0x54,0xab,0x00,0xd8,0xd8,0xff,0xff,0xd8,0xff,0x00, ++ 0x00,0x00,0xab,0xfe,0xfe,0xfe,0xfe,0xab,0x00,0xd8,0xd8,0xff,0xff,0xff, ++ 0xff,0x00,0x00,0xff,0xab,0xab,0xab,0xab,0xab,0xab,0x00,0xff,0xff,0xff, ++ 0xd8,0xd8,0xff,0xff,0xff,0xff,0x00,0x00,0xfd,0xfd,0xd8,0x00,0x00,0xff, ++ 0xd8,0xd8,0xd8,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0x00, ++ 0x00,0x00,0xff,0xff,0xd8,0xff,0x00,0x00,0x00,0x00,0xf9,0xfd,0xfd,0xfd, ++ 0xfd,0xfd,0xfd,0xfd,0xff,0xff,0xfd,0xff,0xfd,0xf9,0xf9,0xf6,0xf9,0x33, ++ 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xf9,0xf6, ++ 0x00,0x00,0x01,0x00,0x00,0x00,0x09,0xd8,0x00,0x00,0x08,0xd8,0x00,0x00, ++ 0x00,0x96,0x03,0x71,0xf6,0xc4,0x31,0x94,0x00,0x00,0x00,0x1c,0x00,0x96, ++ 0x00,0x05,0x69,0x63,0x6c,0x34,0x00,0x00,0x00,0x32,0x69,0x63,0x6c,0x38, ++ 0x00,0x00,0x00,0x3e,0x49,0x43,0x4e,0x23,0x00,0x00,0x00,0x4a,0x69,0x63, ++ 0x73,0x23,0x00,0x00,0x00,0x56,0x69,0x63,0x73,0x34,0x00,0x00,0x00,0x62, ++ 0x69,0x63,0x73,0x38,0x00,0x00,0x00,0x6e,0xbf,0xb9,0xff,0xff,0x00,0x00, ++ 0x00,0x00,0x03,0x71,0xf4,0x4c,0xbf,0xb9,0xff,0xff,0x00,0x00,0x02,0x04, ++ 0x03,0x71,0xee,0xa4,0xbf,0xb9,0xff,0xff,0x00,0x00,0x06,0x08,0x03,0x71, ++ 0xf5,0xa4,0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x0c,0x03,0x71,0xf0,0x78, ++ 0xbf,0xb9,0xff,0xff,0x00,0x00,0x07,0x50,0x03,0x71,0xf4,0x68,0xbf,0xb9, ++ 0xff,0xff,0x00,0x00,0x07,0xd4,0x03,0x71,0xf5,0x54 ++ }; ++ #endif /* HOST_ICON */ ++ ++ /* ++ * Finder info for our Icon file, ++ * name "Icon^M", type 'rsrc', owner 'RSED', invisible, custom_icon ++ * ++ */ ++ unsigned char color_cap_icon_fndr[ICON_FNDR_LEN] = { /* FinderInfo */ ++ 0x72,0x73,0x72,0x63,0x52,0x53,0x45,0x44,0x45,0x00,0x00,0x9c,0x00,0x80, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x10,0xda,0x02,0x49,0x63,0x6f,0x6e, ++ 0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x63,0x6f,0x6e,0x0d, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0xda,0x03,0x33,0xe7,0xfe,0x70,0x33,0xe7,0xfe, ++ 0xca,0x33,0xe8,0x7d,0xfe,0x00 ++ }; ++ ++ char icon_path[MAXPATHLEN]; ++ ++ /* ++ * check if vol/Icon:0d file already in place ++ * ++ */ ++ ++ int ++ icon_exists(vol) ++ char *vol; ++ { ++ sprintf(icon_path, "%s/%s", vol, ICON_NAME); ++ if (access(icon_path, R_OK) == 0) ++ return(1); ++ ++ return(0); ++ } ++ ++ /* ++ * create three forks of the Icon:0d file ++ * in the specified volume root ++ * ++ */ ++ ++ int ++ icon_create(vol) ++ char *vol; ++ { ++ int fd; ++ char *cp; ++ IDirP pdir; ++ ++ /* ++ * data fork, zero length ++ * ++ */ ++ sprintf(icon_path, "%s/%s", vol, ICON_NAME); ++ if ((fd = open(icon_path, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) ++ return(fd); ++ close(fd); ++ ++ /* ++ * resource fork ++ * ++ */ ++ sprintf(icon_path, "%s/.resource/%s", vol, ICON_NAME); ++ if ((fd = open(icon_path, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) ++ return(fd); ++ (void)write(fd, color_cap_icon, sizeof(color_cap_icon)); ++ close(fd); ++ ++ /* ++ * Finder information ++ * ++ */ ++ sprintf(icon_path, "%s/.finderinfo/%s", vol, ICON_NAME); ++ if ((fd = open(icon_path, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) ++ return(fd); ++ #ifdef USE_MAC_DATES ++ (void)write(fd, color_cap_icon_fndr, ICON_FNDR_LEN); ++ #else /* USE_MAC_DATES */ ++ (void)write(fd, color_cap_icon_fndr, ICON_FNDR_WAS); ++ #endif /* USE_MAC_DATES */ ++ close(fd); ++ ++ /* ++ * set the volume comment field ++ * ++ */ ++ strncpy(icon_path, vol, sizeof(icon_path)); ++ if ((cp = (char *)rindex(icon_path, '/')) == NULL) ++ return(0); ++ *cp++ = '\0'; ++ #ifdef BSD_ICON ++ if ((pdir = Idirid(icon_path)) != NILDIR) ++ OSSetComment(pdir, cp, INFO_MESSAGE1, strlen(INFO_MESSAGE1)); ++ #else /* BSD_ICON */ ++ if ((pdir = Idirid(icon_path)) != NILDIR) ++ OSSetComment(pdir, cp, INFO_MESSAGE2, strlen(INFO_MESSAGE2)); ++ #endif /* BSD_ICON */ ++ ++ return(0); ++ } +*** applications/aufs/Makefile.m4.orig Thu Aug 7 18:07:41 1997 +--- applications/aufs/Makefile.m4 Thu Aug 7 16:59:19 1997 +*************** +*** 48,59 **** + afpmisc.c afpserver.c aufsicon.c abmisc2.c \ + afpdt.c afpdid.c afposenum.c afpavl.c \ + afposfi.c afpgc.c afppasswd.c afposlock.c aufsv.c \ +! afpudb.c afposncs.c afpspd.c afpfid.c afpdsi.c + OBJS=afpos.o afpvols.o afpfile.o \ + afpmisc.o afpserver.o aufsicon.o abmisc2.o \ + afpdt.o afpdir.o afpfork.o afpdid.o afposenum.o afpavl.o \ + afposfi.o afpgc.o afppasswd.o aufsv.o \ +! afpudb.o afposncs.o afpspd.o afpfid.o afpdsi.o + SYMLINKS=att_getopt.c + + all: aufs sizeserver afpidsrvr afpidlist afpidtool +--- 48,59 ---- + afpmisc.c afpserver.c aufsicon.c abmisc2.c \ + afpdt.c afpdid.c afposenum.c afpavl.c \ + afposfi.c afpgc.c afppasswd.c afposlock.c aufsv.c \ +! afpudb.c afposncs.c afpspd.c afpfid.c afpdsi.c aufscicon.c + OBJS=afpos.o afpvols.o afpfile.o \ + afpmisc.o afpserver.o aufsicon.o abmisc2.o \ + afpdt.o afpdir.o afpfork.o afpdid.o afposenum.o afpavl.o \ + afposfi.o afpgc.o afppasswd.o aufsv.o \ +! afpudb.o afposncs.o afpspd.o afpfid.o afpdsi.o aufscicon.o + SYMLINKS=att_getopt.c + + all: aufs sizeserver afpidsrvr afpidlist afpidtool diff --git a/cap60.patches/desktop.patch b/cap60.patches/desktop.patch new file mode 100644 index 0000000..bda1339 --- /dev/null +++ b/cap60.patches/desktop.patch @@ -0,0 +1,657 @@ +Patch #: none yet +Type: operational change +Priority: none +Modification: add support for .IDeskTop color icon familes +Submitted: David Hornsby +IMPORTANT: +IMPORTANT: This patch assumes CAP at patch level 198. +IMPORTANT: This is an interim patch only. You will need to keep this +IMPORTANT: patch file in order to reverse the code changes before patch +IMPORTANT: 199 can be applied without error. +IMPORTANT: +File: cap60/contrib/DeskTop/builddt.c +File: cap60/contrib/DeskTop/dt.h +File: cap60/contrib/DeskTop/dtmisc.c +File: cap60/contrib/DeskTop/dumpdt.c +File: cap60/contrib/DeskTop/Makefile +File: cap60/contrib/DeskTop/README + + +*** contrib/DeskTop/builddt.c.orig Mon Aug 11 00:02:19 1997 +--- contrib/DeskTop/builddt.c Sun Aug 10 22:16:52 1997 +*************** +*** 30,37 **** + int totfiles = 0; + int idesk = 0, adesk = 0; + long iconlen, bndllen, freflen; +! short iconnum, bndlnum, frefnum; +! long iconoffset, bndloffset, frefoffset; + long listoffset; + u_char icon[1024]; + +--- 30,37 ---- + int totfiles = 0; + int idesk = 0, adesk = 0; + long iconlen, bndllen, freflen; +! short bndlnum, frefnum; +! long bndloffset, frefoffset; + long listoffset; + u_char icon[1024]; + +*************** +*** 47,52 **** +--- 47,63 ---- + struct bids bids; + struct fdata fdata; + ++ struct icn icn[8] = { ++ "", 0, 0, ++ "ICN#", 0, 0, ++ "icl4", 0, 0, ++ "icl8", 0, 0, ++ "ics#", 0, 0, ++ "ics4", 0, 0, ++ "ics8", 0, 0, ++ "", 0, 0 ++ }; ++ + main(argc, argv) + int argc; + char *argv[]; +*************** +*** 225,230 **** +--- 236,242 ---- + makeEntry(path, file, resource, finderinfo) + char *path, *file, *resource, *finderinfo; + { ++ int h; + int i; + int j; + int k; +*************** +*** 234,240 **** + short ilen; + long offset; + short numres; +! char *ic, *findICN(); + struct fdata *ff, *findFREF(); + + totfiles++; +--- 246,252 ---- + short ilen; + long offset; + short numres; +! char *ic, *findIcon(); + struct fdata *ff, *findFREF(); + + totfiles++; +*************** +*** 345,351 **** + return; + } + +! bndlnum = iconnum = frefnum = 0; + + for (j = 0; j < numres; j++) { + /* read resource type list */ +--- 357,365 ---- + return; + } + +! bndlnum = frefnum = 0; +! for (j = 0; j < 8; j++) +! icn[j].iconnum = icn[j].iconoffset = 0; + + for (j = 0; j < numres; j++) { + /* read resource type list */ +*************** +*** 366,374 **** + bndlnum = ntohs(tlist.rnum)+1; + bndloffset = ntohs(tlist.roff); + } +! if (bcmp(tlist.rtype, "ICN#", 4) == 0) { +! iconnum = ntohs(tlist.rnum)+1; +! iconoffset = ntohs(tlist.roff); + } + if (bcmp(tlist.rtype, "FREF", 4) == 0) { + frefnum = ntohs(tlist.rnum)+1; +--- 380,391 ---- + bndlnum = ntohs(tlist.rnum)+1; + bndloffset = ntohs(tlist.roff); + } +! /* check for icon family */ +! for (h = 1; h < 7; h++) { +! if (bcmp(tlist.rtype, icn[h].ityp, 4) == 0) { +! icn[h].iconnum = ntohs(tlist.rnum)+1; +! icn[h].iconoffset = ntohs(tlist.roff); +! } + } + if (bcmp(tlist.rtype, "FREF", 4) == 0) { + frefnum = ntohs(tlist.rnum)+1; +*************** +*** 377,383 **** + } + + /* check for BNDL/ICN#/FREF resources */ +! if (bndlnum == 0 || iconnum == 0 || frefnum == 0) { + if (debug) printf("no BNDL/ICN#/FREF resources\n"); + close(fd); + return; +--- 394,400 ---- + } + + /* check for BNDL/ICN#/FREF resources */ +! if (bndlnum == 0 || icn[1].iconnum == 0 || frefnum == 0) { + if (debug) printf("no BNDL/ICN#/FREF resources\n"); + close(fd); + return; +*************** +*** 461,494 **** + printf("local ID %d, actual ID %d\n", + (short)ntohs(bids.localID), (short)ntohs(bids.actualID)); + +! if (bcmp(bdata.btype, "ICN#", 4) == 0) { +! if ((ic = findICN(fd, ntohs(bids.actualID), &ilen)) != NULL) { +! if ((ff = findFREF(fd, ntohs(bids.localID))) != NULL) { +! idt.magic = htonl(MAGIC); +! idt.isize = htonl(ilen); +! bcopy(bhdr.creat, idt.creat, 4); +! bcopy(ff->ftype, idt.ftype, 4); +! idt.itype = 1; /* ICN# */ +! idt.pad1 = 0; +! bzero(idt.userb, 4); +! idt.pad2[0] = 0; +! idt.pad2[1] = 0; +! +! if (debug) { +! printf("creator"); +! printsig(idt.creat); +! printf("type"); +! printsig(idt.ftype); +! printf("found ICN# of length %d\n", ilen); +! printicn(ic, ilen); + } +! +! if (writing) { +! write(fdi, &idt, sizeof(idt)); +! write(fdi, ic, ilen); +! idesk++; +! } +! } + } + } + } +--- 478,514 ---- + printf("local ID %d, actual ID %d\n", + (short)ntohs(bids.localID), (short)ntohs(bids.actualID)); + +! if ((ff = findFREF(fd, ntohs(bids.localID))) != NULL) { +! if (bcmp(bdata.btype, "ICN#", 4) == 0) { +! /* check icon family */ +! for (h = 1; h < 7; h++) { +! if ((ic=findIcon(fd,h,ntohs(bids.actualID),&ilen))!=NULL){ +! idt.magic = htonl(MAGIC); +! idt.isize = htonl(ilen); +! bcopy(bhdr.creat, idt.creat, 4); +! bcopy(ff->ftype, idt.ftype, 4); +! idt.itype = h; +! idt.pad1 = 0; +! bzero(idt.userb, 4); +! idt.pad2[0] = 0; +! idt.pad2[1] = 0; +! +! if (debug) { +! printf("creator"); +! printsig(idt.creat); +! printf("type"); +! printsig(idt.ftype); +! printf("found '%s' of length %d\n", icn[h].ityp, ilen); +! printicn(ic, h, ilen); +! } +! +! if (writing) { +! write(fdi, &idt, sizeof(idt)); +! write(fdi, ic, ilen); +! idesk++; +! } + } +! } + } + } + } +*************** +*** 501,514 **** + } + + /* +! * find the ICN# with the specified resource number, +! * return a pointer to the ICN# data and it's length + * + */ + + char * +! findICN(fd, rsrcID, len) + int fd; + short rsrcID; + short *len; + { +--- 521,536 ---- + } + + /* +! * find the icon with the specified resource number +! * and type (ICN#, icl4, icl8, ics#, ics4, ics8), +! * return a pointer to the icon data and it's length + * + */ + + char * +! findIcon(fd, typ, rsrcID, len) + int fd; ++ int typ; + short rsrcID; + short *len; + { +*************** +*** 521,547 **** + + /* keep current file pointer position */ + if ((curpos = lseek(fd, 0, SEEK_CUR)) >= 0) { +! /* move to start of ICN# resource reference list */ +! if (lseek(fd, listoffset+iconoffset, SEEK_SET) >= 0) { +! for (i = 0; i < iconnum; i++) { +! /* get resource reference list for each ICN# */ + if (read(fd, &rlist, sizeof(rlist)) == sizeof(rlist)) { +! if (debug) { +! printf("found ICN# rsrc ID %d ", (short)ntohs(rlist.rsrcID)); +! printf("offset %d\n", (long)ntohl(rlist.attrOff) & 0xffffff); +! } + if ((short)ntohs(rlist.rsrcID) == rsrcID) + break; + } + } +! if (i < iconnum) { + offset = ntohl(rhdr.rdataOffset)+(ntohl(rlist.attrOff)&0xffffff); +! /* move to beginning of ICN# resource data */ + if (lseek(fd, offset, SEEK_SET) >= 0) { +! /* get ICN# resource length */ + if (read(fd, &iconlen, 4) == 4) { + if ((iconlen = ntohl(iconlen)) <= sizeof(icon)) { +! /* read ICN# data */ + if (read(fd, icon, iconlen) == iconlen) { + /* restore original pointer */ + lseek(fd, curpos, SEEK_SET); +--- 543,569 ---- + + /* keep current file pointer position */ + if ((curpos = lseek(fd, 0, SEEK_CUR)) >= 0) { +! /* move to start of icon resource reference list */ +! if (lseek(fd, listoffset+icn[typ].iconoffset, SEEK_SET) >= 0) { +! for (i = 0; i < icn[typ].iconnum; i++) { +! /* get resource reference list for each icon */ + if (read(fd, &rlist, sizeof(rlist)) == sizeof(rlist)) { +! if (debug) +! printf("found '%s' rsrc ID %d offset %d\n", +! icn[typ].ityp, (short)ntohs(rlist.rsrcID), +! (long)ntohl(rlist.attrOff) & 0xffffff); + if ((short)ntohs(rlist.rsrcID) == rsrcID) + break; + } + } +! if (i < icn[typ].iconnum) { + offset = ntohl(rhdr.rdataOffset)+(ntohl(rlist.attrOff)&0xffffff); +! /* move to beginning of icon resource data */ + if (lseek(fd, offset, SEEK_SET) >= 0) { +! /* get icon resource length */ + if (read(fd, &iconlen, 4) == 4) { + if ((iconlen = ntohl(iconlen)) <= sizeof(icon)) { +! /* read icon data */ + if (read(fd, icon, iconlen) == iconlen) { + /* restore original pointer */ + lseek(fd, curpos, SEEK_SET); +*** contrib/DeskTop/dt.h.orig Mon Aug 11 00:04:07 1997 +--- contrib/DeskTop/dt.h Sun Aug 10 23:00:33 1997 +*************** +*** 1,7 **** + /* + * (re)build/dump CAP desktop files .IDeskTop and .ADeskTop + * +! * Copyright (c) 1993, The University of Melbourne. + * All Rights Reserved. Permission to publicly redistribute this + * package (other than as a component of CAP) or to use any part + * of this software for any purpose, other than that intended by +--- 1,7 ---- + /* + * (re)build/dump CAP desktop files .IDeskTop and .ADeskTop + * +! * Copyright (c) 1993-1997, The University of Melbourne. + * All Rights Reserved. Permission to publicly redistribute this + * package (other than as a component of CAP) or to use any part + * of this software for any purpose, other than that intended by +*************** +*** 11,16 **** +--- 11,17 ---- + * djh@munnari.OZ.AU + * 15 February 1993 + * 30 November 1993 ++ * 10 August 1997 + * + * Refer: "Inside Macintosh", Volume 1, page I-128 "Format of a Resource File" + * +*************** +*** 50,60 **** + */ + + struct adt { +! long magic; + u_char creat[4]; + u_char userb[4]; +! long dlen; +! long flen; + /* names follow */ + }; + +--- 51,61 ---- + */ + + struct adt { +! int magic; + u_char creat[4]; + u_char userb[4]; +! int dlen; +! int flen; + /* names follow */ + }; + +*************** +*** 64,71 **** + */ + + struct idt { +! long magic; +! long isize; + u_char creat[4]; + u_char ftype[4]; + u_char itype; +--- 65,72 ---- + */ + + struct idt { +! int magic; +! int isize; + u_char creat[4]; + u_char ftype[4]; + u_char itype; +*************** +*** 89,95 **** + u_short iconID; + u_short pad[4]; + u_short commID; +! u_long dirID; + }; + + struct finfo { +--- 90,96 ---- + u_short iconID; + u_short pad[4]; + u_short commID; +! u_int dirID; + }; + + struct finfo { +*************** +*** 112,121 **** + + /* resource hdr */ + struct rhdr { +! long rdataOffset; +! long rmapOffset; +! long rdataLength; +! long rmapLength; + u_char filler[240]; + }; + +--- 113,122 ---- + + /* resource hdr */ + struct rhdr { +! int rdataOffset; +! int rmapOffset; +! int rdataLength; +! int rmapLength; + u_char filler[240]; + }; + +*************** +*** 137,144 **** + struct rlist { + short rsrcID; + short nameOffset; +! u_long attrOff; +! u_long handle; + }; + + /* bundle header */ +--- 138,145 ---- + struct rlist { + short rsrcID; + short nameOffset; +! u_int attrOff; +! u_int handle; + }; + + /* bundle header */ +*************** +*** 165,170 **** +--- 166,178 ---- + u_char ftype[4]; + short localID; + /* ignore filename */ ++ }; ++ ++ /* icon family */ ++ struct icn { ++ char ityp[6]; ++ short iconnum; ++ int iconoffset; + }; + + /* defines */ +*** contrib/DeskTop/dtmisc.c.orig Mon Aug 11 00:03:36 1997 +--- contrib/DeskTop/dtmisc.c Sun Aug 10 21:39:07 1997 +*************** +*** 46,60 **** + */ + + int +! printicn(icn, len) + u_char *icn; + short len; + { + u_char *p; + int i, j, k; + u_long data1, data2, mask; + +! if (len != 256) + return; + + for (i = 0, p = icn; i < 32; i += 2, p += 8) { +--- 46,61 ---- + */ + + int +! printicn(icn, typ, len) + u_char *icn; ++ int typ; + short len; + { + u_char *p; + int i, j, k; + u_long data1, data2, mask; + +! if (typ != 1) + return; + + for (i = 0, p = icn; i < 32; i += 2, p += 8) { +*** contrib/DeskTop/dumpdt.c.orig Mon Aug 11 00:02:59 1997 +--- contrib/DeskTop/dumpdt.c Sun Aug 10 23:29:57 1997 +*************** +*** 1,7 **** + /* + * dump CAP desktop files .IDeskTop and .ADeskTop + * +! * Copyright (c) 1993, The University of Melbourne. + * All Rights Reserved. Permission to publicly redistribute this + * package (other than as a component of CAP) or to use any part + * of this software for any purpose, other than that intended by +--- 1,7 ---- + /* + * dump CAP desktop files .IDeskTop and .ADeskTop + * +! * Copyright (c) 1993-1997, The University of Melbourne. + * All Rights Reserved. Permission to publicly redistribute this + * package (other than as a component of CAP) or to use any part + * of this software for any purpose, other than that intended by +*************** +*** 10,15 **** +--- 10,16 ---- + * + * djh@munnari.OZ.AU + * 15 February 1993 ++ * 10 August 1997 + * + * Refer: "Inside Macintosh", Volume 1, page I-128 "Format of a Resource File" + * +*************** +*** 23,28 **** +--- 24,32 ---- + struct adt adt; + struct idt idt; + ++ u_char last_creat[4] = { 0, 0, 0, 0 }; ++ u_char last_ftype[4] = { 0, 0, 0, 0 }; ++ + main(argc, argv) + int argc; + char *argv[]; +*************** +*** 95,101 **** + exit(1); + } + +! printf("\nICN# Mappings from %s/%s\n\n", argv[1], IFILE); + + for ( ; ; ) { + if (read(fd, &idt, sizeof(idt)) < sizeof(idt)) +--- 99,105 ---- + exit(1); + } + +! printf("\nIcon Family Mappings from %s/%s\n\n", argv[1], IFILE); + + for ( ; ; ) { + if (read(fd, &idt, sizeof(idt)) < sizeof(idt)) +*************** +*** 106,111 **** +--- 110,123 ---- + break; + } + ++ if (last_creat[0] != '\0' ++ && (bcmp(last_creat, idt.creat, 4) != 0 ++ || bcmp(last_ftype, idt.ftype, 4) != 0)) ++ printf("\n\n"); ++ ++ bcopy(idt.creat, last_creat, 4); ++ bcopy(idt.ftype, last_ftype, 4); ++ + printf("creat"); + printsig(idt.creat); + printf("ftype"); +*************** +*** 116,130 **** + printsig(idt.userb); + } + printf("\n"); +- + if ((len = read(fd, icon, ntohl(idt.isize))) != ntohl(idt.isize)) { + fprintf(stderr, "bad icon length %d\n", ntohl(idt.isize)); + break; + } +! +! printf("\n"); +! printicn(icon, len); +! printf("\n"); + } + + close(fd); +--- 128,138 ---- + printsig(idt.userb); + } + printf("\n"); + if ((len = read(fd, icon, ntohl(idt.isize))) != ntohl(idt.isize)) { + fprintf(stderr, "bad icon length %d\n", ntohl(idt.isize)); + break; + } +! printicn(icon, idt.itype, len); + } + + close(fd); +*** contrib/DeskTop/Makefile.orig Wed Sep 25 00:08:49 1996 +--- contrib/DeskTop/Makefile Tue Aug 12 00:50:35 1997 +*************** +*** 1,5 **** +--- 1,7 ---- + all: builddt dumpdt + ++ CFLAGS=-I../.. ++ + builddt: builddt.o dtmisc.o + ${CC} -o builddt builddt.o dtmisc.o + +*** contrib/DeskTop/README.orig Mon Aug 11 00:13:36 1997 +--- contrib/DeskTop/README Mon Aug 11 00:13:01 1997 +*************** +*** 3,14 **** + + The University of Melbourne + djh@munnari.OZ.AU +! February, 1993 +! version 1.0.2 + + + 'dumpdt' dumps the content of the .IDeskTop and .ADeskTop files in the +! specified volume. usage: + + dumpdt volumename + +--- 3,14 ---- + + The University of Melbourne + djh@munnari.OZ.AU +! August, 1997 +! version 1.0.3 + + + 'dumpdt' dumps the content of the .IDeskTop and .ADeskTop files in the +! specified CAP AUFS volume. usage: + + dumpdt volumename + +*************** +*** 41,47 **** + for sensible data first. Always keep copies of existing .?DeskTop files. + + +! Copyright (c) 1993, The University of Melbourne. + All Rights Reserved. Permission to publicly redistribute this + package (other than as part of CAP) or use any part of this software + for any purpose other than as part of the original distribution must +--- 41,47 ---- + for sensible data first. Always keep copies of existing .?DeskTop files. + + +! Copyright (c) 1993-1997, The University of Melbourne. + All Rights Reserved. Permission to publicly redistribute this + package (other than as part of CAP) or use any part of this software + for any purpose other than as part of the original distribution must diff --git a/cap60.patches/extnd.patch b/cap60.patches/extnd.patch new file mode 100644 index 0000000..b82f9fe --- /dev/null +++ b/cap60.patches/extnd.patch @@ -0,0 +1,280 @@ +Patch #: none yet +Type: operational change +Priority: none +Modification: add support for AFP2.2 extended 8-byte size/free fields +IMPORTANT: +IMPORTANT: This patch assumes CAP at patch level 198 plus asip.patch +IMPORTANT: plus cicon.patch +IMPORTANT: +IMPORTANT: This is an interim patch only. You will need to keep this +IMPORTANT: patch file in order to reverse the code changes before patch +IMPORTANT: 199 can be applied without error. +IMPORTANT: +File: cap60/applications/aufs/afpvols.h +File: cap60/applications/aufs/afpvols.c +File: cap60/applications/aufs/afpos.c + + +*** applications/aufs/afpvols.h.orig Thu Dec 11 17:00:38 1997 +--- applications/aufs/afpvols.h Thu Dec 11 17:00:41 1997 +*************** +*** 40,45 **** +--- 40,47 ---- + sdword v_bdate; /* volume backup date */ + dword v_size; /* size of volume in bytes */ + dword v_free; /* free bytes on volume */ ++ byte v_esize[8]; /* size of volume in bytes */ ++ byte v_efree[8]; /* free bytes on volume */ + } VolEntry, *VolPtr; /* user volume table */ + + #define NILVOL ((VolPtr) 0) +*** applications/aufs/afpvols.c.orig Thu Dec 11 16:57:43 1997 +--- applications/aufs/afpvols.c Thu Dec 11 16:58:22 1997 +*************** +*** 70,75 **** +--- 70,77 ---- + PAKB(VolPtr,P_DWRD,v_free,VP_FREE), /* free bytes */ + PAKB(VolPtr,P_DWRD,v_size,VP_SIZE), /* size in bytes */ + PKSB(VolPtr,P_OSTR,v_name,VP_NAME), /* name of volume */ ++ PKSB(VolPtr,P_BYTS,v_efree,VP_EFREE), /* extended free bytes */ ++ PKSB(VolPtr,P_BYTS,v_esize,VP_ESIZE), /* extended total bytes */ + PACKEND() + }; + +*** applications/aufs/afpos.c.orig Thu Dec 11 17:01:58 1997 +--- applications/aufs/afpos.c Tue Jan 13 15:09:25 1998 +*************** +*** 2410,2415 **** +--- 2410,2516 ---- + } + + /* ++ * set the extended volume size ++ * parameters defined by AFP 2.2 ++ * ++ * size, free and blocks are 32-bit numbers. ++ * We want to end up with a 64-bit number in ++ * network-byte-order. ++ * ++ * For the moment we note that block-sizes ++ * are usually a simple power of two so we ++ * just bit-shift the original numbers. ++ * ++ */ ++ ++ void ++ extendedVolSize(v, size, free, blk) ++ VolPtr v; ++ dword size, free, blk; ++ { ++ int off; ++ int i, j; ++ int shift; ++ ++ bzero((char *)v->v_esize, sizeof(v->v_esize)); ++ bzero((char *)v->v_efree, sizeof(v->v_efree)); ++ ++ switch (blk) { ++ case 1: ++ case 2: ++ case 4: ++ case 8: ++ case 16: ++ case 32: ++ case 64: ++ case 128: ++ off = 0; ++ break; ++ case 256: ++ case 512: ++ case 1024: ++ case 2048: ++ case 4096: ++ case 8192: ++ case 16384: ++ case 32768: ++ off = 1; ++ break; ++ case 65536: ++ case 131072: ++ case 262144: ++ off = 2; ++ break; ++ default: ++ /* set some arbitrary number */ ++ v->v_esize[4] = 0x80; ++ v->v_efree[4] = 0x40; ++ return; ++ break; ++ } ++ ++ /* ++ * initialize the array in network byte ++ * order. If the multiplier is 1, 256 or ++ * 65536 there is nothing else to do. ++ * ++ */ ++ v->v_esize[7-off] = size & 0xff; ++ v->v_esize[6-off] = (size >> 8) & 0xff; ++ v->v_esize[5-off] = (size >> 16) & 0xff; ++ v->v_esize[4-off] = (size >> 24) & 0xff; ++ ++ v->v_efree[7-off] = free & 0xff; ++ v->v_efree[6-off] = (free >> 8) & 0xff; ++ v->v_efree[5-off] = (free >> 16) & 0xff; ++ v->v_efree[4-off] = (free >> 24) & 0xff; ++ ++ if (blk == 1 || blk == 256 || blk == 65536) ++ return; ++ ++ /* ++ * now bit shift each group of bytes ++ * ++ */ ++ shift = (blk < 256) ? blk : ((blk < 65536) ? (blk/256) : (blk/65536)); ++ ++ for (i = 1; i < 20; i++) { ++ for (j = 0 ; j < 8; j++) { ++ v->v_esize[j] <<= 1; ++ v->v_efree[j] <<= 1; ++ if (j < 7 && (v->v_esize[j+1] & 0x80)) ++ v->v_esize[j] |= 1; ++ if (j < 7 && (v->v_efree[j+1] & 0x80)) ++ v->v_efree[j] |= 1; ++ } ++ if (shift == (0x0001 << i)) ++ break; ++ } ++ ++ return; ++ } ++ ++ /* + * OSErr OSVolInfo(VolPtr v) + * + * Update volume information for volume pointed to by v. +*************** +*** 2448,2453 **** +--- 2549,2555 ---- + # endif SOLARIS + #endif USESTATFS + time_t sometime; ++ void extendedVolSize(); + void scaleVolSize(); + + if (stat(path,&buf) != 0) /* directory exists? */ +*************** +*** 2484,2490 **** + v->v_attr &= ~V_RONLY; /* clear read-only */ + } + +! if ((bitmap & (VP_FREE|VP_SIZE)) == 0) + return(noErr); /* naught else to do */ + + /* All the following is good and fine unless: (a) the volume */ +--- 2586,2592 ---- + v->v_attr &= ~V_RONLY; /* clear read-only */ + } + +! if ((bitmap & (VP_FREE|VP_SIZE|VP_EFREE|VP_ESIZE)) == 0) + return(noErr); /* naught else to do */ + + /* All the following is good and fine unless: (a) the volume */ +*************** +*** 2516,2521 **** +--- 2618,2625 ---- + dqblk.dqb_bhardlimit != 0) { /* make sure not unlimited */ + v->v_size = dqblk.dqb_bhardlimit*512; + v->v_free = (dqblk.dqb_bhardlimit-dqblk.dqb_curblocks)*512; ++ extendedVolSize(v, dqblk.dqb_bhardlimit, ++ dqblk.dqb_bhardlimit-dqblk.dqb_curblocks, 512); + scaleVolSize(v); + return(noErr); + } +*************** +*** 2532,2537 **** +--- 2636,2643 ---- + dqblk.dqb_bhardlimit != 0) { + v->v_size = dqblk.dqb_bhardlimit*512; + v->v_free = (dqblk.dqb_bhardlimit-dqblk.dqb_curblocks)*512; ++ extendedVolSize(v, dqblk.dqb_bhardlimit, ++ dqblk.dqb_bhardlimit-dqblk.dqb_curblocks, 512); + scaleVolSize(v); + return(noErr); + } +*************** +*** 2548,2553 **** +--- 2654,2660 ---- + v->v_size = ubuf.f_tfree*1024; + } + v->v_free = ubuf.f_tfree*1024; ++ extendedVolSize(v, ubuf.f_tfree, ubuf.f_tfree, 1024); + scaleVolSize(v); + return(noErr); + } +*************** +*** 2558,2563 **** +--- 2665,2671 ---- + v->v_size = fsbuf.f_frsize * fsbuf.f_blocks; + /* limiting factor: cannot report on overutilization of a volume */ + v->v_free = (fsbuf.f_bavail < 0) ? 0 : fsbuf.f_frsize * fsbuf.f_bavail; ++ extendedVolSize(v, fsbuf.f_blocks, fsbuf.f_bavail, fsbuf.f_frsize); + scaleVolSize(v); + return(noErr); + } +*************** +*** 2567,2572 **** +--- 2675,2681 ---- + v->v_size = fsbuf.f_bsize * fsbuf.f_blocks; + /* limiting factor: cannot report on overutilization of a volume */ + v->v_free = (fsbuf.f_bfree < 0) ? 0 : fsbuf.f_bsize * fsbuf.f_bfree; ++ extendedVolSize(v, fsbuf.f_blocks, fsbuf.f_bfree, fsbuf.f_bsize); + scaleVolSize(v); + return(noErr); + } +*************** +*** 2581,2590 **** +--- 2690,2701 ---- + v->v_size = fsbuf.f_fsize * fsbuf.f_blocks; + /* limiting factor: cannot report on overutilization of a volume */ + v->v_free = (fsbuf.f_bavail < 0) ? 0 : fsbuf.f_fsize * fsbuf.f_bavail; ++ extendedVolSize(v, fsbuf.f_blocks, fsbuf.f_bavail, fsbuf.f_fsize); + #else /* __386BSD__ || __osf__ */ + v->v_size = fsbuf.f_bsize * fsbuf.f_blocks; + /* limiting factor: cannot report on overutilization of a volume */ + v->v_free = (fsbuf.f_bavail < 0) ? 0 : fsbuf.f_bsize * fsbuf.f_bavail; ++ extendedVolSize(v, fsbuf.f_blocks, fsbuf.f_bavail, fsbuf.f_bsize); + #endif /* __386BSD__ || __osf__ */ + scaleVolSize(v); + return(noErr); +*************** +*** 2598,2603 **** +--- 2709,2715 ---- + v->v_size = 0x1000000; /* some random number */ + v->v_free = 0x1000000; /* same random number */ + #endif SIZESERVER ++ extendedVolSize(v, 0x1000000, 0x1000000, 512); + scaleVolSize(v); + return(noErr); /* all ok */ + } +*************** +*** 2694,2699 **** +--- 2806,2812 ---- + struct fs_data buffer[NUMGETMNTBUF]; + struct fs_data *bp; + int nbytes = sizeof(struct fs_data)*NUMGETMNTBUF; ++ void extendedVolSize(); + void scaleVolSize(); + + if (!oldgetmnt) { +*************** +*** 2723,2728 **** +--- 2836,2843 ---- + /* bfreen must be "good" in that it cannot go below 0 when */ + /* out of space -- it is unsigned! */ + v->v_free = ((getuid() == 0) ? bp->fd_req.bfree : bp->fd_req.bfreen) * 1024; ++ extendedVolSize(v, bp->fd_req.btot, ++ (getuid() == 0) ? bp->fd_req.bfree : bp->fd_req.bfreen, 1024); + scaleVolSize(v); + return(noErr); + } diff --git a/cap60.pl198+asip.README b/cap60.pl198+asip.README new file mode 100644 index 0000000..c6ed8b5 --- /dev/null +++ b/cap60.pl198+asip.README @@ -0,0 +1,22 @@ + +This is a very unofficial distribution of CAP, Columbia AppleTalk Package, +that incorporates all the source patches thru 198 plus the +AppleShare-IP patches that were not numbered after that -- mostly +or all done by David Hornsby. + +See ftp://iubio.bio.indiana.edu/util/cap/ + +These unnumbered patches include asip1.patch, extnd.patch, +desktop.patch, cicon.patch, and can be found at David Hornsby's +CAP site, munnari.OZ.AU, if you hunt for them. The latest patch +date I could find for CAP is Aug 7 1998 for the asip and extnd +patches (both are essential for proper CAP use with MacOS 8+). +These are in the mac/unsupported/cap/ directory on munnari. + +If anyone has more recent news of CAP development or patches, +please let me know. I recently updated our CAP service to this +cap60.pl198+asip group, and things seem to be hunky-dory (the +unsupported patches were needed to fix an earlier asip problem). + +-- Don Gilbert, software@bio.indiana.edu, March 1999 + diff --git a/conf.func.lst b/conf.func.lst new file mode 100644 index 0000000..9904897 --- /dev/null +++ b/conf.func.lst @@ -0,0 +1,37 @@ +# $Author: djh $ $Date: 1996/09/10 14:26:51 $ +# $Header: /mac/src/cap60/RCS/conf.func.lst,v 2.11 1996/09/10 14:26:51 djh Rel djh $ +# $Revision: 2.11 $ +# +# see Conf.func.sh for format of file +# +# fill empty fields with x -- some machines strip extra IFS's +N+-,GETOPT,x,getopt,"cap: argument processing" +E,GETOPT,x,x,"cap: will use public domain getopt" +N+-,GETMNT,/usr/include/sys/mount.h,getmnt,"aufs: info on file systems (dec)" +N+-,STATFS,/usr/include/sys/statvfs.h,statvfs,"aufs: info on file systems (solaris)" +N+-,STATFS,/usr/include/sys/vfs.h,statfs,"aufs: info on file systems (sun nfs)" +N+-,STATFS,/usr/include/sys/statfs.h,statfs,"aufs: info on file systems (irix,domainos)" +N+-,STATFS,/usr/include/sys/mount.h,statfs,"aufs: info on file systems (bsd44)" +E,STATFS,x,x,"aufs: no space information on volumes will be available" +N+-,QUOTA,/usr/include/sys/quota.h,quota,"aufs: info on user quota" +N+-,QUOTA,/usr/include/sys/fs/ufs_quota.h,ioctl,"aufs: info on user quota (solaris)" +E,QUOTA,x,x,"aufs: no information on user quotas" +A+,SUNQUOTA,/usr/include/ufs/quota.h,quotactl,"aufs: info on user quota" +O+,SUNQUOTA,/usr/include/ufs/quotas.h,quotactl,"aufs: info on user quota (next)" +N+-,SUNQUOTA,/usr/include/mntent.h,getmntent,"aufs: used by sunquota" +N+-,BSDQUOTA,/usr/include/ufs/quota.h,quotactl,"aufs: used by bsdquota" +E,SUNQUOTA,x,x,"aufs: not using sun/bsd quota system" +N+-,FLOCK,/usr/include/sys/file.h,flock,"afp: file locking" +E,FLOCK,x,x,"afp: don't run with multiple writes on a volume: no file locking" +N+-,LOCKF,/usr/include/unistd.h,lockf,"afp: byte range locking using unistd.h" +N+-,FCNTLLOCKF,/usr/include/fcntl.h,lockf,"afp: byte range locking using fcntl.h" +E,LOCKF,x,x,"afp: don't run with multiple writes on a volume: no byte range lock" +A+,VPRINTF,/usr/include/varargs.h,vprintf,"cap: variable arg printf" +N+-,VPRINTF,/usr/include/varargs.h,vfprintf,"cap: variable arg fprintf" +E,VPRINTF,x,x,"cap: no vprintf available, will do the best we can" +N-+,NORECVMSG,x,recvmsg,"cap: lib: scatter gather recv" +E,NORECVMSG,x,x,"cap: lib: no recvmsg in system" +N-+,NOSENDMSG,x,sendmsg,"cap: lib: scatter gather send" +E,NOSENDMSG,x,x,"cap: lib: no sendmsg in system" +N-+,NOFFS,x,ffs,"cap: lib: ffs - find first set bit" +E,NOFFS,x,x,"cap: lib: no ffs" diff --git a/conf.func.sh b/conf.func.sh new file mode 100755 index 0000000..977b5bb --- /dev/null +++ b/conf.func.sh @@ -0,0 +1,345 @@ +#!/bin/sh +# $Author: djh $ $Date: 1996/09/10 14:21:04 $ +# $Header: /mac/src/cap60/RCS/conf.func.sh,v 2.9 1996/09/10 14:21:04 djh Rel djh $ +# $Revision: 2.9 $ +# +# CAP function configuration script. This ain't perfect, but it's a start +# execute with /bin/sh Configure if your system won't run it (ksh is okay +# too). +# +# Takes a function description list and outputs a set of cpp or m4 defines +# that tell us whether the various items are defined +# +# Usage: Conf.func.sh ["m4"|"cpp] +# m4 - causes m4 output, cpp - cpp output +# of library or libraries to search for function defs +# function list file in following format: +# comments start with "# " +# First field: type - see below +# Second field: define name +# Third field: name of include file +# Fourth field: function name to lookup in name list +# Fifth field: Reason for needing it +# +# type is one of: +# AND condition +# A+ - postive match: must match conditions +# A- - negative match: mustn't match conditions +# OR condition +# O+ - postive match: must match conditions +# O- - negative match: mustn't match conditions +# negative and postive matches are conditions on a "N" line +# N+ - last entry in a list of needed matches: output true definition +# N- - last entry in a list of needed matches: output false definition +# N+- - says to output a true if match else false definition +# N-+ - is the reverse of N+- +# by outputting true or false - it means outputting an uncommented +# vs. commented definition +# N - last entry in a list of needed matches: output nothing +# E - End of list - default value: don't output definition +# +# Conf.func.sh does the following: +# If C, then keep going until next N line matching "N" line +# iff all preceeding "C" lines match +# If "N" line has matched, scan to next "E" line +# If no "N" lines match, then put commented defintion out and +# tell via comment +# +NLIST=$1 +FLIST=$2 +OTYPE=$3 +OFILE=$4 +# debug option - don't bother compiling if set +FULLCHECK=$5 +fullcheck=1 +if [ -n "${FULLCHECK}" ]; then fullcheck=0; fi +# see if they gave us a grep +if [ -z "$PGREP" ]; then + PGREP=grep +fi +if [ -z "${OFILE}" ]; then + echo "No output file" + exit 255 +fi +if [ "$OTYPE" != "m4" -a "$OTYPE" != "cpp" ]; then + echo "Type out of output must be m4 or cpp" + exit 255 +fi +if [ "$OTYPE" = "m4" ]; then + m4out=1 +fi +if [ ! -f ${FLIST} ]; then + echo "Function list ${FLIST} not found" + exit 255 +fi +if [ ! -f ${NLIST} ]; then + echo "Name list ${NLIST} not found" + exit 255 +fi +echo +echo "Conf.func.sh - find what functions cap wants are available" +echo "Tries to do this by reading the list ${FLIST} that tells" +echo "what functions to look for and the necessary include files." +echo +echo "Names are searched from the namelist passed in ${NLIST}" +echo "produced by nm. If a function is found in the name list," +echo "a simple program is compiled and loaded to ensure that it is there." +echo +echo "Analyzing name list output" +grep "Symbols from" < ${NLIST} > /dev/null 2>/dev/null +rc=$? +if [ $rc -eq 0 ]; then + if [ `uname` = "SunOS" ]; then +# must be Solaris 2. SunOS 4 has no "Symbols" in nm output + echo "nm output has \"Symbols from\" in it, and it looks like a" + echo "Sun Solaris system, will grep for function name at end of line" + gc="$" + else + if [ `uname` = "unix" ]; then + if [ `uname -r` = "4.0" ]; then + echo "nm output has \"Symbols from\" in it, but it looks like a" + echo "Sys VR4 system, will grep for function name at end of line" + gc="$" + fi + else + if [ `uname` = "IRIX" -a `uname -r` -ge "5.0" ]; then + echo "nm output has \"Symbols from\" in it, but it looks like an" + echo "SGI IRIX system, will grep for function name at end of line" + gc="$" + else + echo "nm output has \"Symbols from\" in it, assuming" + echo "System V style, will grep for function name followed by space" + gc=" " + fi + fi + fi +else + echo "BSD style, will grep for function name at end of the line" + gc="$" +fi +echo +if [ $fullcheck -eq 1 ]; then + echo "Temporary files: /tmp/cfs$$.c, /tmp/cfs$$" +else + echo "Won't compile, because we are testing" +fi +if [ -f ${OFILE} ]; then + echo Will overwrite ${OFILE} +else + echo Will create ${OFILE} +fi +echo +echo "[Hit carriage return to continue]" +read ans +trap " +echo Exiting... Wait +if [ -f /tmp/cfs$$.c ]; then rm -f /tmp/cfs$$.c; fi +if [ -f /tmp/cfs$$ ]; then rm -f /tmp/cfs$$; fi +exec < /dev/tty +IFS=$oldifs +exit 255 +" 2 +exec < ${FLIST} +# save file seperators +oldifs=$IFS +IFS="${IFS}," +# foundit takes three values: +# 0 - include and/or call not found +# 1 - found a "N" entry +# 2 - found entry for a "C+","C-" line +# 3 - bad match on a "C+" or "C-" +foundit=0 +echo +if [ -f ${OFILE} ]; then + rm ${OFILE} + echo Overwriting ${OFILE} +else + echo Creating ${OFILE} +fi +echo +while read type what inc call comment +do +# comment + if [ -z "$type" -o "$type" = "#" ]; then + continue; + fi +# map x to empty + if [ "$call" = "x" ]; then call=""; fi + if [ "$inc" = "x" ]; then inc=""; fi + if [ -n "$call" ]; then callmsg="${call} - "; else callmsg="" ; fi + if [ -n "$inc" ]; then + case "${confos}" in +# special case EP/IX in bsd43 environment + "epix") + inc="/bsd43${inc}" + ;; +# special case NEXTSTEP + "next") + inc=`echo ${inc} | sed -e 's%/usr/include%/usr/include/{ansi,bsd}%'` + ;; + esac + fi +# end of list + if [ "$type" = "E" ]; then + if [ $fullcheck -eq 0 ]; then echo "At end: $foundit"; fi + if [ $foundit -ne 1 ]; then + echo Defaulting to "$comment" + fi + foundit=0 + continue + fi + case "$type" in + "N"|"N+"|"N-"|"N+-"|"N-+") + ;; + "A+"|"A-") +# check conditions failed + if [ ${foundit} -eq 3 -o ${foundit} -eq 1 ]; then continue; fi + ;; + "O-"|"O+") +# if foundit is 2, then a matching condition, since we are or +# we just continue. 1 implies previous n match in group and so +# continue there too + if [ ${foundit} -eq 2 -o ${foundit} -eq 1 ]; then continue; fi + ;; + *) continue;; + esac +# if foundit is 2, then we have a good continuation sequence + if [ $foundit -eq 2 ]; then + also="Also c" + else + also="C" + fi +# if foundit is one, then we have matched a previous "N" line +# and output is always commented +# also the case when the previous line was a condition that failed +# if foundit 3 and we have "N" then reset foundit + if [ $foundit -eq 3 -o $foundit -eq 1 ]; then + case "$type" in + "N+"*|"N-"*) + if [ $m4out ]; then + echo "# ${callmsg}${comment}" >> ${OFILE} + echo "# define([X_${what}],1)" >> ${OFILE} + else + echo "/* # define ${what} */ /* ${callmsg}${comment} */" >> ${OFILE} + fi + if [ $foundit -eq 3 ]; then foundit=0 ; fi + continue + esac + fi +# now to the guts +# just to pretty print + if [ -n "${inc}" -o -n "${call}" ]; then + if [ -n "${call}" -a -n "${inc}" ]; then + echo "${also}hecking for existence of ${call} and ${inc}" + else + echo "${also}hecking for existence of ${call}${inc}" + fi + fi +# if include there or empty + good=1 + if [ -x "${inc}" -o "${inc}" = "" ]; then + good=0 + else + echo "${inc}" | grep '{' > /dev/null 2> /dev/null + rc=$? + if [ ${rc} = 0 ]; then +# ${inc} has the form "head/{A,B,...,Z}/tail". + head=`echo ${inc} | sed -e 's/\([^{}]*\){.*/\1/'` + list=`echo ${inc} | sed -e 's/[^{}]*{\([^}]*\)}.*/\1/'` + tail=`echo ${inc} | sed -e 's/[^{}]*{[^}]*}\(.*\)/\1/'` + for i in `echo ${list} | sed -e 's/,/ /g'`; do + if [ -f "${head}${i}${tail}" ]; then + good=0 + break + fi + done + else + if [ -f "${inc}" ]; then good=0; fi + fi + fi +# if call and include okay + if [ $good -eq 0 ]; then + if [ -n "${call}" ]; then + ${PGREP} "${call}${gc}" < ${NLIST} 2>/dev/null >/dev/null + good=$? + if [ $fullcheck -eq 1 ]; then + if [ $good -eq 0 ]; then +# special case ffs() for gcc + if [ "${call}" = "ffs" ]; then + echo "main(){int i; ffs(i);}" > /tmp/cfs$$.c + else + echo "main(){$call();}" > /tmp/cfs$$.c + fi + ${ccompiler} -o /tmp/cfs$$ /tmp/cfs$$.c ${libs} >/dev/null 2>/dev/null + good=$? + else + echo "$call not found in namelist" + fi + fi + else + good=0 + fi + fi + msg="" + if [ -n "$call" ]; then msg="$call "; fi + if [ -n "$inc" ]; then msg="${msg}and "; fi + if [ -n "$inc" ]; then msg="${msg}${inc} "; fi + if [ -n "$msg" ]; then msg="${msg}for "; fi + if [ $good -eq 0 ]; then + case "${type}" in + "A+"|"O+") echo "TRUE: match $msg$comment"; foundit=2;; + "A-"|"O-") echo "FALSE: match $msg$comment"; foundit=3;; + *) echo "Found $msg$comment"; foundit=1 ;; + esac + else + case "${type}" in + "A+"|"O+") echo "FALSE: no match $msg$comment"; foundit=3;; + "A-"|"O-") echo "TRUE: no match $msg$comment"; foundit=2;; + *) ;; + esac + fi +# output match + if [ $foundit -eq 1 ]; then + case "$type" in + "N+"*) + if [ $m4out ]; then + echo "# ${callmsg}${comment}" >> ${OFILE} + echo "define([X_${what}],1)" >> ${OFILE} + else + echo "# define ${what} /* ${callmsg}${comment} */" >> ${OFILE} + fi + ;; + "N-"*) + if [ $m4out ]; then + echo "# ${callmsg}${comment}" >> ${OFILE} + echo "# define([X_${what}],1)" >> ${OFILE} + else + echo "/* # define ${what} */ /* ${callmsg}${comment} */" >> ${OFILE} + fi + ;; + esac + continue + else +# failed: output second + if [ "$type" = "N-+" ]; then + if [ $m4out ]; then + echo "# ${callmsg}${comment}" >> ${OFILE} + echo "define([X_${what}],1)" >> ${OFILE} + else + echo "# define ${what} /* ${callmsg}${comment} */" >> ${OFILE} + fi + fi + if [ "$type" = "N+-" ]; then + if [ $m4out ]; then + echo "# ${callmsg}${comment}" >> ${OFILE} + echo "# define([X_${what}],1)" >> ${OFILE} + else + echo "/* # define ${what} */ /* ${callmsg}${comment} */" >> ${OFILE} + fi + fi + fi +done +IFS=$oldifs +trap 2 +rm -f /tmp/cfs$$.c /tmp/cfs$$ +exec < /dev/tty diff --git a/conf.sysv.lst b/conf.sysv.lst new file mode 100644 index 0000000..060b08f --- /dev/null +++ b/conf.sysv.lst @@ -0,0 +1,41 @@ +# $Author: djh $ $Date: 91/02/15 20:46:01 $ +# $Header: conf.sysv.lst,v 2.1 91/02/15 20:46:01 djh Rel $ +# $Revision: 2.1 $ +# +# List of system call/include file prereqs for System V. Used +# by conf.sysv.sh to generate a list for inclusion into sysvcompat.h +# (Needs to be manual so can be checked and possibly customized). +# +# see conf.func.sh for format of file +# +# make sure empty fields have null entry "x" for systems that strip +# multiple deliminators +# +O-,ISINDEX,/usr/include/strings.h,index,"if no index or strings.h" +O-,ISINDEX,/usr/include/strings.h,rindex,"or no rindex then use use system v funcs" +N+-,B2S_STRING_MAPON,x,x,"use string.h and strchr, strrchr" +E,B2S_STRING_MAPON,x,x,"else use bsd rindex, index, and strings.h" +O-,ISMEM,x,bcopy,"if no bcopy" +O-,ISMEM,x,bcmp,"or no bcmp" +O-,ISMEM,x,bzero,"or no bzero, then use system v funcs" +N+-,B2S_BSTRING_MAPON,x,x,"Use memcpy, memcmp, memset" +E,B2S_BSTRING_MAPON,x,x,"else use bsd bcopy, bzero, bcmp" +N-+,USETIMES,/usr/include/sys/resource.h,getrusage,"use times not rusage" +E,USETIMES,x,x,"SYSV: use times instead of getrusage" +N-+,NOWAIT3,/usr/include/sys/wait.h,wait3,"no wait3, use wait" +E,NOWAIT3,x,x,"SYSV: no wait3" +N-+,NODUP2,x,dup2,"no dup2" +E,NODUP2,x,x,"SYSV: no dup2" +N-+,NOLSTAT,/usr/include/sys/stat.h,lstat,"no stat" +E,NOLSTAT,x,x,"SYSV: no lstat for symlinks" +N-+,USERAND,x,random,"use rand,srand not random" +E,USERAND,x,x,"SYSV: use srand, rand not random" +N-+,USEGETCWD,x,getwd,"use getcwd not getwd" +E,USEGETCWD,x,x,"SYSV: use getcwd, not getwd" +N-+,NOUTIMES,x,utimes,"use utime not utimes" +E,NOUTIMES,x,utime,"SYSV: use utime not utimes" +A+,HAVESETPG,x,setpgrp,"BSD: have setpgrg" +N-+,NOPGRP,x,killpg,"missing setpgrp or killpg" +E,NOPGRP,x,x,"SYSV: missing setpgrg or killpg" +N-+,NOVFORK,x,vfork,"novfork, use fork" +E,NOVFORK,x,x,"SYSV: no vfork in system" diff --git a/conf.sysv.sh b/conf.sysv.sh new file mode 100755 index 0000000..9fdc74f --- /dev/null +++ b/conf.sysv.sh @@ -0,0 +1,191 @@ +#!/bin/sh +# $Author: djh $ $Date: 1994/10/10 08:54:05 $ +# $Header: /mac/src/cap60/RCS/conf.sysv.sh,v 2.2 1994/10/10 08:54:05 djh Rel djh $ +# $Revision: 2.2 $ +# CAP System V configuration aid shell script. This ain't perfect, +# but it's a start +# +# execute with /bin/sh conf.sysv.sh if your system won't run it (ksh is okay +# too) +# +# Usage: conf.sysv.sh [output file name] +# +mydir=`pwd` +PCAT=/bin/cat +PGREP=grep +if [ -f /usr/ccs/bin/nm ]; then + PNM="/usr/ccs/bin/nm -p" +else + PNM=/bin/nm +fi +ccompiler=cc +export PGREP +# define to sh or /bin/sh if shell scripts can't be "executed" for some reason +USESH="" + +needfcntldoth=0 +usechown=0 + +echo "This is the CAP System V configuration aid script. This will" +echo "attempt to help you generate "define"s suitable for inclusion in" +echo "netat/sysvcompat.h for a particular machine" +echo +echo "Please refer to NOTES and PORTING before you run if you haven't" +echo "already" +echo +echo + +if [ -f /bin/uname ]; then + uname > /dev/null 2>/dev/null + sysv=$? +else + sysv=1 +fi +if [ $sysv -ne 0 ]; then + echo "Your system is probably not a System V based machine, but" + echo "we shall proceed in any event" +fi +echo +echo "Seeing if we need to include fcntl.h for definitions normally" +echo "found in under BSD" +echo "Temporary files: /tmp/csv$$.c, csv$$.o" +echo "[Hit carriage return to continue]" +read ans +trap " +echo Exiting... Wait.. +if [ -f /tmp/csv$$.c ]; then rm -f /tmp/csv$$.c; fi; +if [ -f csv$$.o ]; then rm -f csv$$.o fi +exit 255" 2 +if [ -f /usr/include/sys/file.h ]; then + exec > /tmp/csv$$.c + echo "#include " + echo "main(){int i = O_RDONLY|O_WRONLY|O_RDWR|O_TRUNC;}" + exec > /dev/tty + ${ccompiler} -c /tmp/csv$$.c > /dev/null 2>/dev/null + rc=$? +else + rc=1 +fi +if [ $rc -ne 0 ]; then + echo + echo "We will include to get mappings for O_RDONLY, etc" + echo "since they don't seem to be in " + needfcntldoth=1 +else + echo + echo "Shouldn't need fcntl.h" +fi +if [ -f /tmp/csv$$.c ]; then rm -f /tmp/csv$$.c; fi +if [ -f csv$$.o ]; then rm -f csv$$.o; fi +trap 2 +echo +echo "Seeing if we can use chown to give away files" +echo "Temporary files: /tmp/csv$$.c, xsv$$" +echo "[Hit carriage return to continue]" +read ans +trap " +echo Exiting... Wait... +if [ -f /tmp/csv$$.c ]; then rm -f /tmp/csv$$.c; fi; +if [ -f xsv$$ ]; then rm -f xsv$$ fi +exit 255" 2 +exec > /tmp/csv$$.c +echo "main(){int u=getuid(),g=getgid();u++;exit(chown(\"/tmp/csv$$.c\",u,g));}" +exec > /dev/tty +${ccompiler} -o xsv$$ /tmp/csv$$.c > /dev/null 2>/dev/null +rc=$? +if [ $rc -eq 0 -a -f xsv$$ ]; then + ./xsv$$ + rc=$? +else + rc=1 +fi +if [ $rc -eq 0 ]; then + echo + echo "Yes, we can use chown to give away files." + usechown=1 +else + echo + echo "Can't use chown" +fi +if [ -f /tmp/csv$$.c ]; then rm -f /tmp/csv$$.c; fi +if [ -f xsv$$ ]; then rm -f xsv$$; fi +trap 2 +echo +echo "Checking for various system calls and required header files for" +echo "System V compatibility" +echo "Temporary files: defines.tmp (not erased), /tmp/csv$$" +echo +echo "[Hit carriage return to continue]" +read ans +echo +trap " +echo Exiting... Wait... +if [ -f /tmp/csv$$ ]; then rm -f /tmp/csv$$; fi; exit 255" 2 +if [ -f /lib/386/Slibc.a ]; then + echo "Getting name list from /lib/386/Slib[cx].a..." + ${PNM} /lib/386/Slibc.a > /tmp/csv$$ + ${PNM} /lib/386/Slibx.a >> /tmp/csv$$ +else + echo "Getting name list from /lib/libc.a..." + ${PNM} /lib/libc.a > /tmp/csv$$ +fi +names=$? +if [ $names -ne 0 ]; then + echo "Couldn't get the name list!" +else + echo "Done, running function configuration" + ${USESH} ./conf.func.sh /tmp/csv$$ conf.sysv.lst cpp defines.tmp + rc=$? + if [ $rc -eq 1 ]; then + if [ -z "${USESH}" ]; then + sh conf.func.sh /tmp/csv$$ conf.sysv.lst cpp defines.tmp + fi + fi + echo "Done." +fi +rm -f /tmp/csv$$ +trap 2 +# now setup +if [ -z "$1" ]; then + of=sysv.cpp +else + of=$1 +fi +echo +echo "[Hit carriage return to continue]" +read ans +echo +if [ -f ${of} ]; then + echo "Getting ready to overwrite existing ${of}" +else + echo "Getting ready to create ${of}" +fi +echo "[Hit carriage return to continue]" +read ans +echo "Creating ${of}" +exec > ${of} +echo "If all the defines are commented out, then you really don't need" +echo "to put the output into sysvcompat.h" +echo +cat defines.tmp +if [ $needfcntldoth -eq 1 ]; then + echo "# define NEEDFCNTLDOTH /* if need fcntl.h for O_... */" +else + echo "/* # define NEEDFCNTLDOTH */ /* if need fcntl.h for O_... */" +fi +if [ $usechown -eq 1 ]; then + echo "# define USECHOWN /* sysv allows us */" +else + echo "/* # define USECHOWN */ /* sysv allows us */" +fi +exec > /dev/tty +echo "${of} configured" +echo +echo "Done. ${of} now contains a set of defines suitable for inclusion" +echo "in sysvcompat.h. You should include them with an ifdef approriate" +echo "for your machine" +echo +echo "If all the defines are commented out, then you really don't need" +echo "to put the output into sysvcompat.h" + + diff --git a/contrib/AppManager/Makefile b/contrib/AppManager/Makefile new file mode 100644 index 0000000..bc516ff --- /dev/null +++ b/contrib/AppManager/Makefile @@ -0,0 +1,13 @@ +all: aufsmon aufslock + +aufsmon: aufsmon.c + cc -o aufsmon aufsmon.c + +aufslock: aufslock.c + cc -o aufslock aufslock.c + +clean: + rm -f *.o aufsmon aufslock + +spotless: + rm -f *.o *.orig aufsmon aufslock diff --git a/contrib/AppManager/README b/contrib/AppManager/README new file mode 100644 index 0000000..c8aa3dd --- /dev/null +++ b/contrib/AppManager/README @@ -0,0 +1,93 @@ +Application Manager v1.0 - CAP 6.0 +---------------------------------- + +The Application Manager controls the number of times an Application +may be run. This is most useful in restricting use to the number of legal +copies of software. The Application Manager uses a new argument to aufs: + + aufs -A + +The file contains information of the format + +/full/Path/To/Application1:N +/full/Path/To/Application2:M +/full/Path/To/Application3:O +... + +where N/M/O are integers that specify the number of times that each +application may be run. The full path is to the DATA fork. If you +specify the character flag 'P' after the number, the file will be +protected from simple Finder copying. + +EG: +/mac/servers/studeApplications/Word 4.0/Word:20P + +limits Word to 20 simultaneous uses. The file cannot be Finder copied +(copy protection can be broken by a determined user, run control cannot). +NB: If the maximum run count is reached, file copying will fail, this is +independant of the state of the protection flag. + +When the run limit is reached, a Mac user attempting to start the +Application receives a message which varies somewhat with system version ... + +6.0.2 "The following application is busy or damaged + ." + +6.0.5 "The file could not be + opened/printed (the file/folder is locked)." + +7.0 "The Application program could + not be opened, because it is locked." + +There is no need for extra software to be loaded onto the Mac, this is +strictly a UNIX AppleShare server modification. The basic functionality +for the Application Manager exists on SUN, ULTRIX and SGI machines. It is +known to not work under HP-UX. + +The Application Manager is included in AUFS with the m4.features define +APPLICATION_MANAGER. This can be edited within Configure or edited into +an existing m4.features and 'gen.makes' rerun. + +There are a couple of tools, 'aufsmon' which lists the files in +together with the number of time each is open, it can also optionally list +the process IDs of the running aufs'. The second is 'aufslock' which can be +used simply to add another lock from the UNIX end, it aids testing. It is +IMPORTANT to note that the filename specified to aufslock must be for the +resource fork, IE: include "/.resource/". + + aufsmon [-fpv] [-s N] + + -f print a formfeed before each section + -p print the process IDs of the locking aufs + -v be verbose, print a 'bitmap' of the locks + -s N sleep for N seconds (default 10 seconds) + is the same file provided to aufs with -A + + aufslock [ ] + + is the actual file (resource fork!!). + is an optional numeric argument specifying the + byte to lock, otherwise the next free is used. + +Caveats +------- + +The only major problem I have found is with TMON (2.8) and System 6.0.2 and +6.0.5. The machine simply bombs on locked applications. Removing TMON restores +normality. These are only 1 Mb machines so I suspect a memory problem. + +A minor problem is due to a probable bug in the lockf() implementation +(SunOS only so far). Any aufs server that isn't running with the Application +Manager will see a block on the file if more than one other person and the +modified aufs has the application open. This could be seen as a feature. +You shouldn't be using two aufs servers on the same directory tree anyway! + +NOTE CAREFULLY: +Locks are neither set nor checked if the aufs session has write permission +on the application resource fork. I recommend that once set up, write +permission for owner, group and other is removed from the application's +resource fork. Since the Application Manager works by using read locking +on the resource fork, copying or writing the application on a server with +the file in use is a no-no. + +The AM uses fcntl(2) locking, see the manual entry for more information. diff --git a/contrib/AppManager/aufslock.c b/contrib/AppManager/aufslock.c new file mode 100644 index 0000000..5c72ff8 --- /dev/null +++ b/contrib/AppManager/aufslock.c @@ -0,0 +1,95 @@ +static char rcsid[] = "$Author: djh $ $Date: 1993/11/23 09:01:24 $"; +static char rcsident[] = "$Header: /mac/src/cap60/contrib/AppManager/RCS/aufslock.c,v 2.3 1993/11/23 09:01:24 djh Rel djh $"; +static char revision[] = "$Revision: 2.3 $"; + +/* + * aufslock [ ] + * + * Add an advisory shared lock to a single byte of (mark it busy). + * This is a program to assist in testing the CAP/AUFS Application Manager. + * NB: must specify a path to the resource fork of the Application. + * + * Copyright (c) 1991, The University of Melbourne + * djh@munnari.OZ.AU + * September 1991 + * August 1993 + * + */ + +#include +#include +#include +#include +#include + +#ifdef apollo +# include +#endif apollo + +main(argc, argv) +int argc; +char *argv[]; +{ + int fd, i; + void dolock(); + + if (argc < 2 || argc > 3) { + printf("usage: %s [ ]\n", argv[0]); + exit(1); + } + + if ((fd = open(argv[1], O_RDONLY, 0644)) < 0) { + perror("read()"); + exit(1); + } + + printf("Locking %s", argv[1]); + dolock(fd, (argc == 3) ? atoi(argv[2]) : -1); + + /* hang around to keep the fd open */ + + for (;;) + sleep(3600); +} + +void +dolock(fd, byten) +int fd; +int byten; +{ + int i, qty; + struct flock flck; + + if (byten == -1) { + for (i = 1; i <= 128 ; i++) { + flck.l_type = F_WRLCK; + flck.l_whence = SEEK_SET; + flck.l_start = i+4; + flck.l_len = 1; + if (fcntl(fd, F_GETLK, &flck) == -1) { + printf("lock test failed at %d\n", i); + exit(1); + } + if (flck.l_type == F_UNLCK) { + byten = i; + break; + } + } + if (i > 128) { + printf("no free locks\n"); + exit(1); + } + } + + flck.l_type = F_RDLCK; + flck.l_whence = SEEK_SET; + flck.l_start = byten+4; + flck.l_len = 1; + + if (fcntl(fd, F_SETLK, &flck) == -1) { + printf("FAIL @%d\n", byten); + close(fd); + exit(1); + } + printf(" (byte %d)\n", byten); +} diff --git a/contrib/AppManager/aufsmon.c b/contrib/AppManager/aufsmon.c new file mode 100644 index 0000000..b21cd49 --- /dev/null +++ b/contrib/AppManager/aufsmon.c @@ -0,0 +1,212 @@ +static char rcsid[] = "$Author: djh $ $Date: 1994/10/11 07:28:06 $"; +static char rcsident[] = "$Header: /mac/src/cap60/contrib/AppManager/RCS/aufsmon.c,v 2.4 1994/10/11 07:28:06 djh Rel djh $"; +static char revision[] = "$Revision: 2.4 $"; + +/* + * aufsmon [-fpv] [-s N] + * + * Display the number of uses (eg: copies of an application running) + * for each file named in the file. The running aufs server + * must have had 'APPLICATION_MANAGER' defined and have been started with + * the -A option. + * + * Options: + * -v verbose, print a '1' if byte in range is locked, else '0' + * -p verbose, print the process ids of the locking process + * -f formfeed, print a ^L at the start of each group of files + * -s N sleep for N seconds between printouts, default 10 seconds + * + * Copyright (c) 1991, The University of Melbourne + * djh@munnari.OZ.AU + * September 1991 + * August 1993 + * + */ + +#include +#include +#include +#include +#include + +#ifdef sun +#ifdef __svr4__ +#define SOLARIS +#endif __svr4__ +#endif sun + +#include + +#ifdef apollo +#include +#endif apollo + +struct flist { + char *filename; + int protected; + short incarnations; + struct flist *next; +}; + +#define PERIOD 10 + +struct flist *head, *newp; /* file list */ +int verboseflag = 0; /* more detail */ +int processid = 0; /* show process ids */ +int formfeed = 0; /* show a ^L */ +int period = PERIOD; +char *progname; + + +main(argc, argv) +int argc; +char *argv[]; +{ + char buf[MAXPATHLEN*2]; + FILE *fp, *fopen(); + void checklocks(); + int num, protect; + char *cp; + + head = NULL; + progname = *argv; + + while(--argc > 0 && (*++argv)[0] == '-') { + for(cp = argv[0]+1 ; *cp != '\0' ; cp++) { + switch (*cp) { + case 'f': + formfeed++; + break; + case 'p': + processid++; + /* fall thro' */ + case 'v': + verboseflag++; + break; + case 's': + if (--argc > 0) + period = atoi(*++argv); + if (period == 0) + period = PERIOD; + break; + default: + usage(); + break; + } + } + } + + if (argc != 1) + usage(); + + if ((fp = fopen(*argv, "r")) == NULL) { + perror(progname); + exit(1); + } + + /* read the file contents */ + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (buf[0] == '#') /* comment, ignore */ + continue; + if ((cp = (char *)rindex(buf,':')) == NULL) { + printf("%s: bad format in %s\n", progname, *argv); + exit(1); + } + *cp++ = '\0'; + if ((num = atoi(cp)) <= 0) { + printf("%s: illegal count in %s (%d)\n", progname, *argv, num); + exit(1); + } + protect = ((char *)index(cp, 'P') == NULL) ? 0 : 1; + if ((newp = (struct flist *) malloc(sizeof(struct flist))) == NULL) { + perror(progname); + exit(1); + } + if ((newp->filename = (char *) malloc(strlen(buf)+12)) == NULL) { + perror(progname); + exit(1); + } + if ((cp = (char *)rindex(buf,'/')) == NULL) + continue; /* just ignore it */ + *cp++ = '\0'; + strcpy(newp->filename, buf); + strcat(newp->filename, "/.resource/"); + strcat(newp->filename, cp); + newp->incarnations = num; + newp->protected = protect; + newp->next = head; + head = newp; + } + fclose(fp); + + for ( ; ; ) { + if (formfeed) + putchar(014); + for (num = 0, newp = head ; newp != NULL ; newp = newp->next) { + if ((cp = (char *)rindex(newp->filename, '/')) == NULL) + cp = newp->filename; + else + cp++; + printf("%-16s\t%4d\t", cp, newp->incarnations); + checklocks(newp->filename, newp->incarnations); + if (newp->protected) + printf(" (no copy)"); + printf("\n"); + num++; + } + if (num > 1) + putchar('\n'); + fflush(stdout); + sleep(period); + } +} + +/* + * check the file 'name' for a shared advisory lock + * on a single byte in the range 1 - maxm + * + */ + +void +checklocks(name, maxm) +char *name; +int maxm; +{ + int i, fd, qty; + struct flock flck; + + if ((fd = open(name, O_RDONLY, 0644)) < 0) { + perror(name); + return; + } + for (i = 1, qty = 0 ; i <= maxm ; i++) { + flck.l_type = F_WRLCK; + flck.l_whence = SEEK_SET; + flck.l_start = i+4; + flck.l_len = 1; + if (fcntl(fd, F_GETLK, &flck) != -1) { + if (flck.l_type == F_UNLCK) { + if (verboseflag) + if(!processid) + printf("0"); + } else { + if (verboseflag) + if (processid) + printf("%d ", flck.l_pid); + else + printf("1"); + qty++; + } + } else + printf("F"); + } + close(fd); + printf(" [%d open]", qty); +} + +usage() +{ + printf("usage: %s [-fpv] [-s N] \n", progname); + exit(1); +} diff --git a/contrib/AsyncATalk/INSTALLATION b/contrib/AsyncATalk/INSTALLATION new file mode 100644 index 0000000..b2c27af --- /dev/null +++ b/contrib/AsyncATalk/INSTALLATION @@ -0,0 +1,50 @@ +NOTE: This software currently only works in conjunction with a Webster +MultiPort Gateway or UAB from CAP 6.0. To install async appletalk using +UAB, read the UAB documentation and follow steps 0, 2, 6, 7, 8 & 9 below. +YOU DON'T NEED TO RUN asyncad WITH UAB + +To install for use with a hardware gateway +------------------------------------------ + +0. Read the file 'README' for an overview of the way Async Appletalk works. +1. Compile and install async and asyncad (eg: in /usr/local/cap). +2. add a line such as the following to /etc/services (NB: 750 is not an +officially assigned port). + +aabroad 750/udp # async atalk broadcast + +3. create an AppleTalk Net Number for each host that is to run asyncad and +add lines to /etc/atalkatab to map the network number to the host IP address. +(You must be using the modified atalkad.c available via ftp from munnari.OZ.AU) + +170.26 A 128.250.1.26 unimelb-Async # async net running on murtoa + +4. add another entry to /etc/atalk.local to identify the Net Number and +Zone Name for Asynchronous AppleTalk on this host. This must be the fourth +non-comment line in the file, after the one that identifies the NIS host. + +# asyncnet asynczone +170.26 unimelb-Async + +5. Add an entry to /etc/rc.local to run the daemon at boot time (it may be +necessary to put this line before 'portmap'. It is possible for portmap to +steal ports in this range). + +/usr/local/cap/asyncad + +6. On your Macintosh, install the 'Network' adev that normally comes with +the 'EtherTalk' driver. This software is available from Apple. + +7. Copy 'Async AppleTalk' into the System Folder. + +8. Connect a serial line to the Macintosh Modem Port and a modem/host port. + +9. Open the Control Panel and select the ICON 'Async ATalk'. Choose a +serial line speed and 'Connect'. Follow the normal login procedure and run +/usr/local/cap/async. The login window will disappear. Selecting the +Chooser (probably twice consecutively on a Mac Plus at 1200 baud) should +show the normal AppleTalk services. + +I recommend that at least System Sotware version 6 be used. + +Problems, bugs to djh@munnari.OZ.AU diff --git a/contrib/AsyncATalk/Makefile b/contrib/AsyncATalk/Makefile new file mode 100644 index 0000000..2e7fd28 --- /dev/null +++ b/contrib/AsyncATalk/Makefile @@ -0,0 +1,56 @@ +# +# Makefile for Asynchronous AppleTalk, UNIX end +# +# djh@munnari.oz.au, October 1988 +# Copyright 1988, 1991, The University of Melbourne +# + +# +# if you define BROAD, you can use async (setuid root!) +# for one user and don't need to have asyncad running +# +# CFLAGS=-DBROAD +CFLAGS= +LFLAGS= +CAPLIB= -lcap +# CAPLIB=-Wl,-L/usr/local/lib -lcap +O= +PROGS = async asyncad +DESTDIR = /usr/local/cap + +all: ${PROGS} + +async: async.o atalkdbm.o $(O) + cc ${LFLAGS} -o async async.o atalkdbm.o $(O) $(CAPLIB) + +async.o: async.c async.h macros.h + cc ${CFLAGS} -c async.c + +asyncad: asyncad.o atalkdbm.o $(O) + cc ${LFLAGS} -o asyncad asyncad.o atalkdbm.o $(O) $(CAPLIB) + +asyncad.o: asyncad.c async.h macros.h + cc ${CFLAGS} -c asyncad.c + +atalkdbm.o: atalkdbm.c + cc ${CFLAGS} -c atalkdbm.c + +install: ${PROGS} + cp ${PROGS} ${DESTDIR} + (cd ${DESTDIR}; strip ${PROGS}) + rm ${PROGS} + +tar: + tar cvf async.1.4.tar async.1.4.hqx INSTALLATION README Makefile \ + async.h macros.h async.c atalkdbm.c asyncad.c + +shar: + /usr/local/bin/shar async.1.4.hqx INSTALLATION README Makefile \ + async.h macros.h async.c atalkdbm.c asyncad.c \ + > SHAR/async.atalk.1.4.shar + +clean: + rm -f ${PROGS} *.o core make.log err + +spotless: + rm -f ${PROGS} *.o *.orig core make.log err diff --git a/contrib/AsyncATalk/README.rfc b/contrib/AsyncATalk/README.rfc new file mode 100644 index 0000000..e3b31ab --- /dev/null +++ b/contrib/AsyncATalk/README.rfc @@ -0,0 +1,123 @@ +Asynchronous AppleTalk for UNIX/CAP hosts - Request For Comment. 17/10/88 +------------------------------------------------------------------------- + +The Department of Computer Science at The University of Melbourne has +developed and is currently testing a version of Asynchronous AppleTalk +for remote AppleTalk access via UNIX/CAP hosts. + +This system is based on the code presented in the October 1987 issue of +"Dr. Dobb's Journal of Software Tools" by Richard E. Brown and Steve +Ligett of Kiewit Computer Centre, Dartmouth College, Hanover, New +Hampshire, and contains later protocol extensions. The UNIX code uses some +library routines and database files from the Columbia AppleTalk Package +(CAP) developed by Charlie Kim, Center for Computing Activities at +Columbia University. + +The purposes of this document are to discuss the problems encountered and +the solutions used in implementing Asynchronous AppleTalk capability +on a UNIX host and to solicit comments from interested parties. + +Background: + +Asynchronous AppleTalk replaces the normal Link Access Protocol (LAP) +used on LocalTalk networks with an Async AppleTalk LAP (AALAP) which +sends AppleTalk frames over an async link such as a modem or twisted pair. +To achieve this, one usually needs to purchase extra hardware to connect +to the other end of the link and to bridge to a normal LocalTalk network. +After some discussions and experimentation, it was decided that it was +possible to use a UNIX host which already had ample dial-up facilities +to perform this function. This naturally left some problems to solve ... + +1. How to use the Macintosh to perform the functions of dialling a modem + and allowing a normal login. Most async implementations could + dial modems but fell short of giving a login window. + + SOLUTION: massage the Async code from Dartmouth to provide a window + and speed/parity dialog box. At the same time, it was felt that + the system should conform to current Apple standards for choosing + alternate LAPs, therefore implement as 'atlk'/'adev' resources. + NB: the window currently emulates a VT52. + + STATUS: complete. + +2. How to get AALAP packets onto LocalTalk networks. + + SOLUTION: repackage packets from the serial line as UDP + encapsulated DDP packets (otherwise known as KIP format) and + send them via ethernet to the local gateway (a Multigate in this + case but Kboxes should do). + + PROBLEM: How do we know who the local gateway is and who we are ? + The current solution is to use the file 'atalk.local' with a new + (4th) line. The file now looks like this ... + +--------------------atalk.local------------------------------- +# myNet myNode myZone +93.30 26 unimelb-CompSci # host information +# bridgeNet bridgeNode bridgeIP +93.20 23 128.250.1.23 # bridge information +# nisNet nisNode +93.30 26 # Name Info Server info. +# asyncNet asyncZone +170.26 unimelb-Async # Async Info (***NEW***) +-------------------------------------------------------------- + + See below for explanations of Async information. + +3. How to get packets back to the UNIX host to send via AALAP. + + This is a bit trickier because we need to allow Async Macs to + have different node numbers and KIP/CAP has no mechanism for more + than one node number per host (yet). + + SOLUTION: Map Mac node numbers into a UDP user port range and have + the gateway forward anything destined for node N on an Async network + to the host UDP port = N + PortOffset [PortOffset has notionally + been assigned the value 43520 (0xaa00)]. This port is monitored by + our user process. Additionally, we have to assign a new AppleTalk + network number to our UNIX host and make a suitable entry in the + file atalkatab to tell the gateway of this arrangement. IE: + +------------------portion of atalkatab------------------------ +# Test Asynchronous AppleTalk network +170.26 A 128.250.1.26 unimelb-Async # async net running on munnari +-------------------------------------------------------------- + + Where: + "170.26" is the Async AppleTalk net number on this host + 'A' indicates this is an Async net + "128.250.1.26" is the host IP address. + "unimelb-Async" is the zone assigned for testing the code. + + PROBLEM 1: gateways won't normally do this, we have to modify the + gateway KIP code. The modification is minor but requires the + acceptance and cooperation of the network community. The 'A' flag + is equivalent to the 'H2' flag not currently in use. We have + modified 'atalkad.c' to recognise 'A', the modification is trivial. + + + PROBLEM 2: Broadcast packets! We obviously can't have a gateway + sending to all the 254 possible UDP ports (node numbers). The + current solution to this problem is to assign a Well Known Socket + for broadcast packets (currently 750). An Async daemon running on + the host monitors this port for: + + 1. notification from user processes that they have + started and are using node number N. + + 2. broadcast packets sent from the gateway for + redirection to the port N + PortOffset. + + STATUS of 2 & 3: Complete and testing. + +Please send comments, request for information to ... + + djh@munnari.oz.au + + David Hornsby + Professional Officer + Department of Computer Science + Richard Berry Building + University of Melbourne + Parkville 3052 + Victoria, Australia diff --git a/contrib/AsyncATalk/async.1.4.hqx b/contrib/AsyncATalk/async.1.4.hqx new file mode 100644 index 0000000..00c36af --- /dev/null +++ b/contrib/AsyncATalk/async.1.4.hqx @@ -0,0 +1,475 @@ +(This file must be converted with BinHex 4.0) + +:%f&cH@jM,Q&dB@aV,M%Z0#jcDA3!8dP8)90*9#%!N!4AU`#3"0IX8dP8)3!#!!" +AUh*-BA8"!*!(!J!23A0jEQ-J3A"`E'98B@aV[6)!6,e%!%bp9!#3!`-!5XXH!!X +cH!"+@`!!$B*f!%V&,!-X!3F!(`!"!*!$5PXDB@4PGR9YB@%K!+1hf@HMYpPV!!! +ma!#3"Lrr!*!%9q)!N!M'mJ!!"!J)3!HcJ$N1#X4(X+($Ka!K2JJb*iqE-5##`)( +$TJb9-'c@##!BKN`C1hADK!N$)Z#3!)!P6kCFf4,!5d!4F`)BG5[EQBFmL1NF5V5 +SdD0)NbU&#)8"cS"PcLK`5Y$-'4C1kJ64"`#'&#%-""%-F%B!'!VJ!M%)")#-'M4 +Q$%$J!H"I9L%!XXi9Q&8%$lGSmYC430(LQ#!I3a+%J3@-(kS"b6+!$%$!'50`l0C +j%F#*VJQBP!#*#i2([hpJA0!!F%6I!K!Nm!M!jMN"1$-HB!M`E!$HeRr#*0!&3B0 +ZJ0+94H"CJ-&*UZ3IXVC@!-mc!Q2@59R(B4d0L$8!k)$kJ(b!FZE1+fG&FJi%Q1R +%6L!"J1"%%J!%6#K5XJ)!-$-%3,'1I``3G)B,&!J#4bMm!H#-'3("dL!bCPJ3"J! +&L#2D6`#!mJ-!&bkJ#J`IJ"%3*8)!B5)!MS#3!!3G!-J"!!0392C'3)!S!F8C#&! +$K44"F)%)'26!)BS5JEc""KbN-##%!&Zi33BFlbLa#J"FR)&"JmfF!B'9!)"KKJ, +fK5R&+J%S!)HC3!!M!"!!`'''%8#FB`3r*YLNM`!dJ)"$!$l-!Jb2*m$*33M*!'V +%0hi'S!BN9k)"@38BV+("K38SXD%%(iC)!BP-I'-!!Gr4j`3dRY%AkUKCP8S!$"L +)J)9m@&B"a!e`fN*&)QA#)5!!a2`k6"3$'L-%H,,!83SBC$4iM"PZf0#I,@p)1S3 +!A,JKaJPi10V@&%18#JF%@rCRM"NZ$$'J,"FD!&C!%'KC)!#PR#'""*80p!F$!de +`KJ(m!K#"'63@1biB%#J!*a#d`R($5lB3#i!a[`UMA!$Fl$S"R!5XQC869J4%`Tp +U'#"@3"k%`3("Y1$LU"VfP$D20Q!)%)Bc6RL4e44&1%(%%%J%)386q!`8!$C8))) +!"1V9d4S#r*!!--@C$NSX6"J@0#J-`4GRR)J%[!#!JJZAQ$L!$L$dPb8F5S"R#i6 +rH!!#%$TkH3-@!0Kb!Yj`d-U&LImJd#!aChJJ-6%`p#F,1&J%-5!Z-!$5m!em9*D +hLIk3!02J-&**2)bbJ10`E0j#A-N&PrC+h%cDBIil0qD1h(8P''Z@LX#TL31J#c$ +m$&#!&%"m'!i5%(b!UqjCa9#j"`!Sm,308MI)M,dAaRf'$A"%F+%rf%M-$"JUJ*N +P#BdqHL8C$-$*`aS@!Z!22Pj+I!c9dYbCCr8DC*A&%qaeeGS"jSKHIj34P`BYi`` +QB!mJJ(%K!9M!$2Si8`"XjMjGR-%"F*M!pZM`+fC)d!#K'N!!AJ!H$Z!R9!8bJ`# +`m!Lp31!)q"#!-iS33f1!$3!2N!$!"#`!L&J%3"J3#"J#9)J&*G`3!L53!))6T2% +r2%L0DYVB5J"di!4)F!J6EkV,mAL",`(S+f!6'"29[2'VBi!"'`%E'!@S*JljEBm +&Cb$'pV3!"Pr-UekR'q!CC-%!NA!"!,3BJ`+SCJk*+3--S*JA"1,bUf+!J4)0+JD +2&!B!)%LX'1S#!#`Z0!"BP)S0%!#'&-"$Lci#B!#!"&#Tk%!V0J"1$#B+J!XDT)` +c6!Bm)k'&'**i*A8-34d#NB!d*!$-$*5*$Ep5aL9("i[4d3)-bKb3!$,DTXPr8Fd +HB0!&eGc",@qKiCHRK%!'P!!!m!!!(K-kJ``Z"!"QE)'8-ZJ2,C!!X!iB")3")J! +2+qr$KL6Xb4"(!+B"+,##Ir3$13%i6h1H)`#Ub30`CM#J'4!3"JC-0!``!!-"`U! +'-r#M9IdK"5bZ"!Sc3-"a!*!!h(a!35Y5V!N-$*JEZ9TDbB&NK@LS2&V5GV@dJ&c +PKTf*!`3JN!"&1*c"!MH%J42L!05PhR!#5k@D05#!![c89$"K'"3&-JN+%%4K&XL +3!%SSD3@+UhiXC!$SfUjZb"fGeB%)6dK#&DB!JVV+)!+VT%!3!f))JZR'M4Eb!m, +QGBJa%68-1K#0&3D%M+a%!KNd8!%!0+#%)2bV"N'BE-eNi%!*m)LFCR!!"!B!"J9 +FL"rQb1)I`-#1#X!T!a3!""d1FGD!b%)0N`eYCE8%#`Q3!-BdU+'!%R#VJBPLGV+ +aeHL[NT'LR'8&D%8B`K+U)!83J#`JiM-$#M+CLJRJ43!cb1`!0SBKP1)""Q%c3!M +Z)`!K"-B![fV'KI5K$ZI@J@Fq5d)6U!ZbIqMM"YeLJqiJ)!%C5&B$%N1'a*,4JmS +Ci"rq'*QM6%B32,4!#2kPJb%-2&N#Ki%4DjU3!"Nid+"NQ)%#*(#"Ie[f-R4-`3d +30S-b&P3c1(K#02Bp3K'Si)3H1k%P$TJ4!"S!J#$M"aB!m!)!c*!!!"1j!!!bN!! +!-!"J!3"JS!3i83!!&V#%J"$J0+Hj,J#1ji[I@!-@1e+!N!"d",2-T%B1RG(&!0b +3!,iB%b"TFKJ!$+SUJ$9Ji$HNL!#F"-!i3H-R!TNe!#FCd9bh0S&R9""#%+T!K05 +`"`6J'))q"N!-"VaNb3333KS!+B-8!")'!4$#'5`4"$BJ3"T$3,82H(%D*U6$RM- +#3`,ENqP0@k,8Tdle'53a"$Bi`0DiCX#X6j-LMF*J!%))ekMC))#RR!B*`+!-!,` +8L9(6)KV2BB-)PR#%H33%&1RD0"5!63Y3IH-!"-"a(F4-J`M)!!#e#))!-[X!D33 +"(8U3!-%1)'##@3MKhZK)3VlhM80r!ecJ"$Di$!4KAbF8!3Y8'))8`K83CK`Y!"3 +iC`$S8P9b"!!8!4!`(J!CJ"Ta)J#)#!!4!0"0BBLYQmB!!!ZkUBbZG0-C!+$"9[, +KM"!JiiGEf3F*M"i!Bb4G#%aA4Y*e`(5Fe5%%!d,"$l'ZmkCc(GA+i$S0!Q$e[5! +!"R`3!#3%J!-F"Q!Ik5"Hf"!3L$rY)bYS9c[EhEi2#)60,VTJJ"UbXT8"d%&j!X' +2T[@KJI8#`!#)Cji#h$XM8-b!m*ZQ!LKSJ(Hk%#!84U!9$T!!3i#Y!!!(S$K#jr& +6+Y5V[Ji"B)&rcNN!*0X6&%ff-J#J)'8KS`$,MlGbPdm*!!KFqaa6CJ!DrP%2+jp +BqA@Cl"PJ!(f'D1!-+&J4**6r$hXiRkr3hiId*3$pINKr+S(Kb[A0SJC0"m!9-$# +e,c6p$dE!)JSmiN)LP"!&Q)&"#YcAI"K`92'((a*394c!F2m!#d(`"MTJD"`!"M4 +3J!*`J!$3!JV)J'p!!)E@!P3&!#m`,rp!J(54$aEi!KRBDJCJ!&@PC5m3+UININ! +J!U2'(G%!!R44'9a3#Kl`,k84CR9!+h6J'IjJ$R9L2hQK#riJ!d4)F8V)#SY51[J +KK9`JK3JJK4`J-F#3!'!,*M(1%!ERa30BGPV%8$k&dKCRL!BST9)GdP+8FaS!`!Y +8+)9J-$S4-b$(B%LAT$S5X`bHFcMHic&e`"jJ!!bH`!m!`#R'J33h3J#H33)Q!!- +[J"rHJ)J5%%)'m$'km!rF-#p$e$%%B!U&B(T-Z)R+8'IqS%*K3!!eXe4UP35`NMi +q4@#!X&4,a6J58)YaX&4JJ!jC!3J*3!3%8"R!-!$L8!$"G`$0B`8,8'3"i!$"-!$ +U8!"-`c3)m!!)N!!!%c!3'!)'$K!%!p!0)3!!0E!8jRL1k*L1kVL1l0L1l[L1lGK +IpY!$32!2q0!$H+!!@Y"pU!!&9I!)5+!(!1!*UA!*2I!&%-B!-(BDUFJ!2d!A2*! +!#XM`Jcm!"RE`N!#2*j'P!3mpS*!!rq!*pRJ'Vm)$m-!$@I!)(b)%Tr!#`(8DUF! +(,B#3!2V`!LUQ$kN3#2qB#R4!)Qrb$pY'$c6TAkN`#&Vi+mk!(!@J81NK!$#JB[K +!")"!!%ra!+"!MrEBP"!'#c"Q$b,j)DQJ#9L*$rB&9eFJ0$m'!K04%4H4%4[4%BQ +a"YN#'%$3"RAJ"Qi3"R+3!!BZm!4Di!+6PLe8J!CP!!*9i!CTB!GP)!GcN!!'G*! +!"b$`"QB!!Ne3"Q`J"Qp3"h,J"Q@3!#hpa3lXdABQm'3)J!02jJ")!'2mJ!Tdm!) +$J!Tc`*Sr3!#RfAfcL3qck3kTd!JB13#lL3UEF*X2q3rm%*bTQ3V!54F3J*%3J!U +`J*%"i*bq'C5T13G-i!i!`!(r!!lf"3+6k)McCJAr3!2XB8mJ)*T0@@6B0TXd-)Q +Hd3(JS*,`H3VUk4NF!!SRqC!!m!!%XdN&,!PQrr#5)2!"HI)%lf"PKG12V*N+&B! +%+cG#!q!Crk!$+MQErT!!#T'3!+##!!@28*dcmJpBi9D21B9"J'VB0J6S%!"9'!- +)!!"!`J#C"3KY8"IX%!'9%`!S5U-!X)eGJ+-%%!"HJ+-'%!"IJ+-)%!"JJ+-(%!" +KJ+-D%!"LJ+-B%!"MJ+-L4!BiDJ%"8!BikN9RJ+-4%!"SJ+-A%!"TJ+-&i#Ji+J% +"X!BiQJ%TKk-8%!"YJ+-2%!"lJ+-9%!"mJ+-*%!"qJ+-3%!"rJ+2m%JJiUJ!")!J +iZJ!"3!JikJ!"N!!)10S!!D!)12S(!I!)1,S"!G!(1-S"-)HM(4!!HS#M(K!!FS# +M(a!!Gh"@rq#Cj`!$X1%'p!%#Y-S!0P"9SP99"6!5G4&bHiBI23N!i!!%q2*Xf&C +YNE&Y&(#54)8%J1#92K#Jl+N,!F!1B-!%b1SQ6c%5YK3`6"!A$*TV-,#YbNSIfkB +%!I-(Y*Sh5L#R%3+ZN@QZhETYTVPbcD%,##!),+4pifS!B!!2J3UYf"B%kS#LRl+ +L())(+i)q!p%%*M@Z'r!##Z!C"m!"iaS%Ca%`5'!''L#a&'Za"M#Z3K#bZQ!!r%# +Ja3F'2"!`3K#a+cHa&AZbjM#Z3`!'hK!`4J#c!##c[!%0ieTTb2#XJ)"YNfLb"X! +,'PUG"-!!f@PIP`BE0q)N+M!",#!%DM!"0P!$06S"3UF2&H!)*B!%%q!#+YQIB1B +C'e#b-qX!#Q#b"'!2Uk'bLm3"*UX!(M"F-m*#&f5b$L!1*YX2Q+#KZS&YS8)!hNQ +a*(!MYh1b1+#h$-!M*KX"8X"#*2!&H$H*!!-R!3!JRY'dVJU#62PN#M#E!'#d!(# +IG)%-UC!!"05DK"V!#DEV'4P!!h5EB[l`8@k&JT,B22mi"F%$!+NJ$'Y52%`6&mi +TN533(AA!RIJK!j-i&CR9Z3K3V3Q!"Lbd(V%+!#B`L3a!!T2i*B,!)@S!!J)J"a! +J$U$3Nrr3AUFKLmM3"Nd"*m[J"NS!#C@$$0!!"$#`!irR"1)h%LXDFLhK!S8,$0A +k$dJ!"KC``!R-!UAP`%Ri$fE3V`iJ`D`*SADJSDU@#MJ!J`cJUVDj[5P'(k%j)c$ +3![q!#0JQ"a3!$B&JPIIB"[8RR$e!`iVJAcIm$ii!BE@*JKQX#acJ$#V,!"cUY0@ +aZ`,J[!MJ[!``LDR`!F[VU[a`D6"J!V5+!#4`Ul0CaEJeXabJ"aSULh+U"E+)!'j +!"I[E[rJ"`*C$6NbM!@)$!168GZ5N""3VRDN`#f!JL5VQaFd$SCB!8p+*"&m-S6k +JS@!J!DcM!,$J#X)j85q`!*iK!%6!3YA*!&q@a+h5L#,!Z##JUl$!8*,FZ!bJ"0Q +l'3,`bE8kY3K393(a!&,a!Rcf!FE"`#"JDSk-#T84&k,QY@"3[A`Q"#H3!!SF--A +IqDT4ZmAdX5++N!!+Y0#2XrN2F!X$5J!)'LV*-rX!F-"#raLF#JaMZ$NZ(c!&5kB +&@1N1C#brCfbrDqbrMKM!a9G*edR2*V3DF))#YfQbr["-)V$2-lX2QX$!!!fK+P+ +kiQbbri!"'MTFAJXhFX#dAdB1&3HHm"#e5S!$##!2Cb!&U&#K%T!!#XbJNUQ`"d+ +!#`V!"YqmL$"@(EU`!Crb!GKQ6qLLN!#9m3rbi!BQS!3`aY%JJ*mlN!!+LE$5m,! +$*(d)*)*Y3*d+LY"X!H%-*mQ5B8!()B'4UT!!#PdPa8Z'!"LT!"+*XbMS'4IJ!f' +Y#aFJ""`X"!`k!94NeKK!!N!JeNa`c5`%)3#J###J!"a##$)L!6LJGQY"%#-B%!l +3ehm0#A#!!,!3#'B!"4'!L1flGQeATrYJX[[!$2E%YkaB&h`!ZC)lXrl!"LcNd$C +!J&8V!mRk&!0!c8P)!"#`'LaNfICJ`@Kl'LDE$`+JS5CV!Bl!3JFXdIrJLfl9Ae3 +`RV0+(kS,$cq`RlApRh`3S(NbI8'-$lU3!+!,fU$6$336#Q-9HU%$N!!+JQ$6!3" +K46a4+p*H@8X$61Zd5&$4`Df5RQ%"Z#!%-+B13H!1rk!1,i!!i`S"YCd2RN%"r!$ +ICXd'+NX"lXd0bjc&"-j$X2d))VNLU!!$TifkYDd2BUd+Uk'K%4i3M-#J+fV)'(2 +KBJd,FeZJN@X@EN$K%i$D#!b#,3RJCLd('`lKCK&T6LN%+TB2(9!j%H"I2CkMrl! +21LkF4Gi236lHr[$$,BRKCNd)6!jQ-Ri"GM#EmN$LCUd0*fkJ%)$"-hX2LU$)(Y" +JJ!!)R(MIR,MIrIhI!5i!4IcElS#ird!0pU@iM0Zl&ecK,ilP&Z!,pX6K$'$KAqi +#'JVLTkX')plNmFd,pP6%YL6SRR%2#%#iHRkd-4lI1lPl%"lSHckcpN!1'RUjGh% ++RP%"j*!!b[h&$bKB`JJ!"`(3![9BaE6k!&h-jTqK$)f1iSqqjlZK#rB!#KVUkSm +XR(hENY6FkrB3jSq3!',fk0ldB0c-c0qSbHGqm1HE$ZQqEJ80$FRmi!EZBU3"`3Q +q-Zcm`!B'i#Z`,Tb'R!!+2Zf+rKNmN!$V-f)'NMLc#f!!0U"LrL!%3Y#['R$!"X[ +ZI3d$+mSCNbX('[S9Y&"X$J!'i)$Yp@!1KEk9I#i"eVlV4cZcp8!aMk!%+XD9$S! +!6eBAA'N!bBX-F&Dm!E$H`3fefpZp-f,V&3!)*jN+cIX"eJ!)r5S!d&cc0fm,J%# +Vr`#8Pki,&D!(Q2`0-j+jMbH,,Y!'"1!'%5J$XNJ!ED!$EQ!@9!mR#U"#B)!#X[K +!fFF"XZJ&E@#VCM%93+#R&3)("h!@LBd`F0"N$!!(#K$-'J8($&"M#i"Y6,qj!2$ +d84q"0P$eE8!%@3m'1L#,A@m@e!FR$b3$B#!#CGm'+T!!q*1apR``Vc`3Hf#3!"X +m8(YJJ!'Pd@5,A"T5"P0JB!#Pd@A1*SC&#`arEdpYi!'*E`(TFlaQJ3$T3p0lhc% +0J2FGm`!eCTUbA`HpZlNid3BFN!$i3G3Q5fB@Q3mRBa,-fE2kF#!"03B"ISrmQTX +q+`VeEU$p*$DA)Z$mk61-CS!"J`8%+33"TC9"-!8(&#$mF'!"0EDJappIf(#H#Y" +JHZSIB)-'Nf6qJ6BJJ#U''a"!#r!2[!%"9!$CL3"q'A(3B*M'2b!($FDHr!0c30q +#@i1"%rQ0c-QS"M0PrS%lk)!FL3qX+*c@B)E4d'X`"B)f!B-[!!LJ!5#!!6B!!@! +$[cIF,0Ti!J$PL6l)J0+8la,4*2S"!@!fkB-ZCZ9bNh6k!GpY$"baPYF$rm&&1`F +N4c3933HJ"aV!#b!!2k!!+%%Qf00bNa)B!L,TT4J!(#6BRU$`kJ*5S!J`J#*!C@l +%2i!'(-SdD+FUq1c1`E5+GML)##3"+X!%SKS!`%mq!+X&U*E`%`D!!JL%61!*(!& +$L!J9i3GJK*9"(L3h(f$)!)!VD'i!kJ08PCcd#T)D-+"TPF%CI%(2)!pX3@4TF8K +!'!L!Nl(DbPSr8!*@b4qm3Y3Q#dq'2E'&k)!Zl-*B1!Y2`j3TDri!!&JPIM!-Hq% +p%9e%b!*BT5ABS'L![SZ&3-`4+5&A-+i#!!dB@F)J'a)K6p!0VD'k#iF#B"8US3p +A$@X50Nb(fY!Ik)%HJ&ZF&Mj3@83J$4ST!E!Q*Y'iZJ$Ud"p`J5qSS,UK1S`(JX! +FfLCd'"$aJ-VL!0YYk&NSm6Edl*SX`%T,m-9jKRkJ#NK!9rKMU'XMFS)m!30'PNE +8"Id!%NJ@V,6N6Q)r%!5+!"!BJN*!#!C"$m!`-H1dDDAZ3lIX@TkE%6X4IJJ%*P! +$YP+,+iVfD%D-Ub@Q"K3!#hJ"h'mVHBB(8,T@$P0dLL!!!P!"31!2BBaBdJ81!#L +T!+HB#P6"#aL,,#!9Q,J+aa"R9Mk!"LV,233%Hf$6$X$31hXfc@R4!cH!!95519a +b$&%EpJ-Yd!0J$$f!-*22TM@!ZdMjCYh3F`-D`#q#3h')%SZ$LU%(4@a&1!+ECRc +S!9kN94,J-IE&8f!1UeKJh)J)S$!12H%%"U#"6CX!Ga%%f$3'Z"G(ScPFJUG4&r! +$F+!D$k-q!!1S`+CGJ)T)!a4L,$3#JZ!QKU51X3rliHPD13"4'r)$%qH8hU%kj!I +Di-!4Y(Bip+bMG&3%4B`LdJ1TB0-%3%9d(iZ29SdhHN!`)&GFi&S#3@Z9aiSBDZS +K343%6)X2F+++PVX89aB,FR-JG`@j-Z!2N!"FjD!$rX!I"$N%q3q#A"V`58%1aJ5 +!)(F'fKH!r!I34XA)TT@M$4Z!!5J"3-!T0KJk!!(j3B-"-d[1+S+!T[J8Sk*`mJ` +0`!BXa4@*&E8L9caGh!e'JS$6e"q"5hjL58`!dTa&T`JMeB$+BJ&El(fi[,UbZ'C +%91-"3A,(i#FJB!P"T&28CK#+"r5a)BN9!JmNN!!$"%!@k$`)K`'iC#TS"9c5-p3 +pK@B"&0RE3SYUNK1`N!#PD-KD()eXN@T5%Y$*HRJRXq*@V&B-J!qS,!a`Z,a6US0 +f@Db,UDDc@'4F%k0d!+P!"%5E(k!#*!!@f&Bfd'!GS"p!m!!!+TJ%fkSF!!+$&3' +3!!%)k*46i%DL!N+`F6b0PBP4Gp"N$3""m#K6`3L3!%kZ#C2&156'(dFB$cKZ##$ +)"B(h!5$p!BqV((R!(qL$#1N2pN'3!"X$!V*bJ)!#+5`6C*!!J`2qS%&@MX`!!)) +F'rJ(%l*bP!%,@6QS!)B-FQ(J(a!!ErN2#N#3!&X$rm!!"$N3m!m13*!!B`+")mL +pJAq3!!!Sj$p3!!lb(bb!#2N2R)5+D3!JJ)EaJe`&!)K!`K411+#U+!'-4!"3`5( +!5!N!&5L#6PJa&`%8Y*La5@25!&9!(bMFim%f(%Bf"3-B9J-'$3'%!9G*XJJ!E"- +-%"%'N!!$1!#',8-#@!$m#`b3!!!d%a&"!6R3!(4Q2GThmJQl'EYa&@VNNaYi(l@ +T%dS!''N13'54HC5HS4e`*mN#+5[!Sh40&"0NdS@-fG61!#T)"+T'eD!#2,!Ld90 +J@%jl!$i5!%[C,93@#D!"XNNB8!0"S#N[*G!%"M*!C8k"3#!0!X%[XC3b)!CJJ01 +e-J0"$!!%988+%!%'-(1JJ"f8"R*6!ZL$ES%hBq(He*6k`'616-!T1!QRi93(QM0 +a,NiNd$JICq5FR$-(!pK"D'!bC4-``!+!S'8H)-kC0cmRkN3"SK0PPXl#H6K4J1T +NR$$-GE)Sf2NUk`)d%!*%!!EB6SqB$J""RK#9d'!33!1qU3i8!!b)!'%$#U`S2A! +FTLF!U*lAX`CU6qlT2@@!'!`'rf`D!)-*%$C!`1+-Jh0`ppK"Da!i@@Fj#!5NFL+ +C6r3*!,$Rq[`PhR-)a)-L&3)5`IeNRKJ!9LS#Nq8JG#8"i!'CSmiY-FC&biT-+QJ +"b+brp%%QQF8SA!$3!YJQ8k*1IG!$%PYGS!I"%h"H4YCT0'0Q!-!#+A48SNiXd%* +AB-ciQd13!$BecQQS+&F6eh40XiNHS!*5J#ZT8jcl-ZbJcLQ!*LNiUKB,##d+M3p +8J+l&!L`AjYS-8j4a-3$*%J#X&X&3D&A)LRV46MD&J%!-@(YDi)hH8$EDY+T+'Ef +LeDYar32'd%+5f3L69G(Z*+%#E!$GP+%Dd*cfL"j1!"9J``iT&Y"KKa3Iq$#[U'T +L+&6k%+`*&Eb$6*S06)!++*AmlS#*bJ$+@9LR&!J%rr1AP-S3%!X@jiVk#Ni#!&` +!@-N05X!3N!!!*##8#J%3`0q!`Gb)"X2!l`""@)SAS!#X9!FX,iT@`6m+,!8T)49 +3KK54hU1Xa8K0T#1&T"*!NLij5RS'+'9Pb+5EY$9edNT*!ZM!"4#P1V48PY*J-$K +6U6TBTDd8A8d"qPBA`)%Y"D9#!!EXdPlk5i0Tr55QZqHB*P2haNbMR39i-Jq!!D# +5K"N3d!FUm30ZJ![)!%5("84!!Q#G)Q"%UB!6LQe'k6Y&"Y-!(U`S&f%,iS8+J!# +$`)6L8'#J3LdPTea4*K5&XP3GZLNljDG%!S2JB`i#+8"4EF"'a3+ZdTJ5J#P!83I +U-Pe`*A1MbY5@bN*GD-bX$#Lc1Jl4@+G5FfMIl+&eJB9'dNPUMe$"+)LT(6@G)J- +4d#P9+X+!!8#!"2!hB3!#K%!d#!E#&!3!$ePN61X#-RfL9T!!S#*+qU$3e)$JND8 +RP'm&"(UR9,'0&+L"0,@TVX"TZNJEUDA%TYUd0RP9!h0#[ak*!JBHY955J%kC)Y# +U#H"[``!%$)&S!%6qUIZ$%h8e[l@Sc"*"RDG4e9j-FQT&PK[DAJ$!!3!3G!!"M,j +BDN`P!"ei!YjSA(%Rd#B%jQP$SkFQX&"Q"438SK#!"*K%h+ci"!"C&!MDJ!Z3!%@ +0`!fB!I`&!!,"1rYImNc!6*Ra1RZQ$)bS()J!!(##mJS!,-%k1cHbU!'i!4,`AH1 +CjG!",#+r1J,p#J$`!MN*'EKP[qjAp!%R0S"GF`-V3Jc)SNd3S`#!'%Km9%!@P3) +cB!A!J"-`23LfSCim-'!$P!!DZ+%LD3Tmf-DJ![BEK*)![`%*p#[Y0Q)C'23C8eT +J6*KBhDJ$qK8"N!!#(eB,r!B5m2R!J#b3!#+`J)'T2$R`"i"")$KDMDXIk)$I!&m +H!5p&CRD12MaA($*NLf`JN!!$@8"-HVXfB!4@K$+!-eSf),#$0Q!@K)!F#!*KPPM +*J58J*KXX0S!%B5-J"!%Ef,m33#D5Cj+PlF`([*&cqQaEL"-!`"!!!%SJ"l5!Q,4 +VUQ"&q!(6``T@a*&!G&SJa%*DlI0ABDa)iUQ9&8Ime5F!BQmX49d4E)1L5YSE#JE +%3)TSQ5F8e6B'+c"F6UL-$@MD`-B5eFS+"!+A'0J+%DCIf3#6a3qF3F!#!e!ZCTQ +XGd!"aY8,',C9%3!dJ@%,!,C#2m#-"BVpP4jpd!r8`C2pSL(+Y8fL"H#VpN9!!!& +P!!2-LaXJ"L4!AiX6)%!5!))B%!41U1R4IJV!R(NpHZ"X2FRY8'A0eErKN!"Z1br +!,IN*#'p!`CjE1T!!EJ8"ZlfKmPB")*ibDJ2FLpGbYJ4ALc'[E$YP&i$$a3%U6aB +C![&ArdM!1S-"L8mf$KScB!*f+j`i",8[m6N!#AYLB-V#R3!ii0f@PYr`6+CY2p! +![*519Yb-#bFL!FHY3l)S!V3"qQB@F)!X#L-iS)r*SNR3"N#Z@6!",(HRS3'BH`B +d`-YYZ&0h268Z,$'ZH%#U!(@2B#[`!efJ8Aj$9lQhKc*@!FZ$LN1+k0HF!k1&B(@ +k&9QfUJL),&Y0G'RYbVaD"DqJ2@&e3@i)@%&pq3rL!F'8"`4c(Jc)SIFXQFq#l$k +,pai%16*JMqEP2cL@J3r)93i9ib`hVj&6-IdJfSbqCl0aX!d%b)%A,IBGV9kR$d` +!i4S!f%B*S&kii3fQS($6A[B%jM%!RaHJFKl[r3"!6qMC!a#fA0[,'3K4E`X"5!% +)i"QNdGbB!RBd&GL#Ba[)G%(6`J*2`"5'82$N$0#F-iJ"Y%V,B!$`@f3QLa*`"FA +(5jLSkf5VLXrp3cIH&a6B6Je+I4[A!+!%e*IjbKP'3(hce3!J"06AZXJC2T!!#KC +"r@fq6$4m09p1d(qE,rkG4!*i!$##5P4mN!"C&!J'03-+")2qdRha@p'K98`$"*! +!Ar&lrQC%#)J#!##A"SDVaFYQ5HFL!1ZVCS!#mh#V4!![dfHbckfXL4MJ*ZV+[A9 +HRdF%1!&pB!@c,5JB!80i!aVK%R$05JBND#a%f"V-M56X$)"`+2!!5GJB'1(S3)4 +jJ4&1!8PB&KKK*C!!K&'"%f0P6D`4SCXNE!PJ`&rENFF2L%dLVMD*RZXNXJ#T`!M +NhdT'#l![-NYq5)"r4Dp!$!UB%,kG`b(+5Cb"91!1CU`!8!4qQ(R*BEiD[Rl!*0) +!e"F,k`)"%*&@!I9&F*ZB$%4L9q8-aK--S(rRPck8"6M!!8"!YfM&Z1B-2$FJN!! +9ci!GQ"Y+S+VF9MIJ*aka'`!#FQ#*b3CA!!@3!!!-f#Yei9mm2$0!"H$-rQ"*2%0 +b)N'XCJ)Z3'M"[QlilJ')(k3Lb%!UN!!%SK8"3!2EDR3HX3Q3!!f+`!4-!6L3!!i +B`#U)aM1(!-`*%5#D"X"I8`3%JjdJJJ%33a5"Uh-#Ni!1"!$[UfN!!#4)a3K!*"A +N51"i*!$PX3"#S+TXJ,*#-#50"-J!dL!B4!&Pm0ld%c"!)5'X#TEL@!8$k$&p#!- +fS"FVJMFJ$S!",KJ!1*F@0`98!)raJaQ!"J-!&caLMh'3!1qa'@#VphKZ(1(-%!, +FK#,`XA"!(lbV9B`2[1mEY6)S4m4#jFj3"`L!AdS#EU!!q+8R8!ISJ&8@!PKC+`X +"VMb%Z+p6$Vp@"['X++Y,Ui*-ICUr)[NX0iqdA$N5`'3*!EIU1T&II!%#j#m2*-9 +b@I`L(LfM!8S`!`J"@H!kR@!Jk*Gcm1S*B@EC!irJY!`R0%"MYLr)JC)4BHd8QIr +"pdA,ilGbM!6,R*4U-#r$@)ijQA9JccbC3c1k)Xf'@35JJqXN!hJC(0j1h3Q"c)e +r*Ni+K`5)!&)Q8*N"I$!*6'8#!&k8i4qmJ6@BJqS#"!!%3,K9j'C'"J5+&racEE- +j!%3"C1"IK!!%L-kUl'ajVdp-I6N!K2)%(f!5R3",V(1S,iQB4$Z!qLE##I#*)C3 +X`,i"#XSDAq3,&DP[5'-"fPF5Td0%I#2mQhqE4"JJ&C3!C+B%C)-M@!0NP5j!JP& +J"2iCF[J%Tm!)J"8[)3+q!!Mi!Zmj!B!#52!$1!BG45MrE2@0&fil%*!!3rS#!X# +VhfeRi53$`NB!F-8#`"A-!2fB$!8!!kJU2LN"R!%KN!#,k3-[hY&`5!!dD!(`S'( +!MNim5RSjL!!e3!+Hp%J`#a"ae2KS2Q1Ni`8#H0,$b#`BJ#H05YV,U'PDEFH,[!! +hS!!J`*pi!F#$["b!*IfJ3elE)3!,3!6mL`P3SqJ$!m!$2k"p13)aB!$iG1**!'j +D!(L!hl!&C)$Rm54A)"N)*b!4ATT(4K-!NF$2q,@hid9`5-$SA)p30R#$#4!BP&% +$Q!'bJ@NiJ!Bp!1c"i4-e4FC4!`!HS'ULJ+6Q"b$J#F5!I)3h-%"L%`9LJJ6%J-# +`'Lb!+,LYM5F`D)'Z%!,i3"'3!!**S-Y`!&F(V12#$!J-9FB"8'!pi#)k0"Gi!V0 +'(4!#*a!0#!)(Q!RX4#!NJ5(J"%B!33!%qFN)a%%Mm+lc%d%i!$("$U58X9!A`-a +6#!!ii3(N!j`!!3S!6VJ!p3!R8)#C"`!@YX*Qf!kl!Gb#K#d"S)$"EJ'`)'&$JB+ +0c6Kf9M6B'aYNIi"q!*d&!MBcf68L!!3#4+5bKe(,&JL"`'@[E*20b`"fcAi&*q! +I9)16(3"H`523fIrD2`RYS8fdLlE42YT)1fNVlDA0Y*Xf(#*X$F"&U5@-%!4H`&X +L#&XV!"5Q`j5B&P0MHNb4D6*9TXZ8Q3C6BEJ)D%!1[!%hF*RQ!&[L#"i"*+`"%'! +(BS"U%!-%)3)!!'6`%&U!'["2)+FKY!4*$@B#JYrf6`6!#+5"MR!!SN!GB%aeT5@ +JB)G!"3!!4blF&qK[RiCp3!#+!"PJ6!HJ-*'"0a#j!F#b&JJYS#%FJ#(3P50h5m! +EYR8)[!%ii*MULNd)#!F!#S5"18!("P0,3#X&S0L8!E`8%FE4-["2KPYcR`B%3!8 +58aY)!hH*$4!!*J#pbd"%)!!NmQN$!1C""[lXdC%#!S%E1B6mP!#+J"a3fh+JVEk +"P4-31J!!'!-''3#SJJ!3"m)h4&"Z#-"a3fiKX,i*`MJ+'Aq#"33!'ad4i)%2B!" +6S!c3!6S![Fq!fqEIl"X!9)$`8""BMU#e8IHl"c5%#8kVC)3c1!lfqb'8*-(0SJ# +!@2!'!cb%MqmGX!#+!"lJ#'(J,M(`Y8d3#J5-`+qd)!!B!Sj8"DT!$3F!(Ga"()H +5e-0rH)f!%FdJ!)5#SSBK#)+*m&8%!8j8EVc"#3L!Qc-R"+%P,%jl!Ji3!33`hqM +EZU3%-C!!Q0#'&m!e"'&&VDM&fF9$J1bZ!fb!$,L"%d"`ChFCi-9d3$#"J!5HPd! +###!#H3Nab3%A3'3)3Sf!iJ&"XTJ312%&V2J6`1+*(!$m!ASf$!L!!4J#-A`-8#D +(`$4#MJK`%)KJ!a!"dTd(,K-)N!!$`jX0X!((G!GL1-'P!k3l#P5"3IJK!S)!H$) +C`#&X!K-15!$!0#!!UhYYDkBa3!GfH82Bj9`KKbF"5il*,m)QE`JK*jGl4$'!#"+ +!%!J$GB!-b0@T9JC#$M3(!#ZR"!#!8i!)i!YHBNaj`#'%(,jG!Tj40Cm#X"`1Y&A +'0!FQJ%2B4UZ""1LTDNi%TKT,%$9dB!kNlSD3!,VGY3p`X!CJ!-b!FN@['`+pGYG +AS#f-LKJJ!mU93rJ#!-"Gh`K#63"N`!4#i`&KjEMV3`!L4J80'$dJ(3"%JS`1!$i +"3LF!1F!'@25'm"cFp5k3!1J&)!ENJ)UH`Ka#kPij'M`9*23R3!E)3%-R#24kj86 +d9M!ULJ"LDPF%!D1[R"Y"#eM&fVEH"(e[+d-!J!q'ZQBkkJ'"A[0Y+a!!3S!"8!" +23$1"!"1`)d'#AB[U!#!Ca)N!8!!51K@i!fp!pM3%!@i$CR8!m(D*QfXl"#-3!2S +k@ImA!f!+T!%mJ0%E`KFSl!$J$33!1Q!!#N!#IqS42+4$pTJ6"bTl%8J$C`!0d!' +pAQ(!13#)14LJ!%b!)R#Aa%"(!!&-i!fF!HJ0!Ui!p"lGGk!Q1)39&DK3!#)i!9Y +EN!"lEFF%Q536CE*-Q%QYRffUhCELGNKi#Jd"h!8U3)!)#%$G9Jd1BFUXU'JJU0b +%!5F)5NCm"i3S&4!%H1KcFm1()&5MVJ!!d!%Lk!'"+3fiE6M`"Z3!`CA[YZS0%0` +`B!I#`1)1!kjp-*Q"qJi#'N!GQ!1$53`iTZ@HYYGffhlEENPZ0`3RN!"i'X,#!!# +9Q`mim[3H%(JlHiF&L+!&0(1f6A$(J$![!m3F"-"b&Dr(PcZ%Gqjc'm(,!8(Z!Qj +%30$AL0ad8h"D43`fr'kRCeV(YaF"&km4i2CE!J(bIBl[prlqh`-m#*KU))"hib8 +k8!INqGTQmILp-F'"`96FQd!B'!23'jE2!66J!TU!3p!b)8Hbd)$e9H%$3$C`m`" +J'!!!!8-0!X!iq&)$3F[)H6T[jc%"HjIcJD(1cqBp$`$Ur$L)mh1qd0Yj@ekZ"8# +M,pF'3"X%"'#3!!p!%,jJhiB#ADAJ!3!)X)(PJ!$*X!+d"'#!$c6!!!J%)q%!-)e +Q5am#J5X1"!%!(*LI&i![CJip`aFV+J86!2hdk80p5k$&#Q$I#$")%"$J"0d"!N6 +!4N%!ma-$*K%41!Ba)"K`!R[6$E""-VJ$65X*1!,DJ3,5!3i!"P1!%c#"%0$84-! +JN!!%##!(4!)B-!4b3#F!"S#J#'5#B&!"-N%"Q$)aB),M!45J")5!&)J!#b!"P)! +83!))J343!P)!%J##&#$`Tk88X!!J3!N)!K%J#+6!!6Mi38-*))&!!!A`3#N)!3% +N&E5%CYm`cB%Hm2F43!F)I)*[m*H!0)!(B)!2*!0a(N'm3!KB"J,J"!J$m$d*P!% +H#!*,!"N)!K!!"8*"#%!a)Km!N!$m)K!#q*d812JV[q",!5-###!!%8J"*3!%+!) +T!!9%J-3R![iH!8L#A4m%P-#q+XBI(m9%JC%rLI#!1iMfNS!$k)!#3!+1!38B")a +!fcZm)R!-5!!`@!6L2J3SJb&!#)D!)cJ!#B!3`)!mN!!$#%%33!4%)"1%!(+!!D+ +!4l3!mX#@*m-HCFXKJ2LT$)L)2RJIc(p20Vm"k2bIR`'%IKMJI4apCAMdVcrkU#0 +3C3eQ4,iQ#'TJULQ''bm%I!B6)!L#3&iAJD2Z#0CeZbB)QQ!+8)(U3K"-!ESQ#,+ +J#I3-(ij+I%'8V0`"`4d3J8Ki"*Ui2JJ#6%!+9'i[!Md(iHmr8ieYA@2rpb1r!d+ +0J!JN8`'mIq`I%2,6D6!4-!$*%!6lMaUk`MMHrr@)6p)9E!-0!ImA%-3!5Sp$m,6 +&!2'!3m#G#B$aK%0!Z!8%-J#Fd""FEK4JppB3('m3B*a9rm%*hT`-%!i8J!&JM5! +$T!-&B+$f$p3)-d!cX[rj!+F"#dMDR4-p3!`)!-`!Ne[p"eb`J,M&rVF$f)!c`#" +3!0D!+q!0H!S8J$[J$AJ,&)!rB"%i!a`$*#!3H!fNJ$BJ$H#r[)"9i#-`"0U!0N! +`J!6DJ$H!ZqB$JS&e3!%)!ri$,F%08+!3"!Q!#e!&5(p-3,l@"Vk"48!F50@X!3d +!pDE@*A!,A!-h"`!#!!a"FhPZBe4PFQdJ4%(d!!!`k!"+L,`!5XXH!!'CKJ"+L,a +!J'9%!%U)[!!+qG`!$B&@!!X`k%#!eFiJ"!!+!!T%4NP-4%e29J%!Sl!+ZD1`#VS +!!$)&!*!'*VB!N!6[T`#3#'BX!!!%#!J!KVb!,`i+R%#`SF1(%#%b#$)RMjXa9-V +)D31#5*!!-J))%M'5K!Q4*NqX0!`CF'6*NbPA4T`CF"3X"ENHbLK!XkI2Rd#$#Ke ++p1%+!h3#"JLJKU!-!$B!k!$J!i#3!!38,@,8f+BS36!"*B!&3''XKE%BaQT!SXF +U'!aX!pJ$)i'YJ,3-i[S$Bm#Z,5GaN!#F!`"-b*NA#&iN!2%'J!3RN!"JI'$bE3! +!,%m3S"!)`)!C!Lp`)1$RT)i6+rri#FB!!i3,!!a)Z!'!3)NU!"l1'"!#"'`!4dj +dQ8#e1EJ*A5m!'"GPA"`*jF*&*6FZbEJd@S#J2(V$`,LPkF,pB(PNjam52$9B'@G +8!JMCm@F%kRLKJUaaCI6Y#bH&a%eU9(3)FFS,22aMi$r'U30#8a5B!!8-pF%&$"' +!6!()1S!!!!3`!0`!a!4''#I##8!%J-8,#`3hJ#%S"RH"-#hUNJ%d-4VJ"!KV!1" +!$%eK!!!E*5#"'a`!3)#$!*!!-*!!B8$r22%1!##B!B-!1!$`3!$la2L!+M"FpSJ +C8X!4!)N6f"!M"3)SN5-$m2R``JSkTP+"%%1!33144L)T"`+b!'+'"M&Z`%UAijR +"JCKNkU!QE'G!!!Bd2r!!J!+T)"-M"Y+-K`S8,lM`haa+$(%'%dUX8ZBC"!3(J5f +P6U$$'BQPU!X(1S`A)`+!eNG"F#@`NYqZZT6JbUqmUL(%E43FLpX,U3D,!ab81%N +@'!S`bkX+[)hAJhX3-1%1!aMm`ii6AS$`KAC13!0#FJLX#`!"[2hJ(J2QSUXZZql +##i5mX0Al5,URr312B"$!B-*XXFe@ffdHQ,&EE`(j%4`*aTJ!*B-N3%%,2`'G!3C +p*Nl-DP0X2R)'Tr8jJ-SF--#T!32Z!G%'%$!$N!#%'cT!`SH'd!!"``kG1E%2!#& +4!-"c,!#3!2655MHpG"$V!-$"#a#mS%"`!1#L(4L*ADe,!!MB1X$%@'6lb,C&HXX +!![q3!%1Z[qN+9T!!Za#3!0!B!lEKjM"[BkNa-3M2,8L@JlC`$-#SEiDX#`N3Y'S +$I-dqJ%5VVmBDR!E-f#UV!ElU1V'ERLm1",'kM-#2XXNb,'h9!J3hJM&QmdX"C3a +!HHkrd)L3!"`"M0%'!`3e"p!'%M8Mi)B32IrmVY"%!r"DPFp"Ab3!93+D@!!U+J$ +'e0HVb!&GL,f![5iB%-"eq1-,i!DkG3J'!Z"hfqCUI!"))S33Bm'KK""QG"fF!CJ +B$`1%-*!!ci`&%4#D!"1NS!B&)J%B39$("+"3-!3!!!Q!H'"bV"BF"Q##!@S!!3! +8U*h2#+'"6*K#!j'3!%$L!F'#E1#!%KVSK$0Bi$`!L!!1`m""A@a!!#HFJ"1Na3! +T#+'('Q$#H"+S"&LJ%&BcR%"r!2!22G"&IMBi!`9kk!$p!D*3rY0&!F3a(XS3)&9 +ek"fmd#"&)E"aIfaNJK"3-!%B#!%9VR,M"1l(4LHi%6Baf&N!*J#%1jCT#2T)!!" +#N!#%#FLJ0'U83A)8!!-k'Z'"5!J'0!,a`0,!)$N'#0jRA%50dJ3-!#m3!!aHSi$ +qr11#`%K1&Ki4+3!J)a9*!-%(`#-#8,J51L(!`1T)i!*rN!$'#k9*j5FR9B9(6'& +I!%L&-2$d!3J!`''e$%#P52!"51j1NT1DL%$k"ai)''!mT4&-Pdb3!*cB*+GaJU" +I#!8J"`L)!a3#!)+"J'LJQL'M$9#Sf6,FS!5Gf6*j3,24d%*L3D1*d!96dL"dc!- +'#d3dPK0P!E8Z#TjrQ'%mB(!!4e%j0Phm``lD-8`UF%!Cf*J50IK3jp&HJ`!6[)B +"-'M"2a$a3$P3B*2EqJFqHY#'Ic!L086pKb,qSBqN1Z)IrR!P2P,jJT+5J"9%p"C +X"%DZC!V!AHeUCh*5m3&Z[M3eFM1B`Q3$'eHUTLPH)`%@Y"-mJ"D2S!KGhN)ea&G +VHS#[ViPCTp!+eq$m!a4JB-"JhcST`b*KDa)!!C`F!!YAT)DFXV+!"FVi,5V5SkY +TE!`"40#BGQf'XUNJ'MN4J$8AS,-1U34VlpDfQB!mi!c9fS`!2L#T!2$P"ES9`JP +5`3'cQJBeUMP(DpL+!,F#+$mUQ`0d8b%#1[e!"4,J!J`'m%"S!#+#%Q$"$f"J394 +-BVX2,-Ghe4%"CS#![!#`8(*333JT$)%"3`!!"VVb$aTjE3"NQ1i)&VXbcK+!!9a +&jR(p33r"m!"`#Z[!cS,!B!N$B!lqU)H&mq!2HeMB$IkiKiA(i!pm@*J+rXL(KF[ +J$heB@!lqf)H&ff"-#i2!(rf`-!Vmi3m,am!Irl#`T`"JB4Mm)`!@6X%rJ1JTl[U +((`!#`C0YX"NL5$Ne10L-%QT*!&3FSTB*3)8LN!$3P#i[iJF$m2)2ZU`)'Y`'!4$ +Uc!0P8"m#C(+60A#2!(U`-b26)mi#H'!`1)B"1H!!U2SFkXi+m!pl`+!Z`"Jd!+! +JK`BJQUN#`Q%!#(3Jmq$KACN'FB2pdq#Q2%BA)N"!Hh3%hH"%`![3A4QAe5bT-#[ +#-+K)K'&`M3I"13!*D#J5+[C3Jjd4),Yi#)#d5%!$1`Z$'S)!VhBK,3-l"f-+JC! +!4L#'S)lXbL!'2VTc)')!L-e)J3J-)-+N'r-2D46lA4,`4l+EMB4R4l[ErJ$dR+f +0E@ec1plI$VFQaeeZ!*`lhITP0c6LE'GJB!%3H3l[[*d0EI#b30r!U(BQqlhYEV- +Ji"FF1,R0M@jeljH+d"!#%@$3m1HN!a!@8bmd"J'0HbZ!"K(JaD3YU)F!X2b",SF +j!'41Fj[6B#HC&!%5TJ'-#HJF"$k53K%B8)4e8p%D'Jp'13)4`IEq("K"McNJCPj +cEYpm#2&!3!"#N!#)U*GF[ra94!m"3!d#cm(!22!(1%!Efp)#0diY-'l!'Rb1pc% +A3J(`3RIA'qmH`-'#rrKcI3+GF8meH1""a3ILZE"iD6XHmSkQYUIX)HkJkX1jG)M +e(&a*$e53!-,ZhMUMZ2LZ!,[44JNUU+-C(0$$Ir#K!S+XihK)m)9NeVkd$+M2)'& +J"X9kl4pF%%,ZB6$miSF@e%#)3BQmS2d!F!'5SQA!CTDr@X05rer(&DSk)8bE@D) +#'lUd@!r8%'qKcRm#RZ,(r,2,e2hc!kT5C4JB4b%r3&+Sm!j9K3VCB!)Ud&lhFe( +UeAA-)!1"&Ja5%!KEafhY&3+ai#-@C%3$"!!A`&rF8!*$)!%Sm)"#!!,0"3`J!!6 +4-!`3m(3J+!56aPrU%(X)0Li+&M!aT9cXK`$Z"hmI)(rd"f4$e8$jYhpFd(rdphp +4j4riB"MA463*L)"TYS$BK3*dF!%3Z&lY4B%EKi%GYi%G5"[a*35cm3rJB))1+!4 +fa))2p))a1)038S-h5%8jk!i(0RXp#&2Vae`@m"S2`!#@!3*G!3"NB)J!i!GZ`!8 +bS!EH*`)*N!"d3B"iLJF-%FKHc$!0m'"")*!!",B!!@HJ!K!`#*[AHGh'"H0P3CL +SLZ(9LJ"JANJ`#'L'#S2!30jR!bVJIIKeJ`5J3YkRJhji9MpSH!VcLX$JAH$P$jp +(4CShHA2Q+6'&HIV%$kQiM)c("FrSK")!K9+&#U1JM*[BAL)!AjX("J8$"#63A-) +!!N)3$F&JKb!J"66M(P#!Jl&(!!PQM)'S-,fR"L!NJSN((`%K*EfBL9)`GScRM)m +(MIHhK26AK%heK!!SKH*)CiN("LJ!!`'3!&jJb!`N!&qm!30!B!,003`J-!64)!c +dD)meNipk+!9")%iReer%b)2T9(M`!aXdd)YF!%3!F!#I33F)m"BKQ)m53!G2%&) +iY#jHi`p#-!9#S"hlXiEZB%CSe(Hd)3(*m3"i%J!eF`KY)!)ed`KZ!"NlF`KjT9$ +03`8!`!8!%'`l3`S!B*Gf8$-(N!#)5H)H$H!''*!!9rM!2#(a'P&KJbVK"idiD6@ +c!@B3%'S`&PT3-jZ3!)KHi!B#!!C-8$1P!#CJX'@p95PJB!0+J!EHKbT6B*TF!!B +UJ"L'*30"S!rrJ!BJ434Zj(d9a3$"GJ'*C`E9`PUk`!pD!&)%)!@Qk3@aq3p%!!B +L%*X!B!,E%jX"B!J9&CCbm!I!%!JE"*cpB!A*+3,D`B,'C6@fK`"HD5A@LCf")!G +"d#Hc!3$Q)!G,d#GYB!+3!+"c!4%%h[8c#1#@)C%d8k%%!+!&!1!-"AUJ`CB8LJ! +!Q#!(@G!R"K33JL!(@J#K!B%+Bm%(-j4iU#Q*AM!@PM#3!,cT"DLLLamD%)B`N!" +2F*V&+BQV'4"fX+&Fd+'i'4"S`"YjPRJjZTTDX+&Hi*Xpa!mP!`E"k+&J3!3pe!q +8%*[qB!8J04pHd`m!!!E`i*`f!!BXJ%-Ed%-#-$#IpJ*FDJiie!4FbJe-+J,5NKB +%`+3B)*l@TdC9!`!,`"*r8$Y38JBBB+Fh)!B5)!H1!3"`!!+5!!Ja%!5*jj`-3+5 +af3rN`),Jpbja1UF"8DF"!3*P)!&fqJCL`!"q+J'!+UL%DUKFX+Lii+MAGfc*X3" +k4!4K@61'd!B%i!CP33*e"31B#3BJ8$-#B!E3QDIZ3CBHF+X1`*PQ8"B-%%J,i4( +ki"M8NTbdS+Mkd!qibPURHTi,d+VZ%3Q`+UYJB!)e%`(eHDXF8$-6B#KJ3!%e-`P +Y3!'hUJ$%UJ#VZUMIXkDbb3ZQ'M$*Y9`+-iK@JRUU"`(FP8%BC68,iNL3!0%HMQ4 +hfN'-m!"D!F-0lK1%!Z)L'N!$AS)UBi%+#H3$%X9T"U)2`B%"4'#afS'a!F%)jf& +"C"B!i1#a6"@b5Q#a4(3'QHN''pZa"@)Jq4#b$%#b*T1C9#!%e#Ldrk"L1a-"6'9 +K%r!2qd#dTq-T15C)&bP8,JZbj1-"8Z@b1dXq%1"+mP#e)HX%-[XN'0!S)p9$Z,) +e(X"RJ!!)rf#Ql[#fL)&$9C1c44XF&-!(4(44qrJ2TD4Jj&PDbb45ZFHa!fZh9RX +"@0!P*CXA!qXe"q!,fT'b&b5*,BZi,Q)&A6+cMVY"N!#,#GTaXiGl)&Yl!5)eD5E +6ZDJ%ZA5J(F4A'K1V#aBJ(ZL(Vq5T-2,c+Q[aD3)!!B5)34hV03`J#d3%"N)J*Le +`M9-N18qQYAJlNJ$!ZF%E(!F!!GSK*T@9'QI!HhEl$ceN!2VJZP,SX+K"H2T+'fi +&YV)V$BbEZY1V#`CJ$&CT@IcJ"V[K@`("#A!!"GR,$faJ!2ZE[)59!1#3!,kBU`X +6B!QEqb3-)#8pG!miB!1H)T9#!&)DF&%44-#G#PrLNk49q3K'4!Y$`!B1!!B&r,M +rB`@6karfS,i@)#@SH`DUqldfB*@MYld)m"T8C!qkN8f9)JF"%*E%L!kJY8bKj"i +ZX+hI)`-e3`"YS!1hbX6ZS3"QN!#C+&!c,i`#fe-cAX!4l3Sc!F!(CQ!"F(!!jrT +ikJJ(#C!!@(#J28G*!(#3!+J#!!F,d%PeB-4GdJE"QTN@!$2@9-9F!c0GdRa%#JF +0`$@&r!"J--Hr"JbH"%S`Nb&Y`!'h#M`EFNfCb3!`iakqb69`%!&m!3GL-FGGkmK +h$-N["!$E+KBb*$-L8-N`3`$AP"D@A$YQi#J+!!F6`-CPmFC`F"CcA!&f($$BX%U +6`QGKr!rB`'F!i!ArS!h-l#RF`-`@m!rH`-`+`)E-c)rL`'I@e$CmeLArB!jUq!r +S`'IZm3rU`,C8a!jmaL(ri!lP$!pm"RRb`'Hb((Pm9MZ0pN"I`*!!-'!$#)!0RI5 +(!L06VN%E-[!D$L$"MT%F2r#4rR&k6qDeUG!)Li9QU6!'@S9J4'c3!h-12K#%AKY +P2%!%58!&6$",bB&$!p"$lr"BY"40S+",)K3I5!)#*md%6h!%,(e"RrE5A[-1$M, +6U9$6(h$641-'Xq65--d!fP'!C!B!`p+pUF!(ZV3CcC3+Vb!C$c6)4$-kA5CGc6) +!k[#6%m#a`L!!JK!3!q#pVD-,r3!03H82D+h@E"d3AC*+`H%2$L!TGee[E'dJ(-, +AZZ!2*K"8r"$BDph@X)&+FHd22'"kQNB$%ea[908XrN!0PGdA-#)!eR,Ba&$CqBI +CS+hCXN$D6'ADSHd2S-!XFU*TS5d!e#!YFR3+VQ32U4!*$$!E!G"SM`N!LV"+Uie +4`G%2r2!Fa+dDaLhAlQ!a-0!AcGd2iP!Ia"e9ddd0LJ!)KP!)K$!)25!%3V@aX## +&Ud-rmP!$8TM@1249DN"*9#09UZ))p%&*UD!+p3d$U3!,M%e9AU-2b#!YC4%3Z0$ +E3pPSC@RJ#'B2EU!"!Q,C!2MCSGd2e0!$,!a9Bd%("Yi!#$kZXr%!M5DV$qlCQAh +FV-!$S`F'6Q$J%)$JZ6SE%K$L'2$JT5hKcG)2KQ$KMCBDh@VJ5fX2#6iEeFcJ$Ri ++%(jk0RlFAU$MpX"84+6DT*INFLd%4'6J[mh$lMSE!J$F!1$D9JlF"["ZJb3%"Vl +PpQ"#*$0BmV!bX#d)XFF(bNbqaR5lY'&K'-B2+iCM&NB(2,ER3'CKDI"+(hCN&RB +'5fERrc!!4'YRRpBX$Q!0l8&*I%B(fDarF'iJ8F@l#`,IAIYN`I%![kETlpdD%%! +&J0$5rZ%2`H%!-39LU[&TQqBel'!"fV&"2*!!3N9!"IRpk6)J,5`J'b8'@K&VH`c +!dVFq"ENZfekc$Yi3e65Y5j(1I'(d$bS!"TqN!T5dkNiJ"`63*r#"!IQG#Uf`klV +J!#l3H`5`0G@#l6#`kQ)`(Ze0CQQpkCm%!DZ1"I&1-[31!U9qkY$K!&41YPTj9Z` +J'&9L8l5"!`cYVfNQA31`CVNY9DlN$KGG5`m[+@Qf#4-I+DR"m9#@#KX[+4"35a# +!#V#3!%hCK!UE!([Z)$9X#&VjFPBdB2$2592'h-LZ4!2Jd3$U)#!qMp[q33-GT!K +0$HY9&4cU`![1lNUkEY9Br3'ENDCRi!1E)YXPT3k@i1bTN!!(X#h-4YmXr`#23!p +9Zfhe5,qr4Ud(EEk`,Fm"ri!#j2X29%$c##q%Mp$H4km,2ErdrY(dRAE9@Adi9!m +&H(re)+$eA%m!XIheIDd+S8EfN9$i`C%1iC[fDrm)@ZAf5!$cSL8%PrL4`$!%k1" +E3K!$(eL633!!J-"Il"!"JZ4EVMp#!4!'X8m!!5!'X@m!!6!'XDpfC"$l"a!!C4$ +l'K!!CK$l'"!!Ca$l!a!!D"$l&K!!D4$l!K!!Da$l%4!!E"$l[0N'X9m!!H!'X5m +"!I!'XCm"34cl&"!!F4$l9r)(X9m"!3!)XCm!!5!)X3m"!6!)XFm!!9!)X8m"6+I +BY`!#!#+)I3iJ!$5#f0F!IN2Xq`-"B",%[Jd3!!*"l1-!!B!4a,i1%!$m31cc!!( +!$X5q$a!!pJ$"qdHd3FV!"LUM)b$!CLJ!,1%I')e+mLlbb5X""d$!8k%AB#!!PX4 +!1!-BB*C!J%!$#!T3+KKTZ`3kT!-T!&)#!LRBJ6d`)&L3!$0!U`E#(hK2N!"!#3L +2J+!)p"r`JL$U`,G3!2K#2r$!@&K%!k%*r!NYe82XJ3R!)8'J$A+!e3%'T)$rZb# +hM!ek$A[3&ci0r[!'Gj!!Ih5Y6l1P[%Bpm&)!S%iJJcYS"2)J)H`Kp8!Ei*!!j53 +,KZ!Ap&c"S4l),mc(Ka$-hM&SF`m)-CHLCi@maMQJ"ha2lVQXI`$iLY$Kq#3PK4q +)JkUbq!4#hZ-(0fVXqB2G4JX&`CIc"d5NIi`&)0*!J!!afR`+CYMGM@B5(,U-C," +B*JX!#!*QU!X3J#CiKSI$I!3%18!0%3!H`)Bd#`b)!4bL!B*$!LK"3'!'D!(kS!k +$J`)!E5UJ(HS#"G#Qe)!k,!&3B!C)!A*S$R&"HjJ"X#BH)K0GN!!![!%l()J*J"i +X#)#)$fH!&6JK-`!+0"-b%a&M9`&`"pK3kK8-bS-*Ja08H`5i5Y2XN!!HS[h%('b +B1h$JIU`1[[80cXK1-Mah`k@04+m4!1C$203#jY!-X%1F5"$4`%idKb6J)*T$*b! +8#5,+!)MQN!!"0*2V%K'$3`&JAarJJ5bEJGFqH0*X%3&S3"m+JG3b@V+L&*J"@4% ++6!UXU!pR!"m3!(!*!T!!4DQ$$QD!'"J"!SS#0"2JSJk&#fm4#2p$%RJeB%!9!e& +MJ!!,83U8*U+K'`M)@%K4NmFYKN0i%"KCi&,b,CSVpbJ""M)"P-!$L5!64!`&!fl +M&L2!'E)J)%5%9%Bd9aPE#'9d)6lL$AL!+1)%c!"Fq$45j02`%+p4%-5F%#%L)@b +%Z5Xem!(qJ46K,jkJpi!!)$BHK&K,l!lTjaM"!1ELZkc%3R--3F3*G)!JN!#dp-N +qQ)J9,G8&"`'J!rDL#E#($h%9[)[[k""[`-l),c8&20i!HbJ@&3"jY!)h)0A"K[G +)$3(!b[N!&m!p1)$hH!(1Sj9iMqDaH8#!paJ%iTBq1!LiqJS-SA)4,-r3%He& +Ym#1G"&X3J"!S,5$J5'!$*G&TC*J5!!5`iJ9pJ'#6!3$!BV!qAL&&UXJ9b5+&!J& +`!8d!#N!"%2!LJ`!9Q*%Y-NIU5"BT0m!!)[N(#J!)R!-M`!mX"JL)!KQ$"G%X%5! +*M-!hq*!!U!)8M!!`B!iSJ`&3$RA!"(`!(`!)USX6L!D"T0(S&bd)+d+!J')!T-" +m%!"3N!!UUN-G)!&)!!3%'aN!RQD!RJ%&+L#C*!!m#Ck%**'d'#,JX+N#41)26)% +)3!418J4B(pac38)NR0NC#Z#e#3iEB&LJ3@N)"5*!#3#"c`!+8S#Q,!eq+Np%JMA +`$Qh,&-"N31`$T$id`Nd-Jf%4"dVJ!r32'-!$jS%iJ!*0L3+!!$`J!,#"ST3"U!! +!D!"&+35L`!34#,b"5hV*D1!U6BSYi!fadJc)!!P`*%BP(2X%(-P)B!Q@m!"1j4Z +!BjlJ,AJ#ML%$3-!6)*5#J*&!amf`!8!"!2!'j**IS"%ld1f'!"N3!$BJ!#`"I4" +99X!5)"SdJ,X)!D-4+P#&%k!%CJ!(()&ST3lX`!+J"61J5eb"CF"8c)!++!G,3!F +-!K2!-YV8!MJ$(X!%j!J#B!+QJ!f!%`-b4e!!*#!%V)%`X!4#`!Q`!Y@5QDL9(9! +1l)"SK!Sf3!"Sj6`B"eB$NI3$G&!%SK8RN!!"%B!%e*0kq#i1R`cB$""!%Jb"3Z8 +B6%!D)"SN)0K3!&S3E#$!Yk3"8*-UY3%%8!&QJ3J4)@#!(K!Il8-djJKYL!"V`%# +B5,J*9Db%fm3!%D!T$)3-J(-S%$P)!N,6"b3IcM!!EPRHE"i!,pJ`!"-3"k!1PU+ +E('0(a-em!"[8a$qS"i`6kYJ36@N'*%!B#!Ji`%2#L34`"PK!)*N(iB"Si)KhJ5H +9!*a!!,L&"F%!52RDL"38L!I!B`4-!ULc1U-5PK3-3+#3!!`"EN-Ej%!##!2$8QX +q6FF3"KV(%+KL8f!)@)+QB!!SJ`"S(A@!0db")!!"-S!e!!,MB!Y%J@3J9%S$)Z% +(PN!1,!"E%!1Xb48JRb$,0-Je,F"0PFKL%%Jj3)N9C95a!#K!)ia)!P-XR%!( +b#`F)!DR2!b!VI#!X`k3pd#pXNR2D!!Z`'6M!"-J[!B"*kB"B%!aieIRN!6*!8L` +!*G!q!F$lV*J3B!)-!QY5qS"(18J#3L!@3!!e45[r35Q!P`,8#83V"a!FC#%hN3c +p%`!%d!&D3+8'XVS($46129!-`#CaU!`38&,#X#J"LpP#V8,lC!![&!6%J4ND3fH +SdJ%"T$1bA-deU3*#85K!!5)J#[43FJ!'e)%1j3HZS)F+J[-*!%6!*j!!!"q!!L5 +e,j!!9MSPji44'82TQ%3"3!&dcJ#!!ca4&TU"FY%ZS%!)F!mf3&h+J#1!$U5#$H! +B%%$df3`6N!!-E)!k+")Qi*-QLbeJ!5aJ8e!N@1"`,J!M5M)QcH&d!!#!#Kc1"`! +!V%!4U+3k`%cLKJKJJcS!XRUJ'U"S1SBHDJ*Sap(3!IX!$Vc4PAN1!-(H#8jj-MK +*!FS3r%b*,YJ(e!#4l!-VB%Ie+-[8SqD#B"J-*1"(0f9!d!-qmaYN$!Y3(ckQ'1K +V2-!-F)&"@NL*"KcJSF&*"*J"*F##$YmMR44#!"+%J(p*!(LTL@5C"Y9#AP%D%!$ ++JDBFNN85!"b"H!"eP-!N3"8P%`cJ!a1!+,m"Y0`-#'#-N9$JF3Ed!D#K&Nm""#L +"-iSR24,Y3!$+8JrS!el`*B2$2V!MCk#(eSB[)#qYD4)&!*46!c64H1NH#!!Ld3H +Ni!T%Jp+J+'fU%#@AjY+"kTF[b4Z1UL&3!K%J$ji$-!!)QZ8rm!@5J6)%!!2`+@% +,%$N$[H-h53&lT`X'3+-#!P0Jr+3#@`#Q'SXZ)!$Rj!RX4F'$'Ta"JI`(cL!'c!B +&S&m%Uii3PNV!&45*4U&*1B!+K!#r$"5iJ,rU$0KN3I-UG48i$3$kpNj8")Y)$KK +!4IL"ZJS#9)3G5!@,),1UL1$B@G[UG`#Y+S+c3JR2LN2V4MHT!e%J'#`b+"!-!SC +IM9[1)!6-"QX#!J`VBF8!V``fK)!SF$6q#!Zi)mj$#h,+c#3!3-%!5+i-3!3%5a4 +`@HX!NBJ"22"p8+Y38!jPNd)8,D"J!qK3GEBV"B!T@+rJS,fUJ[8+IhB(TPb[kJ+ +pMS$eDJc8##JS!4p5$4J!5)!-eUXYH%(Ve4@!!XF+"0DV+I#[+@#pHJ,rQLGP%bA +`VdPJ[5S#rkS%eLXJF"IC&81L9aH`AYd!9@%A2b!jH+ANB!&5J4'SUfa9!$J#c'" +FM"J5m"Q6j-D#JNGL9QA,h9!TlX!ek3)"3!CSl'dpXE3"aUCBB9PA2F"fE,'VS+i +##`(J!icX@A%'kJ3'`!%+N!"BD320JJ110GNieLja1,!D%1"hCm!1[#!PX"Q)NK[ +)N!!#3"'i!5!J"li+Vh3&K1phf+PrS"X+Q"QJ!N!X#c`"!S(XcNe%1fSQi!,X(X` +!!h#!p[J-0E3hN!#"9#!*9K)#J!C$+33JJcKV!RLP)LLCF#!G-)"98'M9$3%`!dC +!"0#8!A!N&%(cqC`!!"%-!(`3Cm@%%jJ%G##$!PG%!J#bS'+&R8Ce@88#G5N"rSJ +&F*[YmTS-N!!J)!%bJ$3)"P&!'CJch["!D%Gh-'KB9VR!!&9,'m+!$B#cLZ!0L!0 +JJ!X'J!Ci3BH2!D##8[XZc!!d'!#iS0BUKbcBDXh!1fUe,dK+`S&r%!*iS#)!!l) +!$ZJ$-MR(m!&PCCY-0!#N*JL+2HT!ME`C"@#b2S%k3!GHT"#!Z#k!bP&FhhT`"fY +Kh4N@4!-B9PM+!5bV)m1f(AG5I&`6+5b&kf2M!)E98i%!Na[hILYJ0DiVPl!15i3 +4!V+!e&LZ8),QSPcJLR-a!,)+#8+e!'"Al5SeZL[(*ESHeqL#A'%*#T3Z`K!"PC3 +$!%XSd9hca4N`!5p)kA42N!!T!5+!"!!'qXm-i)0*J!c!k$04%J6K(l`"!k$6iLi +%q,#l`jZmLc-3@B!!RR!8"!!#90)!%!@3!!'Q!Bajek`+L,UU@*,$&NN1(-#`H)) +NH!+@,!ZSUd!8!1b!ZZS$8X%%N!#bKN8@B!CG-Tl1DPUP'R99!U5#La-9`Fk4"@h +iiLqLAY#D#NU!F5%)")$5bFf!m&I)J#+b*3&!#JL%JI!3JN%J5!"&3!l)J6FJ"q$ +4'rJd!D%$,#EKL`d#!#3B[K!K'!J#""!&kN!DS!21&rSZNPT+23*!m[-&R#(l$J) +'-!A+!"fJ!fR!$Cb"14"q#8)&!!"*B5V3JZQ%IBX[)@J)phFfq#PRi(2kVd-)"S@ +J)BJ3iG[@[%%!d$P,)IXDJJ93"2!!('!$BF!0K!(iq`EHdf-6LdQK'35!8'"m!3" +2#!KJJ583"2F!Pl!!!1!%"%!!2)%FSB$e5d%!!1!!%8#!j,YmQfpTD!0L3#2S!"$ +J"FJX3E!J&X4(j1!3-!6H3"eJ!f6!$C`!m2X'i%!CH,0d!!f8!4$3IZ9!'JJ$E+! +MH'%lS"&F3!0S#)T*"B21aH3%!%!`L-%cf#'iSS)3!(!!)QJ#9%!,Ji!Qm!E)3"R +J#&#!qB,I0%"rad!G8,jAQ!k`J6`!!Z)[#+J$Ff!,Cf!b!!+b`N9!!m[A$6KKqKX +%i-!&,J08!!b[J8C-Ic8aq*8$GF!0Z)(iH`CF!2%p`QKS-k!!I**q!m#bZJ!1B40 +)3`"!#`$!0#!!"k!*Uq)b-!ESJ#jZ#,Ti@HhI*)"AKd!'(J0PJ!di"+04)Tq$'%! +%#8!)K)%k3)QP`!BZ!dDM)4L06e-#!-!T3!3'!!U%!5p-"r,!0,BP!+!%"!!(J)f +R!"fS`[$Sqmi"KY!3*N!!X&JN))aKBb+`JF0!2UB$Fk!&1!5&2!+S!J!3!`CJ!%4 +-''!%(%*&EXKA3"%C!!)3!f3!$$$#!H%,!)#'h"JJ-J'3!!%di#-lK%r6N!!2!3! +)!aZC"Z!!PG`3)X&)"J#Ii#&[j"aJ!fJb38JY$ANAD13#%!0bJ%I1+3XC!(bD(K" +0)[)6)!0NS#)hK)VmD6*b+pM)4B!-Hq#3!+b8!8"MS!8Ef3Pdi$+3!*54!3"Jb[M +!+EZ"-L#9#8*&0XY@B1dB!!A`"0Bb#!#l5!!-"cH#)*+6!D!+!!8J)P1"1r!'NNC +$B!%")#V`J2#hNBe!'L$$&MNaee,P&j'R3"V!!b+j)A`"bI`'!J!G-!!&S2eUCI% +E%**09%!%l!md&i%dF!E3!"d`c(+i5!3!&)!)6%!9@-9N@!l-JHr,L0q!'GM$d9J +-1'%jB*FYm4J!!4PK)m4I-2`3m&If!`5)J!$BJ4J!83L#&dLr"#(j"36%c!2Jm,p +X#0E%QR3*G)!)HN!H*X3J!!i)BP)XCGi!q!d$GL!-T!%-,!EB`"Bf!m`A"$3!5,b +&a3!M0XkBZ!0[iNVXLHec+'B$0$JJY1&A(""L4L`&!(c!1mGQm3`!B!%LD!(1@"@ +lCa!`"MV`@Ll')1!HKfJpE*`*p#Fqd+-i%XZ"h1`#'J1-DKi0J8!PKGP!$#4d3l! +JlJ%&c'C%8!4+G#FqdD+B&%[KpabIjh-BU-q5'2c1!6U`MZQ!Gf8-@"J0S1GfE)8 +C`fpZ!Q&J$-6IHc`(d!#-G!L%e@M8"aT!LaPdLZ3!!'"C%3!L)!@XJ2!0##S!em' +PJ'!$L!"21`)Qf#S%J38bTi8E5P0THGS52$BSNAd$JC`5LbZ!X")%ifXJ%,@R@Y5 +#S&%$J"9JX4Ee)*!!e#ZJ#M5%B%!)-(9E@05&!&02JNeY#$$ec&c8J3"6BeT8MDR +"`DD1e2p!,,)!G@1T*68,-!DEZP2(DZG"$$CeU0l9,m"L,1T5$Da*-f%ecXQj$63 +!*K"rYh$lIEqXH!i!jGi!!!: + diff --git a/contrib/AsyncATalk/async.c b/contrib/AsyncATalk/async.c new file mode 100644 index 0000000..7799191 --- /dev/null +++ b/contrib/AsyncATalk/async.c @@ -0,0 +1,1345 @@ +/* + * $Date: 1993/08/05 15:53:56 $ + * $Header: /mac/src/cap60/contrib/AsyncATalk/RCS/async.c,v 2.2 1993/08/05 15:53:56 djh Rel djh $ + * $Log: async.c,v $ + * Revision 2.2 1993/08/05 15:53:56 djh + * change wait handling + * + * Revision 2.1 91/02/15 21:20:58 djh + * CAP 6.0 Initial Revision + * + * Revision 1.6 91/01/09 02:30:45 djh + * Back out of asynchronous NBP register to avoid core dumps ... :-( + * + * Revision 1.5 91/01/09 01:52:08 djh + * Add XON/XOFF packets for AsyncTerm DA, do NBP asynchronously + * to speedup the startup of the Chooser. + * + * Revision 1.4 91/01/05 02:25:47 djh + * General cleanup and additions for use with UAB and CAP 6.0 + * + * Revision 1.3 90/07/10 18:16:19 djh + * Add support for AsyncTerm DA for simultaneous logins + * Alter speedTab entries for 19200 and 38400 + * + * Revision 1.2 90/03/20 16:10:29 djh + * Fix stupid bug in speedTab contents. + * + * Revision 1.1 90/03/17 22:05:27 djh + * Initial revision + * + * + * async.c + * + * Asynchronous AppleTalk for CAP UNIX boxes. + * David Hornsby, djh@munnari.oz, August 1988 + * Department of Computer Science. + * The University of Melbourne, Parkville, 3052 + * Victoria, Australia. + * + * Copyright 1988, The University of Melbourne. + * + * You may use, modify and redistribute BUT NOT SELL this package provided + * that all changes/bug fixes are returned to the author for inclusion. + * + * + * Ref: Async Appletalk, Dr. Dobbs Journal, October 1987, p18. + * Original Async AppleTalk by: + * + * Rich Brown + * Manager of Special Products + * Dartmouth College + * Kiewit Computer Center + * Hanover, New Hampshire 03755 + * 603/646-3648 + * + * CAP by: + * Charlie Kim + * Academic Computing and Communications Group + * Centre for Computing Activities + * Columbia University + * + * + * If BROAD is defined, SINGLE USE is possible without running the + * associated asyncad daemon. (NB: async must then be setuid root). + * + * if SECURE is defined, the AsyncTerm DA connects directly to a + * shell (/bin/csh), otherwise it just uses /bin/login. + * + * WARNING! + * If your C compiler is brain-damaged and pads structures + * strangely, then this code may not work. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "async.h" + +struct aalap_hdr rx_aalap; /* AALAP receive buffer (WRT the Mac) */ +struct aalap_hdr tx_aalap; /* AALAP xmit buffer (WRT the Mac) */ +struct aalap_hdr rtx_aalap; /* RTMP AALAP xmit buffer (WRT the Mac) */ +struct sockaddr_in sin; /* our UDP socket */ +struct sockaddr_in sin_broad; /* our UDP socket for DDP broadcasts */ +struct sgttyb sgttyb; /* terminal settings from ioctl() */ +struct timeval timeout; /* select() timeout */ +struct stat buf; /* for biff and mesg use */ + +/* various */ + +int s; /* a socket for UDP packets */ +#ifdef BROAD +int s_broad; /* a socket for DDP broadcasts (via UDP)*/ +#endif BROAD +int dstSkt; /* dynamic socket used at other end */ +int skt; /* a DDP socket for NBP */ +int fd; /* file desc. for control terminal */ +int nfds; /* for select call */ +int p; /* our pseudo TTY file descriptor */ +int t; /* our "real" TTY file descriptor */ +int closing; /* 2 CR's or NL's outside a frame */ + +/* Booleans */ + +int alarmed; /* need to send an RTMP packet */ +int inFrame; /* currently within a received frame */ +int maskIt; /* next char was escaped in input */ +int linkUp; /* we have established a link */ +int serOpen; /* shell has been started */ +int serWait; /* character buffer other end full */ +int debug; /* hmmmmm ... */ + +/* Network info */ + +u_char ourNode; /* 1 <= ourNode <= MAXNODE */ +unsigned short ourNet; /* one net per CAP host (atalk.local) */ +char ourZone[34]; /* our local zone (atalk.local) */ +u_char bridgeNode; /* local Multigate KIP node number */ +unsigned short bridgeNet; /* local Multigate KIP AppleTalk net # */ +unsigned long bridgeIP; /* local Multigate KIP IP address */ + +/* more various */ + +u_char lastChar; /* the last char read on input */ +char nbpObj[34]; /* the object we wish to register */ +char *getlogin(); +EntityName en; + +/* externs from modified CAP routine openatalkdb() */ +/* these are all in correct network byte order */ + +extern char async_zone[]; +extern u_short async_net; +extern u_char bridge_node; +extern u_short bridge_net; +extern struct in_addr bridge_addr; + +FILE *fopen(), *dbg; /* log file */ +long timeForRTMP, now; + +unsigned short crctbl[16] = { /* Async AppleTalk CRC lookup table */ + + 0x0000, 0xcc01, 0xd801, 0x1400, + 0xf001, 0x3c00, 0x2800, 0xe401, + 0xa001, 0x6c00, 0x7800, 0xb401, + 0x5000, 0x9c01, 0x8801, 0x4400 +}; + +short speedTab[16] = { /* Bytes/sec for various sgtty speeds */ + + 30, 30, 30, 30, + 30, 30, 30, 30, + 60, 120, 180, 240, + 480, 960, 1920, 3840 +}; + +main(argc, argv) +int argc; +char *argv[]; +{ + char *t; + long len; + fd_set readfds; + int i, cleanup(); + + timeForRTMP = now = serWait = 0; + debug = inFrame = maskIt = linkUp = serOpen = alarmed = FALSE; + + while(--argc > 0 && (*++argv)[0] == '-') + for(t = argv[0]+1 ; *t != '\0' ; t++) + switch (*t) { + case 'd': + debug = TRUE; + break; + } + + if(debug) dbg = fopen("asyncLog", "w"); + + readatalkdb("/etc/atalk.local"); + + /* select() timeout */ + timeout.tv_sec = 2; + timeout.tv_usec = 0; + + signal(SIGHUP, cleanup); + signal(SIGINT, cleanup); + signal(SIGQUIT, cleanup); + signal(SIGTERM, cleanup); + + /* setup the CAP stuff for NBP */ + abInit(0); + nbpInit(); + + /* set terminal line to 8 bits, no parity, 1 stop, raw mode */ + if(ioctl((fd=1),TIOCGETP,&sgttyb) != -1) { + sgttyb.sg_flags |= RAW; sgttyb.sg_flags &= ~ECHO; + ioctl(fd, TIOCSETP, &sgttyb); + sgttyb.sg_flags &= ~RAW; sgttyb.sg_flags |= ECHO; + } + + /* turn off 'biff' mail notification and 'mesg' delivery */ + if(fstat(fd, &buf) == 0) { + buf.st_mode &= ~0122; /* go-w, u-x */ + fchmod(fd, buf.st_mode); + buf.st_mode |= 0122; /* go+w, u+x */ + } + + if((s = socket(AF_INET, SOCK_DGRAM, 0, 0)) < 0) { + fprintf(stderr, "Couldn't open UDP socket\n"); + cleanup(); + } + + len = 6 * 1024; /* should be enough ?? */ + if(setsockopt(s,SOL_SOCKET,SO_RCVBUF,(char *)&len,sizeof(long))!=0){ + fprintf(stderr, "Couldn't set socket options\n"); + cleanup(); + } + +#ifdef BROAD + + if((s_broad = socket(AF_INET, SOCK_DGRAM, 0, 0)) < 0) { + fprintf(stderr, "Couldn't open broadcast UDP socket\n"); + cleanup(); + } + sin_broad.sin_port = htons(aaBroad); + if(bind(s_broad, (caddr_t) &sin_broad, sizeof(sin_broad), 0) < 0) { + fprintf(stderr, "Couldn't bind broadcast UDP port\n"); + cleanup(); + } +#endif BROAD + + skt = 0; + if(DDPOpenSocket(&skt, 0L) != noErr) { + fprintf(stderr, "Couldn't open a DDP socket\n"); + cleanup(); + } + + nfds = 16; + + if(debug) write(1, "Debug ON.\r\n", 11); + + /* announce success */ + write(1, "Connected ...\r\n", 15); + sleep(1); + + /* send something innocuous to make the login window go away */ + write(1, GOAWAY, 4); + sleep(1); + + time(&now); + timeForRTMP = now + RTMPTIME; + + for( ; ; ) { + if(debug) fflush(dbg); + FD_ZERO(&readfds); + FD_SET(0, &readfds); + FD_SET(s, &readfds); +#ifdef BROAD + FD_SET(s_broad, &readfds); +#endif BROAD + if(serOpen) + FD_SET(p, &readfds); + + if(select(nfds, &readfds, (fd_set *) 0, (fd_set *) 0, + (struct timeval *) &timeout) > 0) { + if(FD_ISSET(0, &readfds)) + doAALAPRead(&rx_aalap); + if(FD_ISSET(s, &readfds)) + doDDPRead(&tx_aalap); +#ifdef BROAD + if(FD_ISSET(s_broad, &readfds)) + doDDPRead_broad(&tx_aalap); +#endif BROAD + if(serOpen && !serWait && FD_ISSET(p, &readfds)) + doShellRead(&tx_aalap); + } + if(linkUp) { + time(&now); + if(now > timeForRTMP) { + sendRTMP(); + timeForRTMP = now + RTMPTIME; + } + } + } +} + +/* + * send a UR frame back to the Mac + * (rx_aalap has the corresponding IM frame) + * + */ + +sendUR(rx_aalap) +struct aalap_hdr *rx_aalap; +{ + struct aalap_imur *imur; + unsigned short checkNet(); + u_char checkNode(); + + if(rx_aalap->aalap_datalen != 3) + return; /* drop it */ + + /* ensure the frame has the right stuff */ + + imur = (struct aalap_imur *) rx_aalap->aalap_data; + rx_aalap->aalap_frame = (u_char) FrameChar; + rx_aalap->aalap_datalen = 3; + imur->aalap_net = htons(checkNet(imur->aalap_net)); + imur->aalap_node = checkNode(imur->aalap_node); + + if(debug) { + fprintf(dbg, "Received IM/Sent UR\n"); + fprintf(dbg, "Suggested Node = %02x, Net = %04x\n", + imur->aalap_node, ntohs(imur->aalap_net)); + } + + doAALAPWrite(rx_aalap, URType); /* does checksum & trailing frame */ +} + +/* + * receive a UR frame and check it ... + * + */ + +recvUR(rx_aalap) +struct aalap_hdr *rx_aalap; +{ + struct aalap_imur *imur; + + if(rx_aalap->aalap_datalen != 3) + return; /* drop it */ + + imur = (struct aalap_imur *) rx_aalap->aalap_data; + + if(debug) { + fprintf(dbg, "Received UR\n"); + fprintf(dbg, + "ourNode=%02x, theirNode=%02x, ourNet=%04x, theirNet=%04x\n", + ourNode, imur->aalap_node, ourNet, ntohs(imur->aalap_net)); + } + if(ourNet == ntohs(imur->aalap_net) && ourNode == imur->aalap_node) + linkUp = TRUE; + else + if(ourNode == 0) { + if(checkNode(imur->aalap_node) == imur->aalap_node) + sendIM(rx_aalap); + } else + if(imur->aalap_node == ourNode) + sendIM(rx_aalap); +} + +/* + * send an IM frame to the Mac + * + */ + +sendIM(tx_aalap) +struct aalap_hdr *tx_aalap; +{ + struct aalap_imur *imur; + unsigned short net; + + if(debug) fprintf(dbg, "Sending IM\n"); + + /* ensure the frame has the right stuff */ + + tx_aalap->aalap_frame = (u_char) FrameChar; + tx_aalap->aalap_datalen = 3; + imur = (struct aalap_imur *) tx_aalap->aalap_data; + imur->aalap_net = htons(ourNet); + imur->aalap_node = ourNode; + doAALAPWrite(tx_aalap, IMType); /* does checksum & trailing frame */ +} + +/* + * we have to process a short DDP packet which could be: + * an RTMP request: (dst socket=1, DDP type=5, Command=1) + * respond locally + * an NBP lookup: (dst socket=2, DDP type = 2) + * repackage as long DDP and forward to gateway + * a ZIP query: (dst socket=6, DDP type=6) + * not implemented (yet) + * a ZIP/ATP Zone query: (dst socket=6, DDP type=3) + * GZL: repackage as long DDP and forward to gateway, watch + * for the return packet and repackage it as a short DDP + * GMZ: respond locally + * + */ + +processShort(rx_aalap) +struct aalap_hdr *rx_aalap; +{ + char *q; + int reqType; + unsigned short len, newlen, getMyZone(); + struct sddp *sddp, *sddptx; + struct atp *atp, *atptx; + struct ddp *ddptx; + + sddp = (struct sddp *) rx_aalap->aalap_data; + switch (sddp->ddp_type) { + case DDP_RTMP_R: /* process locally */ + q = (char *) sddp->ddp_data; + if(sddp->ddp_dstSkt == RTMP_SKT && *q == 1/* command */) { + linkUp = TRUE; + sendRTMP(); + } + break; + case DDP_NBP: + if(sddp->ddp_dstSkt == NBP_SKT) { + len = (ntohs(sddp->hop_and_length)&0x03ff) + 8; + tx_aalap.aalap_frame = (u_char) FrameChar; + tx_aalap.aalap_type = DDPType; + tx_aalap.aalap_datalen = len; + ddptx = (struct ddp *) tx_aalap.aalap_data; + ddptx->hop_and_length = htons(len); + ddptx->ddp_checksum = 0; + ddptx->ddp_dstNet = htons(bridgeNet); + ddptx->ddp_srcNet = htons(ourNet); + ddptx->ddp_dstNode = bridgeNode; + ddptx->ddp_srcNode = ourNode; + ddptx->ddp_dstSkt = sddp->ddp_dstSkt; + ddptx->ddp_srcSkt = sddp->ddp_srcSkt; + ddptx->ddp_type = sddp->ddp_type; + if((newlen = len - 13) < 0) + newlen = 0; + bcopy(sddp->ddp_data,ddptx->ddp_data,newlen); + doDDPWrite(&tx_aalap); + } + break; + case DDP_ATP: + if(sddp->ddp_dstSkt == ZIP_SKT) { /* ZIP GMZ/GZL req */ + atp = (struct atp *) sddp->ddp_data; + reqType = atp->atp_user1; + switch (reqType) { + case ATP_GMZ: /* GetMyZone */ + tx_aalap.aalap_frame = (u_char) FrameChar; + tx_aalap.aalap_type = SDDPType; + sddptx = (struct sddp *) tx_aalap.aalap_data; + sddptx->ddp_dstSkt = sddp->ddp_srcSkt; + sddptx->ddp_srcSkt = ZIP_SKT; + sddptx->ddp_type = DDP_ATP; + atptx = (struct atp *) sddptx->ddp_data; + atptx->atp_command = ATP_CMD; + atptx->atp_bitseq = ATP_SEQ; + /* bludgeon through alignment problems */ + bcopy(&atp->atp_transid,&atptx->atp_transid,2); + atptx->atp_user1 = 0; + atptx->atp_user2 = 0; + atptx->atp_user3 = 0; + atptx->atp_user4 = 1; /* one zone name */ + q = (char *) atptx->atp_data; + len = getMyZone(q) + 13; + tx_aalap.aalap_datalen = len; + sddptx->hop_and_length = htons(len); + doAALAPWrite(&tx_aalap, SDDPType); + break; + case ATP_GZL: /* GetZoneList */ + len = (ntohs(sddp->hop_and_length)&0x03ff) + 8; + tx_aalap.aalap_frame = (u_char) FrameChar; + tx_aalap.aalap_type = DDPType; + tx_aalap.aalap_datalen = len; + ddptx = (struct ddp *) tx_aalap.aalap_data; + ddptx->hop_and_length = htons(len); + ddptx->ddp_checksum = 0; + ddptx->ddp_dstNet = htons(bridgeNet); + ddptx->ddp_srcNet = htons(ourNet); + ddptx->ddp_dstNode = bridgeNode; + ddptx->ddp_srcNode = ourNode; + ddptx->ddp_dstSkt = sddp->ddp_dstSkt; + ddptx->ddp_srcSkt = sddp->ddp_srcSkt; + ddptx->ddp_type = sddp->ddp_type; + if((newlen = len - 13) < 0) + newlen = 0; + bcopy(sddp->ddp_data,ddptx->ddp_data,newlen); + doDDPWrite(&tx_aalap); + break; + default: + if(debug) + fprintf(dbg,"Illegal request id=%d\n",reqType); + break; + } + } + break; + case DDP_ZIP: /* Not yet implemented */ + break; + case SERIALType:/* pass to a shell */ + processSerial(rx_aalap); + break; + default: + if(debug) + fprintf(dbg, "Unknown short DDP = %d\n",sddp->ddp_type); + break; + } +} + +/* + * we have had a LAP packet of SERIALType, process it ... + * + */ + +processSerial(rx_aalap) +struct aalap_hdr *rx_aalap; +{ + int startShell(); + struct sddp *sddp; + short len; + + sddp = (struct sddp *) rx_aalap->aalap_data; + switch (sddp->ddp_data[0]) { + case SB_OPEN: + if(serOpen) { + write(p, "exit\n", 5); + close(p); + } + serOpen = startShell(); + dstSkt = sddp->ddp_srcSkt; + break; + case SB_DATA: + len = (ntohs(sddp->hop_and_length)&0x03ff) - 6; + if(serOpen) write(p, sddp->ddp_data+1, len); + serWait = 0; + break; + case SB_CLOSE: + write(p, "exit\n", 5); + close(p); + serOpen = 0; + break; + case SB_XOFF: + serWait = 1; + break; + case SB_XON: + serWait = 0; + break; + default: + break; + } +} + +/* + * start up a Shell to process bytes in special DDP packets. + * + */ + +int +startShell() +{ + int on = 1; + int pid, i; + int funeral(); + char *line; + char c; + + if(debug) fprintf(dbg, "Opening a Shell ...\n"); + + /* find and open a pseudo TTY */ + + p = -1; + for(c = 'p' ; c < 's' ; c++) { + struct stat statb; + + line = "/dev/ptyZZ"; + line[strlen("/dev/pty")] = c; + line[strlen("/dev/ptyZ")] = '0'; + if(stat(line, &statb) < 0) + break; + + for(i = 0 ; i < 16 ; i++) { + line[strlen("/dev/ptyZ")] = "0123456789abcdef"[i]; + if((p = open(line, 2)) >= 0) + break; + } + if(p >= 0) + break; + } + + if(p < 0) { + if(debug) fprintf(dbg, "Failed to open pty\n"); + return(0); + } + + if((pid = fork()) == 0) { /* in child */ + close(p); + close(0); + close(1); + close(2); + + /* dissassociate from our controlling TTY */ + + if((t = open("/dev/tty", 2)) >= 0) { + ioctl(t, TIOCNOTTY, 0); + setpgrp(0, 0); + close(t); + } + + /* open the pseudo TTY */ + + line[strlen("/dev/")] = 't'; + if((t = open(line, 2)) < 0) { + if(debug) fprintf(dbg, "Failed to open tty\n"); + exit(1); + } + + /* set nice modes */ + + { struct sgttyb b; + + gtty(t, &b); + b.sg_flags = ECHO|CRMOD; + stty(t, &b); + } + + dup(t); /* stdout */ + dup(t); /* stderr */ + +#ifndef SECURE + execl("/bin/login", "login", 0); +#else SECURE + execl("/bin/csh", "-asyncsh", 0); +#endif SECURE + + exit(1); /* o, oh */ + } + + /* parent */ + + if(pid == -1) + return(0); + + ioctl(p, FIONBIO, &on); /* non blocking */ + + signal(SIGTSTP, SIG_IGN); + signal(SIGCHLD, funeral); + + return(1); +} + +/* + * write this out as a UDP encapsulated DDP packet + * + */ + +doDDPWrite(rx_aalap) +struct aalap_hdr *rx_aalap; +{ + int length; + struct ap ap; + struct ddp *ddp; + + ap.ldst = 0xFA; /* magic */ + ap.lsrc = 0xCE; /* magic */ + ap.ltype = DDPType; + ddp = (struct ddp *) rx_aalap->aalap_data; + length = rx_aalap->aalap_datalen; + bcopy(rx_aalap->aalap_data, ap.dd, length); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = bridgeIP; /* Gateway */ + if(bridgeIP == inet_addr("127.0.0.1")) /* using UAB */ + sin.sin_port = (word) htons(750); + else + sin.sin_port = (word) htons(ddp2ipskt(ddp->ddp_dstSkt)); + length += 3; + + if(debug) { + fprintf(dbg, "DDPWrite: "); + dumppacket(rx_aalap, 0); + } + if(sin.sin_port == 0) { + if(debug) fprintf(dbg, "Couldn't find the gateway UDP port\n"); + return; /* drop it, do nothing */ + } + if(sendto(s, (caddr_t) &ap, length, 0, &sin, sizeof(sin)) != length) { + if(debug) fprintf(dbg, "Couldn't write to the gateway\n"); + return; /* drop it, do nothing */ + } +} + +/* + * write a frame out the serial port + * + */ + +doAALAPWrite(tx_aalap, type) +struct aalap_hdr *tx_aalap; +u_char type; +{ + struct aalap_trlr *trlr; + unsigned short doCRC(), crc; + unsigned char buf[2 * (AALAP + 3)]; + u_char *q; + int i, j; + + tx_aalap->aalap_frame = FrameChar; + tx_aalap->aalap_type = type; + q = (u_char *) tx_aalap->aalap_data; + trlr = (struct aalap_trlr *) (q + tx_aalap->aalap_datalen); + crc = doCRC(tx_aalap, (int) tx_aalap->aalap_datalen); + /* bludgeon through alignment problems */ + bcopy(&crc, &trlr->aalap_crc, 2); + trlr->aalap_frame = FrameChar; + + if(debug) dumppacket(tx_aalap, 0); + + j = 0; + /* FrameChar and Type */ + buf[j++] = tx_aalap->aalap_frame; + buf[j++] = tx_aalap->aalap_type; + + /* body including CRC */ + for(i = 0 ; i < (tx_aalap->aalap_datalen + 2) ; i++, q++) { + switch (*q) { + case FrameChar: + case DLEChar: + case XONChar1: + case XONChar2: + case XOFFChar1: + case XOFFChar2: + buf[j++] = DLEChar; + buf[j++] = *q ^ XOR; + break; + default: + buf[j++] = *q; + break; + } + } + /* Trailing FrameChar */ + buf[j++] = trlr->aalap_frame; + + /* send it */ + write(1, buf, j); +} + +/* + * process serial chars from the Mac + * + */ + +doAALAPRead(rx_aalap) +struct aalap_hdr *rx_aalap; +{ + int i, j, len; + unsigned char buf[AALAP+5]; + unsigned short doCRC(); + + len = read(0, buf, AALAP+5); + for(i = 0 ; i < len ; i++ ) { + if(buf[i] == lastChar && buf[i] == FrameChar) /* recover */ + inFrame = FALSE; + lastChar = buf[i]; + if(!inFrame) { + switch (buf[i]) { + case FrameChar: + inFrame = TRUE; + rx_aalap->aalap_frame = buf[i]; + rx_aalap->aalap_type = (u_char) 0; + rx_aalap->aalap_framecomplete = FALSE; + rx_aalap->aalap_datalen = 0; + closing = 0; + continue; + break; + case CR: /* 2 CR's or NL's outside the frame */ + case NL: /* closes the async connection. */ + case CRP: /* CR with parity bit set */ + case NLP: /* NL with parity bit set */ + if(++closing >= 2) + cleanup(); + continue; + break; + default: + closing = 0; + continue; + break; + } + } else { + closing = 0; + /* we are in a valid frame */ + switch (buf[i]) { + case FrameChar: /* end of frame */ + rx_aalap->aalap_data[rx_aalap->aalap_datalen] + = buf[i]; + rx_aalap->aalap_datalen -= 2; /* CRC */ + if((j=doCRC(rx_aalap,rx_aalap->aalap_datalen+2)) + || rx_aalap->aalap_datalen < 0 + || rx_aalap->aalap_datalen > AALAP) { + rx_aalap->aalap_datalen = 0; + if(debug)fprintf(dbg,"Bogus pkt %s\n", + (j==0) ? "Length" : "CRC"); + } else + rx_aalap->aalap_framecomplete = TRUE; + inFrame = FALSE; + break; + case DLEChar: /* an escaped character follows */ + maskIt = TRUE; + continue; + break; + case XONChar1: + case XONChar2: + case XOFFChar1: + case XOFFChar2: + if(debug) fprintf(dbg, "Received %s\n", + (buf[i]&0x7f)==XONChar1?"XON":"XOFF"); + /* not yet implemented */ + continue; + break; + default: + if(rx_aalap->aalap_datalen == 0 + && rx_aalap->aalap_type == 0 + && (buf[i]==IMType + || buf[i]==URType + || buf[i]==SDDPType + || buf[i]==DDPType)) { + rx_aalap->aalap_type = buf[i]; + continue; + break; + } + if(maskIt) { + buf[i] = buf[i] ^ XOR; + maskIt = FALSE; + } + rx_aalap->aalap_data[rx_aalap->aalap_datalen] + = buf[i]; + rx_aalap->aalap_datalen++; + continue; + break; + } + } + if(rx_aalap->aalap_framecomplete) { + if(debug) dumppacket(rx_aalap, 1); + switch (rx_aalap->aalap_type) { + case IMType: /* we're being probed */ + sendUR(rx_aalap); /* always send */ + break; + case URType: + recvUR(rx_aalap); + break; + case SDDPType: /* a short DDP packet */ + processShort(rx_aalap); + break; + case DDPType: /* send it out on DDP */ + doDDPWrite(rx_aalap); + break; + default: /* drop it */ + if(debug) fprintf(dbg,"Unknown: %02x\n", + rx_aalap->aalap_type); + break; + } + rx_aalap->aalap_framecomplete = FALSE; + rx_aalap->aalap_datalen = 0; + } + } +} + +/* + * get and process DDP packet destined for the Mac + * NB: the packets aren't really from the DDP interface + * AppleTalk gateways (EG: Multigate) direct packets + * to the UDP port (PortOffset+node#) + * + * If the packet is an ATP pkt destined for the ZIP socket, + * we have to munge the packet into a short DDP to send back + * to the mac, otherwise it is ignored. Stupid Protocol. + * + */ + +doDDPRead(tx_aalap) +struct aalap_hdr *tx_aalap; +{ + struct ap ap; + unsigned short junk; + int zipped; /* junk */ + + tx_aalap->aalap_framecomplete = FALSE; + tx_aalap->aalap_datalen = (short) read(s, (char *) &ap, sizeof(ap)); + + if(ap.ldst == 0xFA && ap.lsrc == 0xCE && ap.ltype == 2) { + if(ap.srcskt == ZIP_SKT && ap.type == DDP_ATP) { + tx_aalap->aalap_datalen -= 11; + if(tx_aalap->aalap_datalen < 0) + tx_aalap->aalap_datalen = 0; + bcopy(ap.dd+10, tx_aalap->aalap_data+2, + (int) tx_aalap->aalap_datalen); + /* bludgeon through alignment problems */ + bcopy(ap.dd, &junk, 2); + junk = ntohs(junk) - 8; + junk = htons(junk); + bcopy(&junk, tx_aalap->aalap_data, 2); + zipped = TRUE; + } else { + tx_aalap->aalap_datalen -= 3; + if(tx_aalap->aalap_datalen < 0) + tx_aalap->aalap_datalen = 0; + bcopy(ap.dd, tx_aalap->aalap_data, + (int) tx_aalap->aalap_datalen); + zipped = FALSE; + } + tx_aalap->aalap_framecomplete = TRUE; + } else + tx_aalap->aalap_datalen = 0; + + if(debug) { + fprintf(dbg,"DDPRead: "); + dumppacket(tx_aalap, 1); + } + + if(tx_aalap->aalap_framecomplete) { + if(shouldSendRTMP(tx_aalap->aalap_datalen)) + sendRTMP(); + doAALAPWrite(tx_aalap, (zipped) ? SDDPType : DDPType); + tx_aalap->aalap_framecomplete = FALSE; + tx_aalap->aalap_datalen = 0; + } +} + +#ifdef BROAD + +doDDPRead_broad(tx_aalap) +struct aalap_hdr *tx_aalap; +{ + struct ap ap; + + tx_aalap->aalap_framecomplete = FALSE; + tx_aalap->aalap_datalen = read(s_broad, (char *) &ap, sizeof(ap)); + if(ap.ldst == 0xFA && ap.lsrc == 0xCE && ap.ltype == 2) { + tx_aalap->aalap_datalen -= 3; + if(tx_aalap->aalap_datalen < 0) + tx_aalap->aalap_datalen = 0; + bcopy(ap.dd, tx_aalap->aalap_data, tx_aalap->aalap_datalen); + tx_aalap->aalap_framecomplete = TRUE; + } else + tx_aalap->aalap_datalen = 0; + + if(debug) { + fprintf(dbg,"DDPRead (broad): "); + dumppacket(tx_aalap, 1); + } + + if(tx_aalap->aalap_framecomplete) { + if(shouldSendRTMP(tx_aalap->aalap_datalen)) + sendRTMP(); + doAALAPWrite(tx_aalap, DDPType); + tx_aalap->aalap_framecomplete = FALSE; + tx_aalap->aalap_datalen = 0; + } +} + +#endif BROAD + +/* + * read from the shell we have forked, send it to our user + * + */ + +doShellRead(tx_aalap) +struct aalap_hdr *tx_aalap; +{ + short len; + struct sddp *sddp; + + sddp = (struct sddp *) tx_aalap->aalap_data; + tx_aalap->aalap_framecomplete = TRUE; + + if((len = read(p, (char *)sddp->ddp_data+1, 512)) <= 0) + return(0); + + len += 6; + tx_aalap->aalap_datalen = len; + sddp->hop_and_length = htons(len); + sddp->ddp_dstSkt = dstSkt; + sddp->ddp_srcSkt = SERIALType; + sddp->ddp_type = SERIALType; + sddp->ddp_data[0] = SB_DATA; + + if(debug) { + fprintf(dbg,"ShellRead: "); + dumppacket(tx_aalap, 1); + } + + if(tx_aalap->aalap_framecomplete) { + if(shouldSendRTMP(tx_aalap->aalap_datalen)) + sendRTMP(); + doAALAPWrite(tx_aalap, SDDPType); + tx_aalap->aalap_framecomplete = FALSE; + tx_aalap->aalap_datalen = 0; + } +} + +/* + * if we are about to send a packet, check to see + * if the time to send it will make our next RTMP + * late (Yes, this is really necessary !). + * + */ + +int +shouldSendRTMP(len) +short len; +{ + short i, ptime; + + i = sgttyb.sg_ospeed; + if(i >= 0 && i < sizeof(speedTab)/sizeof(short)) { + ptime = (len/speedTab[i]) + 1; + time(&now); + if(now+ptime > timeForRTMP) { + timeForRTMP = now + RTMPTIME; + return(1); + } + } + return(0); +} + +/* + * Try to accomodate their wishes for a node number + * (this might be a re-connect) + * + */ + +u_char +checkNode(node) +u_char node; +{ + if(ourNode != node) { + sin.sin_port = htons(PortOffset + node); + if(bind(s, (caddr_t) &sin, sizeof(sin), 0) < 0) { + if(debug) fprintf(dbg, + "Requested node number %x in use\n", node); + return(ourNode); + } + + if(debug) fprintf(dbg, "Mac requested node number %x\n", node); + + /* requested node number is OK with me */ + ourNode = node; +#ifndef BROAD + notify(UP); /* tell asyncad daemon we are here */ +#endif BROAD + sprintf(nbpObj, "%s#%d", getlogin(), ourNode & 0xff); + strncpy(en.objStr.s, nbpObj, sizeof(en.objStr.s)); + strncpy(en.typeStr.s, NBPTYPE, sizeof(en.typeStr.s)); + strncpy(en.zoneStr.s, ZONE, sizeof(en.zoneStr.s)); + nbp_remove(&en); /* if one already running */ + nbp_register(&en, skt); /* synchronous */ + } + return(ourNode); +} + +/* + * Return our network number no matter what they want + * + */ + +unsigned short +checkNet(net) +short net; +{ + return(ourNet); +} + +/* + * return number of chars in our zone name (from atalk.local) + * copy zone name and length byte to q (pascal string) + * + */ + +unsigned short +getMyZone(q) +char *q; +{ + int i; + + i = strlen(ourZone); + *q = (u_char) i; + bcopy(ourZone, q+1, i); + return((unsigned short) i+1); +} + +/* + * read the atalk data file '/etc/atalk.local' + * + * for now, save effort by using a slightly modified + * version of the CAP routine openatalkdb(). If using + * CAP 6.0 or greater, this is all handled automagically. + * + */ + +readatalkdb(db) +char *db; +{ +#ifndef CAP_6_DBM + openatalkdb(db); /* CAP routine in atalkdbm.c */ +#else CAP_6_DBM + { extern short lap_proto; + if(lap_proto == LAP_KIP) + openatalkdb(db); + else + openetalkdb(NULL); /* use the default */ + } +#endif CAP_6_DBM + ourNode = 0; + ourNet = ntohs(async_net); + strncpy(ourZone, async_zone, strlen(async_zone)); + bridgeNode = bridge_node; + bridgeNet = ntohs(bridge_net); + bridgeIP = bridge_addr.s_addr; + + if(debug) { + fprintf(dbg, "ourNode=%02x, ourNet=%02x, ourZone=%s\n", + ourNode, ourNet, ourZone); + fprintf(dbg, "bridgeNode=%02x, bridgeNet=%02x, brIP=%04x\n", + bridgeNode, bridgeNet, ntohl(bridgeIP)); + } +} + +/* + * send an RTMP packet + * 1. in response to a request + * 2. synchronously every ~RTMPTIME seconds to keep the link up + * + */ + +sendRTMP() +{ + struct sddp *sddptx; + struct rtmp *rtmp; + unsigned short senderNet; + + rtx_aalap.aalap_frame = (u_char) FrameChar; + rtx_aalap.aalap_datalen = 9; + sddptx = (struct sddp *) rtx_aalap.aalap_data; + sddptx->hop_and_length = htons(9); + sddptx->ddp_dstSkt = RTMP_SKT; + sddptx->ddp_srcSkt = RTMP_SKT; + sddptx->ddp_type = DDP_RTMP_D; + rtmp = (struct rtmp *) sddptx->ddp_data; + senderNet = htons(ourNet); + /* bludgeon through alignment problems */ + bcopy(&senderNet, &rtmp->rtmp_senderNet, 2); + rtmp->rtmp_idLen = NBitID; + rtmp->rtmp_senderId = bridgeNode; + doAALAPWrite(&rtx_aalap, SDDPType); +} + + +/* + * Dump the contents of an AALAP packet + * + */ +dumppacket(ax_aalap, dirn) +struct aalap_hdr *ax_aalap; +int dirn; +{ + struct aalap_trlr *trlr; + char *q; + int i, len; + + /* Heading */ + fprintf(dbg, "%s: len=0x%02x, pkt=", + (dirn) ? "IN" : "\tOUT", ax_aalap->aalap_datalen); + + q = (char *) ax_aalap->aalap_data; + trlr = (struct aalap_trlr *) (q + ax_aalap->aalap_datalen); + + /* Frame and type */ + fprintf(dbg, "%02x %02x ", ax_aalap->aalap_frame, ax_aalap->aalap_type); +#ifdef ALLPACKET + /* body including CRC */ + for(i = 0 ; i < (ax_aalap->aalap_datalen + 2) ; i++, q++) + fprintf(dbg, "%02x ", (*q & 0xff)); + + /* trailing frame */ + fprintf(dbg, "%02x\n", trlr->aalap_frame); +#else ALLPACKET + len = ax_aalap->aalap_datalen + 2; + if(len > 21) len = 21; /* just enough to cover ATP header */ + for(i = 0 ; i < len ; i++, q++) + fprintf(dbg, "%02x ", (*q & 0xff)); + if(len == ax_aalap->aalap_datalen+2) + fprintf(dbg, "%02x\n", trlr->aalap_frame); + else + fprintf(dbg, "\n"); +#endif ALLPACKET + fflush(dbg); +} + +/* + * calculate CRC + * (if we want to check the CRC, set len to the data length + * plus 2 to include the packet CRC, the result should be 0) + * + * NB: this algorithm calculates the CRC in network byte order on + * machines that are not network byte order and vice-versa. + * Hence the crap at the end. + * + */ + +unsigned short +doCRC(aalap, len) +struct aalap_hdr *aalap; +register int len; +{ + register int c; + register int crc; + register int index; + register char *q; + unsigned short result; + + crc = 0; + q = (char *) &aalap->aalap_type; + while(len-- >= 0) { /* include type */ + c = *q++; + index = c ^ crc; + index &= 0x0f; + crc >>= 4; + crc ^= crctbl[index]; + c >>= 4; + c ^= crc; + c &= 0x0f; + crc >>= 4; + crc ^= crctbl[c]; + } + result = (unsigned short) (crc & 0xffff); + if(result == htons(result)) /* on a network byte order machine */ + result = ((crc & 0xff00) >> 8) | ((crc & 0xff) << 8); + return(result & 0xffff); +} + +/* + * unregister an NBP name + * + */ + +nbp_remove(en) +EntityName *en; +{ + return(NBPRemove(en)); +} + +/* + * register an NBP name + * + */ + +nbp_register(en, skt) +EntityName *en; +int skt; +{ + nbpProto nbpr; /* nbp proto */ + NBPTEntry nbpt[1]; /* table of entity names */ + + nbpr.nbpAddress.skt = skt; + nbpr.nbpRetransmitInfo.retransInterval = 5; + nbpr.nbpRetransmitInfo.retransCount = 3; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + nbpr.nbpDataField = 1; /* max entries */ + nbpr.nbpEntityPtr = en; + + return((int) NBPRegister(&nbpr,FALSE)); /* try synchronous */ +} + +#ifndef BROAD + +/* + * notify asyncad daemon of a change in state + * UP | DOWN + * + */ + +notify(state) +int state; +{ + struct ap ap; + char hostname[64]; + struct hostent *host, *gethostbyname(); + + ap.ldst = 0xC0; /* MAGIC */ + ap.lsrc = 0xDE; /* MAGIC */ + ap.ltype = (u_char) state; + ap.srcskt = ourNode; + sin_broad.sin_family = AF_INET; + sin_broad.sin_port = htons(aaBroad); + gethostname(hostname, sizeof(hostname)); + host = gethostbyname(hostname); + bcopy(host->h_addr, &sin_broad.sin_addr.s_addr, + sizeof(sin_broad.sin_addr.s_addr)); + + if(debug) fprintf(dbg, "Sent %s message to: %s (%08x) on port %d\n", + (state==UP) ? "UP" : "DOWN", hostname, + htonl(sin_broad.sin_addr.s_addr), + ntohs(sin_broad.sin_port)); + + if(sendto(s, (caddr_t)&ap, 16, 0, &sin_broad,sizeof(sin_broad)) != 16) { + if(debug) fprintf(dbg, "Couldn't tell asyncad we exist\n"); + cleanup(); + } +} + +#endif BROAD + +/* + * reset things to a natural state + * + */ + + cleanup() + { + nbp_remove(&en); +#ifndef BROAD + notify(DOWN); +#endif BROAD + ioctl(fd, TIOCSETP, &sgttyb); + fchmod(fd, buf.st_mode); + if(debug) fclose(dbg); + if(serOpen) close(p); + exit(1); +} + +int +funeral() +{ + WSTATUS junk; + + serOpen = 0; +#ifdef POSIX + while( waitpid(-1, &junk, WNOHANG) > 0 ) + ; +#else POSIX +#ifndef NOWAIT3 + while( wait3(&junk, WNOHANG, 0) > 0 ) + ; +#else NOWAIT3 + wait(&junk); /* assume there is at least one process to wait for */ +#endif NOWAIT3 +#endif POSIX + close(p); +} diff --git a/contrib/AsyncATalk/async.h b/contrib/AsyncATalk/async.h new file mode 100644 index 0000000..2133f3a --- /dev/null +++ b/contrib/AsyncATalk/async.h @@ -0,0 +1,172 @@ +/* + * $Date: 91/02/15 21:21:01 $ + * $Header: async.h,v 2.1 91/02/15 21:21:01 djh Rel $ + * $Log: async.h,v $ + * Revision 2.1 91/02/15 21:21:01 djh + * CAP 6.0 Initial Revision + * + * Revision 1.3 91/01/09 01:53:54 djh + * Add XON/XOFF definitions for use with AsyncTerm DA. + * + * Revision 1.2 90/07/10 18:17:57 djh + * Add support for AsyncTerm DA for simultaneous logins + * Alter speedTab entries for 19200 and 38400 + * + * Revision 1.1 90/03/17 22:06:31 djh + * Initial revision + * + * + * async.h + * + * Asynchronous AppleTalk for CAP UNIX boxes. + * David Hornsby, djh@munnari.oz, August 1988 + * Department of Computer Science. + * The University of Melbourne, Parkville, 3052 + * Victoria, Australia. + * + * Copyright 1988, The University of Melbourne. + * + * You may use, modify and redistribute BUT NOT SELL this package provided + * that all changes/bug fixes are returned to the author for inclusion. + * + * + * Ref: Async Appletalk, Dr. Dobbs Journal, October 1987, p18. + */ + +#include "macros.h" + +#define PortOffset 0xaa00 /* obviously async appletalk :-) */ +#define AALAP 600 /* maximum bytes of data */ +#define aaBroad 750 /* a UDP port for DDP broadcasts */ +#define MAXNODE 0x7f /* for the moment (2^n-1 please) */ + +#define GOAWAY "\1\1\1\1" + +#define NBPTYPE "AsyncATalk" +#define ZONE "*" + +#define IMType 0x86 /* an 'I aM' frame */ +#define URType 0x87 /* a 'U aRe' frame */ +#define SDDPType 0x01 /* short DDP frame */ +#define DDPType 0x02 /* ordinary DDP frame */ + +#define SERIALType 0x43 /* a new LAP type to talk serial bytes */ + +#define SB_OPEN 1 +#define SB_DATA 2 +#define SB_CLOSE 3 +#define SB_XON 0x11 +#define SB_XOFF 0x13 + +#define DDP_RTMP_D 1 +#define DDP_NBP 2 +#define DDP_ATP 3 +#define DDP_RTMP_R 5 +#define DDP_ZIP 6 + +#define RTMP_SKT 1 +#define NBP_SKT 2 +#define ECHO_SKT 4 +#define ZIP_SKT 6 + +#define ATP_GMZ 7 +#define ATP_GZL 8 +#define ATP_CMD 0x90 +#define ATP_SEQ 0 + +#define NBitID 8 + +#define FrameChar 0xa5 /* framing char at start and end */ +#define DLEChar 0x10 /* escape character */ +#define XONChar1 0x11 /* XON character, parity bit not set */ +#define XONChar2 0x91 /* XON character, parity bit set */ +#define XOFFChar1 0x13 /* XOFF charaacter, parity bit not set */ +#define XOFFChar2 0x93 /* XOFF charaacter, parity bit set */ +#define XOR 0x40 /* what we XOR escaped characters with */ + +#define CR 0x0d /* carriage return */ +#define CRP 0x8d /* carriage return with parity bit on */ +#define NL 0x0a /* line feed */ +#define NLP 0x8a /* line feed with parity bit on */ + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define UP 1 +#define DOWN 0 + +#define RTMPTIME 6 /* seconds */ + +#define u_char unsigned char + +struct aalap_hdr { /* aalap data + length + flag */ + u_char aalap_frame; + u_char aalap_type; + u_char aalap_data[AALAP + 3]; /* 600 + 2 + 1 (AALAP + CRC + frame) */ + u_char padding; + short aalap_datalen; + short aalap_framecomplete; +}; + +struct aalap_trlr { /* frame trailer */ + unsigned short aalap_crc; + u_char aalap_frame; +}; + +struct aalap_imur { /* data section of IM/UR frame */ + unsigned short aalap_net; + u_char aalap_node; +}; + +struct ap { /* DDP AppleTalk inside UDP */ + u_char ldst; + u_char lsrc; + u_char ltype; + u_char dd[10]; + u_char dstskt; + u_char srcskt; + u_char type; + u_char data[1024]; +}; + +struct ddp { /* DDP AppleTalk inside AALAP */ + unsigned short hop_and_length; + unsigned short ddp_checksum; + unsigned short ddp_dstNet; + unsigned short ddp_srcNet; + u_char ddp_dstNode; + u_char ddp_srcNode; + u_char ddp_dstSkt; + u_char ddp_srcSkt; + u_char ddp_type; + u_char ddp_data[586]; +}; + +struct sddp { /* short DDP AppleTalk inside AALAP */ + unsigned short hop_and_length; + u_char ddp_dstSkt; + u_char ddp_srcSkt; + u_char ddp_type; + u_char ddp_data[586]; +}; + +struct rtmp { /* RTMP in DDP data */ + unsigned short rtmp_senderNet; + u_char rtmp_idLen; + u_char rtmp_senderId +}; + +struct atp { /* ATP header in DDP data area */ + u_char atp_command; + u_char atp_bitseq; + unsigned short atp_transid; + u_char atp_user1; + u_char atp_user2; + u_char atp_user3; + u_char atp_user4; + u_char atp_data[578]; +}; diff --git a/contrib/AsyncATalk/asyncad.c b/contrib/AsyncATalk/asyncad.c new file mode 100644 index 0000000..9e6c49b --- /dev/null +++ b/contrib/AsyncATalk/asyncad.c @@ -0,0 +1,184 @@ +/* + * $Date: 91/03/14 14:27:01 $ + * $Header: asyncad.c,v 2.2 91/03/14 14:27:01 djh Exp $ + * $Log: asyncad.c,v $ + * Revision 2.2 91/03/14 14:27:01 djh + * Fall back to log(). + * + * Revision 2.1 91/02/15 21:21:03 djh + * CAP 6.0 Initial Revision + * + * Revision 1.1 90/03/17 22:05:51 djh + * Initial revision + * + * + * asyncad.c + * + * Asynchronous Appletalk broadcast redirector + * + * Asynchronous AppleTalk for CAP UNIX boxes. + * David Hornsby, djh@munnari.oz, August 1988 + * Department of Computer Science. + * The University of Melbourne, Parkville, 3052 + * Victoria, Australia. + * + * Copyright 1988, The University of Melbourne. + * + * You may use, modify and redistribute BUT NOT SELL this package provided + * that all changes/bug fixes are returned to the author for inclusion. + * + * Ref: Async Appletalk, Dr. Dobbs Journal, October 1987, p18. + * + * Listen on aaBroad for: + * 1. DDP broadcasts and redirect them to each async node + * 2. announcements of node arrivals and departures + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "async.h" + +#define asyncadlog "/usr/tmp/asyncadlog" + +int debug; +short node_is_up[MAXNODE]; +struct sockaddr_in sin, fsin; +struct ap ap; + +main(argc, argv) +char *argv[]; +{ + char *t; + int i, s, n; + int fsinlen; + char hostname[64]; + struct hostent *host, *gethostbyname(); + + while(--argc > 0 && (*++argv)[0] == '-') + for(t = argv[0]+1 ; *t != '\0' ; t++) + switch (*t) { + case 'd': + debug = 1; + break; + } + + for( i = 0 ; i < MAXNODE ; i++) + node_is_up[i] = 0; + + if(debug == 0) { + + int t, f; + + if(fork()) + exit(0); + + for(f = 0; f < 10; f++) + (void) close(f); + (void) open("/", 0); + (void) dup2(0, 1); + (void) dup2(0, 2); + if((t = open("/dev/tty", 2)) >= 0) { + ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } + + while ((s = socket(AF_INET, SOCK_DGRAM, 0, 0)) < 0) { + log("socket call failed"); + sleep(10); + } + + sin.sin_port = htons(aaBroad); + if(bind(s, (caddr_t) &sin, sizeof (sin), 0) < 0) { + log("bind call failed"); + exit(1); + } + + for( ; ; ) { + fsinlen = sizeof (fsin); + if((n = recvfrom(s, (caddr_t) &ap, sizeof(ap), + 0, (caddr_t) &fsin, &fsinlen)) < 0) { + log("recv failed"); + exit(1); + } + + if(ap.ldst == 0xC0 && ap.lsrc == 0xDE) { + /* an async node is registering with us */ + i = ap.srcskt & MAXNODE; /* who from */ + node_is_up[i] = ap.ltype; /* up or down */ + if(debug) + fprintf(stderr, + "node %d is %s",i,(ap.ltype)?"up":"down"); + continue; + } + + if(ap.ldst != 0xFA || ap.lsrc != 0xCE || ap.ltype != 2) { + log("found a dud packet"); + continue; /* not valid lap header */ + } + + /* here we have a valid broadcast, redirect it to nodes */ + + if(debug) + fprintf(stderr, "found a broadcast packet"); + + gethostname(hostname, sizeof(hostname)); + host = gethostbyname(hostname); + bcopy(host->h_addr, &fsin.sin_addr.s_addr, + sizeof(fsin.sin_addr.s_addr)); + + for(i = 1 ; i < MAXNODE ; i++) { /* 0 is invalid node # */ + if(node_is_up[i]) { + if(debug) + fprintf(stderr, "redirect to %d", i); + fsin.sin_port = htons((short) i + PortOffset); + if(sendto(s, (caddr_t) &ap, n, 0, &fsin, + sizeof(fsin)) != n) { + perror("sendto"); + log("sendto failed"); + exit(1); + } + } + } + } +} + + +/* + * log an error message + */ +log(msg) +char *msg; +{ + FILE *fp; + long time(), tloc; + struct tm *tm, *localtime(); + + if(debug) + fp = stderr; + else + if((fp = fopen(asyncadlog, "a+")) == NULL) + return; + + time(&tloc); + tm = localtime(&tloc); + fprintf(fp, "%d/%d %02d:%02d ", tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min); + fprintf(fp, msg); + putc('\n', fp); + + if(!debug) + fclose(fp); + else + fflush(fp); /* for SUN's and other junk */ +} diff --git a/contrib/AsyncATalk/atalkdbm.c b/contrib/AsyncATalk/atalkdbm.c new file mode 100644 index 0000000..019c575 --- /dev/null +++ b/contrib/AsyncATalk/atalkdbm.c @@ -0,0 +1,228 @@ +/* + * $Date: 91/02/15 21:21:05 $ + * $Header: atalkdbm.c,v 2.1 91/02/15 21:21:05 djh Rel $ + * $Log: atalkdbm.c,v $ + * Revision 2.1 91/02/15 21:21:05 djh + * CAP 6.0 Initial Revision + * + * Revision 1.2 91/01/04 22:43:52 djh + * We don't need this file if used with CAP 6.0, add some + * defines to suit. + * + * Revision 1.1 90/03/17 22:06:08 djh + * Initial revision + * + * + * From atalkdbm.c, Revision 1.13, Charlie Kim/CAP distribution. + * mods for Async AppleTalk: djh@munnari.oz, August, 1988 +*/ + +#include +#include +#include +#include +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else +# include +#endif + +#ifndef CAP_6_DBM + +/* + * The following globals are exported to the rest of CAP. + */ +u_short this_net = 0, bridge_net = 0, nis_net = 0, async_net; /* djh */ +u_char this_node, bridge_node,nis_node; +char this_zone[34], async_zone[34]; /* djh */ +struct in_addr bridge_addr; + +#ifndef TAB +# define TAB "/etc/atalk.local" +#endif TAB +#ifndef CONFIGDIR +# define CONFIGDIR "/etc" +#endif + +#include + +static int opened = 0; +#define HAVE_ZONE -1 /* our zone was set */ +#define CONFIGURED 1 /* set when configured */ + +/* + * Set zone name - sets alternate atalk configuration file: atalk. + * + * +*/ +zoneset(zonename) +char *zonename; +{ + strncpy(this_zone, zonename, 32); + opened = HAVE_ZONE; +} + +/* + * get base configuration + * +*/ +openatalkdb(name) +char *name; +{ + FILE *fp; + int a, b; + char fn[255]; + char line[256], st[64]; + int linecnt = 0; + char zonename[34]; /* temp */ + char bridge_name[64]; + char *p, *p2, c; + + if (opened == CONFIGURED) + return; + + if (name == NULL) { + if (opened == HAVE_ZONE) { + strcpy(fn, CONFIGDIR); + strcat(fn,"/atalk."); + a = strlen(fn); /* find where we are */ + p = fn+a; /* move to end */ + a = 0; + while ((c=this_zone[a++]) != '\0') + *p++ = (isascii(c) && isalpha(c) && isupper(c)) ? tolower(c) : c; + *p = '\0'; /* tie string */ + } else + strcpy(fn, TAB); + } else + strcpy(fn, name); + + if ((fp = fopen(fn, "r")) == NULL) { + perror(fn); + exit(1); + } + + while (fgets(line, sizeof line, fp) != NULL) { + linecnt++; /* remember which line */ + if (line[0] == '#' || line[0] == '\n') + continue; + if (this_net == 0) { + if (sscanf(line, "%s %d %s", st, &b, zonename) != 3) { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr, "openatalkdb: bad node id format: %s", line); + exit(1); + } + this_net = htons(atnetshort(st)); + this_node = (b); + /* convert zonename: __ means _ and "_" means space */ + for (p = zonename, p2=this_zone; *p != '\0'; ) { + if (*p == '_') { + p++; + if (*p == '_') { + *p2++ = '_'; + p++; /* skip */ + } else + *p2++ = ' '; + } else + *p2++ = *p++; /* just copy the byte */ + } + continue; + } + if (bridge_net == 0) { + if (sscanf(line, "%s %d %s", st, &b, bridge_name) != 3) { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr, "openatalkdb: bad bridge id format: %s", line); + exit(1); + } + bridge_net = htons(atnetshort(st)); + bridge_node = (b); + if (name_toipaddr(bridge_name, &bridge_addr) < 0) { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr,"openatalkdb: bridge \"%s\" is unknown: %s\n", + bridge_name,line); + exit(1); + } + continue; + } + if (nis_net == 0) { + if (sscanf(line, "%s %d", st, &b) != 2) { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr, "openatalkdb: bad NIS server id format: %s", line); + exit(1); + } + nis_net = htons(atnetshort(st)); + nis_node = (b); + continue; + } + /* djh */ + if (async_net == 0) { + if (sscanf(line, "%s %s", st, zonename) != 2) { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr, "openatalkdb: bad async id format: %s", line); + exit(1); + } + async_net = htons(atnetshort(st)); + /* convert zonename: __ means _ and "_" means space */ + for (p = zonename, p2=async_zone; *p != '\0'; ) { + if (*p == '_') { + p++; + if (*p == '_') { + *p2++ = '_'; + p++; /* skip */ + } else + *p2++ = ' '; + } else + *p2++ = *p++; /* just copy the byte */ + } + continue; + } + /* end djh */ + } + if (this_net == 0 || bridge_net == 0) { + fprintf(stderr, "openatalkdb: %s: node or bridge identification missing\n", + fn); + exit(1); + } + if (nis_net == 0) { /* usual case */ + nis_net = this_net; + nis_node = this_node; + } + opened++; + fclose(fp); +} + +/* + * Get a short number or address. + */ +atnetshort(st) +register char *st; +{ + register char *cp; + + if ((cp = index(st, '.')) == 0) + return (atoi(st)); + *cp++ = 0; + return ((atoi(st)<<8) | atoi(cp)); +} + + +static int +name_toipaddr(name, ipaddr) +char *name; +struct in_addr *ipaddr; +{ + struct hostent *host; + + if (isdigit(name[0])) { + if ((ipaddr->s_addr = inet_addr(name)) == -1) + return(-1); + return(0); + } + if ((host = gethostbyname(name)) == 0) + return(-1); + bcopy(host->h_addr, (caddr_t)&ipaddr->s_addr, sizeof(ipaddr->s_addr)); +} + +#endif CAP_6_DBM diff --git a/contrib/AsyncATalk/macros.h b/contrib/AsyncATalk/macros.h new file mode 100644 index 0000000..2982194 --- /dev/null +++ b/contrib/AsyncATalk/macros.h @@ -0,0 +1,51 @@ +/* + * $Date: 91/02/15 21:21:06 $ + * $Header: macros.h,v 2.1 91/02/15 21:21:06 djh Rel $ + * $Log: macros.h,v $ + * Revision 2.1 91/02/15 21:21:06 djh + * CAP 6.0 Initial Revision + * + * Revision 1.1 90/03/17 22:06:49 djh + * Initial revision + * + * + * macros.h + * + * Make nice macros work on 4.2BSD systems + * + * Asynchronous AppleTalk for CAP UNIX boxes. + * David Hornsby, djh@munnari.oz, August 1988 + * Department of Computer Science. + * The University of Melbourne, Parkville, 3052 + * Victoria, Australia. + * + * Copyright 1988, The University of Melbourne. + * + * You may use, modify and redistribute BUT NOT SELL this package provided + * that all changes/bug fixes are returned to the author for inclusion. + * + */ + +#ifndef NBBY +#define NBBY 8 /* number of bits in a byte */ +#endif + +#ifndef NFDBITS +#define NFDBITS (sizeof(int) * NBBY) /* bits per mask */ +#endif + +#ifndef FD_SET +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#endif + +#ifndef FD_CLR +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#endif + +#ifndef FD_ISSET +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#endif + +#ifndef FD_ZERO +#define FD_ZERO(p) bzero(p, sizeof(*(p))) +#endif diff --git a/contrib/AufsTools/MANIFEST b/contrib/AufsTools/MANIFEST new file mode 100644 index 0000000..499e24d --- /dev/null +++ b/contrib/AufsTools/MANIFEST @@ -0,0 +1,60 @@ + File Name Archive # Description +----------------------------------------------------------- + MANIFEST 1 This shipping list + Makefile 1 + README 1 + binhex 1 + binhex/8to6.c 1 + binhex/Makefile 1 + binhex/aufs.h 1 + binhex/binhex.c 1 + binhex/crc.c 1 + binhex/gethead.c 1 + binhex/run.c 1 + capit 1 + capit/Makefile 1 + capit/capit.c 2 + man 1 + man/binhex.1 1 + man/capit.1 1 + man/cleanup.1 1 + man/drag.1 1 + man/dup.1 1 + man/formatted 1 + man/m2u.1 1 + man/macp.1 1 + man/makeman 1 + man/mcvert.1 2 + man/newfolder.1 1 + man/sit.1 1 + man/toaufs.1 1 + man/trash.1 1 + man/unsit.1 1 + man/unstuffit.1 1 + mcvert 1 + mcvert/Makefile 1 + mcvert/hqxify.c 3 + mcvert/mactypes.h 2 + mcvert/mcvert.c 3 + mcvert/unpack.c 2 + shell 1 + shell/cleanup 1 + shell/drag 1 + shell/dup 1 + shell/m2u 1 + shell/macp 1 + shell/newfolder 1 + shell/toaufs 1 + shell/trash 1 + shell/u2m 1 + stuffit 1 + stuffit/Makefile 1 + stuffit/sit.c 3 + stuffit/sit.h 1 + stuffit/updcrc.c 2 + unstuffit 1 + unstuffit/Makefile 1 + unstuffit/getopt.c 1 + unstuffit/stuffit.h 1 + unstuffit/unsit.c 4 + unstuffit/updcrc.c 2 diff --git a/contrib/AufsTools/Makefile b/contrib/AufsTools/Makefile new file mode 100644 index 0000000..72acca0 --- /dev/null +++ b/contrib/AufsTools/Makefile @@ -0,0 +1,30 @@ +all: + (cd stuffit; make) + (cd unstuffit; make) + (cd binhex; make) + (cd mcvert; make) + (cd capit; make) + +install: + (cd stuffit; make install) + (cd unstuffit; make install) + (cd binhex; make install) + (cd mcvert; make install) + (cd capit; make install) + (cd shell; make install) + +clean: + (cd stuffit; make clean) + (cd unstuffit; make clean) + (cd binhex; make clean) + (cd mcvert; make clean) + (cd capit; make clean) + +reallyclean: clean + rm -f man/formatted/*.1 + +spotless: reallyclean + rm -f *.orig */*.orig + +format: + (cd man; ./makeman) diff --git a/contrib/AufsTools/README b/contrib/AufsTools/README new file mode 100644 index 0000000..c684fcb --- /dev/null +++ b/contrib/AufsTools/README @@ -0,0 +1,24 @@ +Public Domain Aufs Tools Package +================================ + +Nigel Perry, Dec 90. + +This package contains programs for use by users of the CAP aufs system. +Programs are provided to convert downloads direct into aufs files, handle +StuffIt archives and manage aufs files. Most programs are modified versions +of exisiting public domain utilites. To build just run make: + + make + builds binaries in their own directories + make install + builds binaries & moves them to $(BIN), default + /usr/local/cap. Also copies shell scripts to $(BIN). + make format + builds formatted versions of all the manual pages + in the directory formatted. + make clean + cleans up .o files etc. + make reallyclean + as clean + wipes aufslib & formatted + +Enjoy. diff --git a/contrib/AufsTools/binhex/8to6.c b/contrib/AufsTools/binhex/8to6.c new file mode 100644 index 0000000..b9e0de3 --- /dev/null +++ b/contrib/AufsTools/binhex/8to6.c @@ -0,0 +1,64 @@ +/* + * convert 8 bit data stream into 6 bit data stream + * + * David Gentzel, Lexeme Corporation + */ + +#include + +#define MAXLINELEN 62 + +static +char tr[]="!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr"; + +/* + * Output a character to the current output file converting from 8 bit to 6 bit + * representation. When called with EOF, flush all pending output. + */ +void putchar_6(c) +int c; +{ + static unsigned char buf[3]; + static unsigned int num_bytes = 0; + static count = 1; /* # of characters on current line */ + /* start at 1 to include colon */ + + if (c == EOF) /* flush buffer on EOF */ + { + while (num_bytes != 0) + putchar_6(0); + count = 1; /* for next file */ + return; + } + + buf[num_bytes++] = c; + if (num_bytes == 3) + { + + num_bytes = 0; + putchar(tr[buf[0] >> 2]); + if (count++ > MAXLINELEN) + { + count = 0; + putchar('\n'); + } + putchar(tr[((buf[0] & 0x03) << 4) | (buf[1] >> 4)]); + if (count++ > MAXLINELEN) + { + count = 0; + putchar('\n'); + } + putchar(tr[((buf[1] & 0x0F) << 2) | (buf[2] >> 6)]); + if (count++ > MAXLINELEN) + { + count = 0; + putchar('\n'); + } + putchar(tr[buf[2] & 0x3F]); + if (count++ > MAXLINELEN) + { + count = 0; + putchar('\n'); + } + } +} diff --git a/contrib/AufsTools/binhex/Makefile b/contrib/AufsTools/binhex/Makefile new file mode 100644 index 0000000..d2fe629 --- /dev/null +++ b/contrib/AufsTools/binhex/Makefile @@ -0,0 +1,35 @@ +# for Solaris 2.X +# CFLAGS= -O +# +CFLAGS= -O -Dstrrchr=rindex +LFLAGS= +CFILES= binhex.c gethead.c crc.c run.c 8to6.c +OFILES= binhex.o gethead.o crc.o run.o 8to6.o +DESTDIR= /usr/local/cap + +all: binhex unxbin + +unxbin : binhex + -rm -f unxbin + ln binhex unxbin + +binhex: $(OFILES) + cc -o binhex $(OFILES) + +binhex.o: Makefile +gethead.o: Makefile + +debug: + cc -o binhexd -g $(CFILES) + +lint: + lint $(LFLAGS) $(CFILES) + +install: binhex + -strip binhex + cp binhex $(DESTDIR) + -rm -f $(DESTDIR)/unxbin + (cd $(DESTDIR); ln binhex unxbin) + +clean: + -rm -f binhex unxbin $(OFILES) diff --git a/contrib/AufsTools/binhex/aufs.h b/contrib/AufsTools/binhex/aufs.h new file mode 100644 index 0000000..6a81081 --- /dev/null +++ b/contrib/AufsTools/binhex/aufs.h @@ -0,0 +1,95 @@ +/********************************************************************************/ +/* added for aufs, nicked from various places... */ + +#ifdef sun +#ifdef __svr4__ +#define SOLARIS +#endif __svr4__ +#endif sun + +/* following from mcvert program */ + +/* Useful, though not particularly Mac related, values */ +typedef unsigned char byte; /* one byte, obviously */ +typedef unsigned short word; /* must be 2 bytes */ +#ifndef SOLARIS +typedef unsigned long ulong; /* 4 bytes */ +#endif SOLARIS + +#define NAMELEN 63 /* maximum legal Mac file name length */ + +/* Format of a bin file: +A bin file is composed of 128 byte blocks. The first block is the +info_header (see below). Then comes the data fork, null padded to fill the +last block. Then comes the resource fork, padded to fill the last block. A +proposal to follow with the text of the Get Info box has not been implemented, +to the best of my knowledge. Version, zero1 and zero2 are what the receiving +program looks at to determine if a MacBinary transfer is being initiated. +*/ +typedef struct { /* info file header (128 bytes). Unfortunately, these + longs don't align to word boundaries */ + byte version; /* there is only a version 0 at this time */ + byte nlen; /* Length of filename. */ + byte name[NAMELEN]; /* Filename (only 1st nlen are significant)*/ + byte type[4]; /* File type. */ + byte auth[4]; /* File creator. */ + byte flags; /* file flags: LkIvBnSyBzByChIt */ + byte zero1; /* Locked, Invisible,Bundle, System */ + /* Bozo, Busy, Changed, Init */ + byte icon_vert[2]; /* Vertical icon position within window */ + byte icon_horiz[2]; /* Horizontal icon postion in window */ + byte window_id[2]; /* Window or folder ID. */ + byte protect; /* = 1 for protected file, 0 otherwise */ + byte zero2; + byte dflen[4]; /* Data Fork length (bytes) - most sig. */ + byte rflen[4]; /* Resource Fork length byte first */ + byte cdate[4]; /* File's creation date. */ + byte mdate[4]; /* File's "last modified" date. */ + byte ilen[2]; /* GetInfo message length */ + byte flags2; /* Finder flags, bits 0-7 */ + byte unused[14]; + byte packlen[4]; /* length of total files when unpacked */ + byte headlen[2]; /* length of secondary header */ + byte uploadvers; /* Version of MacBinary II that the uploading program is written for */ + byte readvers; /* Minimum MacBinary II version needed to read this file */ + byte crc[2]; /* CRC of the previous 124 bytes */ + byte padding[2]; /* two trailing unused bytes */ + } info_header; + +/* end of mcvert stuff */ +/* from CAP aufs documentation */ + +#define FINFOLEN 32 +#define MAXCLEN 199 +typedef struct +{ + /* be careful with alignment */ + byte fndr_type[4]; + byte fndr_creator[4]; + word fndr_flags; + byte fndr_loc[4]; + word fndr_fldr; + word fndr_icon; + byte fndr_unused[8]; + word fndr_comment; + byte fndr_putaway[4]; + word fi_attr; /* attributes */ +#define FI_MAGIC1 255 + byte fi_magic1; /* was: length of comment */ +#define FI_VERSION 0x10 /* version major 1, minor 0 */ + /* if more than 8 versions then */ + /* something wrong anyway */ + byte fi_version; /* version number */ +#define FI_MAGIC 0xda + byte fi_magic; /* magic word check */ + byte fi_bitmap; /* bitmap of included info */ +#define FI_BM_SHORTFILENAME 0x1 /* is this included? */ +#define FI_BM_MACINTOSHFILENAME 0x2 /* is this included? */ + byte fi_shortfilename[12+1]; /* possible short file name */ + byte fi_macfilename[32+1]; /* possible macintosh file name */ + byte fi_comln; /* comment length */ + byte fi_comnt[MAXCLEN+1]; /* comment string */ +} FinderInfo; + +/* end aufs */ +/********************************************************************************/ diff --git a/contrib/AufsTools/binhex/binhex.c b/contrib/AufsTools/binhex/binhex.c new file mode 100644 index 0000000..6286d56 --- /dev/null +++ b/contrib/AufsTools/binhex/binhex.c @@ -0,0 +1,192 @@ +/* + * binhex -- binhex aufs files + * + * if called binhex handles aufs files, if called unxbin handles *.{rsrc,data,info} files + * will process multiple files + * + * Nigel Perry, Aug 90, np@doc.ic.ac.uk + * + * This is a hacked version of... + * + * unxbin -- convert files generated by xbin or macget into BinHex 4.0 format. + * + * David Gentzel, Lexeme Corporation + * + * (c) 1985 David Gentzel + * may be used but not sold without permission + * + * This is based on a Unix(tm) program with the same name and function written + * by ????. Original was a series of small programs (8to6, crc, etc.) piped + * together and run by a shell script. I completely rewrote the system as a + * C program (speeding it up considerably, needless to say), added run-length + * compression, and bullet-proofed (at least partly) the thing. Unfortunately, + * I have lost the name of the original poster (to net.sources.mac) without + * whom this would never have appeared. + * + * created dbg 09/10/85 -- Version 1.0 + */ + +#ifdef sun +#ifdef __svr4__ +#define SOLARIS +#endif __svr4__ +#endif sun + +#include +#if defined(hpux) || defined(SOLARIS) +#include +#include +#endif /* hpux || SOLARIS */ + +#include "aufs.h" + +#ifdef VMS +# define PROGRAMNAME "unxbin" +# define EXIT_ERROR ((1 << 28) | 2) +# ifndef MAXNAMLEN +# define MAXNAMLEN 127 +# define MAXBASENAME 63 +# endif +#else VMS +# include +# ifdef SOLARIS +# include +# else SOLARIS +# include +# endif SOLARIS +# define PROGRAMNAME (argv[0]) +# define AUFSNAME "binhex" +# define EXIT_ERROR 1 +# ifndef MAXNAMLEN +# ifdef DIRSIZ +# define MAXNAMLEN DIRSIZ +# else +# define MAXNAMLEN 14 +# endif +# endif +# define MAXBASENAME (MAXNAMLEN - 2) +#endif VMS + +#if !defined(hpux) && !defined(SOLARIS) +extern char *sprintf(), *strrchr(); +#endif /* !hpux && !SOLARIS */ + +extern void aufs_gethead(), gethead(), fakehead(), make_buffer_crc(), make_file_crc(), + putchar_run(); + +main(argc, argv) +int argc; +register char *argv[]; +{ + register FILE *rsrc, *data, *info; + char fbuf[256], infobuf[128]; + register char *file; + int aufs; + int i; + char *s, *progname; + FinderInfo fndr_info; + + progname = PROGRAMNAME; +#ifndef VMS + if ((s = (char *) strrchr(progname, '/')) != NULL) + progname = ++s; +#endif VMS + aufs = strcmp(progname, AUFSNAME) == 0; + + if (argc < 2) + { + fprintf(stderr, "Usage: %s file(s)\n", progname); + exit(EXIT_ERROR); + } + + for(i = 1; i < argc; i++) + { +#ifdef VMS + if ((file = strrchr(argv[i], ']')) == NULL) + file = strrchr(argv[i], ':'); +#else + file = strrchr(argv[i], '/'); +#endif + if (file) + file++; + else + file = argv[i]; + if (strlen(file) > MAXBASENAME) + file[MAXBASENAME] = '\0'; + file = argv[i]; + (void) sprintf(fbuf, aufs ? ".resource/%s" : "%s.rsrc", file); + fbuf[MAXNAMLEN] = '\0'; + rsrc = fopen(fbuf, "r"); + (void) sprintf(fbuf, aufs ? "%s" : "%s.data", file); + fbuf[MAXNAMLEN] = '\0'; + data = fopen(fbuf, "r"); + if (rsrc == NULL && data == NULL) + { + fprintf(stderr, "No resource or data forks for %s\n", argv[i]); + exit(EXIT_ERROR); + } + if (rsrc == NULL) + fprintf(stderr, "Warning: no resource file %s\n", fbuf); + if (data == NULL) + fprintf(stderr, "Warning: no data file %s\n", fbuf); + (void) sprintf(fbuf, aufs ? ".finderinfo/%s" : "%s.info", file); + fbuf[MAXNAMLEN] = '\0'; + info = fopen(fbuf, "r"); + if (info == NULL) + fprintf(stderr, "Warning: no info file %s\n", fbuf); + + if(aufs) + { /* make the .finderinfo file */ + FILE *oinfo; + + sprintf(fbuf, ".finderinfo/%s.Hqx", file); + if((oinfo = fopen(fbuf, "w")) == NULL) + { perror(fbuf); + exit(1); + } + bzero(&fndr_info, sizeof(FinderInfo)); + bcopy("TEXT", fndr_info.fndr_type, 4); + bcopy("BnHq", fndr_info.fndr_creator, 4); + fndr_info.fi_magic1 = FI_MAGIC1; + fndr_info.fi_version = FI_VERSION; + fndr_info.fi_magic = FI_MAGIC; + fndr_info.fi_bitmap = FI_BM_MACINTOSHFILENAME; + strcpy(fndr_info.fi_macfilename, file); + fwrite(&fndr_info, sizeof(FinderInfo), 1, oinfo); + fclose(oinfo); + } + + (void) sprintf(fbuf, "%s.Hqx", file); + fbuf[MAXNAMLEN] = '\0'; + if (freopen(fbuf, "w", stdout) == NULL) + { + fputs("Couldn't open output file.\n", stderr); + exit(EXIT_ERROR); + } + fputs("(This file must be converted with BinHex 4.0)\n:", stdout); + if (info != NULL) + { if(aufs) + { (void) fread(&fndr_info, sizeof(FinderInfo), 1, info); + (void) fclose(info); + aufs_gethead(&fndr_info, data, rsrc, infobuf); + } + else + { (void) fread(fbuf, 128, 1, info); + (void) fclose(info); + gethead(fbuf, infobuf); + } + } + else + fakehead(file, rsrc, data, infobuf); + make_buffer_crc(infobuf, 20 + infobuf[0]); + make_file_crc(data); + if (data != NULL) + (void) fclose(data); + make_file_crc(rsrc); + if (rsrc != NULL) + (void) fclose(rsrc); + putchar_run(EOF); + puts(":"); + } + (void) fclose(stdout); +} diff --git a/contrib/AufsTools/binhex/crc.c b/contrib/AufsTools/binhex/crc.c new file mode 100644 index 0000000..fc7107e --- /dev/null +++ b/contrib/AufsTools/binhex/crc.c @@ -0,0 +1,81 @@ +/* + * compute the crc used in .hqx files for unxbin + * + * David Gentzel, Lexeme Corporation + * + * Based on crc code in xbin.c by Darin Adler of TMQ Software. + */ + +#include + +#define BYTEMASK 0xFF +#define WORDMASK 0xFFFF +#define WORDBIT 0x10000 + +#define CRCCONSTANT 0x1021 + +static unsigned int crc; + +extern void putchar_run(); + +/* + * Update the crc. + */ +static void docrc(c) +register unsigned int c; +{ + register int i; + register unsigned long temp = crc; + + for (i = 0; i < 8; i++) + { + c <<= 1; + if ((temp <<= 1) & WORDBIT) + temp = (temp & WORDMASK) ^ CRCCONSTANT; + temp ^= (c >> 8); + c &= BYTEMASK; + } + crc = temp; +} + +/* + * Copy all characters from file in to the current output file computing a crc + * as we go. Append 2 byte crc to the output. in may be NULL (empty file). + */ +void make_file_crc(in) +register FILE *in; +{ + register int c; + + crc = 0; + if (in != NULL) + while ((c = getc(in)) != EOF) + { + putchar_run(c); + docrc(c); + } + docrc(0); + docrc(0); + putchar_run((crc >> 8) & BYTEMASK); + putchar_run(crc & BYTEMASK); +} + +/* + * Copy count characters from buffer in to the current output file computing a + * crc as we go. Append 2 byte crc to the output. + */ +void make_buffer_crc(in, count) +register unsigned char *in; +register int count; +{ + crc = 0; + while (count--) + { + putchar_run(*in); + docrc(*in++); + } + docrc(0); + docrc(0); + putchar_run((crc >> 8) & BYTEMASK); + putchar_run(crc & BYTEMASK); +} diff --git a/contrib/AufsTools/binhex/gethead.c b/contrib/AufsTools/binhex/gethead.c new file mode 100644 index 0000000..050f86d --- /dev/null +++ b/contrib/AufsTools/binhex/gethead.c @@ -0,0 +1,172 @@ +/* + * Change a .info file into a proper header for a .hqx file + * + * David Gentzel, Lexeme Corporation + * + * Based on code written by ????. + */ + +#include +#ifdef VMS +# include +# include +#else +# include +# include +#endif + +#include "aufs.h" + +#define NAMEBYTES 63 +#define H_NLENOFF 1 +#define H_NAMEOFF 2 + +/* 65 <-> 80 is the FInfo structure */ +#define H_TYPEOFF 65 +#define H_AUTHOFF 69 +#define H_FLAGOFF 73 + +#define H_LOCKOFF 81 +#define H_DLENOFF 83 +#define H_RLENOFF 87 +#define H_CTIMOFF 91 +#define H_MTIMOFF 95 + +/* Append cnt bytes to the output buffer starting at head[offset]. */ +#define put(cnt, offset) \ +{ \ + register char *a = &head[(int) offset]; \ + register int b = (int) (cnt); \ + \ + while (b--) \ + *out++ = *a++; \ +} + +/* Append cnt bytes to the output buffer starting at string. */ +#define put2(cnt, string) \ +{ \ + register int b = (int) (cnt); \ + register char *a = (char *) (string); \ + \ + while (b--) \ + *out++ = *a++; \ +} + +/* Append cnt bytes to the output buffer starting at string + (cnt - 1) and + working backwards. */ +#define put2rev(cnt, string) \ +{ \ + register int b = (int) (cnt); \ + register char *a = (char *) (string) + b; \ + \ + while (b--) \ + *out++ = *--a; \ +} + +/* Build a usable header out of the .info information. head is the text from + the .info file, out is an output buffer. */ +void gethead(head, out) +register char *head, *out; +{ + put(1, H_NLENOFF); /* Name length */ + put(head[1], H_NAMEOFF); /* Name */ + put(1, 0); /* NULL */ + put(4, H_TYPEOFF); /* Type */ + put(4, H_AUTHOFF); /* Author */ + put(2, H_FLAGOFF); /* Flags */ + put(4, H_DLENOFF); /* Data length */ + put(4, H_RLENOFF); /* Resource length */ +} + +/* Build a usable header out of the .finderinfo information. + out is an output buffer. */ +void aufs_gethead(info, data, rsrc, out) +register char *out; +register FinderInfo *info; +FILE *data, *rsrc; +{ register int len; + long rlen, dlen; + struct stat st; + + if(info->fi_bitmap & FI_BM_MACINTOSHFILENAME) + { len = strlen(info->fi_macfilename); + *out++ = (char)len; + put2(len+1, info->fi_macfilename); + } + else + { len = strlen(info->fi_shortfilename); + *out++ = (char)len; + put2(len+1, info->fi_shortfilename); + } + put2(4, info->fndr_type); /* Type */ + put2(4, info->fndr_creator); /* Author */ + put2(2, &info->fndr_flags); /* Flags */ + + if (rsrc != NULL) + { + (void) fstat(fileno(rsrc), &st); + rlen = (long) st.st_size; + } + else + rlen = 0L; + if (data != NULL) + { + (void) fstat(fileno(data), &st); + dlen = (long) st.st_size; + } + else + dlen = 0L; + put2(4, &dlen); /* Data length */ + put2(4, &rlen); /* Resource length */ +} + +/* Fake a usable header (there was no .info file). */ +/* VMS NOTE: + It is possible that the use of fstat to figure the sizes of the + .data and .rsrc files will not work correctly if they are not + Stream_LF files. Not easy to get around, but not very common either + (will only cause problem if .info file is missing and either .data + or .rsrc is not Stream_LF, and xbin creates Stream_LF files). +*/ +void fakehead(file, rsrc, data, out) +char *file; +FILE *rsrc, *data; +register char *out; +{ + unsigned char flen; + long rlen, dlen; + char flags[2]; + struct stat st; + + flen = (unsigned char) strlen(file); + if (rsrc != NULL) + { + (void) fstat(fileno(rsrc), &st); + rlen = (long) st.st_size; + } + else + rlen = 0L; + if (data != NULL) + { + (void) fstat(fileno(data), &st); + dlen = (long) st.st_size; + } + else + dlen = 0L; + flags[0] = '\0'; + flags[1] = '\0'; + + put2(1, &flen); /* Name length */ + put2(flen, file); /* Name */ + put2(1, ""); /* NULL */ + put2(4, "TEXT"); /* Type */ + put2(4, "????"); /* Author */ + put2(2, flags); /* Flags */ +#ifdef DONTSWAPINT + put2(4, dlen); /* Data length */ + put2(4, rlen); /* Resource length */ +#else + put2rev(4, dlen); /* Data length */ + put2rev(4, rlen); /* Resource length */ +#endif +} diff --git a/contrib/AufsTools/binhex/run.c b/contrib/AufsTools/binhex/run.c new file mode 100644 index 0000000..24d72fc --- /dev/null +++ b/contrib/AufsTools/binhex/run.c @@ -0,0 +1,59 @@ +/* + * do run length compression for unxbin + * + * David Gentzel, Lexeme Corporation + */ + +#include + +#define RUNCHAR 0x90 +#define MAXREP 255 + +extern void putchar_6(); + +/* + * Output a character to the current output file generating run length + * compression on the fly. All output goes through putchar_6 to do conversion + * from 8 bit to 6 bit format. When c == EOF, call putchar_6 with EOF to flush + * pending output. + */ +void putchar_run(c) +register int c; +{ + static unsigned int rep = 1; /* # of repititions of lastc seen */ + static int lastc = EOF; /* last character passed to us */ + + if (c == lastc) /* increment rep */ + { + /* repetition count limited to MAXREP */ + if (++rep == MAXREP) + { + putchar_6(RUNCHAR); + putchar_6(MAXREP); + rep = 1; + lastc = EOF; + } + } + else + { + switch (rep) + { + case 2: /* not worth running for only 2 reps... */ + putchar_6(lastc); + if (lastc == RUNCHAR) + putchar_6(0); + break; + case 1: + break; + default: + putchar_6(RUNCHAR); + putchar_6(rep); + break; + } + putchar_6(c); /* output character (EOF flushes) */ + rep = 1; + if (c == RUNCHAR) + putchar_6(0); + lastc = c; + } +} diff --git a/contrib/AufsTools/capit/Makefile b/contrib/AufsTools/capit/Makefile new file mode 100644 index 0000000..bd80e92 --- /dev/null +++ b/contrib/AufsTools/capit/Makefile @@ -0,0 +1,11 @@ +DESTDIR= /usr/local/cap + +capit: capit.c + cc -o capit -O capit.c + +install: capit + -strip capit + cp capit $(DESTDIR) + +clean: + -rm -f capit *.o diff --git a/contrib/AufsTools/capit/capit.c b/contrib/AufsTools/capit/capit.c new file mode 100644 index 0000000..cf509fb --- /dev/null +++ b/contrib/AufsTools/capit/capit.c @@ -0,0 +1,284 @@ +/* unmacbin - reverse of macbin - change a MacBinary file back in to + the .info .data .rsrc style that xbin, macput and macget understand. + Stole some from macbin. */ + +/* Written by John M. Sellens, jmsellens@watdragon.uwaterloo.ca, + Math Faculty Computing Facility, + University of Waterloo + Waterloo, Ontario, Canada + N2L 3G1 */ + +/* capit - convert a macbin file into a CAP file on a CAP Unix Mac disc. + Basically: + file.data => file + file.rsrc => .resource/file + file.info => mangle it then .finderinfo/file + + Nigel Perry, Dept of Computing, Imperial College, London SW7 2BZ, England. July 90. + np@doc.ic.ac.uk + */ + +#ifdef sun +#ifdef __svr4__ +#define SOLARIS +#endif __svr4__ +#endif sun + +#include +#include +#include + +#ifdef SOLARIS +#include +#include +#include +#else SOLARIS +#include +#include +#endif SOLARIS + +#define BSIZE (128) /* size of blocks in MacBinary file */ +typedef long int4; + + +/* following from mcvert program */ + +/* Useful, though not particularly Mac related, values */ +typedef unsigned char byte; /* one byte, obviously */ +typedef unsigned short word; /* must be 2 bytes */ +#ifndef SOLARIS +typedef unsigned long ulong; /* 4 bytes */ +#endif SOLARIS + +#define NAMELEN 63 /* maximum legal Mac file name length */ + +/* Format of a bin file: +A bin file is composed of 128 byte blocks. The first block is the +info_header (see below). Then comes the data fork, null padded to fill the +last block. Then comes the resource fork, padded to fill the last block. A +proposal to follow with the text of the Get Info box has not been implemented, +to the best of my knowledge. Version, zero1 and zero2 are what the receiving +program looks at to determine if a MacBinary transfer is being initiated. +*/ +typedef struct { /* info file header (128 bytes). Unfortunately, these + longs don't align to word boundaries */ + byte version; /* there is only a version 0 at this time */ + byte nlen; /* Length of filename. */ + byte name[NAMELEN]; /* Filename (only 1st nlen are significant)*/ + byte type[4]; /* File type. */ + byte auth[4]; /* File creator. */ + byte flags; /* file flags: LkIvBnSyBzByChIt */ + byte zero1; /* Locked, Invisible,Bundle, System */ + /* Bozo, Busy, Changed, Init */ + byte icon_vert[2]; /* Vertical icon position within window */ + byte icon_horiz[2]; /* Horizontal icon postion in window */ + byte window_id[2]; /* Window or folder ID. */ + byte protect; /* = 1 for protected file, 0 otherwise */ + byte zero2; + byte dflen[4]; /* Data Fork length (bytes) - most sig. */ + byte rflen[4]; /* Resource Fork length byte first */ + byte cdate[4]; /* File's creation date. */ + byte mdate[4]; /* File's "last modified" date. */ + byte ilen[2]; /* GetInfo message length */ + byte flags2; /* Finder flags, bits 0-7 */ + byte unused[14]; + byte packlen[4]; /* length of total files when unpacked */ + byte headlen[2]; /* length of secondary header */ + byte uploadvers; /* Version of MacBinary II that the uploading program is written for */ + byte readvers; /* Minimum MacBinary II version needed to read this file */ + byte crc[2]; /* CRC of the previous 124 bytes */ + byte padding[2]; /* two trailing unused bytes */ + } info_header; + +/* end of mcvert stuff */ +/* from CAP aufs documentation */ + +#define FINFOLEN 32 +#define MAXCLEN 199 +typedef struct +{ + /* be careful with alignment */ + byte fndr_type[4]; + byte fndr_creator[4]; + word fndr_flags; + byte fndr_loc[4]; + word fndr_fldr; + word fndr_icon; + byte fndr_unused[8]; + word fndr_comment; + byte fndr_putaway[4]; + word fi_attr; /* attributes */ +#define FI_MAGIC1 255 + byte fi_magic1; /* was: length of comment */ +#define FI_VERSION 0x10 /* version major 1, minor 0 */ + /* if more than 8 versions then */ + /* something wrong anyway */ + byte fi_version; /* version number */ +#define FI_MAGIC 0xda + byte fi_magic; /* magic word check */ + byte fi_bitmap; /* bitmap of included info */ +#define FI_BM_SHORTFILENAME 0x1 /* is this included? */ +#define FI_BM_MACINTOSHFILENAME 0x2 /* is this included? */ + byte fi_shortfilename[12+1]; /* possible short file name */ + byte fi_macfilename[32+1]; /* possible macintosh file name */ + byte fi_comln; /* comment length */ + byte fi_comnt[MAXCLEN+1]; /* comment string */ +} FileInfo; + +/* end aufs */ + +static info_header info; +static FileInfo fndr_info; + +static union trans { + int4 num; + char ch[4]; +} trans; + + +main(argc,argv) +int argc; +char **argv; +{ + FILE *fp, *ofp; + char bname[MAXNAMLEN]; + char iname[MAXNAMLEN]; + char dname[MAXNAMLEN]; + char rname[MAXNAMLEN]; + char buf[BSIZE]; + char * charp; + int verbose = 0; + int len; + int arg; + int err = 0; + long dflen, rflen; + char *ext, *disc; + extern char *getenv(); + + if((ext = getenv("MAC_EXT")) == NULL) ext = ".bin"; + if((disc = getenv("MAC_DISC")) == NULL) disc = "."; + + arg = 1; + if (argc > 1 && strcmp(argv[1], "-v") == 0 ) { + verbose = 1; + ++arg; + } + if ( arg >= argc ) { + fprintf(stderr,"%s: Usage: %s [-v] filename(s)\n",argv[0],argv[0]); + exit(1); + } + for ( ; arg < argc; arg++ ) { + if ( (charp = rindex (argv[arg], '.')) != NULL + && strcmp(charp, ext) == 0 ) { + *charp = '\0'; + strcpy(bname, argv[arg]); + *charp = '.'; + } else + strcpy(bname, argv[arg]); + + sprintf(iname, "%s/.finderinfo/%s", disc, bname); + sprintf(dname, "%s/%s", disc, bname); + sprintf(rname, "%s/.resource/%s", disc, bname); + + if (verbose) + printf( "Converting '%s'\n", argv[arg] ); + if ( (fp = fopen( argv[arg], "r" )) == NULL ) { + fprintf( stderr, "%s: couldn't open '%s' for reading", + argv[0], argv[arg] ); + perror( "" ); + exit(++err); + } + if ( fread(&info, sizeof(info), 1, fp) <= 0 ) { + fprintf( stderr, "%s: couldn't read .info header from '%s'", + argv[0], argv[arg] ); + perror( "" ); + exit(++err); + } + if ( info.zero1 || info.zero2 || info.version ) { + fprintf( stderr, "%s: '%s' is not in MacBinary format - skipped\n", + argv[0], argv[arg] ); + ++err; + continue; + } + + /* make the .finderinfo file */ + bzero(&fndr_info, sizeof(FileInfo)); + bcopy(info.type, fndr_info.fndr_type, 4); + bcopy(info.auth, fndr_info.fndr_creator, 4); + if(info.protect == '\1' ) fndr_info.fndr_flags = 0x40; /* maybe? */ + fndr_info.fi_magic1 = FI_MAGIC1; + fndr_info.fi_version = FI_VERSION; + fndr_info.fi_magic = FI_MAGIC; + fndr_info.fi_bitmap = FI_BM_MACINTOSHFILENAME; + bcopy(info.name, fndr_info.fi_macfilename, info.nlen); + + /* write the .finderinfo file */ + if ( (ofp = fopen( iname, "w" )) == NULL ) { + fprintf( stderr, "%s: couldn't open '%s' for writing", + argv[0], iname ); + perror( "" ); + exit(++err); + } + fwrite( &fndr_info, sizeof(FileInfo), 1, ofp ); + fclose( ofp ); + + /* It appears that the .data and .rsrc parts of the MacBinary file + are padded to the nearest 128 (BSIZE) byte boundary, but they + should be trimmed to their proper size when we split them. */ + + trans.ch[0] = info.dflen[0]; trans.ch[1] = info.dflen[1]; + trans.ch[2] = info.dflen[2]; trans.ch[3] = info.dflen[3]; + dflen = ntohl( trans.num ); + trans.ch[0] = info.rflen[0]; trans.ch[1] = info.rflen[1]; + trans.ch[2] = info.rflen[2]; trans.ch[3] = info.rflen[3]; + rflen = ntohl( trans.num ); + + /* write the data fork */ + if ( (ofp = fopen( dname, "w" )) == NULL ) { + fprintf( stderr, "%s: couldn't open '%s' for writing", + argv[0], dname ); + perror( "" ); + exit(++err); + } + for ( len=0; len dflen ) + fwrite( buf, sizeof(char), BSIZE-len+dflen, ofp ); + else + fwrite( buf, sizeof(char), BSIZE, ofp ); + } + fclose( ofp ); + + /* write the .resource file */ + if ( (ofp = fopen( rname, "w" )) == NULL ) { + fprintf( stderr, "%s: couldn't open '%s' for writing", + argv[0], rname ); + perror( "" ); + exit(++err); + } + for ( len=0; len rflen ) + fwrite( buf, sizeof(char), BSIZE-len+rflen, ofp ); + else + fwrite( buf, sizeof(char), BSIZE, ofp ); + } + fclose( ofp ); + fclose( fp ); + } + exit( err ); +} diff --git a/contrib/AufsTools/man/binhex.1 b/contrib/AufsTools/man/binhex.1 new file mode 100644 index 0000000..970b664 --- /dev/null +++ b/contrib/AufsTools/man/binhex.1 @@ -0,0 +1,23 @@ +.TH BINHEX L "December 1990" +.UC +.SH NAME +binhex \- convert CAP aufs file(s) to Binhex 4.0 Unix file(s) +unxbin \- ditto but for *.{rsrc,data,info} format files +.SH SYNOPSIS +.B binhex +file ... +.br +.B unxbin +file ... +.br +.SH DESCRIPTION +.I Binhex +converts CAP aufs file(s) to BinHex 4.0 format and stores them +as Unix text (i.e. using Unix LF not Mac CR). +.I Unxbin +does the same but for *.{rsrc,data,info} format files. +.SH AUTHORS +binhex: Nigel Perry (np@doc.ic.ac.uk), based on +.br +unxbin: David Gentzel, Lexeme Corporation + diff --git a/contrib/AufsTools/man/capit.1 b/contrib/AufsTools/man/capit.1 new file mode 100644 index 0000000..58a92d4 --- /dev/null +++ b/contrib/AufsTools/man/capit.1 @@ -0,0 +1,22 @@ +.TH CAPIT L "Decemeber 1990" +.UC +.SH NAME +capit \- convert a MacBinary file into a CAP aufs one. +.SH SYNOPSIS +.B capit +[ +.B \-v +] file ... +.br +.SH DESCRIPTION +.I Capit +converts files from MacBinary to CAP aufs format and writes them to the current directory, +which should be a CAP aufs folder. +.SH OPTION +.TP +.B \-v +Verbose option. Causes +.I capit +to list name of each file converted. +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) based on macbin by John M. Sellens (jmsellens@watdragon.uwaterloo.ca). diff --git a/contrib/AufsTools/man/cleanup.1 b/contrib/AufsTools/man/cleanup.1 new file mode 100644 index 0000000..1caa6e4 --- /dev/null +++ b/contrib/AufsTools/man/cleanup.1 @@ -0,0 +1,12 @@ +.TH CLEANUP L "December 1990" +.UC +.SH NAME +cleanup \- cleanup the current CAP aufs folder +.SH SYNOPSIS +.B cleanup +.SH DESCRIPTION +.I Cleanup +removes orphaned files stored in the hidden directories of a CAP aufs folder. +These occur when Unix commands such as mv & rm are used on CAp aufs files. +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) diff --git a/contrib/AufsTools/man/drag.1 b/contrib/AufsTools/man/drag.1 new file mode 100644 index 0000000..2c38692 --- /dev/null +++ b/contrib/AufsTools/man/drag.1 @@ -0,0 +1,18 @@ +.TH DRAG L "December 1990" +.UC +.SH NAME +drag \- rename/move CAP aufs file(s) +.SH SYNOPSIS +.B drag +file file +.br +.B drag +f1 f2 ... folder +.SH DESCRIPTION +.I Drag +renames a CAP aufs file, or if there are multiple arguments moves CAP aufs files from one folder to another. +Do not use +.I mv +to rename/move files as this does not handle the hidden CAP aufs files. +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) diff --git a/contrib/AufsTools/man/dup.1 b/contrib/AufsTools/man/dup.1 new file mode 100644 index 0000000..7917689 --- /dev/null +++ b/contrib/AufsTools/man/dup.1 @@ -0,0 +1,20 @@ +.TH DUP L "December 1990" +.UC +.SH NAME +dup \- copy CAP aufs file(s) +.SH SYNOPSIS +.B dup +file file +.br +.B dup +f1 f2 ... folder +.br +.SH DESCRIPTION +.I Dup +duplicates a CAP aufs file, if given multiple arguments the last must be +a folder and each file will be copied into it. +Do not use +.I cp +to duplicate files as this does not handle the hidden CAP aufs files. +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) diff --git a/contrib/AufsTools/man/m2u.1 b/contrib/AufsTools/man/m2u.1 new file mode 100644 index 0000000..be04763 --- /dev/null +++ b/contrib/AufsTools/man/m2u.1 @@ -0,0 +1,17 @@ +.TH M2U L "December 1990" +.UC +.SH NAME +m2u \- convert file from Mac to Unix text +.br +u2m \- convert file from Unix to Mac text +.SH SYNOPSIS +.B m2u +file ... +.br +.B u2m +file ... +.SH DESCRIPTION +These commands convert files between Mac & Unix text formats i.e. +they convert CR's to LF's and vice versa. +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) diff --git a/contrib/AufsTools/man/macp.1 b/contrib/AufsTools/man/macp.1 new file mode 100644 index 0000000..f6f1997 --- /dev/null +++ b/contrib/AufsTools/man/macp.1 @@ -0,0 +1,16 @@ +.TH MACP L "December 1990" +.UC +.SH NAME +macp \- list an CAP aufs file +.SH SYNOPSIS +.B macp +file ... +.SH DESCRIPTION +.I Macp +lists the given files using your standard pager, obtained from $PAGER, which is usually +.I more +or +.I less . +Do not use the Unix pagers directly as CAP aufs files use CR and not LF as end of line. +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) diff --git a/contrib/AufsTools/man/makeman b/contrib/AufsTools/man/makeman new file mode 100755 index 0000000..ed91647 --- /dev/null +++ b/contrib/AufsTools/man/makeman @@ -0,0 +1,5 @@ +#!/bin/csh +foreach i (*.1) + echo Formatting $i + nroff -man < $i > formatted/$i:t +end diff --git a/contrib/AufsTools/man/mcmp.1 b/contrib/AufsTools/man/mcmp.1 new file mode 100644 index 0000000..6c39f7d --- /dev/null +++ b/contrib/AufsTools/man/mcmp.1 @@ -0,0 +1,11 @@ +.TH MCMP L "December 1990" +.UC +.SH NAME +mcmp \- Macintosh file compare +.SH SYNOPSIS +.B mcmp +file1 file2 +.br +.SH DESCRIPTION +.I Mcmp +compares all forks of two CAP aufs files, using cmp(1). diff --git a/contrib/AufsTools/man/mcvert.1 b/contrib/AufsTools/man/mcvert.1 new file mode 100644 index 0000000..622ed2d --- /dev/null +++ b/contrib/AufsTools/man/mcvert.1 @@ -0,0 +1,149 @@ +.TH MCVERT LOCAL "May 5, 1987" +.UC 4.2 +.SH NAME +mcvert \- BinHex 4.0 to MacBinary file conversion utility +.SH SYNOPSIS +.B mcvert +[-options] name... [[-options] name...]... +.br +.SH DESCRIPTION +The +.I mcvert +program translates MacIntosh files from one format to another. +The primary formats in which MacIntosh files are represented on non-Macs are: +.TP +.B MacBinary: +An eight bit wide representation of the data and resource forks of a Mac +file and of relevant Finder information, MacBinary files are recognized +as "special" by several MacIntosh terminal emulators. These emulators, +using Kermit or Xmodem or any other file transfer protocol, can separate +the incoming file into forks and appropriately modify the Desktop to display +icons, types, creation dates, and the like. +.TP +.B BinHex 4.0: +A seven bit wide representation of a Mac file with CRC error checking, +BinHex 4.0 files are designed for communication of Mac files over long +distance, possibly noisy, seven bit wide paths. +.TP +.B PackIt: +PackIt files are actually representations of collections of Mac files, possibly +Huffman compressed. Packing many small related files together before +a MacBinary transfer or a translation to BinHex 4.0 is common practice. +.TP +.B Text: +A MacIntosh ends each line of a plain text file with a carriage return +character (^M), rather than the newline character (^J) that some systems +seem to prefer. Moreover, a MacBinary file has prepended Finder information +that non-MacIntoshes don't need. +.TP +.B Data, Rsrc: +A Data or Rsrc file is the exact copy of the data or resource fork of a +MacIntosh file. +.PP +It is the purpose of this program to convert to the MacBinary format +files in other of the above formats, and vice versa. +.PP +.SH PARAMETERS +Exactly one of the following operations may be specified for an input name: +.TP +.B x +BinHex 4.0 - files in the MacBinary format are translated to BinHex +files, or vice versa. The name argument may be the name of a file to be +converted or a basename to which an appropriate suffix must be appended +to get a filename. If the conversion is from Binhex 4.0 to MacBinary, +several files may comprise the BinHex representation of the Mac file. +Rather than manually concatenate the files and manually delete mail +headers and other extraneous garbage, one may specify the names of the +files in order and +.I mcvert +will do the concatenating and deleting. Conversely, in converting +a MacBinary file to BinHex 4.0 format for mailing over long distances, +one may be restricted to mail messages of no greater that some fixed +length. In this case, +.I mcvert +can automatically divide the BinHex file into pieces and label each +piece appropriately. +Option 'x' is selected by default. +.TP +.B r +Resource - files in the MacBinary format with empty data forks +and nonempty resource forks are made from ordinary data files, or vice versa. +.TP +.B d +Data - files in the MacBinary format with nonempty data forks +and empty resource forks are made from ordinary data files, or vice versa. +.TP +.B u +Text - files in the MacBinary format with nonempty data forks +and empty resource forks are made from ordinary data files, or vice versa. +Unix newline +characters are interchanged with MacIntosh carriage return +characters, and a newly created MacBinary file has creator field given by +the MAC_EDITOR environment variable. +.PP +.SH OPTIONS +.TP +.B p | q +If a BinHex to MacBinary conversion is taking place and option 'p' is selected, +any file of type "PIT " +will be unpacked into its constituent parts. This option does not recursively +unpack "PIT " files packed in "PIT " files. +If a MacBinary to BinHex conversion is taking place, this option is currently +ignored. By default, option 'q' is selected. +.TP +.B U | D +When option 'U' is selected, the conversion that takes place is the one suitable +for Uploading files. That is, the conversion is from MacBinary to something +else when 'U' is selected. Conversely, option 'D', as in Download, +converts from something to MacBinary. Option 'D' is the default. +.TP +.B s | v +Normally, +.I mcvert +prints to stderr information about the files it is creating. Selecting +option 's', as in silent, disables this reporting. Option 'v', for verbose, +is the default. +.SH "ENVIRONMENT VARIABLES" +There are four environment variables one may use to customize +the behavior of +.I mcvert +slightly. +.TP +.B MAC_EDITOR +The creator of MacBinary text files produced with options -uD. +The default is MACA, the creator type of MacWrite. +.TP +.B MAC_DLOAD_DIR +The MacBinary files created when option -D is selected are placed in this +directory. The default is ".", the current working directory. +.TP +.B MAC_EXT +The MacBinary files created when option -D is selected are named according +to the filename field stored in the file header, with the name extended by +this suffix. The default is ".bin". +.TP +.B MAC_LINE_LIMIT +The BinHex files created when option -U is selected may be no longer than +this many lines long. Files that would otherwise exceed this line limit +are broken up into several files with numbers embedded into their file +names to show their order. Each such file has "Start of part x" and "End +of part x" messages included where appropriate. +.SH BUGS +It should be possible to discard bad input now and successfully translate +good input later, but bad input mostly just causes immediate termination. +.PP +A more diligent person would support BinHex 3.0 and BinHex 2.0 and BinHex +5000.0 B. C., but I've never seen anyone use them in three years. A +more diligent person would also do something for users of macget and +macput, but hopefully someone will make those programs support the +MacBinary file protocol. +.SH SEE ALSO +xbin(1), macget(1), macput(1), xmodem(1), kermit(1) +.SH AUTHOR +Doug Moore, Cornell University Computer Science. Based upon +.I xbin +by Dave Johnson, Brown University, as modified by Guido van Rossum, and upon +.I unpit +by Allan G. Weber, as well as upon correspondence with several helpful +readers of USENET. + diff --git a/contrib/AufsTools/man/newfolder.1 b/contrib/AufsTools/man/newfolder.1 new file mode 100644 index 0000000..670e222 --- /dev/null +++ b/contrib/AufsTools/man/newfolder.1 @@ -0,0 +1,15 @@ +.TH NEWFOLDER L "December 1990" +.UC +.SH NAME +newfolder \- create CAP aufs folder(s) +.SH SYNOPSIS +.B newfolder +folder ... +.SH DESCRIPTION +.I Newfolder +creates CAP aufs folders. +Do not use +.I mkdir +to do this as it does not handle the hidden CAP aufs folders. +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) diff --git a/contrib/AufsTools/man/sit.1 b/contrib/AufsTools/man/sit.1 new file mode 100644 index 0000000..61a611b --- /dev/null +++ b/contrib/AufsTools/man/sit.1 @@ -0,0 +1,59 @@ +.TH SIT L "December 1990" +.UC +.SH NAME +stuffit \- create a Stuffit archive from Unix text or CAP aufs files +.br +sit \- create a Stuffit archive from Unix text or *.{rsrc,data,info} files +.SH SYNOPSIS +.B stuffit +[ +.B \-ru +] [ +.B \-o +.I output +] [ +.B \-C +.I creator +] [ +.B \-T +.I type +] file +.br +.B sit +[ +.B \-ru +] [ +.B \-o +.I output +] [ +.B \-C +.I creator +] [ +.B \-T +.I type +] file +.br +.SH DESCRIPTION +Create a Stuffit archive from CAP aufs (stuffit), *.{rsrc,data,info} (sit) or +Unix text (both) files. +.SH OPTIONS +.TP +.B \-r +Remove input files when done. +.TP +.B \-o output +Specify name of output file. Defaults to +.I archive.sit . +.TP +.B \-u +Input files are Unix text not CAP aufs (stuffit) or *.{rsrc,data,info} (sit). +.TP +.B \-C creator +Set files creator when input is Unix text, defaults to KAHL. +.TP +.B \-T type +Set files type wehn input is Unix text, defaults to TEXT. +.SH AUTHORS +stuffit: Nigel Perry (np@doc.ic.ac.uk), based on +.br +sit: Tom Bereiter ..!{rutgers,ames}!cs.utexas.edu!halley!rolex!twb diff --git a/contrib/AufsTools/man/toaufs.1 b/contrib/AufsTools/man/toaufs.1 new file mode 100644 index 0000000..8080cdb --- /dev/null +++ b/contrib/AufsTools/man/toaufs.1 @@ -0,0 +1,23 @@ +.TH TOAUFS L "December 1990" +.UC +.SH NAME +toaufs \- decode Binhex 4.0 files to CAP aufs format +.SH SYNOPSIS +.B toaufs +file ... +.SH DESCRIPTION +.I Toaufs +takes a collection of files, joins them as appropriate, discards gumph +such as news headers at top & bottom, unbinhexes them and stores them +as CAP aufs files in the current directory, which should be a CAP aufs folder. +To use +.I toaufs +to convert files sent as news articles save each part in a file using +an ascending alphanumeric sequence of names e.g. file.1, file.2 etc., +and then pass them all to +.I toaufs , +e.g. toaufs file.*. +.SH SEE ALSO +mcvert, capit +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) diff --git a/contrib/AufsTools/man/trash.1 b/contrib/AufsTools/man/trash.1 new file mode 100644 index 0000000..8346293 --- /dev/null +++ b/contrib/AufsTools/man/trash.1 @@ -0,0 +1,15 @@ +.TH TRASH L "December 1990" +.UC +.SH NAME +trash \- delete a CAP aufs file +.SH SYNOPSIS +.B trash +file ... +.SH DESCRIPTION +.I Trash +simply deletes the given CAP aufs files. Don't use +.I rm +to delete CAP aufs files as it will not handle the multiple forks +correctly, leaving hidden unwanted files on your disc. +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) diff --git a/contrib/AufsTools/man/unsit.1 b/contrib/AufsTools/man/unsit.1 new file mode 100644 index 0000000..a819306 --- /dev/null +++ b/contrib/AufsTools/man/unsit.1 @@ -0,0 +1,131 @@ +.TH UNSIT L "Septermber 28, 1988" +.UC +.SH NAME +unsit \- extract/list files in a Macintosh Stuffit archive file +.SH SYNOPSIS +.B unsit +[ +.B \-rdulM +] [ +-vqfm +] file +.br +.SH DESCRIPTION +For the Stuffit archive file listed, +.I unsit +extracts the files in the archive into separate files. +This makes it possible, for example, to separate a large StuffIt file +into component files for selective downloading, rather than +downloading the larger archive file just to extract a single, small +file. It also allows the use of StuffIt to compress a group of files +into a single, smaller archive that can be uploaded to a Unix system +for storage, printing, etc. +.PP +In the normal mode, both the data and the resource forks of the +component Macintosh files in the archive are extracted and stored in +Unix files with the extension +.I .data +and +.I .rsrc +appended to the end of the Macintosh file name. +In addition, a +.I .info +file will be created with the Finder information. +These three file are compatible with the +.I macput +program for downloading to a Mac running MacTerminal. The output files can +also be generated in the MacBinary format which is easier to use if they are to +be sent to the Mac over a network connection using an FTP program. The +MacBinary format file will have the same name as the file would have on the +Mac. If only the data or resource fork is extracted, no addition extension is +appended to the Mac file name. Characters in the Mac file name that are +illegal (or unwieldy, like spaces) are changed to underscores in the Unix file +name. The true Mac file name is retained internally in the MacBinary file or +in the +.I .info +file and is restored when the file is downloaded. +.PP +StuffIt version 1.5 has the ability to archive a group of files and folders +in such a way that the hierarchical relationship of the files and folders +is maintained. +.I Unsit +version 1.5 can unpack files archived in this manner and place them in +corresponding subdirectories so as to maintain the hierarchy. As an option, +the hierarcy can be flattened out and all the files stored in the current +directory. +.PP +The options are similar to those for +.I macput +and +.I unpit. +.TP +.B \-M +Generate output files in MacBinary format instead of the .data, .rsrc, +and .info format. +.TP +.B \-f +For StuffIt files containing a "Hierarchy Maintained Folder" entry, extract the +files into a "flat" organization (all in the current directory) rather than +maintaining the hierarchy by creating new directories, etc. +Default is to maintain the hierarchical folder organization. +.TP +.B \-l +List the files in the archive but do not extract them. The name, size, +type, and creator of each file is listed. +.TP +.B \-m +Assumes the input file in MacBinary format rather than macput/macget +format and skips over the MacBinary header. +.TP +.B \-r +Extract resources forks only. +.TP +.B \-d +Extract data forks only. +.TP +.B \-u +Extract data fork and change into a Unix text file. +This only works if the file is really a text file. +.TP +.B \-q +Query user before extracting files and folders. If a "n" answer is given for +a folder, none of the files or folders in that folder will be extracted. +.TP +.B \-v +Verbose option. Causes +.I unsit +to list name, size, type, and creator of each file extracted. +.SH BUGS +Files that were compressed by StuffIt with the Lempel-Ziv method and are +extracted with the +.B \-u +switch (text files) are not checked for a correct CRC value when +.I unsit +uncompresses them. This is because +.I unsit +pipes the data through +.I compress +and +.I tr +to extract the file and never has a chance to do the CRC check. +.PP +The +.I compress +program has been observed to go into strange states when uncompressing a +damaged file. Often it will get stuck writing out bogus data until the +disk fills up. Since +.I unsit +sends data through +.I compress, +the same problem could occur when extracting files from a damaged Stuffit +archive. +.SH FILES +For archives that have been compressed with the Lempel-Ziv method, the +.I compress +program must be present on the system and in the search path since +.I unsit +uses it for the uncompressing. +.I Compress +is available from the comp.sources.unix archives. +.SH AUTHOR +Allan G. Weber (weber@sipi.usc.edu) diff --git a/contrib/AufsTools/man/unstuffit.1 b/contrib/AufsTools/man/unstuffit.1 new file mode 100644 index 0000000..8841e2f --- /dev/null +++ b/contrib/AufsTools/man/unstuffit.1 @@ -0,0 +1,90 @@ +.TH UNSTUFFIT L "December 1990" +.UC +.SH NAME +unstuffit \- unpack a Stuffit archive file to CAP aufs files +.SH SYNOPSIS +.B unstuffit +[ +.B \-rdulM +] [ +-vqfm +] file ... +.br +.SH DESCRIPTION +For the Stuffit archive(s) file listed, +.I unstuffit +extracts the files in the archive into separate files. +This makes it possible, for example, to separate a large StuffIt file +into component files for selective downloading, rather than +downloading the larger archive file just to extract a single, small +file. It also allows the use of StuffIt to compress a group of files +into a single, smaller archive that can be uploaded to a Unix system +for storage, printing, etc. +.PP +.I Unstuffit +stores extracted files in CAP aufs format in the current directory, which +must be a CAP aufs folder. +Characters in the Mac file name that are +illegal (or unwieldy, like spaces) are changed to underscores in the Unix file +name. +.PP +StuffIt version 1.5 has the ability to archive a group of files and folders +in such a way that the hierarchical relationship of the files and folders +is maintained. +.I Unstuffit +version 1.5 can unpack files archived in this manner and place them in +corresponding subdirectories, which it will create in CAP aufs folder format, so as to maintain the hierarchy. As an option, +the hierarcy can be flattened out and all the files stored in the current +directory. +.PP +.TP +.B \-f +For StuffIt files containing a "Hierarchy Maintained Folder" entry, extract the +files into a "flat" organization (all in the current directory) rather than +maintaining the hierarchy by creating new directories, etc. +Default is to maintain the hierarchical folder organization. +.TP +.B \-l +List the files in the archive but do not extract them. The name, size, +type, and creator of each file is listed. +.TP +.B \-m +Assumes the input file in MacBinary format rather than aufs +format and skips over the MacBinary header. +.TP +.B \-r +Extract resources forks only. +.TP +.B \-d +Extract data forks only. +.TP +.B \-q +Query user before extracting files and folders. If a "n" answer is given for +a folder, none of the files or folders in that folder will be extracted. +.TP +.B \-v +Verbose option. Causes +.I unstuffit +to list name, size, type, and creator of each file extracted. +.SH BUGS +The +.I compress +program has been observed to go into strange states when uncompressing a +damaged file. Often it will get stuck writing out bogus data until the +disk fills up. Since +.I unstuffit +sends data through +.I compress, +the same problem could occur when extracting files from a damaged Stuffit +archive. +.SH FILES +For archives that have been compressed with the Lempel-Ziv method, the +.I compress +program must be present on the system and in the search path since +.I unstuffit +uses it for the uncompressing. +.I Compress +is available from the comp.sources.unix archives. +.SH AUTHOR +Nigel Perry (np@doc.ic.ac.uk) based upon unsit by +Allan G. Weber (weber@sipi.usc.edu). diff --git a/contrib/AufsTools/mcvert/Makefile b/contrib/AufsTools/mcvert/Makefile new file mode 100644 index 0000000..0f7e9e1 --- /dev/null +++ b/contrib/AufsTools/mcvert/Makefile @@ -0,0 +1,27 @@ +CSOURCES = mcvert.c hqxify.c unpack.c +SOURCES = mcvert.c hqxify.c unpack.c mactypes.h Makefile +OBJECTS = mcvert.o hqxify.o unpack.o +BIN = . +# for Solaris 2.X +# CFLAGS = -O +# +CFLAGS = -O -DBSD +DESTDIR = /usr/local/cap + +mcvert: $(OBJECTS) + cc $(CFLAGS) $(OBJECTS) -s -o $(BIN)/mcvert + +$(OBJECTS): mactypes.h + +print: + lpr -p -Pvmslp $(SOURCES) + +shar: + shar $(SOURCES) mcvert.1 > mcvert.shar + +install: mcvert + -strip mcvert + cp mcvert $(DESTDIR) + +clean: + -rm -f mcvert $(OBJECTS) diff --git a/contrib/AufsTools/mcvert/hqxify.c b/contrib/AufsTools/mcvert/hqxify.c new file mode 100644 index 0000000..9239499 --- /dev/null +++ b/contrib/AufsTools/mcvert/hqxify.c @@ -0,0 +1,588 @@ +#include "mactypes.h" + +#define HQXBUFLEN 512 +byte hqxbuf[HQXBUFLEN+1], *buf_ptr, *buf_end, *buf_start=hqxbuf+1; + +#define MAXLINE 255 +byte line[MAXLINE+1], *line_ptr, *line_end, *line_start=line+1; + +int line_count, file_count; +int save_state, total_bytes, save_run_length; +word save_nibble; +char binfname[BINNAMELEN], hqxfname[BINNAMELEN]; +FILE *hqxfile, *binfile; + +/* This routine reads the header of a hqxed file and appropriately twiddles it, + determines if it has CRC problems, creates the .bin file, and puts the info + into the .bin file. + Output is hqx_datalen, hqx_rsrclen, type, binfname, binfile */ + +hqx_to_bin_hdr(type, hqx_datalen, hqx_rsrclen) +char *type; +ulong *hqx_datalen, *hqx_rsrclen; +{ register byte *hqx_ptr, *hqx_end; + register ulong calc_crc; + hqx_buf *hqx_block; + hqx_header *hqx; + info_header info; + ulong mtim; + short crc; + + extern word magic[]; + extern FILE *verbose; + extern char *dir, *ext; + extern short calc_mb_crc(); + + /* read the hqx header, assuming that I won't exhaust hqxbuf in so doing */ + fill_hqxbuf(); + hqx_block = (hqx_buf *) buf_ptr; + hqx = (hqx_header *) (hqx_block->name + hqx_block->nlen); + hqx_ptr = buf_ptr; + hqx_end = (byte *) hqx + sizeof(hqx_header) - 1; + calc_crc = 0; + while (hqx_ptr < hqx_end) + calc_crc = (((calc_crc&0xff) << 8) | *hqx_ptr++) ^ magic[calc_crc >> 8]; + calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8]; + calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8]; + buf_ptr = hqx_ptr; + + /* stuff the hqx header data into the info header */ + bzero(&info, sizeof(info_header)); + info.nlen = hqx_block->nlen; + strncpy(info.name, hqx_block->name, info.nlen); /* name */ + bcopy(hqx->type, info.type, 9); /* type, author, flag */ + info.flags &= 0x7e; /* reset lock bit, init bit */ + if (hqx->protect & 0x40) info.protect = 1; /* copy protect bit */ + bcopy(hqx->dlen, info.dlen, 8); /* dlen, rlen */ + mtim = time2mac(time(0)); + bcopy(&mtim, info.mtim, 4); + bcopy(&mtim, info.ctim, 4); + info.uploadvers = '\201'; + info.readvers = '\201'; + + /* calculate MacBinary CRC */ + crc = calc_mb_crc(&info, 124, 0); + info.crc[0] = (char) (crc >> 8); + info.crc[1] = (char) crc; + + /* Create the .bin file and write the info to it */ + unixify(hqx_block->name); + sprintf(binfname, "%s/%s%s", dir, hqx_block->name, ext); + fprintf(verbose, + "Converting %-30s type = \"%4.4s\", author = \"%4.4s\"\n", + hqx_block->name, info.type, info.auth); + if ((binfile = fopen(binfname, "w")) == NULL) + error("Cannot open %s", binfname); + check_hqx_crc(calc_crc, "File header CRC mismatch in %s", binfname); + fwrite(&info, sizeof(info), 1, binfile); + + /* Get a couple of items we'll need later */ + bcopy(info.dlen, hqx_datalen, 4); + *hqx_datalen = mac2long(*hqx_datalen); + bcopy(info.rlen, hqx_rsrclen, 4); + *hqx_rsrclen = mac2long(*hqx_rsrclen); + bcopy(info.type, type, 4); + } + +/* This routine reads the header of a bin file and appropriately twiddles it, + creates the .hqx file, and puts the info into the .hqx file. + Output is hqx_datalen, hqx_rsrclen, type, hqxfname, hqxfile */ + +bin_to_hqx_hdr(hqx_datalen, hqx_rsrclen) +ulong *hqx_datalen, *hqx_rsrclen; +{ register byte *hqx_ptr, *hqx_end; + register ulong calc_crc; + hqx_buf *hqx_block; + hqx_header *hqx; + info_header info; + extern word magic[]; + extern FILE *verbose; + extern char **hqxnames_left; + extern char *ext; + + strcpy(binfname, *hqxnames_left++); + if (!(binfile = fopen(binfname, "r"))) { + /* Maybe we are supposed to figure out the suffix ourselves? */ + strcat(binfname, ext); + if (!(binfile = fopen(binfname, "r"))) + error("Cannot open %s", binfname); + } + if (!fread(&info, sizeof(info), 1, binfile)) + error("Unexpected EOF in header of %s", binfname); + + /* stuff the info header into the hqx header */ + hqx_block = (hqx_buf *) buf_ptr; + hqx_block->nlen = info.nlen; + strncpy(hqx_block->name, info.name, info.nlen); + hqx = (hqx_header *) (hqx_block->name + hqx_block->nlen); + hqx->version = 0; + bcopy(info.type, hqx->type, 9); /* type, author, flags */ + if (info.protect = 1) hqx->protect = 0; /* protect bit: 0x40 */ + else hqx->protect = 0; + bcopy(info.dlen, hqx->dlen, 8); /* dlen, rlen */ + + /* Create the .hqx file and write the info to it */ + strncpy(hqxfname, info.name, info.nlen); + hqxfname[info.nlen] = '\0'; + unixify(hqxfname); + fprintf(verbose, + "Converting %-30s type = \"%4.4s\", author = \"%4.4s\"\n", + hqxfname, info.type, info.auth); + + calc_crc = 0; + hqx_ptr = (byte *) hqx_block; + hqx_end = hqx_ptr + hqx_block->nlen + sizeof(hqx_header); + while (hqx_ptr < hqx_end) + calc_crc = (((calc_crc&0xff) << 8) | *hqx_ptr++) ^ magic[calc_crc >> 8]; + calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8]; + calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8]; + buf_ptr = hqx_end; + write_hqx_crc(calc_crc); + + /* Get a couple of items we'll need later */ + bcopy(info.dlen, hqx_datalen, 4); + *hqx_datalen = mac2long(*hqx_datalen); + bcopy(info.rlen, hqx_rsrclen, 4); + *hqx_rsrclen = mac2long(*hqx_rsrclen); + } + + +/* This routine copies bytes from the decoded input stream to the output. + It also pads to a multiple of 128 bytes on the output, which is part + of the .bin format */ +word hqx_to_bin_fork(nbytes) +register ulong nbytes; +{ register byte *c; + register ulong calc_crc; + register int c_length; + ulong extra_bytes; + extern word magic[]; + + extra_bytes = 127 - (nbytes+127)%128; /* pad fork to mult of 128 bytes */ + calc_crc = 0; + for (;;) { + c = buf_ptr; + c_length = (c + nbytes > buf_end) ? buf_end - c : nbytes; + nbytes -= c_length; + fwrite(c, sizeof(byte), c_length, binfile); + while (c_length--) + calc_crc = (((calc_crc&0xff) << 8) | *c++) ^ magic[calc_crc >> 8]; + if (!nbytes) break; + fill_hqxbuf(); + } + buf_ptr = c; + while (extra_bytes--) putc(0, binfile); + calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8]; + calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8]; + return (word) calc_crc; + } + +/* This routine copies bytes from the input stream to the encoded output. + It also pads to a multiple of 128 bytes on the input, which is part + of the .bin format */ +word bin_to_hqx_fork(nbytes) +register ulong nbytes; +{ register byte *c; + register ulong calc_crc; + register int c_length; + ulong extra_bytes; + extern word magic[]; + + extra_bytes = 127 - (nbytes+127)%128; /* pad fork to mult of 128 bytes */ + calc_crc = 0; + for (;;) { + c = buf_ptr; + c_length = (c + nbytes > buf_end) ? buf_end - c : nbytes; + nbytes -= c_length; + fread(c, sizeof(byte), c_length, binfile); + buf_ptr += c_length; + while (c_length--) + calc_crc = (((calc_crc&0xff) << 8) | *c++) ^ magic[calc_crc >> 8]; + if (!nbytes) break; + empty_hqxbuf(); + } + buf_ptr = c; + + fseek(binfile, extra_bytes, 1); + calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8]; + calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8]; + return (word) calc_crc; + } + +/* Essentials for Binhex 8to6 run length encoding */ +#define RUNCHAR 0x90 +#define MAXRUN 255 +#define IS_LEGAL <0x40 +#define ISNT_LEGAL >0x3f +#define DONE 0x7F /* tr68[':'] = DONE, since Binhex terminator is ':' */ +#define SKIP 0x7E /* tr68['\n'|'\r'] = SKIP, i. e. end of line char. */ +#define FAIL 0x7D /* character illegal in binhex file */ + +byte tr86[] = + "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr"; +byte tr68[] = { + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, SKIP, FAIL, FAIL, SKIP, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, FAIL, FAIL, + 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, FAIL, + 0x14, 0x15, DONE, FAIL, FAIL, FAIL, FAIL, FAIL, + 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, + 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, FAIL, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, FAIL, + 0x2C, 0x2D, 0x2E, 0x2F, FAIL, FAIL, FAIL, FAIL, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, FAIL, + 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, FAIL, FAIL, + 0x3D, 0x3E, 0x3F, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + }; + +/* + * This procedure transparently reads and decodes the hqx input. It does run + * length and 6 to 8 decoding. + */ +#define READING 0 +#define SKIPPING 1 +#define FIND_START_COLON 2 + +/* NP 12/3/90: A dirty hack to handle "X-Mailer: ELM [version 2.2 PL14]" */ +#define X_MAIL_STR "\054\014\043\061\070\073\065\077\177" +#define X_MAIL_LEN strlen(X_MAIL_STR) + +fill_hqxbuf() +{ register ulong c, nibble; + register int not_in_a_run = TRUE, state68; + register byte *fast_buf, *fast_line; + static int status = FIND_START_COLON; + + buf_ptr = fast_buf = buf_start; + fast_line = line_ptr; + state68 = save_state; + nibble = save_nibble; + if (save_run_length > 0) { + c = save_run_length; + save_run_length = 0; + goto continue_run; + } + while (fast_buf < buf_end) { + next_char: + if ((c = *fast_line++) ISNT_LEGAL) { + if (c == DONE) break; + next_line: + if (!fgets(line_start, MAXLINE, hqxfile) && !new_in_hqx_file()) + if (status == FIND_START_COLON) exit(0); + else error("Premature EOF in %s\n", hqxfname); + line_ptr = line_start; + scan_line: + fast_line = line_ptr; + while ((*fast_line = tr68[*fast_line]) IS_LEGAL) fast_line++; + c = *fast_line; + switch (status) { + case READING: + if (c == SKIP && fast_line == line_end) break; + if (c == DONE) { + status = FIND_START_COLON; + break; + } + status = SKIPPING; + goto next_line; + case SKIPPING: + if (c == SKIP && fast_line == line_end) { + status = READING; + break; + } + /* GMT, 1/9/90: Added this clause to avoid losing the last + * line if it was preceeded by a skipped line. */ + if (c == DONE) { + /* NP 12/3/90: A dirty hack to handle "X-Mailer: ELM [version 2.2 PL14]" */ + if( (fast_line - line_ptr == X_MAIL_LEN - 1) + && (strncmp(line_ptr, X_MAIL_STR, X_MAIL_LEN) == 0)) goto next_line; + status = FIND_START_COLON; + break; + } + goto next_line; + case FIND_START_COLON: + if (*line_start == DONE) { + status = READING; + line_ptr++; + goto scan_line; + } + goto next_line; + } + fast_line = line_ptr; + c = *fast_line++; + } + + /* Finally, we have the next 6 bits worth of data */ + switch (state68++) { + case 0: + nibble = c; + goto next_char; + case 1: + nibble = (nibble << 6) | c; + c = nibble >> 4; + break; + case 2: + nibble = (nibble << 6) | c; + c = (nibble >> 2) & 0xff; + break; + case 3: + c = (nibble << 6) & 0xff | c; + state68 = 0; + break; + } + if (not_in_a_run) + if (c != RUNCHAR) *fast_buf++ = c; + else {not_in_a_run = FALSE; goto next_char;} + else { + if (c--) { + not_in_a_run = buf_end - fast_buf; + if (c > not_in_a_run) { + save_run_length = c - not_in_a_run; + c = not_in_a_run; + } + continue_run: + not_in_a_run = fast_buf[-1]; + while (c--) *fast_buf++ = not_in_a_run; + } + else *fast_buf++ = RUNCHAR; + not_in_a_run = TRUE; + } + } + total_bytes += fast_buf - buf_ptr; + buf_start[-1] = fast_buf[-1]; + line_ptr = fast_line; + save_state = state68; + save_nibble = nibble; + } + + +new_in_hqx_file() +{ char *hqx_ext; + extern char **hqxnames_left; + if (*hqxnames_left[0] == '\0' || *hqxnames_left[0] == '-') return FALSE; + strcpy(hqxfname, *hqxnames_left++); + hqx_ext = hqxfname + strlen(hqxfname) - 4; + if (!strcmp(hqx_ext, ".hqx")) + if (!freopen(hqxfname, "r", hqxfile)) + error("Cannot open %s\n", hqxfname); + else; + else { + if (!freopen(hqxfname, "r", hqxfile)) { + hqx_ext += 4; + strcpy(hqx_ext, ".hqx"); + if (!freopen(hqxfname, "r", hqxfile)) { + error("Cannot find %s\n", hqxfname); + } + } + } + fgets(line_start, MAXLINE, hqxfile); + return TRUE; + } + +/* + * This procedure transparently encodes and writes the hqx output. + * It does run length and 8 to 6 encoding. + */ +empty_hqxbuf() +{ register ulong c, nibble, last_c; + register byte *fast_buf, *fast_line; + register int state86, dont_look_for_runs = FALSE, run_length; + extern int maxlines; + + run_length = save_run_length; + last_c = buf_start[-1]; + fast_buf = buf_start; + fast_line = line_ptr; + state86 = save_state; + nibble = save_nibble; + while (fast_buf < buf_ptr) { + c = *fast_buf++; + if (dont_look_for_runs) dont_look_for_runs = FALSE; + else if (last_c == c && run_length < MAXRUN) {run_length++; continue;} + else { + if (run_length >1) { + --fast_buf; + if (run_length == 2 && last_c != RUNCHAR) c = last_c; + else { + c = RUNCHAR; + *--fast_buf = run_length; + dont_look_for_runs = TRUE; + } + run_length = 1; + } + else last_c = c; + if (c == RUNCHAR && !dont_look_for_runs) { + *--fast_buf = 0; + dont_look_for_runs = TRUE; + } + } + + if (fast_line == line_end) { + if (line_count++ == maxlines) new_out_hqx_file(); + fputs(line_start, hqxfile); + fast_line = line_start; + } + + switch (state86++) { + case 0: + *fast_line++ = tr86[ c >> 2 ]; + nibble = (c << 4) & 0x3f; + break; + case 1: + *fast_line++ = tr86[ (c >> 4) | nibble ]; + nibble = (c << 2) & 0x3f; + break; + case 2: + *fast_line++ = tr86[ (c >> 6) | nibble ]; + if (fast_line == line_end) { + if (line_count++ == maxlines) new_out_hqx_file(); + fputs(line_start, hqxfile); + fast_line = line_start; + } + *fast_line++ = tr86[ c & 0x3f ]; + state86 = 0; + break; + } + } + save_run_length = run_length; + buf_start[-1] = last_c; + buf_ptr = buf_start; + line_ptr = fast_line; + save_state = state86; + save_nibble = nibble; + } + +new_out_hqx_file() +{ char filename[NAMELEN + 7]; + extern int maxlines; + fprintf(hqxfile, "<<< End of Part %2d >>>\n", file_count); + fclose(hqxfile); + file_count++; + if (maxlines) sprintf(filename, "%s%02d.hqx", hqxfname, file_count); + else sprintf(filename, "%s.hqx", hqxfname); + if ((hqxfile = fopen(filename, "w")) == NULL) + error("Can't create %s", filename); + if (file_count > 1) + fprintf(hqxfile, "<<< Start of Part %2d >>>\n", file_count); + else fprintf(hqxfile, "(This file must be converted with BinHex 4.0)\n\n"); + line_count = 3; + } + +check_hqx_crc(calc_crc, msg, name) +word calc_crc; +char msg[], name[]; +{ word read_crc; + if (buf_ptr >= buf_end) fill_hqxbuf(); + read_crc = *buf_ptr++ << 8; + if (buf_ptr >= buf_end) fill_hqxbuf(); + read_crc |= *buf_ptr++; + if (read_crc != calc_crc) error(msg, name); + } + +write_hqx_crc(calc_crc) +word calc_crc; +{ if (buf_ptr == buf_end) empty_hqxbuf(); + *buf_ptr++ = calc_crc >> 8; + if (buf_ptr == buf_end) empty_hqxbuf(); + *buf_ptr++ = calc_crc; + } + +un_hqx(unpit_flag) +int unpit_flag; +{ char type[4]; + ulong hqx_datalen, hqx_rsrclen; + word un_pit(); + int unpitting, bytes_read; + word calc_crc; + extern char **hqxnames_left; + + hqxfile = fopen("/dev/null", "r"); + line_end = line_start + HQXLINELEN; + buf_end = buf_start + HQXBUFLEN; + for (;;) { + total_bytes = 0; + line_ptr = line_start; + line_ptr[0] = SKIP; + save_state = 0; + save_run_length = 0; + + hqx_to_bin_hdr(type, &hqx_datalen, &hqx_rsrclen); /* binfname */ + + unpitting = unpit_flag && !strcmp(type, "PIT "); + if (unpitting) { + fclose(binfile); + unlink(binfname); + bytes_read = total_bytes - (buf_end - buf_ptr); + calc_crc = un_pit(); + bytes_read = total_bytes - (buf_end - buf_ptr) - bytes_read; + if (bytes_read != hqx_datalen) + fprintf(stderr, + "Warning - Extraneous characters ignored in %s\n", binfname); + } + else calc_crc = hqx_to_bin_fork(hqx_datalen); + check_hqx_crc(calc_crc, "File data CRC mismatch in %s", binfname); + + calc_crc = hqx_to_bin_fork(hqx_rsrclen); + check_hqx_crc(calc_crc, "File rsrc CRC mismatch in %s", binfname); + + if (!unpitting) fclose(binfile); + } + } + +re_hqx() +{ word calc_crc; + ulong hqx_datalen, hqx_rsrclen; + extern char **hqxnames_left; + extern int maxlines; + line_end = line_start + HQXLINELEN; + buf_end = buf_start + HQXBUFLEN; + while (*hqxnames_left[0] != '-') { + hqxfile = fopen("/dev/null", "w"); + line_count = maxlines; + file_count = 0; + line_ptr = line_start; + *line_ptr++ = ':'; + strcpy(line_end, "\n"); + buf_ptr = buf_start; + save_state = 0; + save_run_length = 1; + + bin_to_hqx_hdr(&hqx_datalen, &hqx_rsrclen); /* calculates hqxfname */ + + calc_crc = bin_to_hqx_fork(hqx_datalen); + write_hqx_crc(calc_crc); + + calc_crc = bin_to_hqx_fork(hqx_rsrclen); + write_hqx_crc(calc_crc); + *buf_ptr = !buf_ptr[-1]; /* To end a run and to get the last */ + buf_ptr++; + empty_hqxbuf(); /* stray bits, temporarily add a char */ + if (save_state != 2) --line_ptr; + if (line_ptr == line_end) { + fputs(line_start, hqxfile); + line_ptr = line_start; + } + strcpy(line_ptr, ":\n"); + fputs(line_start, hqxfile); + fclose(hqxfile); + } + } diff --git a/contrib/AufsTools/mcvert/mactypes.h b/contrib/AufsTools/mcvert/mactypes.h new file mode 100644 index 0000000..3efcced --- /dev/null +++ b/contrib/AufsTools/mcvert/mactypes.h @@ -0,0 +1,177 @@ +#ifdef sun +#ifdef __svr4__ +#define SOLARIS +#endif __svr4__ +#endif sun + +#include +#include +#include +#include + +#ifdef SOLARIS +#include +#include +#include +#else SOLARIS +#include +#include +#endif SOLARIS + +/* Useful, though not particularly Mac related, values */ +typedef unsigned char byte; /* one byte, obviously */ +typedef unsigned short word; /* must be 2 bytes */ +#ifndef SOLARIS +typedef unsigned long ulong; /* 4 bytes */ +#endif SOLARIS +#define TRUE 1 +#define FALSE 0 +#define CR 0x0d +#define LF 0x0a + +/* Compatibility issues */ +#ifdef BSD +#define mac2word (word) ntohs +#define mac2long (ulong) ntohl +#define word2mac (word) htons +#define long2mac (ulong) htonl +#else +#define mac2word +#define mac2long +#define word2mac +#define long2mac +#endif + +#ifdef MAXNAMLEN/* 4.2 BSD, stdio.h */ +#define SYSNAMELEN MAXNAMLEN +#else +#define SYSNAMELEN DIRSIZ +#endif + +#define NAMELEN 63 /* maximum legal Mac file name length */ +#define BINNAMELEN 68 /* NAMELEN + len(".bin\0") */ + +/* Format of a bin file: +A bin file is composed of 128 byte blocks. The first block is the +info_header (see below). Then comes the data fork, null padded to fill the +last block. Then comes the resource fork, padded to fill the last block. A +proposal to follow with the text of the Get Info box has not been implemented, +to the best of my knowledge. Version, zero1 and zero2 are what the receiving +program looks at to determine if a MacBinary transfer is being initiated. +*/ +typedef struct { /* info file header (128 bytes). Unfortunately, these + longs don't align to word boundaries */ + byte version; /* there is only a version 0 at this time */ + byte nlen; /* Length of filename. */ + byte name[NAMELEN]; /* Filename (only 1st nlen are significant)*/ + byte type[4]; /* File type. */ + byte auth[4]; /* File creator. */ + byte flags; /* file flags: LkIvBnSyBzByChIt */ + byte zero1; /* Locked, Invisible,Bundle, System */ + /* Bozo, Busy, Changed, Init */ + byte icon_vert[2]; /* Vertical icon position within window */ + byte icon_horiz[2]; /* Horizontal icon postion in window */ + byte window_id[2]; /* Window or folder ID. */ + byte protect; /* = 1 for protected file, 0 otherwise */ + byte zero2; + byte dlen[4]; /* Data Fork length (bytes) - most sig. */ + byte rlen[4]; /* Resource Fork length byte first */ + byte ctim[4]; /* File's creation date. */ + byte mtim[4]; /* File's "last modified" date. */ + byte ilen[2]; /* GetInfo message length */ + byte flags2; /* Finder flags, bits 0-7 */ + byte unused[14]; + byte packlen[4]; /* length of total files when unpacked */ + byte headlen[2]; /* length of secondary header */ + byte uploadvers; /* Version of MacBinary II that the uploading program is written for */ + byte readvers; /* Minimum MacBinary II version needed to read this file */ + byte crc[2]; /* CRC of the previous 124 bytes */ + byte padding[2]; /* two trailing unused bytes */ + } info_header; + +/* The *.info file of a MacTerminal file transfer either has exactly this +structure or has the protect bit in bit 6 (near the sign bit) of byte zero1. +The code I have for macbin suggests the difference, but I'm not so sure */ + +/* Format of a hqx file: +It begins with a line that begins "(This file +and the rest is 64 character lines (except possibly the last, and not +including newlines) where the first begins and the last ends with a colon. +The characters between colons should be only from the set in tr86, below, +each of which corresponds to 6 bits of data. Once that is translated to +8 bit bytes, you have the real data, except that the byte 0x90 may +indicate, if the following character is nonzero, that the previous +byte is to be repeated 1 to 255 times. The byte 0x90 is represented by +0x9000. The information in the file is the hqx_buf (see below), +a CRC word, the data fork, a CRC word, the resource fork, and a CRC word. +There is considerable confusion about the flags. An official looking document +unclearly states that the init bit is always clear, as is the following byte. +The experience of others suggests, however, that this is not the case. +*/ + +#define HQXLINELEN 64 +typedef struct { + byte version; /* there is only a version 0 at this time */ + byte type[4]; /* File type. */ + byte auth[4]; /* File creator. */ + byte flags; /* file flags: LkIvBnSyBzByChIt */ + byte protect; /* ?Pr??????, don't know what ? bits mean */ + byte dlen[4]; /* Data Fork length (bytes) - most sig. */ + byte rlen[4]; /* Resource Fork length byte first */ + byte bugblank; /* to fix obscure sun 3/60 problem + that always makes sizeof(hqx_header + even */ + } hqx_header; +typedef struct { /* hqx file header buffer (includes file name) */ + byte nlen; /* Length of filename. */ + byte name[NAMELEN]; /* Filename: only nlen actually appear */ + hqx_header all_the_rest;/* and all the rest follows immediately */ + } hqx_buf; + +/* Format of a Packit file: +Repeat the following sequence for each file in the Packit file: + 4 byte identifier ("PMag" = not compressed, "Pma4" = compressed) + 320 byte compression data (if compressed file) + = preorder transversal of Huffman tree + 255 0 bits corresponding to nonleaf nodes + 256 1 bits corresponding to leaf nodes + 256 bytes associating leaf nodes with bytes + 1 completely wasted bit + 92 byte header (see pit_header below) * + 2 bytes CRC word for header * + data fork (length from header) * + resource fork (length from header) * + 2 bytes CRC word for forks * + +Last file is followed by the 4 byte Ascii string, "Pend", and then the EOF. +The CRC calculations differ from those in the binhex format. + +* these are in compressed form if compression is on for the file + +*/ + +typedef struct { /* Packit file header (92 bytes) */ + byte nlen; /* Length of filename. */ + byte name[NAMELEN]; /* Filename (only 1st nlen are significant)*/ + byte type[4]; /* File type. */ + byte auth[4]; /* File creator. */ + byte flags; /* file flags: LkIvBnSyBzByChIt */ + byte zero1; + byte protect; /* = 1 for protected file, 0 otherwise */ + byte zero2; + byte dlen[4]; /* Data Fork length (bytes) - most sig. */ + byte rlen[4]; /* Resource Fork length byte first */ + byte ctim[4]; /* File's creation date. */ + byte mtim[4]; /* File's "last modified" date. */ + } pit_header; + +/* types for constructing the Huffman tree */ +typedef struct branch_st { + byte flag; + struct branch_st *one, *zero; + } branch; + +typedef struct leaf_st { + byte flag; + byte data; + } leaf; diff --git a/contrib/AufsTools/mcvert/mcvert.c b/contrib/AufsTools/mcvert/mcvert.c new file mode 100644 index 0000000..3526eff --- /dev/null +++ b/contrib/AufsTools/mcvert/mcvert.c @@ -0,0 +1,379 @@ +/* mcvert.c - version 1.05 - 10 January, 1990 modified 12 March, 1990 by NP + * Written by Doug Moore - Rice University - dougm@rice.edu - April '87 + * Sun bug fixes, assorted stuff - Jim Sasaki, March '89 + * Changed default max_line_size from 2000 to unlimited - Doug Moore, April, '89 + * Sun 3/60 doesn't like odd-sized structs. Bug fixed - Doug Moore, April, '89 + * - aided by Spencer W. Thomas + * Didn't handle properly many hqx files combined in one file. Bug fixed - + * Doug Moore, June, '89 + * Modified to handle MacBinaryII specification. Jim Van Verth, Sept, '89 + * + * Fixed a bug when there are blank lines in hqx data, as happens when newline + * get translated to CRLF and then to \n\n, common for some file transfers. + * The last hqx line would be lost if the previous line was blank or junk. + * Glenn Trewitt, Stanford University, 1990 (1.05) + * + * Mcvert would hiccup on mail header lines "X-Mailer: ELM [version 2.2 PL14]" + * as "X-Mailer:" is a vaild hqx line! Added in code to special case this + * line and keep scanning for the real hqx data. + * Nigel Perry, Imperial College, 12 March 1990 [NP] + * + * This program may be freely distributed for non-profit purposes. It may not + * be sold, by itself or as part of a collection of software. It may be freely + * modified as long as no modified version is distributed. Modifications of + * interest to all can be incorporated into the program by sending them to me + * for distribution. Parts of the code can be used in other programs. I am not + * responsible for any damage caused by this program. I hope you enjoy it. + */ + +#include "mactypes.h" + +#define HQX 0 +#define TEXT 1 +#define DATA 2 +#define RSRC 3 +#define HOST 4 +#define FORWARDS 0 +#define BACKWARDS 1 + +FILE *verbose; +char **hqxnames, **hqxnames_left; +char *dir, *ext, *text_author; +char *maxlines_str; +int maxlines; + +main(argc, argv) +int argc; +char **argv; +{ char *flags, *getenv(); + int direction, mode, unpit_flag; + + argv++; + argc--; + verbose = stderr; + direction = FORWARDS; + mode = HQX; + unpit_flag = 0; + + if ((text_author = getenv("MAC_EDITOR")) == NULL) text_author = "MACA"; + if ((ext = getenv("MAC_EXT")) == NULL) ext = ".bin"; + if ((dir = getenv("MAC_DLOAD_DIR")) == NULL) dir = "."; + if ((maxlines_str = getenv("MAC_LINE_LIMIT")) == NULL) maxlines = 0; + else maxlines = atoi(maxlines_str); + + /* Make command line arguments globally accessible */ + hqxnames = (char **) calloc(argc+1, sizeof(char *)); + hqxnames_left = hqxnames; + while (argc--) *hqxnames_left++ = *argv++; + *hqxnames_left = "-"; + hqxnames_left = hqxnames; + + while (strcmp(*hqxnames_left, "-")) { + if (hqxnames_left[0][0] == '-') { + flags = *hqxnames_left++; + while (*++flags) + switch (*flags) { + case 'x': + mode = HQX; + break; + case 'u': + mode = TEXT; + break; + case 'd': + mode = DATA; + break; + case 'r': + mode = RSRC; + break; + case 'h': + mode = HOST; + break; + case 'D': + direction = FORWARDS; + break; + case 'U': + direction = BACKWARDS; + break; + case 'q': + unpit_flag = 0; + break; + case 'p': + unpit_flag = 1; + break; + case 's': + verbose = fopen("/dev/null", "w"); + break; + case 'v': + verbose = stderr; + break; + default: + error( + "Usage: mcvert [ -[r|d|u|x|h] [D|U] [p|q] [s|v] ] filename...", + NULL); + } + } + + if (direction == BACKWARDS) + if (mode == HQX && unpit_flag) re_hqx();/* no re_pit() yet */ + else if (mode == HQX) re_hqx(); + else re_other(mode); + else + if (mode == HQX) un_hqx(unpit_flag); + else un_other(mode); + } + } + +/* An array useful for CRC calculations that use 0x1021 as the "seed" */ +word magic[] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 + }; + + +/* + * calc_crc() -- + * Compute the MacBinary II-style CRC for the data pointed to by p, with the + * crc seeded to seed. + * + * Modified by Jim Van Verth to use the magic array for efficiency. + */ +short calc_mb_crc(p, len, seed) +unsigned char *p; +long len; +short seed; +{ + short hold; /* crc computed so far */ + long i; /* index into data */ + + extern unsigned short magic[]; /* the magic array */ + + hold = seed; /* start with seed */ + for (i = 0; i < len; i++, p++) { + hold ^= (*p << 8); + hold = (hold << 8) ^ magic[(unsigned char)(hold >> 8)]; + } + + return (hold); +} /* calc_crc() */ + + +/* Report a fatal error */ +error(msg, name) +char msg[], name[]; +{ fprintf(stderr, msg, name); + putc('\n', stderr); + exit(1); + } + +/* replace illegal Unix characters in file name */ +/* make sure host file name doesn't get truncated beyond recognition */ +unixify(np) +register byte *np; +{ register ulong c; + c = strlen(np); + if (c > SYSNAMELEN - 4) c = SYSNAMELEN - 4; + np[c] = '\0'; + np--; + while (c = *++np) + if (c <= ' ' || c == '/' || c > '~') *np = '_'; + } + +/* Convert Unix time (GMT since 1-1-1970) to Mac + time (local since 1-1-1904) */ +#define MACTIMEDIFF 0x7c25b080 /* Mac time of 00:00:00 GMT, Jan 1, 1970 */ + +ulong time2mac(time) +ulong time; +{ struct timeb tp; + ftime(&tp); + return long2mac(time + MACTIMEDIFF + - 60 * (tp.timezone - 60 * tp.dstflag)); + } + + +/* This procedure copies the input file to the output file, basically, although + in TEXT mode it changes LF's to CR's and in any mode it forges a Mac info + header. Author type for TEXT mode can come from the MAC_EDITOR environ- + ment variable if it is defined. */ + +un_other(mode) +int mode; +{ register ulong b; + register ulong nchars; + char txtfname[BINNAMELEN], binfname[BINNAMELEN]; + FILE *txtfile, *binfile; + char *suffix; + struct stat stbuf; + info_header info; + int extra_chars; + ulong dlen, rlen, mtim, ctim; + short crc, calc_mb_crc(); + + if (mode == DATA) suffix = ".data"; + else if (mode == RSRC) suffix = ".rsrc"; + else suffix = ".text"; + + while (hqxnames_left[0][0] != '-') { + + strcpy(txtfname, *hqxnames_left++); + if (!(txtfile = fopen(txtfname, "r"))) { + /* Maybe we are supposed to figure out the suffix ourselves? */ + strcat(txtfname, suffix); + if (!(txtfile = fopen(txtfname, "r"))) + error("Cannot open %s", txtfname); + } + + if (stat(txtfname, &stbuf)) + error("Cannot read %s", txtfname); + + /* stuff header data into the info header */ + bzero(&info, sizeof(info_header)); + info.nlen = strlen(txtfname); + info.nlen = (info.nlen > NAMELEN) ? NAMELEN : info.nlen; + info.name[info.nlen] = '\0'; + strcpy(info.name, txtfname); /* name */ + mtim = time2mac(stbuf.st_mtime); + ctim = time2mac(stbuf.st_ctime); + bcopy(&mtim, info.mtim, 4); + bcopy(&ctim, info.ctim, 4); + info.uploadvers = '\201'; + info.readvers = '\201'; + + if (mode == RSRC) { + /* dlen is already zero */ + rlen = long2mac(stbuf.st_size); + bcopy(&rlen, info.rlen, 4); + bcopy("APPL", info.type, 4); + bcopy("CCOM", info.auth, 4); + } + else { + dlen = long2mac(stbuf.st_size); + bcopy(&dlen, info.dlen, 4); + /* rlen is already zero */ + bcopy("TEXT", info.type, 4); + if (mode == DATA) bcopy("????", info.auth, 4); + else bcopy(text_author, info.auth, 4); + } + + /* calculate CRC */ + crc = calc_mb_crc(&info, 124, 0); + info.crc[0] = (char) (crc >> 8); + info.crc[1] = (char) crc; + + /* Create the .bin file and write the info to it */ + sprintf(binfname, "%s/%s%s", dir, txtfname, ext); + if ((binfile = fopen(binfname, "w")) == NULL) + error("Cannot open %s", binfname); + fprintf(verbose, + "Converting %-30s type = \"%4.4s\", author = \"%4.4s\"\n", + txtfname, info.type, info.auth); + fwrite(&info, sizeof(info), 1, binfile); + + nchars = stbuf.st_size; + extra_chars = 127 - (nchars+127) % 128; + if (mode == TEXT) while (nchars--) { + b = getc(txtfile); + if (b == LF) b = CR; + putc(b, binfile); + } + else while (nchars--) putc(getc(txtfile), binfile); + + while (extra_chars--) putc(0, binfile); + fclose(binfile); + fclose(txtfile); + } + } + +/* This procedure copies the input file to the output file, basically, although + in TEXT mode it changes CR's to LF's and in any mode it skips over the Mac + info header. */ + +re_other(mode) +int mode; +{ register ulong b; + register ulong nchars; + char txtfname[BINNAMELEN], binfname[BINNAMELEN]; + FILE *txtfile, *binfile; + char *suffix; + info_header info; + + if (mode == DATA) suffix = ".data"; + else if (mode == RSRC) suffix = ".rsrc"; + else suffix = ".text"; + + while (hqxnames_left[0][0] != '-') { + + strcpy(binfname, *hqxnames_left++); + if ((binfile = fopen(binfname, "r")) == NULL) { + /* Maybe we are supposed to figure out the suffix ourselves? */ + strcat(binfname, ext); + if (!(binfile = fopen(binfname, "r"))) + error("Cannot open %s", binfname); + } + + /* Read the info from the .bin file, create the output file */ + fread(&info, sizeof(info), 1, binfile); + strncpy(txtfname, info.name, info.nlen); + txtfname[info.nlen] = '\0'; + fprintf(verbose, + "Converting %-30s type = \"%4.4s\", author = \"%4.4s\"\n", + txtfname, info.type, info.auth); + if ((txtfile = fopen(txtfname, "r")) == NULL) { + if ((txtfile = fopen(txtfname, "w")) == NULL) + error("Cannot open %s", txtfname); + } + else { + fclose(txtfile); + strcat(txtfname, suffix); + if ((txtfile = fopen(txtfname, "w")) == NULL) + error("Cannot open %s", txtfname); + } + + nchars = mac2long(* (ulong *) info.dlen); + if (mode == TEXT) while (nchars--) { + b = getc(binfile); + if (b == CR) b = LF; + putc(b, txtfile); + } + else if (mode == DATA) while (nchars--) + putc(getc(binfile), txtfile); + else { + while (nchars--) getc(binfile); + nchars = mac2long(* (ulong *) info.rlen); + while (nchars--) putc(getc(binfile), txtfile); + } + + fclose(binfile); + fclose(txtfile); + } + } diff --git a/contrib/AufsTools/mcvert/unpack.c b/contrib/AufsTools/mcvert/unpack.c new file mode 100644 index 0000000..f7dc9bf --- /dev/null +++ b/contrib/AufsTools/mcvert/unpack.c @@ -0,0 +1,192 @@ +#include "mactypes.h" + +extern word magic[]; +extern FILE *verbose; +extern char *dir, *ext; + +ulong pit_datalen, pit_rsrclen; +word hqx_crc, write_pit_fork(); +char pitfname[BINNAMELEN]; /* name of file being unpacked */ +FILE *pitfile; /* output file */ + +branch branchlist[255], *branchptr, *read_tree(); +leaf leaflist[256], *leafptr; +word Huff_nibble, Huff_bit_count; +byte (*read_char)(), get_crc_byte(), getHuffbyte(); + +word un_pit() +{ char PitId[4]; + int i; + word pit_crc; + + hqx_crc = 0; + /* Read and unpack until the PackIt End message is read */ + for (;;) { + read_char = get_crc_byte; + for (i = 0; i < 4; i++) PitId[i] = (char) get_crc_byte(); + if (!strncmp(PitId, "PEnd", 4)) break; + + if (strncmp(PitId, "PMag", 4) && strncmp(PitId, "PMa4", 4)) + error("Unrecognized Packit format message %s", PitId); + + if (PitId[3] == '4') { /* if this file is compressed */ + branchptr = branchlist; /* read the Huffman decoding */ + leafptr = leaflist; /* tree that is on the input */ + Huff_bit_count = 0; /* and use Huffman decoding */ + read_tree(); /* subsequently */ + read_char = getHuffbyte; + } + + read_pit_hdr(); /* also calculates datalen, rsrclen, + pitfile, pitfname */ + pit_crc = write_pit_fork(pit_datalen, 0); + pit_crc = write_pit_fork(pit_rsrclen, pit_crc); + check_pit_crc(pit_crc, " File data/rsrc CRC mismatch in %s", pitfname); + fclose(pitfile); + } + hqx_crc = (hqx_crc << 8) ^ magic[hqx_crc >> 8]; + hqx_crc = (hqx_crc << 8) ^ magic[hqx_crc >> 8]; + return hqx_crc; + } + +check_pit_crc(calc_crc, msg, name) +word calc_crc; +char msg[], name[]; +{ word read_crc; + read_crc = (*read_char)() << 8; + read_crc |= (*read_char)(); + if (read_crc != calc_crc) error(msg, name); + } + +/* This routine reads the header of a packed file and appropriately twiddles it, + determines if it has CRC problems, creates the .bin file, and puts the info + into the .bin file. + Output is pit_datalen, pit_rsrclen, pitfname, pitfile */ +read_pit_hdr() +{ register int n; + register byte *pit_byte; + register ulong pit_crc; + pit_header pit; + info_header info; + short crc; + + extern short calc_mb_crc(); + /* read the pit header and compute the CRC */ + pit_crc = 0; + pit_byte = (byte *) &pit; + for (n = 0; n < sizeof(pit_header); n++) { + *pit_byte = (*read_char)(); + pit_crc = ((pit_crc & 0xff) << 8) + ^ magic[*pit_byte++ ^ (pit_crc >> 8)]; + } + + /* stuff the pit header data into the info header */ + bzero(&info, sizeof(info_header)); + info.nlen = pit.nlen; + strncpy(info.name, pit.name, pit.nlen); /* name */ + bcopy(pit.type, info.type, 9); /* type, author, flag */ + bcopy(pit.dlen, info.dlen, 16); /* (d,r)len, (c,m)tim */ + info.flags &= 0x7e; /* reset lock bit, init bit */ + if (pit.protect & 0x40) info.protect = 1; /* copy protect bit */ + info.uploadvers = '\201'; + info.readvers = '\201'; + + /* calculate MacBinary CRC */ + crc = calc_mb_crc(&info, 124, 0); + info.crc[0] = (char) (crc >> 8); + info.crc[1] = (char) crc; + + /* Create the .bin file and write the info to it */ + pit.name[pit.nlen] = '\0'; + unixify(pit.name); + sprintf(pitfname, "%s/%s%s", dir, pit.name, ext); + fprintf(verbose, + " %-14s%-30s type = \"%4.4s\", author = \"%4.4s\"\n", + (read_char == get_crc_byte) ? "Unpacking" : "Decompressing", + pit.name, pit.type, pit.auth); + if ((pitfile = fopen(pitfname, "w")) == NULL) + error(" Cannot open %s", pitfname); + check_pit_crc(pit_crc, " File header CRC mismatch in %s", pitfname); + fwrite(&info, sizeof(info_header), 1, pitfile); + + /* Get a couple of items we'll need later */ + bcopy(pit.dlen, &pit_datalen, 4); + pit_datalen = mac2long(pit_datalen); + bcopy(pit.rlen, &pit_rsrclen, 4); + pit_rsrclen = mac2long(pit_rsrclen); + } + +/* This routine copies bytes from the decoded input stream to the output + and calculates the CRC. It also pads to a multiple of 128 bytes on the + output, which is part of the .bin format */ +word write_pit_fork(nbytes, calc_crc) +register ulong nbytes; +register ulong calc_crc; +{ register ulong b; + int extra_bytes; + + extra_bytes = 127 - (nbytes+127)%128; /* pad fork to mult of 128 bytes */ + while (nbytes--) { + b = (*read_char)(); + calc_crc = ((calc_crc & 0xff) << 8) ^ magic[b ^ (calc_crc >> 8)]; + putc(b, pitfile); + } + while (extra_bytes--) putc(0, pitfile); + return (word) calc_crc; + } + +/* This routine recursively reads the compression decoding data. + It appears to be Huffman compression. Every leaf is represented + by a 1 bit, then the byte it represents. A branch is represented + by a 0 bit, then its zero and one sons */ +branch *read_tree() +{ register branch *branchp; + register leaf *leafp; + register ulong b; + if (!Huff_bit_count--) { + Huff_nibble = get_crc_byte(); + Huff_bit_count = 7; + } + if ((Huff_nibble<<=1) & 0x0100) { + leafp = leafptr++; + leafp->flag = 1; + b = get_crc_byte(); + leafp->data = Huff_nibble | (b >> Huff_bit_count); + Huff_nibble = b << (8 - Huff_bit_count); + return (branch *) leafp; + } + else { + branchp = branchptr++; + branchp->flag = 0; + branchp->zero = read_tree(); + branchp->one = read_tree(); + return branchp; + } + } + +/* This routine returns the next 8 bits. It finds the byte in the + Huffman decoding tree based on the bits from the input stream. */ +byte getHuffbyte() +{ register branch *branchp; + branchp = branchlist; + while (!branchp->flag) { + if (!Huff_bit_count--) { + Huff_nibble = get_crc_byte(); + Huff_bit_count = 7; + } + branchp = ((Huff_nibble<<=1) & 0x0100) ? branchp->one : branchp->zero; + } + return ((leaf *) branchp)->data; + } + +/* This routine returns the next byte on the .hqx input stream, hiding + most file system details at a lower level. .hqx CRC is maintained + here */ +byte get_crc_byte() +{ register ulong c; + extern byte *buf_ptr, *buf_end; + if (buf_ptr == buf_end) fill_hqxbuf(); + c = *buf_ptr++; + hqx_crc = ((hqx_crc << 8) | c) ^ magic[hqx_crc >> 8]; + return (byte) c; + } diff --git a/contrib/AufsTools/shell/Makefile b/contrib/AufsTools/shell/Makefile new file mode 100644 index 0000000..5344509 --- /dev/null +++ b/contrib/AufsTools/shell/Makefile @@ -0,0 +1,6 @@ +DESTDIR= /usr/local/cap + +all: + +install: + cp mcmp cleanup drag dup m2u macp newfolder toaufs trash u2m $(DESTDIR) diff --git a/contrib/AufsTools/shell/cleanup b/contrib/AufsTools/shell/cleanup new file mode 100755 index 0000000..56be4fd --- /dev/null +++ b/contrib/AufsTools/shell/cleanup @@ -0,0 +1,7 @@ +# clean up an Aufs folder +foreach i ({.resource,.finderinfo}/*) + if(! -e "$i:t") then + ls -l "$i" + rm "$i" + endif +end diff --git a/contrib/AufsTools/shell/drag b/contrib/AufsTools/shell/drag new file mode 100755 index 0000000..e96efe9 --- /dev/null +++ b/contrib/AufsTools/shell/drag @@ -0,0 +1,21 @@ +# +set echo +set argc = $#argv +if( $argc > 2 ) then + set dest = $argv[argc] + foreach i ( $argv[-(argc-1)] ) + set h1 = $i:h + if( $h1 == $i ) set h1 = "." + mv $i $dest + mv h1/.finderinfo/$i:t $dest/.finderinfo + mv h1/.resource/$i:t $dest/.resource + end +else + set h1 = $argv[1]:h + if( $h1 == $argv[1] ) set h1 = "." + set h2 = $argv[2]:h + if( $h2 == $argv[2] ) set h2 = "." + mv $argv[1] $argv[2] + mv $h1/.finderinfo/$argv[1]:t $h2/.finderinfo/$argv[2]:t + mv $h1/.resource/$argv[1]:t $h2/.resource/$argv[2]:t +endif diff --git a/contrib/AufsTools/shell/dup b/contrib/AufsTools/shell/dup new file mode 100755 index 0000000..e350639 --- /dev/null +++ b/contrib/AufsTools/shell/dup @@ -0,0 +1,21 @@ +# +# set echo +set argc = $#argv +if( $argc > 2 ) then + set dest = $argv[argc] + foreach i ( $argv[-(argc-1)] ) + set h1 = $i:h + if( $h1 == $i ) set h1 = "." + cp $i $dest + cp h1/.finderinfo/$i:t $dest/.finderinfo + cp h1/.resource/$i:t $dest/.resource + end +else + set h1 = $argv[1]:h + if( $h1 == $argv[1] ) set h1 = "." + set h2 = $argv[2]:h + if( $h2 == $argv[2] ) set h2 = "." + cp $argv[1] $argv[2] + cp $h1/.finderinfo/$argv[1]:t $h2/.finderinfo/$argv[2]:t + cp $h1/.resource/$argv[1]:t $h2/.resource/$argv[2]:t +endif diff --git a/contrib/AufsTools/shell/m2u b/contrib/AufsTools/shell/m2u new file mode 100755 index 0000000..ba34b43 --- /dev/null +++ b/contrib/AufsTools/shell/m2u @@ -0,0 +1,7 @@ +# +set f = _tmp_$$ +foreach i ($argv) + mv $i $f + tr '\015' '\012' <$f >$i + rm $f +end diff --git a/contrib/AufsTools/shell/macp b/contrib/AufsTools/shell/macp new file mode 100755 index 0000000..394ae2e --- /dev/null +++ b/contrib/AufsTools/shell/macp @@ -0,0 +1,4 @@ +# +foreach i ($argv) + tr '\015' '\012' <$i | $PAGER +end diff --git a/contrib/AufsTools/shell/mcmp b/contrib/AufsTools/shell/mcmp new file mode 100755 index 0000000..7ce76d3 --- /dev/null +++ b/contrib/AufsTools/shell/mcmp @@ -0,0 +1,9 @@ +# +set argc = $#argv +if( $argc == 2 ) then + cmp $argv[1] $argv[2] + cmp .resource/$argv[1] .resource/$argv[2] + cmp .finderinfo/$argv[1] .finderinfo/$argv[2] +else + echo "usage: mcmp file1 file2" +endif diff --git a/contrib/AufsTools/shell/newfolder b/contrib/AufsTools/shell/newfolder new file mode 100755 index 0000000..a3c4a0b --- /dev/null +++ b/contrib/AufsTools/shell/newfolder @@ -0,0 +1,7 @@ +# +set u = `umask` +umask 007 +foreach i ($argv) + mkdir $i $i/.finderinfo $i/.resource +end +umask $u diff --git a/contrib/AufsTools/shell/toaufs b/contrib/AufsTools/shell/toaufs new file mode 100755 index 0000000..2253c7a --- /dev/null +++ b/contrib/AufsTools/shell/toaufs @@ -0,0 +1,6 @@ +# +setenv MAC_EXT .$$conv +mcvert -v $argv +capit *$MAC_EXT +rm *$MAC_EXT + diff --git a/contrib/AufsTools/shell/trash b/contrib/AufsTools/shell/trash new file mode 100755 index 0000000..453d31f --- /dev/null +++ b/contrib/AufsTools/shell/trash @@ -0,0 +1,4 @@ +# +foreach i ($argv) + rm -f $i .finderinfo/$i .resource/$i +end diff --git a/contrib/AufsTools/shell/u2m b/contrib/AufsTools/shell/u2m new file mode 100755 index 0000000..b23a83e --- /dev/null +++ b/contrib/AufsTools/shell/u2m @@ -0,0 +1,7 @@ +# +set f = _tmp_$$ +foreach i ($argv) + mv $i $f + tr '\012' '\015' <$f >$i + rm $f +end diff --git a/contrib/AufsTools/stuffit/Makefile b/contrib/AufsTools/stuffit/Makefile new file mode 100644 index 0000000..e07ba2c --- /dev/null +++ b/contrib/AufsTools/stuffit/Makefile @@ -0,0 +1,19 @@ +DESTDIR = /usr/local/cap + +all: sit stuffit + +stuffit: sit + -rm -f stuffit + ln sit stuffit + +sit: sit.o updcrc.o + cc -o sit sit.o updcrc.o + +install: sit + -strip sit + cp sit $(DESTDIR) + -rm -f $(DESTDIR)/stuffit + (cd $(DESTDIR); ln sit stuffit) + +clean: + -rm -f stuffit sit *.o diff --git a/contrib/AufsTools/stuffit/sit.c b/contrib/AufsTools/stuffit/sit.c new file mode 100644 index 0000000..0b73d40 --- /dev/null +++ b/contrib/AufsTools/stuffit/sit.c @@ -0,0 +1,476 @@ +/* + * sit - Stuffit for UNIX + * Puts unix data files into stuffit archive suitable for downloading + * to a Mac. Automatically processes files output from xbin. + * + * Reverse engineered from unsit by Allan G. Weber, which was based on + * macput, which was based on ... + * Just like unsit this uses the host's version of compress to do the work. + * + * Examples: + * 1) take collection of UNIX text files and make them LSC text files + * when uncompressed on the mac: + * sit -u -T TEXT -C KAHL file ... + * 2) Process output from xbin: + * xbin file1 (produces FileOne.{info,rsrc,data}) + * sit file1 + * + * Tom Bereiter + * ..!{rutgers,ames}!cs.utexas.edu!halley!rolex!twb + * + * This version for CAP aufs files based on info from aufs source + mcvert etc. + * Aufs version is program is called AUFSNAME (default stuffit) + * + * Aug 90. Nigel Perry, np@doc.ic.ac.uk + * + */ + +#define BSD + +#ifdef sun +#ifdef __svr4__ +#define SOLARIS +#undef BSD +#endif __svr4__ +#endif sun + +#include +#include +#include +#include "sit.h" +#ifdef BSD +#include +#include +#else BSD +#include +extern long timezone; +#endif BSD + +#ifdef SOLARIS +#include +#endif SOLARIS + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + +/* Mac time of 00:00:00 GMT, Jan 1, 1970 */ +#define TIMEDIFF 0x7c25b080 + +/* if called by this name, program will work on aufs files */ +#define AUFSNAME "stuffit" + +struct sitHdr sh; +struct fileHdr fh; + +char buf[BUFSIZ]; +char *defoutfile = "archive.sit"; +int ofd; +ushort crc; +int clen; +int rmfiles; +int unixf; +char *Creator, *Type; +int aufs; + +usage() { fprintf(stderr,"Usage: sit file\n"); } +extern char *optarg; +extern int optind; + +/********************************************************************************/ +/* added for aufs, nicked from various places... */ + +/* following from mcvert program */ + +/* Useful, though not particularly Mac related, values */ +typedef unsigned char byte; /* one byte, obviously */ +typedef unsigned short word; /* must be 2 bytes */ +#ifndef SOLARIS +typedef unsigned long ulong; /* 4 bytes */ +#endif SOLARIS + +#define NAMELEN 63 /* maximum legal Mac file name length */ + +/* Format of a bin file: +A bin file is composed of 128 byte blocks. The first block is the +info_header (see below). Then comes the data fork, null padded to fill the +last block. Then comes the resource fork, padded to fill the last block. A +proposal to follow with the text of the Get Info box has not been implemented, +to the best of my knowledge. Version, zero1 and zero2 are what the receiving +program looks at to determine if a MacBinary transfer is being initiated. +*/ +typedef struct { /* info file header (128 bytes). Unfortunately, these + longs don't align to word boundaries */ + byte version; /* there is only a version 0 at this time */ + byte nlen; /* Length of filename. */ + byte name[NAMELEN]; /* Filename (only 1st nlen are significant)*/ + byte type[4]; /* File type. */ + byte auth[4]; /* File creator. */ + byte flags; /* file flags: LkIvBnSyBzByChIt */ + byte zero1; /* Locked, Invisible,Bundle, System */ + /* Bozo, Busy, Changed, Init */ + byte icon_vert[2]; /* Vertical icon position within window */ + byte icon_horiz[2]; /* Horizontal icon postion in window */ + byte window_id[2]; /* Window or folder ID. */ + byte protect; /* = 1 for protected file, 0 otherwise */ + byte zero2; + byte dflen[4]; /* Data Fork length (bytes) - most sig. */ + byte rflen[4]; /* Resource Fork length byte first */ + byte cdate[4]; /* File's creation date. */ + byte mdate[4]; /* File's "last modified" date. */ + byte ilen[2]; /* GetInfo message length */ + byte flags2; /* Finder flags, bits 0-7 */ + byte unused[14]; + byte packlen[4]; /* length of total files when unpacked */ + byte headlen[2]; /* length of secondary header */ + byte uploadvers; /* Version of MacBinary II that the uploading program is written for */ + byte readvers; /* Minimum MacBinary II version needed to read this file */ + byte crc[2]; /* CRC of the previous 124 bytes */ + byte padding[2]; /* two trailing unused bytes */ + } info_header; + +/* end of mcvert stuff */ +/* from CAP aufs documentation */ + +#define FINFOLEN 32 +#define MAXCLEN 199 +typedef struct +{ + /* be careful with alignment */ + byte fndr_type[4]; + byte fndr_creator[4]; + word fndr_flags; + byte fndr_loc[4]; + word fndr_fldr; + word fndr_icon; + byte fndr_unused[8]; + word fndr_comment; + byte fndr_putaway[4]; + word fi_attr; /* attributes */ +#define FI_MAGIC1 255 + byte fi_magic1; /* was: length of comment */ +#define FI_VERSION 0x10 /* version major 1, minor 0 */ + /* if more than 8 versions then */ + /* something wrong anyway */ + byte fi_version; /* version number */ +#define FI_MAGIC 0xda + byte fi_magic; /* magic word check */ + byte fi_bitmap; /* bitmap of included info */ +#define FI_BM_SHORTFILENAME 0x1 /* is this included? */ +#define FI_BM_MACINTOSHFILENAME 0x2 /* is this included? */ + byte fi_shortfilename[12+1]; /* possible short file name */ + byte fi_macfilename[32+1]; /* possible macintosh file name */ + byte fi_comln; /* comment length */ + byte fi_comnt[MAXCLEN+1]; /* comment string */ +} FileInfo; + +FileInfo fndr_info; + +/* end aufs */ +/********************************************************************************/ + +main(argc,argv) char **argv; { + int i,n; + int total, nfiles; + int c; + char *s, *progname; + + rmfiles = unixf = 0; + progname = argv[0]; + if ((s = (char *) rindex(progname, '/')) != NULL) + progname = ++s; + aufs = strcmp(progname, AUFSNAME) == 0; + + while ((c=getopt(argc, argv, "ro:uC:T:")) != EOF) + switch (c) { + case 'r': + rmfiles++; /* remove files when done */ + break; + case 'o': /* specify output file */ + defoutfile = optarg; + break; + case 'u': /* unix file -- change '\n' to '\r' */ + unixf++; + break; + case 'C': /* set Mac creator */ + Creator = optarg; + break; + case 'T': /* set Mac file type */ + Type = optarg; + break; + case '?': + usage(); + exit(1); + } + + if(aufs && (strlen(defoutfile) > 32)) + { fprintf(stderr, "Output name must not exceed 32 characters: %s\n", defoutfile); + exit(-1); + } + + if(aufs) + { /* make the .finderinfo file */ + char buf[32+12+1]; + + strcpy(buf, ".finderinfo/"); + strcat(buf, defoutfile); + if ((ofd=creat(buf,0644))<0) + { perror(buf); + exit(1); + } + bzero(&fndr_info, sizeof(FileInfo)); + bcopy("SIT!", fndr_info.fndr_type, 4); + bcopy("SIT!", fndr_info.fndr_creator, 4); + fndr_info.fi_magic1 = FI_MAGIC1; + fndr_info.fi_version = FI_VERSION; + fndr_info.fi_magic = FI_MAGIC; + fndr_info.fi_bitmap = FI_BM_MACINTOSHFILENAME; + strcpy(fndr_info.fi_macfilename, defoutfile); + write(ofd, &fndr_info, sizeof(FileInfo)); + close(ofd); + } + + if ((ofd=creat(defoutfile,0644))<0) { + perror(defoutfile); + exit(1); + } + /* empty header, will seek back and fill in later */ + write(ofd,&sh,sizeof sh); + + for (i=optind; i=0 && st.st_size) { /* resource fork exists */ + dofork(nbuf); + cp4(st.st_size,fh.rLen); + cp4(clen,fh.cRLen); + cp2(crc,fh.rsrcCRC); + fh.compRMethod = lpzComp; + fork++; + } + if (rmfiles) unlink(nbuf); /* ignore errors */ + + /* look for data fork */ + st.st_size = 0; + strcpy(nbuf,name); + if (stat(nbuf,&st)<0) { /* first try plain name */ + strcat(nbuf,".data"); + stat(nbuf,&st); + } + if (st.st_size) { /* data fork exists */ + dofork(nbuf); + cp4(st.st_size,fh.dLen); + cp4(clen,fh.cDLen); + cp2(crc,fh.dataCRC); + fh.compDMethod = lpzComp; + fork++; + } + if (fork == 0) { + fprintf(stderr,"%s: no data or resource files\n",name); + return 0; + } + if (rmfiles) unlink(nbuf); /* ignore errors */ + + /* look for .info file */ + if(aufs) + { strcpy(nbuf, ".finderinfo/"); + strcat(nbuf, name); + } + else + { strcpy(nbuf,name); + strcat(nbuf,".info"); + } + if((fd=open(nbuf,0))>=0 + && ((!aufs && read(fd,&ih,sizeof(ih))==sizeof(ih)) + || (aufs && read(fd,&fndr_info,sizeof(FileInfo))==sizeof(FileInfo)) + ) + ) + { if(aufs) + { char *np; + + np = (char *)(fndr_info.fi_bitmap & FI_BM_MACINTOSHFILENAME ? fndr_info.fi_macfilename + : fndr_info.fi_shortfilename); + fh.fName[0] = (char)strlen(np); + strncpy(fh.fName+1, np, 64); + bcopy(fndr_info.fndr_type, fh.fType, 4); + bcopy(fndr_info.fndr_creator, fh.fCreator, 4); + bcopy(&fndr_info.fndr_flags, fh.FndrFlags, 2); +#ifdef BSD + ftime(&tbuf); + tp = localtime(&tbuf.time); + tdiff = TIMEDIFF - tbuf.timezone * 60; + if (tp->tm_isdst) + tdiff += 60 * 60; +#else + /* I hope this is right! -andy */ + time(&bs); + tp = localtime(&bs); + tdiff = TIMEDIFF - timezone; + if (tp->tm_isdst) + tdiff += 60 * 60; +#endif + cp4(st.st_ctime + tdiff, fh.cDate); + cp4(st.st_mtime + tdiff, fh.mDate); + } + else + { strncpy(fh.fName, ih.name,64); + strncpy(fh.fType, ih.type, 4); + strncpy(fh.fCreator, ih.creator, 4); + strncpy(fh.FndrFlags, ih.flag, 2); + strncpy(fh.cDate, ih.ctime, 4); + strncpy(fh.mDate, ih.mtime, 4); + } + } + else { /* no info file so fake it */ + strncpy(&fh.fName[1], name,63); fh.fName[0] = min(strlen(name),63); + /* default to LSC text file */ + strncpy(fh.fType, Type ? Type : "TEXT", 4); + strncpy(fh.fCreator, Creator ? Creator : "KAHL", 4); + /* convert unix file time to mac time format */ +#ifdef BSD + ftime(&tbuf); + tp = localtime(&tbuf.time); + tdiff = TIMEDIFF - tbuf.timezone * 60; + if (tp->tm_isdst) + tdiff += 60 * 60; +#else + /* I hope this is right! -andy */ + time(&bs); + tp = localtime(&bs); + tdiff = TIMEDIFF - timezone; + if (tp->tm_isdst) + tdiff += 60 * 60; +#endif + cp4(st.st_ctime + tdiff, fh.cDate); + cp4(st.st_mtime + tdiff, fh.mDate); + } + close(fd); + if (rmfiles) unlink(nbuf); /* ignore errors */ + + crc = updcrc(0,&fh,(sizeof fh)-2); + cp2(crc, fh.hdrCRC); + + fpos2 = lseek(ofd,0,1); /* remember where we are */ + lseek(ofd,fpos1,0); /* seek back over file(s) and header */ + write(ofd,&fh,sizeof fh); /* write back header */ + fpos2=lseek(ofd,fpos2,0); /* seek forward file */ + + return (fpos2 - fpos1); +} + +dofork(name) +char name[]; +{ + FILE *fs; + int n, fd, ufd; + char *p; + + if ((fd=open(name,0))<0) { + perror(name); + return 0; + } + if (unixf) /* build conversion file */ + if ((ufd=creat("sit+temp",0644))<0) { + perror("sit+temp"); + return 0; + } + /* do crc of file: */ + crc = 0; + while ((n=read(fd,buf,BUFSIZ))>0) { + if (unixf) { /* convert '\n' to '\r' */ + for (p=buf; p<&buf[n]; p++) + if (*p == '\n') *p = '\r'; + write(ufd,buf,n); + } + crc = updcrc(crc,buf,n); + } + close(fd); + /* + * open pipe to compress file + * If a unix file ('\n' -> '\r' conversion) 'sit+temp' will be a new copy + * with the conversion done. Otherwise, 'sit+temp' is just a link to + * the input file. + */ + if (unixf) + close(ufd); + else link(name,"sit+temp"); + fs = popen("compress -c -n -b 14 sit+temp","r"); + if (fs == NULL) { + perror(name); + return 0; + } + /* write out compressed file */ + clen = 0; + while ((n=fread(buf,1,BUFSIZ,fs))>0) { + write(ofd,buf,n); + clen += n; + } + pclose(fs); + unlink("sit+temp"); +} + +cp2(x,dest) +unsigned short x; +char dest[]; +{ + dest[0] = x>>8; + dest[1] = x; +} + +cp4(x,dest) +unsigned long x; +char dest[]; +{ + dest[0] = x>>24; + dest[1] = x>>16; + dest[2] = x>>8; + dest[3] = x; +} diff --git a/contrib/AufsTools/stuffit/sit.h b/contrib/AufsTools/stuffit/sit.h new file mode 100644 index 0000000..0ba354f --- /dev/null +++ b/contrib/AufsTools/stuffit/sit.h @@ -0,0 +1,72 @@ + +/* sit.h: contains declarations for SIT headers */ + +struct sitHdr { /* 22 bytes */ + u_char sig1[4]; /* = 'SIT!' -- for verification */ + u_char numFiles[2]; /* number of files in archive */ + u_char arcLen[4]; /* length of entire archive incl. */ + u_char sig2[4]; /* = 'rLau' -- for verification */ + u_char version; /* version number */ + char reserved[7]; +}; + +struct fileHdr { /* 112 bytes */ + u_char compRMethod; /* rsrc fork compression method */ + u_char compDMethod; /* data fork compression method */ + u_char fName[64]; /* a STR63 */ + char fType[4]; /* file type */ + char fCreator[4]; /* creator... */ + char FndrFlags[2]; /* copy of Finder flags */ + char cDate[4]; /* creation date */ + char mDate[4]; /* !restored-compat w/backup prgms */ + u_char rLen[4]; /* decom rsrc length */ + u_char dLen[4]; /* decomp data length */ + u_char cRLen[4]; /* compressed lengths */ + u_char cDLen[4]; + u_char rsrcCRC[2]; /* crc of rsrc fork */ + u_char dataCRC[2]; /* crc of data fork */ + char reserved[6]; + u_char hdrCRC[2]; /* crc of file header */ +}; + +/* file format is: + sitArchiveHdr + file1Hdr + file1RsrcFork + file1DataFork + file2Hdr + file2RsrcFork + file2DataFork + . + . + . + fileNHdr + fileNRsrcFork + fileNDataFork +*/ + + + +/* compression methods */ +#define noComp 0 /* just read each byte and write it to archive */ +#define repComp 1 /* RLE compression */ +#define lpzComp 2 /* LZW compression */ +#define hufComp 3 /* Huffman compression */ + +/* all other numbers are reserved */ + +/* + * the format of a *.info file made by xbin + */ +struct infohdr { + char res0; + char name[64]; /* 2 (a str 63) */ + char type[4]; /* 65 */ + char creator[4]; /* 69 */ + char flag[2]; /* 73 */ + char res1[8]; + char dlen[4]; /* 83 */ + char rlen[4]; /* 87 */ + char ctime[4]; /* 91 */ + char mtime[4]; /* 95 */ +}; diff --git a/contrib/AufsTools/stuffit/updcrc.c b/contrib/AufsTools/stuffit/updcrc.c new file mode 100644 index 0000000..46788ff --- /dev/null +++ b/contrib/AufsTools/stuffit/updcrc.c @@ -0,0 +1,191 @@ +/* updcrc(3), crc(1) - calculate crc polynomials + * + * Calculate, intelligently, the CRC of a dataset incrementally given a + * buffer full at a time. + * + * Usage: + * newcrc = updcrc( oldcrc, bufadr, buflen ) + * unsigned int oldcrc, buflen; + * char *bufadr; + * + * Compiling with -DTEST creates a program to print the CRC of stdin to stdout. + * Compile with -DMAKETAB to print values for crctab to stdout. If you change + * the CRC polynomial parameters, be sure to do this and change + * crctab's initial value. + * + * Notes: + * Regards the data stream as an integer whose MSB is the MSB of the first + * byte recieved. This number is 'divided' (using xor instead of subtraction) + * by the crc-polynomial P. + * XMODEM does things a little differently, essentially treating the LSB of + * the first data byte as the MSB of the integer. Define SWAPPED to make + * things behave in this manner. + * + * Author: Mark G. Mendel, 7/86 + * UUCP: ihnp4!umn-cs!hyper!mark, GEnie: mgm + */ + +/* The CRC polynomial. + * These 4 values define the crc-polynomial. + * If you change them, you must change crctab[]'s initial value to what is + * printed by initcrctab() [see 'compile with -DMAKETAB' above]. + */ + /* Value used by: CITT XMODEM ARC */ +#define P 0xA001 /* the poly: 0x1021 0x1021 A001 */ +#define INIT_CRC 0L /* init value: -1 0 0 */ +#define SWAPPED /* bit order: undef defined defined */ +#define W 16 /* bits in CRC:16 16 16 */ + + /* data type that holds a W-bit unsigned integer */ +#if W <= 16 +# define WTYPE unsigned short +#else +# define WTYPE unsigned long +#endif + + /* the number of bits per char: don't change it. */ +#define B 8 + +static WTYPE crctab[1<>(W-B)) ^ *cp++]; +#else + crc = (crc>>B) ^ crctab[(crc & ((1< +main() +{ + initcrctab(); +} + +initcrctab() +{ + register int b, i; + WTYPE v; + + + for( b = 0; b <= (1<= 0; ) + v = v & ((WTYPE)1<<(W-1)) ? (v<<1)^P : v<<1; +#else + for( v = b, i = B; --i >= 0; ) + v = v & 1 ? (v>>1)^P : v>>1; +#endif + crctab[b] = v; + + printf( "0x%lx,", v & ((1L< +#include + +#define MAXBUF 4096 + + + +main( ac, av ) + int ac; char **av; +{ + int fd; + int nr; + int i; + char buf[MAXBUF]; + WTYPE crc, crc2; + + fd = 0; + if( ac > 1 ) + if( (fd = open( av[1], O_RDONLY )) < 0 ) { + perror( av[1] ); + exit( -1 ); + } + crc = crc2 = INIT_CRC; + + while( (nr = read( fd, buf, MAXBUF )) > 0 ) { + crc = updcrc( crc, buf, nr ); + } + + if( nr != 0 ) + perror( "reading" ); + else { + printf( "%lx\n", crc ); + } + +#ifdef MAGICCHECK + /* tack one's complement of crc onto data stream, and + continue crc calculation. Should get a constant (magic number) + dependent only on P, not the data. + */ + crc2 = crc ^ -1L; + for( nr = W-B; nr >= 0; nr -= B ) { + buf[0] = (crc2 >> nr); + crc = updcrc(crc, buf, 1); + } + + /* crc should now equal magic */ + buf[0] = buf[1] = buf[2] = buf[3] = 0; + printf( "magic test: %lx =?= %lx\n", crc, updcrc(-1, buf, W/B)); +#endif MAGICCHECK +} + +#endif diff --git a/contrib/AufsTools/unstuffit/Makefile b/contrib/AufsTools/unstuffit/Makefile new file mode 100644 index 0000000..7d33425 --- /dev/null +++ b/contrib/AufsTools/unstuffit/Makefile @@ -0,0 +1,24 @@ +GETOPT = +#GETOPT = getopt.o +DESTDIR = /usr/local/cap + +all: unsit unstuffit + +unstuffit: unsit + -rm -f unstuffit + ln unsit unstuffit + +unsit : unsit.o updcrc.o $(GETOPT) + cc -o unsit unsit.o updcrc.o $(GETOPT) + +unsit.o : unsit.c stuffit.h +getopt.o : getopt.c + +install: unsit + -strip unsit + cp unsit $(DESTDIR) + -rm -f $(DESTDIR)/unstuffit + (cd $(DESTDIR); ln unsit unstuffit) + +clean: + -rm -f unstuffit unsit *.o diff --git a/contrib/AufsTools/unstuffit/getopt.c b/contrib/AufsTools/unstuffit/getopt.c new file mode 100644 index 0000000..17f0c21 --- /dev/null +++ b/contrib/AufsTools/unstuffit/getopt.c @@ -0,0 +1,64 @@ +/* + * getopt - get option letter from argv + */ + +#include + +char *optarg; /* Global argument pointer. */ +int optind = 0; /* Global argv index. */ + +static char *scan = NULL; /* Private scan pointer. */ + +extern char *index(); + +int +getopt(argc, argv, optstring) +int argc; +char *argv[]; +char *optstring; +{ + register char c; + register char *place; + + optarg = NULL; + + if (scan == NULL || *scan == '\0') { + if (optind == 0) + optind++; + + if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') + return(EOF); + if (strcmp(argv[optind], "--")==0) { + optind++; + return(EOF); + } + + scan = argv[optind]+1; + optind++; + } + + c = *scan++; + place = index(optstring, c); + + if (place == NULL || c == ':') { + fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); + return('?'); + } + + place++; + if (*place == ':') { + if (*scan != '\0') { + optarg = scan; + scan = NULL; + } else if (optind < argc) { + optarg = argv[optind]; + optind++; + } else { + fprintf(stderr, "%s: -%c argument missing\n", argv[0], c); + return('?'); + } + } + + return(c); +} + diff --git a/contrib/AufsTools/unstuffit/stuffit.h b/contrib/AufsTools/unstuffit/stuffit.h new file mode 100644 index 0000000..b48bb7a --- /dev/null +++ b/contrib/AufsTools/unstuffit/stuffit.h @@ -0,0 +1,64 @@ +/* StuffIt.h: contains declarations for SIT headers */ + +struct sitHdr { /* 22 bytes */ + OSType signature; /* = 'SIT!' -- for verification */ + unsigned short numFiles; /* number of files in archive */ + unsigned long arcLength; /* length of entire archive incl. + hdr. -- for verification */ + OSType signature2; /* = 'rLau' -- for verification */ + unsigned char version; /* version number */ + char reserved[7]; +}; + +struct fileHdr { /* 112 bytes */ + unsigned char compRMethod; /* rsrc fork compression method */ + unsigned char compDMethod; /* data fork compression method */ + unsigned char fName[64]; /* a STR63 */ + OSType fType; /* file type */ + OSType fCreator; /* er... */ + short FndrFlags; /* copy of Finder flags. For our + purposes, we can clear: + busy,onDesk */ + unsigned long creationDate; + unsigned long modDate; /* !restored-compat w/backup prgms */ + unsigned long rsrcLength; /* decompressed lengths */ + unsigned long dataLength; + unsigned long compRLength; /* compressed lengths */ + unsigned long compDLength; + unsigned short rsrcCRC; /* crc of rsrc fork */ + unsigned short dataCRC; /* crc of data fork */ + char reserved[6]; + unsigned short hdrCRC; /* crc of file header */ +}; + + +/* file format is: + sitArchiveHdr + file1Hdr + file1RsrcFork + file1DataFork + file2Hdr + file2RsrcFork + file2DataFork + . + . + . + fileNHdr + fileNRsrcFork + fileNDataFork +*/ + + + +/* compression methods */ +#define noComp 0 /* just read each byte and write it to archive */ +#define rleComp 1 /* RLE compression */ +#define lzwComp 2 /* LZW compression */ +#define hufComp 3 /* Huffman compression */ + +#define encrypted 16 /* bit set if encrypted. ex: encrypted+lpzComp */ + +#define startFolder 32 /* marks start of a new folder */ +#define endFolder 33 /* marks end of the last folder "started" */ + +/* all other numbers are reserved */ diff --git a/contrib/AufsTools/unstuffit/unsit.c b/contrib/AufsTools/unstuffit/unsit.c new file mode 100644 index 0000000..4dda796 --- /dev/null +++ b/contrib/AufsTools/unstuffit/unsit.c @@ -0,0 +1,1158 @@ +/* Modified Aug & Dec 90, Nigel Perry, Dept of Computing, Imperial College, + London SW7 2BZ, np@doc.ic.ac.uk + + If called by AUFSNAME (default unstuffit) will input/output to + CAPS aufs disc. + + Will now untiff multiple files. + + */ +/* + unsit - Macintosh StuffIt file extractor + + Version 1.5f, for StuffIt 1.5 + + July 23, 1990 + +This program will unpack a Macintosh StuffIt file into separate files. +The data fork of a StuffIt file contains both the data and resource +forks of the packed files. The program will unpack each Mac file into +either separate .data, .rsrc., and .info files that can be downloaded +to a Mac using macput and MacTerminal over a tty line, or into a +single MacBinary format file. The MacBinary format is generally more +convenient for those with network connections and FTP capability. The +program is much like the "unpit" program for breaking apart Packit +archive files. + + ***** IMPORTANT ***** +To extract StuffIt files that have been compressed with the Lempel-Ziv +compression method, unsit pipes the data through the "compress" +program with the appropriate switches, rather than incorporate the +uncompression routines within "unsit". Therefore, it is necessary to +have the "compress" program on the system and in the search path to +make "unsit" work. "Compress" is available from the comp.sources.unix +archives. + +The program syntax is much like unpit and macput/macget, with some added +options: + + unsit [-rdulM] [-vqfm] stuffit-file.data + +Only one of the flags r, d, u, l, or M should be specified. The +default mode is to create the three macput/MacTerminal compatible +file. The -M flag will cause the output to be in MacBinary format (a +single file). This can be swapped (default = MacBinary, -M = macput) +by changing the definitions of DEFAULT_MODE and OTHER_MODE below. The +-r and -d flags will cause only the resource and data forks to be +written. The -u flag will cause only the data fork to be written and +to have carriage return characters changed to Unix newline characters. +The -l flag will make the program only list the files in the StuffIt +file. + +The -v flag causes the program to list the names, sizes, type, and +creators of the files it is writing. The -q flag causes it to list +the name, type and size of each file and wait for a 'y' or 'n' for +either writing that file or skipping it, respectively. The -m flag is +used when the input file in in the MacBinary format instead of just +the data fork. It causes the program to skip the 128 byte MacBinary +header before looking for the StuffIt header. It is not necessary to +specify the -m flag since the program now checks for MacBinary format +input files and handles them correctly + +Version 1.5 of the unsit supports extracting files and folders as +implemented by StuffIt 1.5's "Hierarchy Maintained Folder" feature. +Each folder is extracted as a subdirectory on the Unix system with the +files in the folder placed in the corresponding subdirectory. The -f +option can be used to "flatten" out the hierarchy and unsit will store +all the files in the current directory. If the query option (-q) is +used and a "n" response is given to a folder name, none of the files +or folders in that folder will be extraced. + +Some of the program is borrowed from the macput.c/macget.c programs. +Many, many thanks to Raymond Lau, the author of StuffIt, for including +information on the format of the StuffIt archives in the +documentation. Several changes and enhancements supplied by David +Shanks (cde@atelabs.UUCP) have been incorporated into the program for +doing things like supporting System V and recognizing MacBinary files. +Christopher Bingham supplied some Macbinary +patches. Code was also borrowed from the macbin program by Jim Budler +for convert macput format files to MacBinary. I'm always glad to +receive advice, suggestions, or comments about the program so feel free +to send whatever you think would be helpful + + + Author: Allan G. Weber + weber@sipi.usc.edu + ...!usc!sipi!weber + Date: July 23, 1990 + +*/ + +#ifdef sun +#ifdef __svr4__ +#define SOLARIS +#endif __svr4__ +#endif sun + +#include +#include +#include + +#ifdef SOLARIS +#include +#include +#endif SOLARIS + +typedef long OSType; + +#include "stuffit.h" + +/* + * Define the following if your Unix can only handle 14 character file names + * (e.g. Version 7 and System V). + */ +/* #define SHORTNAMES */ + +/* + * The following defines the name of the compress program that is used for the + * uncompression of Lempel-Ziv compressed files. If the path is set up to + * include the right directory, this should work. + */ +#define COMPRESS "compress" + +#define IOBUFSIZ 4096 + +#define MACBINHDRSIZE 128L + +#define INIT_CRC 0L +extern unsigned short updcrc(); + +#define INFOBYTES 128 + +#define BYTEMASK 0xff + +#define S_SIGNATURE 0 +#define S_NUMFILES 4 +#define S_ARCLENGTH 6 +#define S_SIGNATURE2 10 +#define S_VERSION 14 +#define SITHDRSIZE 22 + +#define F_COMPRMETHOD 0 +#define F_COMPDMETHOD 1 +#define F_FNAME 2 +#define F_FTYPE 66 +#define F_CREATOR 70 +#define F_FNDRFLAGS 74 +#define F_CREATIONDATE 76 +#define F_MODDATE 80 +#define F_RSRCLENGTH 84 +#define F_DATALENGTH 88 +#define F_COMPRLENGTH 92 +#define F_COMPDLENGTH 96 +#define F_RSRCCRC 100 +#define F_DATACRC 102 +#define F_HDRCRC 110 +#define FILEHDRSIZE 112 + +#define F_NAMELEN 63 +#ifdef SHORTNAMES /* short file names */ +# define I_NAMELEN 15 /* 14 char file names + '\0' terminator */ +#else +# define I_NAMELEN 69 /* 63 + strlen(".info") + 1 */ +#endif + +/* The following are copied out of macput.c/macget.c */ +#define I_NAMEOFF 1 +/* 65 <-> 80 is the FInfo structure */ +#define I_TYPEOFF 65 +#define I_AUTHOFF 69 +#define I_FLAGOFF 73 +#define I_LOCKOFF 81 +#define I_DLENOFF 83 +#define I_RLENOFF 87 +#define I_CTIMOFF 91 +#define I_MTIMOFF 95 + +#define INITED_BUG +#define INITED_OFF I_FLAGOFF /* offset to byte with Inited flag */ +#define INITED_MASK (~1) /* mask to '&' with byte to reset it */ + +#define TEXT 0 +#define DATA 1 +#define RSRC 2 +#define MACPUT 3 +#define DUMP 4 +#define MACBINARY 5 + +/* Swap the following definitions if you want the output to default to + MacBinary, and the -M switch to create macput file (.data, .rsrc, .info) */ +#define DEFAULT_MODE MACPUT +#define OTHER_MODE MACBINARY + +/* #define ADDBIN */ /* add .bin to macbinary file names */ + +#define NODECODE 0 +#define DECODE 1 + +#define H_ERROR -1 +#define H_EOF 0 +#define H_WRITE 1 +#define H_SKIP 2 + +/* if called by this name program works on aufs files */ +#define AUFSNAME "unstuffit" + +struct node { + int flag, byte; + struct node *one, *zero; +} nodelist[512], *nodeptr, *read_tree(); /* 512 should be big enough */ + +struct sitHdr sithdr; + +char f_info[I_NAMELEN]; +char f_data[I_NAMELEN]; +char f_rsrc[I_NAMELEN]; + +char info[INFOBYTES]; +char mname[F_NAMELEN+1]; +char uname[F_NAMELEN+1]; +char dname[F_NAMELEN+1]; /* directory name for aufs */ +char iobuf[IOBUFSIZ]; +char zbuf[128]; /* buffer of zeros to pad MacBinary forks */ + +int mode, txtmode, listonly, verbose, query, flatten; +int bit, chkcrc, numfiles, depth; +int aufs; /* aufs flag */ +int debug = 0; +FILE *infp; + +long get4(); +short get2(); +unsigned short write_file(); + +/********************************************************************************/ +/* added for aufs, nicked from various places... */ + +/* following from mcvert program */ + +/* Useful, though not particularly Mac related, values */ +typedef unsigned char byte; /* one byte, obviously */ +typedef unsigned short word; /* must be 2 bytes */ + +#define NAMELEN 63 /* maximum legal Mac file name length */ + +/* Format of a bin file: +A bin file is composed of 128 byte blocks. The first block is the +info_header (see below). Then comes the data fork, null padded to fill the +last block. Then comes the resource fork, padded to fill the last block. A +proposal to follow with the text of the Get Info box has not been implemented, +to the best of my knowledge. Version, zero1 and zero2 are what the receiving +program looks at to determine if a MacBinary transfer is being initiated. +*/ +typedef struct { /* info file header (128 bytes). Unfortunately, these + longs don't align to word boundaries */ + byte version; /* there is only a version 0 at this time */ + byte nlen; /* Length of filename. */ + byte name[NAMELEN]; /* Filename (only 1st nlen are significant)*/ + byte type[4]; /* File type. */ + byte auth[4]; /* File creator. */ + byte flags; /* file flags: LkIvBnSyBzByChIt */ + byte zero1; /* Locked, Invisible,Bundle, System */ + /* Bozo, Busy, Changed, Init */ + byte icon_vert[2]; /* Vertical icon position within window */ + byte icon_horiz[2]; /* Horizontal icon postion in window */ + byte window_id[2]; /* Window or folder ID. */ + byte protect; /* = 1 for protected file, 0 otherwise */ + byte zero2; + byte dflen[4]; /* Data Fork length (bytes) - most sig. */ + byte rflen[4]; /* Resource Fork length byte first */ + byte cdate[4]; /* File's creation date. */ + byte mdate[4]; /* File's "last modified" date. */ + byte ilen[2]; /* GetInfo message length */ + byte flags2; /* Finder flags, bits 0-7 */ + byte unused[14]; + byte packlen[4]; /* length of total files when unpacked */ + byte headlen[2]; /* length of secondary header */ + byte uploadvers; /* Version of MacBinary II that the uploading program is written for */ + byte readvers; /* Minimum MacBinary II version needed to read this file */ + byte crc[2]; /* CRC of the previous 124 bytes */ + byte padding[2]; /* two trailing unused bytes */ + } info_header; + +/* end of mcvert stuff */ +/* from CAP aufs documentation */ + +#define FINFOLEN 32 +#define MAXCLEN 199 +typedef struct +{ + /* be careful with alignment */ + byte fndr_type[4]; + byte fndr_creator[4]; + word fndr_flags; + byte fndr_loc[4]; + word fndr_fldr; + word fndr_icon; + byte fndr_unused[8]; + word fndr_comment; + byte fndr_putaway[4]; + word fi_attr; /* attributes */ +#define FI_MAGIC1 255 + byte fi_magic1; /* was: length of comment */ +#define FI_VERSION 0x10 /* version major 1, minor 0 */ + /* if more than 8 versions then */ + /* something wrong anyway */ + byte fi_version; /* version number */ +#define FI_MAGIC 0xda + byte fi_magic; /* magic word check */ + byte fi_bitmap; /* bitmap of included info */ +#define FI_BM_SHORTFILENAME 0x1 /* is this included? */ +#define FI_BM_MACINTOSHFILENAME 0x2 /* is this included? */ + byte fi_shortfilename[12+1]; /* possible short file name */ + byte fi_macfilename[32+1]; /* possible macintosh file name */ + byte fi_comln; /* comment length */ + byte fi_comnt[MAXCLEN+1]; /* comment string */ +} FileInfo; + +FileInfo fndr_info; + +/* end aufs */ +/********************************************************************************/ + +main(argc, argv) +int argc; +char *argv[]; +{ + int status; + int c; + extern int optind; + extern char *optarg; + int errflg; + int macbin; + char *s, *progname; + + mode = DEFAULT_MODE; + errflg = 0; + macbin = 0; + flatten = 0; + numfiles = 0; + depth = 0; + + progname = argv[0]; + if ((s = (char *) rindex(progname, '/')) != NULL) + progname = ++s; + aufs = strcmp(progname, AUFSNAME) == 0; + + while ((c = getopt(argc, argv, "DMdflmqruvx")) != EOF) + switch (c) { + case 'r': /* extract resource fork only */ + mode = RSRC; + break; + case 'd': /* extract data fork only */ + mode = DATA; + break; + case 'u': /* extract data fork as Unix text file */ + mode = TEXT; + break; + case 'l': /* list contents of archive */ + listonly++; + break; + case 'q': /* query user on each extraction */ + query++; + break; + case 'v': /* verbose mode */ + verbose++; + break; + case 'x': /* don't decode data, just dump to files*/ + mode = DUMP; + break; + case 'm': /* input file is in Macbinary format */ + macbin = 1; + break; + case 'M': /* output file in OTHER_MODE */ + mode = OTHER_MODE; + break; + case 'f': /* don't create flat directory tree */ + flatten = 1; + break; + case 'D': /* debugging mode */ + debug = 1; + break; + case '?': + errflg++; + break; + } + if (errflg) { + usage(); + exit(1); + } + + /* -a incompatible with -M and -u */ + if(aufs && (mode == MACBINARY || mode == TEXT)) + { fprintf(stderr, "Aufs mode cannot be combined with MacBinary (-M) or text (-t) mode\n"); + exit(1); + } + + if (optind == argc) { + usage(); + exit(1); + } + else + while(optind < argc) + { if ((infp = fopen(argv[optind], "r")) == NULL) + { fprintf(stderr,"Can't open input file \"%s\"\n",argv[optind]); + exit(1); + } + + if (macbin) { + if (fseek(infp, MACBINHDRSIZE, 0) == -1) { + fprintf(stderr, "Can't skip over MacBinary header\n"); + exit(1); + } + } + + if (readsithdr(&sithdr) == 0) { + fprintf(stderr, "Can't read file header\n"); + exit(1); + } + if (debug) { + printf("archive header (%d bytes):\n", SITHDRSIZE); + printf("numFiles=%d, arcLength=%ld, version=%d\n", + sithdr.numFiles, sithdr.arcLength, sithdr.version & 0xff); + } + status = extract("", 0); + if(status < 0) exit(1); + + optind++; + } + + exit(0); +} + +usage() +{ + fprintf(stderr, "Usage: unsit/unstuffit [-rdulM] [-vqfm] filename\n"); +} + +/* + extract(parent, skip) - Extract all files from the current folder. + char *parent; name of parent folder + int skip; 1 to skip all files and folders in this one + 0 to extract them + + returns 1 if came an endFolder record + 0 if EOF + -1 if error (bad fileHdr, bad file, etc.) +*/ + +extract(parent, skip) +char *parent; +int skip; +{ + struct fileHdr filehdr; + struct stat sbuf; + int status, rstat, sstat, skipit; + char name[256]; + + while (1) { + rstat = readfilehdr(&filehdr, skip); + if (rstat == H_ERROR || rstat == H_EOF) { + status = rstat; + break; + } + if (debug) { + printf("file header (%d bytes):\n", FILEHDRSIZE); + printf("compRMethod=%d, compDMethod=%d\n", + filehdr.compRMethod, filehdr.compDMethod); + printf("rsrcLength=%ld, dataLength=%ld\n", + filehdr.rsrcLength, filehdr.dataLength); + printf("compRLength=%ld, compDLength=%ld\n", + filehdr.compRLength, filehdr.compDLength); + printf("rsrcCRC=%d=0x%04x, dataCRC=%d=0x%04x\n", + filehdr.rsrcCRC, filehdr.rsrcCRC, + filehdr.dataCRC, filehdr.dataCRC); + } + + skipit = (rstat == H_SKIP) ? 1 : 0; + + if (filehdr.compRMethod == endFolder && + filehdr.compDMethod == endFolder) { + status = 1; /* finished with this folder */ + break; + } + else if (filehdr.compRMethod == startFolder && + filehdr.compDMethod == startFolder) { + if (!listonly && rstat == H_WRITE && !flatten) { + sstat = stat(uname, &sbuf); + if (sstat == -1) { /* directory doesn't exist */ + if (mkdir(uname, 0777) == -1) { + fprintf(stderr, + "Can't create subdirectory %s\n", uname); + return(-1); + } + } + else { /* something exists with this name */ + if ((sbuf.st_mode & S_IFMT) != S_IFDIR) { + fprintf(stderr, "Directory name %s already in use\n", + uname); + return(-1); + } + } + if(aufs) /* create .finderinfo & .resource subdirectories */ + { strcpy(dname, uname); + strcat(dname, "/.finderinfo"); + sstat = stat(dname, &sbuf); + if (sstat == -1) { /* directory doesn't exist */ + if (mkdir(dname, 0777) == -1) { + fprintf(stderr, + "Can't create subdirectory %s\n", dname); + return(-1); + } + } + else { /* something exists with this name */ + if ((sbuf.st_mode & S_IFMT) != S_IFDIR) { + fprintf(stderr, "Directory name %s already in use\n", + dname); + return(-1); + } + } + strcpy(dname, uname); + strcat(dname, "/.resource"); + sstat = stat(dname, &sbuf); + if (sstat == -1) { /* directory doesn't exist */ + if (mkdir(dname, 0777) == -1) { + fprintf(stderr, + "Can't create subdirectory %s\n", dname); + return(-1); + } + } + else { /* something exists with this name */ + if ((sbuf.st_mode & S_IFMT) != S_IFDIR) { + fprintf(stderr, "Directory name %s already in use\n", + dname); + return(-1); + } + } + } + if (chdir(uname) == -1) { + fprintf(stderr, "Can't chdir to %s\n", uname); + return(-1); + } + sprintf(name,"%s:%s", parent, uname); + } + depth++; + status = extract(name, skipit); + depth--; + if (status != 1) + break; /* problem with folder */ + if (depth == 0) /* count how many top-level files done */ + numfiles++; + if (!flatten) + chdir(".."); + } + else { + if ((status = extractfile(&filehdr, skipit)) != 1) + break; + if (depth == 0) /* count how many top-level files done */ + numfiles++; + } + if (numfiles == sithdr.numFiles) + break; + } + return(status); +} + +extractfile(fh, skip) +struct fileHdr *fh; +int skip; +{ + unsigned short crc; + FILE *fp, *fp1; + int n; + + f_data[0] = f_rsrc[0] = f_info[0] = '\0'; /* assume no output files */ + /* figure out what file names to use and what to do */ + if (!listonly && !skip) { + switch (mode) { + case MACPUT: /* do both rsrc and data forks */ + if(aufs) + { sprintf(f_data, "%.*s", I_NAMELEN - 1, uname); + sprintf(f_rsrc, ".resource/%.*s", I_NAMELEN - 1, uname); + sprintf(f_info, ".finderinfo/%.*s", I_NAMELEN - 1, uname); + } + else + { sprintf(f_data, "%.*s.data", I_NAMELEN - 6, uname); + sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname); + sprintf(f_info, "%.*s.info", I_NAMELEN - 6, uname); + } + break; + case RSRC: /* rsrc fork only */ + if(aufs) + { sprintf(f_rsrc, ".resource/%.*s", I_NAMELEN - 1, uname); + sprintf(f_info, ".finderinfo/%.*s", I_NAMELEN - 1, uname); + } + else + sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname); + break; + case DATA: /* data fork only */ + if(aufs) + { sprintf(f_data, "%.*s", I_NAMELEN - 1, uname); + sprintf(f_info, ".finderinfo/%.*s", I_NAMELEN - 1, uname); + } + else + sprintf(f_data, "%.*s.data", I_NAMELEN - 6, uname); + break; + case TEXT: + sprintf(f_data, "%.*s.text", I_NAMELEN - 6, uname); + break; + case DUMP: /* for debugging, dump data as is */ + sprintf(f_data, "%.*s.ddump", I_NAMELEN - 7, uname); + sprintf(f_rsrc, "%.*s.rdump", I_NAMELEN - 7, uname); + fh->compRMethod = fh->compDMethod = noComp; + break; + case MACBINARY: /* output file in MacBinary format */ + sprintf(f_data, "%.*s.data", I_NAMELEN - 6, uname); + sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname); +#ifndef ADDBIN + sprintf(f_info, "%.*s", I_NAMELEN - 1, uname); +#else + sprintf(f_info, "%.*s.bin", I_NAMELEN - 5, uname); +#endif /*ADDBIN*/ + break; + } + } + + fp = NULL; /* so we can tell if a file is open */ + if (f_info[0] != '\0' && check_access(f_info) != -1) { + fp = fopen(f_info, "w"); + if (fp == NULL) { + perror(f_info); + exit(1); + } + if(aufs) /* convert info structure */ + { register info_header *pinfo; + + pinfo = (info_header *)info; + + /* make the .finderinfo file */ + bzero(&fndr_info, sizeof(FileInfo)); + bcopy(pinfo->type, fndr_info.fndr_type, 4); + bcopy(pinfo->auth, fndr_info.fndr_creator, 4); + bcopy(&pinfo->flags, &fndr_info.fndr_flags, 2); + fndr_info.fi_magic1 = FI_MAGIC1; + fndr_info.fi_version = FI_VERSION; + fndr_info.fi_magic = FI_MAGIC; + fndr_info.fi_bitmap = FI_BM_MACINTOSHFILENAME; + bcopy(pinfo->name, fndr_info.fi_macfilename, pinfo->nlen); + + fwrite(&fndr_info, sizeof(FileInfo), 1, fp); + } + else + { if (mode == MACBINARY) { /* convert to MacBinary header */ + /* taken from the macbin program */ + if (info[74] & 0x40) info[81] = '\1'; /* protected */ + info[74] = '\0'; /* clear zero2 */ + info[82] = '\0'; /* force zero3 clear */ + } + fwrite(info, 1, INFOBYTES, fp); + } + } + + if (f_rsrc[0] != '\0') { + txtmode = 0; + crc = write_file(f_rsrc, fh->compRLength, + fh->rsrcLength, fh->compRMethod); + if (chkcrc && fh->rsrcCRC != crc) { + fprintf(stderr, + "CRC error on resource fork: need 0x%04x, got 0x%04x\n", + fh->rsrcCRC, crc); + return(-1); + } + } + else { + fseek(infp, (long) fh->compRLength, 1); + } + if (f_data[0] != '\0') { + txtmode = (mode == TEXT); + crc = write_file(f_data, fh->compDLength, + fh->dataLength, fh->compDMethod); + if (chkcrc && fh->dataCRC != crc) { + fprintf(stderr, + "CRC error on data fork: need 0x%04x, got 0x%04x\n", + fh->dataCRC, crc); + return(-1); + } + } + else { + fseek(infp, (long) fh->compDLength, 1); + } + if (fp != NULL) { + /* if Macbinary output, copy the data and resource forks to the + end of the info file, and pad each to multiples of 128 bytes. */ + if (mode == MACBINARY) { + fp1 = fopen(f_data, "r"); /* re-open the file we just wrote */ + if (fp1 == NULL) { + perror(f_data); + exit(1); + } + while ((n = fread(iobuf, 1, IOBUFSIZ, fp1)) > 0) + fwrite(iobuf, 1, n, fp); /* append it to the info file */ + /* pad out to multiple of 128 if in MacBinary format */ + n = fh->dataLength % 128; + if (n > 0) + outc(zbuf, 128 - n, fp); + fclose(fp1); + unlink(f_data); + fp1 = fopen(f_rsrc, "r"); /* re-open the file we just wrote */ + if (fp1 == NULL) { + perror(f_rsrc); + exit(1); + } + while ((n = fread(iobuf, 1, IOBUFSIZ, fp1)) > 0) + fwrite(iobuf, 1, n, fp); /* append it to the info file */ + /* pad out to multiple of 128 if in MacBinary format */ + n = fh->rsrcLength % 128; + if (n > 0) + outc(zbuf, 128 - n, fp); + fclose(fp1); + unlink(f_rsrc); + } + fclose(fp); + } + return(1); +} + +readsithdr(s) +struct sitHdr *s; +{ + char temp[FILEHDRSIZE]; + int count = 0; + + for (;;) { + if (fread(temp, 1, SITHDRSIZE, infp) != SITHDRSIZE) { + fprintf(stderr, "Can't read file header\n"); + return(0); + } + + if (strncmp(temp + S_SIGNATURE, "SIT!", 4) == 0 && + strncmp(temp + S_SIGNATURE2, "rLau", 4) == 0) { + s->numFiles = get2(temp + S_NUMFILES); + s->arcLength = get4(temp + S_ARCLENGTH); + return(1); + } + + if (++count == 2) { + fprintf(stderr, "Not a StuffIt file\n"); + return(0); + } + + if (fread(&temp[SITHDRSIZE], 1, FILEHDRSIZE - SITHDRSIZE, infp) != + FILEHDRSIZE - SITHDRSIZE) { + fprintf(stderr, "Can't read file header\n"); + return(0); + } + + if (strncmp(temp + I_TYPEOFF, "SIT!", 4) == 0 && + strncmp(temp + I_AUTHOFF, "SIT!", 4) == 0) { /* MacBinary format */ + fseek(infp, (long)(INFOBYTES-FILEHDRSIZE), 1); /* Skip over header */ + } + } +} + +/* + readfilehdr - reads the file header for each file and the folder start + and end records. + + returns: H_ERROR = error + H_EOF = EOF + H_WRITE = write file/folder + H_SKIP = skip file/folder +*/ + +readfilehdr(f, skip) +struct fileHdr *f; +int skip; +{ + unsigned short crc; + int i, n, write_it, isfolder; + char hdr[FILEHDRSIZE]; + char ch, *mp, *up; + char *tp, temp[10]; + + for (i = 0; i < INFOBYTES; i++) + info[i] = '\0'; + + /* read in the next file header, which could be folder start/end record */ + n = fread(hdr, 1, FILEHDRSIZE, infp); + if (n == 0) /* return 0 on EOF */ + return(H_EOF); + else if (n != FILEHDRSIZE) { + fprintf(stderr, "Can't read file header\n"); + return(H_ERROR); + } + + /* check the CRC for the file header */ + crc = INIT_CRC; + crc = updcrc(crc, hdr, FILEHDRSIZE - 2); + f->hdrCRC = get2(hdr + F_HDRCRC); + if (f->hdrCRC != crc) { + fprintf(stderr, "Header CRC mismatch: got 0x%04x, need 0x%04x\n", + f->hdrCRC, crc); + return(H_ERROR); + } + + /* grab the name of the file or folder */ + n = hdr[F_FNAME] & BYTEMASK; + if (n > F_NAMELEN) + n = F_NAMELEN; + info[I_NAMEOFF] = n; + copy(info + I_NAMEOFF + 1, hdr + F_FNAME + 1, n); + strncpy(mname, hdr + F_FNAME + 1, n); + mname[n] = '\0'; + /* copy to a string with no illegal Unix characters in the file name */ + mp = mname; + up = uname; + while ((ch = *mp++) != '\0') { + if (ch <= ' ' || ch > '~' || index("/!()[]*<>?\\\"$\';&`", ch) != NULL) + ch = '_'; + *up++ = ch; + } + *up = '\0'; + + /* get lots of other stuff from the header */ + f->compRMethod = hdr[F_COMPRMETHOD]; + f->compDMethod = hdr[F_COMPDMETHOD]; + f->rsrcLength = get4(hdr + F_RSRCLENGTH); + f->dataLength = get4(hdr + F_DATALENGTH); + f->compRLength = get4(hdr + F_COMPRLENGTH); + f->compDLength = get4(hdr + F_COMPDLENGTH); + f->rsrcCRC = get2(hdr + F_RSRCCRC); + f->dataCRC = get2(hdr + F_DATACRC); + + /* if it's an end folder record, don't need to do any more */ + if (f->compRMethod == endFolder && f->compDMethod == endFolder) + return(H_WRITE); + + /* prepare an info file in case its needed */ + + copy(info + I_TYPEOFF, hdr + F_FTYPE, 4); + copy(info + I_AUTHOFF, hdr + F_CREATOR, 4); + copy(info + I_FLAGOFF, hdr + F_FNDRFLAGS, 2); +#ifdef INITED_BUG + info[INITED_OFF] &= INITED_MASK; /* reset init bit */ +#endif + copy(info + I_DLENOFF, hdr + F_DATALENGTH, 4); + copy(info + I_RLENOFF, hdr + F_RSRCLENGTH, 4); + copy(info + I_CTIMOFF, hdr + F_CREATIONDATE, 4); + copy(info + I_MTIMOFF, hdr + F_MODDATE, 4); + + isfolder = f->compRMethod == startFolder && f->compDMethod == startFolder; + + /* list the file name if verbose or listonly mode, also if query mode */ + if (skip) /* skip = 1 if skipping all in this folder */ + write_it = 0; + else { + write_it = 1; + if (listonly || verbose || query) { + for (i = 0; i < depth; i++) + putchar(' '); + if (isfolder) + printf("Folder: \"%s\"", uname); + else + printf("name=\"%s\", type=%4.4s, author=%4.4s, data=%ld, rsrc=%ld", + uname, hdr + F_FTYPE, hdr + F_CREATOR, + f->dataLength, f->rsrcLength); + if (query) { /* if querying, check with the boss */ + printf(" ? "); + fgets(temp, sizeof(temp) - 1, stdin); + tp = temp; + write_it = 0; + while (*tp != '\0') { + if (*tp == 'y' || *tp == 'Y') { + write_it = 1; + break; + } + else + tp++; + } + } + else /* otherwise, terminate the line */ + putchar('\n'); + } + } + return(write_it ? H_WRITE : H_SKIP); +} + +check_access(fname) /* return 0 if OK to write on file fname, -1 otherwise */ +char *fname; +{ + char temp[10], *tp; + + if (access(fname, 0) == -1) { + return(0); + } + else { + printf("%s exists. Overwrite? ", fname); + fgets(temp, sizeof(temp) - 1, stdin); + tp = temp; + while (*tp != '\0') { + if (*tp == 'y' || *tp == 'Y') { + return(0); + } + else + tp++; + } + } + return(-1); +} + +unsigned short write_file(fname, ibytes, obytes, type) +char *fname; +unsigned long ibytes, obytes; +unsigned char type; +{ + unsigned short crc; + int i, n, ch, lastch; + FILE *outf; + char temp[256]; + + crc = INIT_CRC; + chkcrc = 1; /* usually can check the CRC */ + + if (check_access(fname) == -1) { + fseek(infp, ibytes, 1); + chkcrc = 0; /* inhibit crc check if file not written */ + return(-1); + } + + switch (type) { + case noComp: /* no compression */ + outf = fopen(fname, "w"); + if (outf == NULL) { + perror(fname); + exit(1); + } + while (ibytes > 0) { + n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes; + n = fread(iobuf, 1, n, infp); + if (n == 0) + break; + crc = updcrc(crc, iobuf, n); + outc(iobuf, n, outf); + ibytes -= n; + } + fclose(outf); + break; + case rleComp: /* run length encoding */ + outf = fopen(fname, "w"); + if (outf == NULL) { + perror(fname); + exit(1); + } + while (ibytes > 0) { + ch = getc(infp) & 0xff; + ibytes--; + if (ch == 0x90) { /* see if its the repeat marker */ + n = getc(infp) & 0xff; /* get the repeat count */ + ibytes--; + if (n == 0) { /* 0x90 was really an 0x90 */ + iobuf[0] = 0x90; + crc = updcrc(crc, iobuf, 1); + outc(iobuf, 1, outf); + } + else { + n--; + for (i = 0; i < n; i++) + iobuf[i] = lastch; + crc = updcrc(crc, iobuf, n); + outc(iobuf, n, outf); + } + } + else { + iobuf[0] = ch; + crc = updcrc(crc, iobuf, 1); + lastch = ch; + outc(iobuf, 1, outf); + } + } + fclose(outf); + break; + case lzwComp: /* LZW compression */ + sprintf(temp, "%s%s", COMPRESS, " -d -c -n -b 14 "); + if (txtmode) { + strcat(temp, "| tr \'\\015\' \'\\012\' "); + chkcrc = 0; /* can't check CRC in this case */ + } + strcat(temp, "> '"); + strcat(temp, fname); + strcat(temp, "'"); + outf = popen(temp, "w"); + if (outf == NULL) { + perror(fname); + exit(1); + } + while (ibytes > 0) { + n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes; + n = fread(iobuf, 1, n, infp); + if (n == 0) + break; + fwrite(iobuf, 1, n, outf); + ibytes -= n; + } + pclose(outf); + if (chkcrc) { + outf = fopen(fname, "r"); /* read the file to get CRC value */ + if (outf == NULL) { + perror(fname); + exit(1); + } + while (1) { + n = fread(iobuf, 1, IOBUFSIZ, outf); + if (n == 0) + break; + crc = updcrc(crc, iobuf, n); + } + fclose(outf); + } + break; + case hufComp: /* Huffman compression */ + outf = fopen(fname, "w"); + if (outf == NULL) { + perror(fname); + exit(1); + } + nodeptr = nodelist; + bit = 0; /* put us on a byte boundary */ + read_tree(); + while (obytes > 0) { + n = (obytes > IOBUFSIZ) ? IOBUFSIZ : obytes; + for (i = 0; i < n; i++) + iobuf[i] = gethuffbyte(DECODE); + crc = updcrc(crc, iobuf, n); + outc(iobuf, n, outf); + obytes -= n; + } + fclose(outf); + break; + default: + fprintf(stderr, "Unknown compression method\n"); + chkcrc = 0; /* inhibit crc check if file not written */ + return(-1); + } + + return(crc & 0xffff); +} + +outc(p, n, fp) +char *p; +int n; +FILE *fp; +{ + register char *p1; + register int i; + if (txtmode) { + for (i = 0, p1 = p; i < n; i++, p1++) + if ((*p1 & BYTEMASK) == '\r') + *p1 = '\n'; + } + fwrite(p, 1, n, fp); +} + +long get4(bp) +char *bp; +{ + register int i; + long value = 0; + + for (i = 0; i < 4; i++) { + value <<= 8; + value |= (*bp & BYTEMASK); + bp++; + } + return(value); +} + +short get2(bp) +char *bp; +{ + register int i; + int value = 0; + + for (i = 0; i < 2; i++) { + value <<= 8; + value |= (*bp & BYTEMASK); + bp++; + } + return(value); +} + +copy(p1, p2, n) +char *p1, *p2; +int n; +{ + while (n-- > 0) + *p1++ = *p2++; +} + +/* This routine recursively reads the Huffman encoding table and builds + and decoding tree. */ + +struct node *read_tree() +{ + struct node *np; + np = nodeptr++; + if (getbit() == 1) { + np->flag = 1; + np->byte = gethuffbyte(NODECODE); + } + else { + np->flag = 0; + np->zero = read_tree(); + np->one = read_tree(); + } + return(np); +} + +/* This routine returns the next bit in the input stream (MSB first) */ + +getbit() +{ + static char b; + if (bit == 0) { + b = getc(infp) & 0xff; + bit = 8; + } + bit--; + return((b >> bit) & 1); +} + +/* This routine returns the next 8 bits. If decoding is on, it finds the +byte in the decoding tree based on the bits from the input stream. If +decoding is not on, it either gets it directly from the input stream or +puts it together from 8 calls to getbit(), depending on whether or not we +are currently on a byte boundary +*/ +gethuffbyte(decode) +int decode; +{ + register struct node *np; + register int i, b; + if (decode == DECODE) { + np = nodelist; + while (np->flag == 0) + np = (getbit()) ? np->one : np->zero; + b = np->byte; + } + else { + if (bit == 0) /* on byte boundary? */ + b = getc(infp) & 0xff; + else { /* no, put a byte together */ + b = 0; + for (i = 8; i > 0; i--) { + b = (b << 1) + getbit(); + } + } + } + return(b); +} diff --git a/contrib/AufsTools/unstuffit/updcrc.c b/contrib/AufsTools/unstuffit/updcrc.c new file mode 100644 index 0000000..46788ff --- /dev/null +++ b/contrib/AufsTools/unstuffit/updcrc.c @@ -0,0 +1,191 @@ +/* updcrc(3), crc(1) - calculate crc polynomials + * + * Calculate, intelligently, the CRC of a dataset incrementally given a + * buffer full at a time. + * + * Usage: + * newcrc = updcrc( oldcrc, bufadr, buflen ) + * unsigned int oldcrc, buflen; + * char *bufadr; + * + * Compiling with -DTEST creates a program to print the CRC of stdin to stdout. + * Compile with -DMAKETAB to print values for crctab to stdout. If you change + * the CRC polynomial parameters, be sure to do this and change + * crctab's initial value. + * + * Notes: + * Regards the data stream as an integer whose MSB is the MSB of the first + * byte recieved. This number is 'divided' (using xor instead of subtraction) + * by the crc-polynomial P. + * XMODEM does things a little differently, essentially treating the LSB of + * the first data byte as the MSB of the integer. Define SWAPPED to make + * things behave in this manner. + * + * Author: Mark G. Mendel, 7/86 + * UUCP: ihnp4!umn-cs!hyper!mark, GEnie: mgm + */ + +/* The CRC polynomial. + * These 4 values define the crc-polynomial. + * If you change them, you must change crctab[]'s initial value to what is + * printed by initcrctab() [see 'compile with -DMAKETAB' above]. + */ + /* Value used by: CITT XMODEM ARC */ +#define P 0xA001 /* the poly: 0x1021 0x1021 A001 */ +#define INIT_CRC 0L /* init value: -1 0 0 */ +#define SWAPPED /* bit order: undef defined defined */ +#define W 16 /* bits in CRC:16 16 16 */ + + /* data type that holds a W-bit unsigned integer */ +#if W <= 16 +# define WTYPE unsigned short +#else +# define WTYPE unsigned long +#endif + + /* the number of bits per char: don't change it. */ +#define B 8 + +static WTYPE crctab[1<>(W-B)) ^ *cp++]; +#else + crc = (crc>>B) ^ crctab[(crc & ((1< +main() +{ + initcrctab(); +} + +initcrctab() +{ + register int b, i; + WTYPE v; + + + for( b = 0; b <= (1<= 0; ) + v = v & ((WTYPE)1<<(W-1)) ? (v<<1)^P : v<<1; +#else + for( v = b, i = B; --i >= 0; ) + v = v & 1 ? (v>>1)^P : v>>1; +#endif + crctab[b] = v; + + printf( "0x%lx,", v & ((1L< +#include + +#define MAXBUF 4096 + + + +main( ac, av ) + int ac; char **av; +{ + int fd; + int nr; + int i; + char buf[MAXBUF]; + WTYPE crc, crc2; + + fd = 0; + if( ac > 1 ) + if( (fd = open( av[1], O_RDONLY )) < 0 ) { + perror( av[1] ); + exit( -1 ); + } + crc = crc2 = INIT_CRC; + + while( (nr = read( fd, buf, MAXBUF )) > 0 ) { + crc = updcrc( crc, buf, nr ); + } + + if( nr != 0 ) + perror( "reading" ); + else { + printf( "%lx\n", crc ); + } + +#ifdef MAGICCHECK + /* tack one's complement of crc onto data stream, and + continue crc calculation. Should get a constant (magic number) + dependent only on P, not the data. + */ + crc2 = crc ^ -1L; + for( nr = W-B; nr >= 0; nr -= B ) { + buf[0] = (crc2 >> nr); + crc = updcrc(crc, buf, 1); + } + + /* crc should now equal magic */ + buf[0] = buf[1] = buf[2] = buf[3] = 0; + printf( "magic test: %lx =?= %lx\n", crc, updcrc(-1, buf, W/B)); +#endif MAGICCHECK +} + +#endif diff --git a/contrib/DeskTop/Makefile b/contrib/DeskTop/Makefile new file mode 100644 index 0000000..4b24381 --- /dev/null +++ b/contrib/DeskTop/Makefile @@ -0,0 +1,24 @@ +all: builddt dumpdt + +CFLAGS=-I../.. + +builddt: builddt.o dtmisc.o + ${CC} -o builddt builddt.o dtmisc.o + +dumpdt: dumpdt.o dtmisc.o + ${CC} -o dumpdt dumpdt.o dtmisc.o + +builddt.o: builddt.c dt.h + ${CC} ${CFLAGS} -c builddt.c + +dumpdt.o: dumpdt.c dt.h + ${CC} ${CFLAGS} -c dumpdt.c + +dtmisc.o: dtmisc.c dt.h + ${CC} ${CFLAGS} -c dtmisc.c + +shar: + /usr/local/bin/shar README Makefile *.h *.c > capdt.1.0.shar + +clean: + rm -rf builddt dumpdt *.o a.out core diff --git a/contrib/DeskTop/README b/contrib/DeskTop/README new file mode 100644 index 0000000..c46aab1 --- /dev/null +++ b/contrib/DeskTop/README @@ -0,0 +1,50 @@ + dumpdt/builddt + CAP DeskTop Handling Tools + + The University of Melbourne + djh@munnari.OZ.AU + August, 1997 + version 1.0.3 + + +'dumpdt' dumps the content of the .IDeskTop and .ADeskTop files in the +specified CAP AUFS volume. usage: + + dumpdt volumename + +ie: + + dumpdt ~/mac + + +'builddt' traverses the specified volume and builds new .IDeskTop and +.ADeskTop files based on the current volume contents. The -w flag must +be used explicitly to cause new .?DeskTop files to be written. The -q +flag sets 'quiet' mode, otherwise a message is printed for each file +examined. The -d flag can be used to obtain a verbose debug listing. + +usage: + + builddt [ -d ] [ -w ] [ -q ] volumename + +ie: + + builddt -w ~/mac + +WARNING: The CAP .?DeskTop file contents are cached within running AUFS +processes. To make effective changes for global volumes (afpvols specified +with the -V option), the parent AUFS server should be killed, the DeskTop +rebuilt and the server restarted. For personal volumes, the volume should +be unmounted from all Macintoshes before the DeskTop is rebuilt. + +WARNING: this is beta software. Run without -w and check the output +for sensible data first. Always keep copies of existing .?DeskTop files. + + +Copyright (c) 1993-1997, The University of Melbourne. +All Rights Reserved. Permission to publicly redistribute this +package (other than as part of CAP) or use any part of this software +for any purpose other than as part of the original distribution must +be obtained in writing from the copyright owner. + +Please report problems to djh@munnari.OZ.AU diff --git a/contrib/DeskTop/builddt.c b/contrib/DeskTop/builddt.c new file mode 100644 index 0000000..397b71f --- /dev/null +++ b/contrib/DeskTop/builddt.c @@ -0,0 +1,632 @@ +/* + * (re)build CAP desktop files .IDeskTop and .ADeskTop + * + * Copyright (c) 1993, The University of Melbourne. + * All Rights Reserved. Permission to publicly redistribute this + * package (other than as a component of CAP) or to use any part + * of this software for any purpose, other than that intended by + * the original distribution, *must* be obtained in writing from + * the copyright owner. + * + * djh@munnari.OZ.AU + * 15 February 1993 + * 30 November 1993 + * + * Refer: "Inside Macintosh", Volume 1, page I-128 "Format of a Resource File" + * + * $Author: djh $ + * $Revision: 2.2 $ + * + */ + +#include "dt.h" + +/* variables */ + +int fdi, fda; +int debug = 0; +int quiet = 0; +int writing = 0; +int totfiles = 0; +int idesk = 0, adesk = 0; +long iconlen, bndllen, freflen; +short bndlnum, frefnum; +long bndloffset, frefoffset; +long listoffset; +u_char icon[1024]; + +struct adt adt; +struct idt idt; +struct finfo finfo; +struct rhdr rhdr; +struct rmap rmap; +struct tlist tlist; +struct rlist rlist; +struct bhdr bhdr; +struct bdata bdata; +struct bids bids; +struct fdata fdata; + +struct icn icn[8] = { + "", 0, 0, + "ICN#", 0, 0, + "icl4", 0, 0, + "icl8", 0, 0, + "ics#", 0, 0, + "ics4", 0, 0, + "ics8", 0, 0, + "", 0, 0 +}; + +main(argc, argv) +int argc; +char *argv[]; +{ + int c; + extern char *optarg; + extern int optind, opterr; + void enumerate(); + + opterr = 0; + + while ((c = getopt(argc, argv, "qdw")) != -1) { + switch (c) { + case 'd': + debug = 1; + break; + case 'w': + writing = 1; + break; + case 'q': + quiet = 1; + break; + case '?': + usage(); + break; + } + } + + if (optind >= argc) + usage(); + + if (chdir(argv[optind]) < 0) { + perror(argv[optind]); + exit(1); + } + + if (writing) { + if ((fdi = open(IFILE, O_RDWR | O_TRUNC | O_CREAT, 0644)) < 0) { + perror(IFILE); + exit(1); + } + if ((fda = open(AFILE, O_RDWR | O_TRUNC | O_CREAT, 0644)) < 0) { + perror(AFILE); + exit(1); + } + } + + enumerate("."); /* handle recursively */ + + if (writing) { + close(fdi); + close(fda); + } + + printf("\n"); + printf("wrote %d .ADeskTop entries\n", adesk); + printf("wrote %d .IDeskTop entries\n", idesk); + printf("from a total of %d files\n", totfiles); + if (adesk == 0 && idesk == 0) + printf("(nothing written to .?DeskTop, did you use the -w flag ?)\n"); +} + +/* + * print a usage message and exit + * + */ + +usage() +{ + fprintf(stderr, "usage: builddt [ -d ] [ -w ] [ -q ] volname\n"); + exit(1); +} + +/* + * check files in directory 'dir' + * + */ + +void +enumerate(dir) +char *dir; +{ + DIR *dirp; + int dirlen; + struct stat sbuf; + char path[MAXPATHLEN]; + char resource[MAXPATHLEN]; + char finderinfo[MAXPATHLEN]; +#ifdef SOLARIS + struct dirent *dp, *readdir(); +#else SOLARIS + struct direct *dp, *readdir(); +#endif SOLARIS + void makeEntry(); + + if ((dirp = opendir(dir)) == NULL) { + perror(dir); + return; + } + + strcpy(path, dir); + dirlen = strlen(path); + + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + if (skip(dp->d_name)) + continue; + strcpy(path+dirlen, "/"); + strcpy(path+dirlen+1, dp->d_name); + if (stat(path, &sbuf) < 0) + continue; + if (S_ISDIR(sbuf.st_mode)) { + enumerate(path); + continue; + } + /* must be a file then */ + sprintf(resource, "%s/%s/%s", dir, RSRCF, dp->d_name); + sprintf(finderinfo, "%s/%s/%s", dir, FNDRI, dp->d_name); + if (access(resource, F_OK) < 0) + continue; + if (access(finderinfo, F_OK) < 0) + continue; + strcpy(path, dir); + if (path[0] == '.' && path[1] == '\0') + strcpy(path, "./"); + else + path[dirlen] = '\0'; + makeEntry(path, dp->d_name, resource, finderinfo); + if (debug) + printf("\n"); + } + + closedir(dirp); + + return; +} + +/* + * skip subdirectory and file names we aren't interested in + * + */ + +int +skip(name) +char *name; +{ + if (strcmp(name, ".") == 0) + return(1); + if (strcmp(name, "..") == 0) + return(1); + if (strcmp(name, FNDRI) == 0) + return(1); + if (strcmp(name, RSRCF) == 0) + return(1); + if (strcmp(name, AFILE) == 0) + return(1); + if (strcmp(name, IFILE) == 0) + return(1); + if (strcmp(name, "afpvols") == 0) + return(1); + if (strcmp(name, ".afpvols") == 0) + return(1); + if (strcmp(name, "afpfile") == 0) + return(1); + if (strcmp(name, ".afpfile") == 0) + return(1); + + return(0); +} + +/* + * grope around in the resource file ... :-( + * + */ + +void +makeEntry(path, file, resource, finderinfo) +char *path, *file, *resource, *finderinfo; +{ + int h; + int i; + int j; + int k; + int fd; + char *p; + long len; + short ilen; + long offset; + short numres; + char *ic, *findIcon(); + struct fdata *ff, *findFREF(); + + totfiles++; + + if (!quiet) + printf("Checking file \"%s%s%s\"\n", path+2, + (*(path+2) == '\0') ? "" : "/", file); + + if ((fd = open(finderinfo, O_RDONLY, 0644)) < 0) { + perror(finderinfo); + return; + } + if (read(fd, &finfo, sizeof(finfo)) != sizeof(finfo)) { + if (debug) printf("\"%s\": too small, ignoring\n", finderinfo+2); + close(fd); + return; + } + close(fd); + + /* handle .ADeskTop file (Application Mapping) */ + + if (bcmp(finfo.fi.ftype, "APPL", 4) == 0) { + bcopy(finfo.fi.creat, adt.creat, 4); + bzero(adt.userb, 4); + adt.magic = htonl(MAGIC); + adt.dlen = htonl(strlen(path+2)+1); /* ignore leading "./" */ + adt.flen = htonl(strlen(file)+1); /* but include trailing null */ + + if (debug) { + printf("creat"); + printsig(adt.creat); + printf("userb"); + printsig(adt.userb); + printf("path :%s: ", path+2); + printf("file :%s:\n", file); + } + + if (writing) { + write(fda, &adt, sizeof(adt)); + write(fda, path+2, ntohl(adt.dlen)); + write(fda, file, ntohl(adt.flen)); + adesk++; + } + } + + /* handle .IDeskTop file (ICON/creator/type mapping) */ + + if (debug) { + printf("ftype"); + printsig(finfo.fi.ftype); + printf("icon ID %d ", (short)ntohs(finfo.fi.iconID)); + printf("path :%s: ", path+2); + printf("file :%s: ", file); + printf("\n"); + } + + if ((fd = open(resource, O_RDONLY, 0644)) < 0) { + perror(resource); + return; + } + + /* get resource header */ + if (read(fd, &rhdr, sizeof(rhdr)) != sizeof(rhdr)) { + if (debug) printf("\"%s\": too small, ignoring\n", resource+2); + close(fd); + return; + } + + /* get resource map */ + if (lseek(fd, ntohl(rhdr.rmapOffset), SEEK_SET) < 0) { + perror("SEEK_SET"); + close(fd); + return; + } + if (read(fd, &rmap, sizeof(rmap)) != sizeof(rmap)) { + if (debug) printf("\"%s\": too small, ignoring\n", resource+2); + close(fd); + return; + } + + /* file pointer now at start of resource map + sizeof(rmap) */ + + if (lseek(fd, ntohs(rmap.listOffset)-sizeof(rmap), SEEK_CUR) < 0) { + perror("SEEK_CUR"); + close(fd); + return; + } + + listoffset = ntohl(rhdr.rmapOffset) + ntohs(rmap.listOffset); + + /* get number of resource types */ + if (read(fd, &numres, 2) != 2) { + if (debug) printf("failed to read resource type list qty\n"); + close(fd); + return; + } + + numres = ntohs(numres) + 1; + + if (debug) + printf("found %d distinct resource type%s\n", + numres, (numres == 1) ? "" : "s"); + + /* (in)sanity check */ + if (numres > 1000) { + if (debug) printf("that's a lot of resources, probably bogus\n"); + close(fd); + return; + } + + bndlnum = frefnum = 0; + for (j = 0; j < 8; j++) + icn[j].iconnum = icn[j].iconoffset = 0; + + for (j = 0; j < numres; j++) { + /* read resource type list */ + if (read(fd, &tlist, sizeof(tlist)) != sizeof(tlist)) { + if (debug) printf("bad resource type list\n"); + close(fd); + return; + } + + if (debug) { + printf("type"); + printsig(tlist.rtype); + printf("num %d off %d\n", + ntohs(tlist.rnum)+1, ntohs(tlist.roff)); + } + + if (bcmp(tlist.rtype, "BNDL", 4) == 0) { + bndlnum = ntohs(tlist.rnum)+1; + bndloffset = ntohs(tlist.roff); + } + /* check for icon family */ + for (h = 1; h < 7; h++) { + if (bcmp(tlist.rtype, icn[h].ityp, 4) == 0) { + icn[h].iconnum = ntohs(tlist.rnum)+1; + icn[h].iconoffset = ntohs(tlist.roff); + } + } + if (bcmp(tlist.rtype, "FREF", 4) == 0) { + frefnum = ntohs(tlist.rnum)+1; + frefoffset = ntohs(tlist.roff); + } + } + + /* check for BNDL/ICN#/FREF resources */ + if (bndlnum == 0 || icn[1].iconnum == 0 || frefnum == 0) { + if (debug) printf("no BNDL/ICN#/FREF resources\n"); + close(fd); + return; + } + + /* handle each BNDL present */ + for (j = 0; j < bndlnum; j++) { + /* move to NBDL reference list */ + if (lseek(fd, listoffset+bndloffset, SEEK_SET) < 0) { + perror("SEEK_SET"); + close(fd); + return; + } + if (read(fd, &rlist, sizeof(rlist)) != sizeof(rlist)) { + if (debug) printf("bundle reference list too small\n"); + close(fd); + return; + } + + if (debug) { + printf("found BNDL rsrc ID %d ", (short)ntohs(rlist.rsrcID)); + printf("offset %d\n", (long)ntohl(rlist.attrOff) & 0xffffff); + } + + offset = ntohl(rhdr.rdataOffset)+(ntohl(rlist.attrOff) & 0xffffff); + + /* move to beginning of BNDL resource data */ + if (lseek(fd, offset, SEEK_SET) < 0) { + perror("SEEK_SET"); + close(fd); + return; + } + /* get BNDL resource length */ + if (read(fd, &bndllen, 4) != 4) { + if (debug) printf("failed to read BNDL resource length\n"); + close(fd); + return; + } + + bndllen = ntohl(bndllen); + + /* get BNDL resource header */ + if (read(fd, &bhdr, sizeof(bhdr)) != sizeof(bhdr)) { + if (debug) printf("bundle header too small\n"); + close(fd); + return; + } + + bndllen -= sizeof(bhdr); + + if (debug) { + printf("signature"); + printsig(bhdr.creat); + printf("version %d ", (short)ntohs(bhdr.version)); + printf("numtypes %d\n", (short)ntohs(bhdr.numType)+1); + } + + for (i = 0; i < ntohs(bhdr.numType)+1; i++) { + /* get BNDL resource data */ + if (read(fd, &bdata, sizeof(bdata)) != sizeof(bdata)) { + if (debug) printf("bundle data too short\n"); + close(fd); + return; + } + + if (debug) { + printf("type"); + printsig(bdata.btype); + printf("has %d ID(s)\n", (short)ntohs(bdata.numID)+1); + } + + for (k = 0; k < ntohs(bdata.numID)+1; k++) { + /* scan BNDL ID array */ + if (read(fd, &bids, sizeof(bids)) != sizeof(bids)) { + if (debug) printf("bundle data too short\n"); + close(fd); + return; + } + + if (debug) + printf("local ID %d, actual ID %d\n", + (short)ntohs(bids.localID), (short)ntohs(bids.actualID)); + + if ((ff = findFREF(fd, ntohs(bids.localID))) != NULL) { + if (bcmp(bdata.btype, "ICN#", 4) == 0) { + /* check icon family */ + for (h = 1; h < 7; h++) { + if ((ic=findIcon(fd,h,ntohs(bids.actualID),&ilen))!=NULL){ + idt.magic = htonl(MAGIC); + idt.isize = htonl(ilen); + bcopy(bhdr.creat, idt.creat, 4); + bcopy(ff->ftype, idt.ftype, 4); + idt.itype = h; + idt.pad1 = 0; + bzero(idt.userb, 4); + idt.pad2[0] = 0; + idt.pad2[1] = 0; + + if (debug) { + printf("creator"); + printsig(idt.creat); + printf("type"); + printsig(idt.ftype); + printf("found '%s' of length %d\n", icn[h].ityp, ilen); + printicn(ic, h, ilen); + } + + if (writing) { + write(fdi, &idt, sizeof(idt)); + write(fdi, ic, ilen); + idesk++; + } + } + } + } + } + } + } + } + + close(fd); + + return; +} + +/* + * find the icon with the specified resource number + * and type (ICN#, icl4, icl8, ics#, ics4, ics8), + * return a pointer to the icon data and it's length + * + */ + +char * +findIcon(fd, typ, rsrcID, len) +int fd; +int typ; +short rsrcID; +short *len; +{ + int i; + long offset; + long curpos; + struct rlist rlist; + + *len = 0; + + /* keep current file pointer position */ + if ((curpos = lseek(fd, 0, SEEK_CUR)) >= 0) { + /* move to start of icon resource reference list */ + if (lseek(fd, listoffset+icn[typ].iconoffset, SEEK_SET) >= 0) { + for (i = 0; i < icn[typ].iconnum; i++) { + /* get resource reference list for each icon */ + if (read(fd, &rlist, sizeof(rlist)) == sizeof(rlist)) { + if (debug) + printf("found '%s' rsrc ID %d offset %d\n", + icn[typ].ityp, (short)ntohs(rlist.rsrcID), + (long)ntohl(rlist.attrOff) & 0xffffff); + if ((short)ntohs(rlist.rsrcID) == rsrcID) + break; + } + } + if (i < icn[typ].iconnum) { + offset = ntohl(rhdr.rdataOffset)+(ntohl(rlist.attrOff)&0xffffff); + /* move to beginning of icon resource data */ + if (lseek(fd, offset, SEEK_SET) >= 0) { + /* get icon resource length */ + if (read(fd, &iconlen, 4) == 4) { + if ((iconlen = ntohl(iconlen)) <= sizeof(icon)) { + /* read icon data */ + if (read(fd, icon, iconlen) == iconlen) { + /* restore original pointer */ + lseek(fd, curpos, SEEK_SET); + *len = iconlen; + return((char *)icon); + } + } + } + } + } + } + } + lseek(fd, curpos, SEEK_SET); + return(NULL); +} + +/* + * find the FREF with the specified local ID + * + */ + +struct fdata * +findFREF(fd, localID) +int fd; +short localID; +{ + int i; + long offset; + long curpos; + struct rlist rlist; + + /* keep current file pointer position */ + if ((curpos = lseek(fd, 0, SEEK_CUR)) >= 0) { + for (i = 0; i < frefnum; i++) { + /* move to start of FREF resource reference list */ + if (lseek(fd, listoffset+frefoffset+i*sizeof(rlist),SEEK_SET)>=0){ + /* get resource reference list for each FREF */ + if (read(fd, &rlist, sizeof(rlist)) == sizeof(rlist)) { + if (debug) { + printf("found FREF rsrc ID %d ", (short)ntohs(rlist.rsrcID)); + printf("offset %d\n", (long)ntohl(rlist.attrOff) & 0xffffff); + } + offset=ntohl(rhdr.rdataOffset)+(ntohl(rlist.attrOff)&0xffffff); + /* move to beginning of FREF resource data */ + if (lseek(fd, offset, SEEK_SET) >= 0) { + /* get FREF resource length */ + if (read(fd, &freflen, 4) == 4) { + if ((freflen = ntohl(freflen)) >= sizeof(fdata)) { + /* read FREF data */ + if (read(fd, &fdata, sizeof(fdata)) == sizeof(fdata)) { + if (ntohs(fdata.localID) != localID) + continue; + /* restore original pointer */ + lseek(fd, curpos, SEEK_SET); + return(&fdata); + } + } + } + } + } + } + } + } + lseek(fd, curpos, SEEK_SET); + return(NULL); +} diff --git a/contrib/DeskTop/dt.h b/contrib/DeskTop/dt.h new file mode 100644 index 0000000..d1e08e6 --- /dev/null +++ b/contrib/DeskTop/dt.h @@ -0,0 +1,184 @@ +/* + * (re)build/dump CAP desktop files .IDeskTop and .ADeskTop + * + * Copyright (c) 1993-1997, The University of Melbourne. + * All Rights Reserved. Permission to publicly redistribute this + * package (other than as a component of CAP) or to use any part + * of this software for any purpose, other than that intended by + * the original distribution, *must* be obtained in writing from + * the copyright owner. + * + * djh@munnari.OZ.AU + * 15 February 1993 + * 30 November 1993 + * 10 August 1997 + * + * Refer: "Inside Macintosh", Volume 1, page I-128 "Format of a Resource File" + * + * $Author: djh $ + * $Revision: 2.2 $ + * + */ + +#ifdef sun +#ifdef __svr4__ +#define SOLARIS +#endif __svr4__ +#endif sun + +#include +#include +#include +#include +#include +#include +#include + +#ifdef SOLARIS +#include +#include +#else SOLARIS +#include +#endif SOLARIS + +#if defined(hpux) || defined(SOLARIS) +# include +#endif /* hpux || SOLARIS */ + +/* + * header for .ADeskTop + * + */ + +struct adt { + int magic; + u_char creat[4]; + u_char userb[4]; + int dlen; + int flen; + /* names follow */ +}; + +/* + * header for .IDeskTop + * + */ + +struct idt { + int magic; + int isize; + u_char creat[4]; + u_char ftype[4]; + u_char itype; + u_char pad1; + u_char userb[4]; + u_char pad2[2]; + /* bitmap follows */ +}; + +/* + * headers for .finderinfo files + * + */ + +struct fi { + u_char ftype[4]; + u_char creat[4]; + u_short flags; + u_short locn[2]; + u_short win; + u_short iconID; + u_short pad[4]; + u_short commID; + u_int dirID; +}; + +struct finfo { + struct fi fi; + u_short attr; +#define FMAGIC1 255 + u_char magic1; +#define FVERS 0x10 + u_char version; +#define FMAGIC2 0xda + u_char magic2; + u_char pad1; + /* ignore rest */ +}; + +/* + * headers for .resource files + * + */ + +/* resource hdr */ +struct rhdr { + int rdataOffset; + int rmapOffset; + int rdataLength; + int rmapLength; + u_char filler[240]; +}; + +/* resource map */ +struct rmap { + u_char filler[24]; + short listOffset; + short nameOffset; +}; + +/* resource type list */ +struct tlist { + u_char rtype[4]; + short rnum; + short roff; +}; + +/* resource reference list */ +struct rlist { + short rsrcID; + short nameOffset; + u_int attrOff; + u_int handle; +}; + +/* bundle header */ +struct bhdr { + u_char creat[4]; + u_short version; + short numType; +}; + +/* bundle data */ +struct bdata { + u_char btype[4]; + short numID; +}; + +/* bundle IDs */ +struct bids { + short localID; + short actualID; +}; + +/* FREF header */ +struct fdata { + u_char ftype[4]; + short localID; + /* ignore filename */ +}; + +/* icon family */ +struct icn { + char ityp[6]; + short iconnum; + int iconoffset; +}; + +/* defines */ + +#define MAGIC 0x00010002 +#define IFILE ".IDeskTop" +#define AFILE ".ADeskTop" +#define RSRCF ".resource" +#define FNDRI ".finderinfo" diff --git a/contrib/DeskTop/dtmisc.c b/contrib/DeskTop/dtmisc.c new file mode 100644 index 0000000..84664c5 --- /dev/null +++ b/contrib/DeskTop/dtmisc.c @@ -0,0 +1,82 @@ +/* + * Miscellaneous DeskTop routines + * + * Copyright (c) 1993, The University of Melbourne. + * All Rights Reserved. Permission to publicly redistribute this + * package (other than as a component of CAP) or to use any part + * of this software for any purpose, other than that intended by + * the original distribution, *must* be obtained in writing from + * the copyright owner. + * + * djh@munnari.OZ.AU + * 15 February 1993 + * + * Refer: "Inside Macintosh", Volume 1, page I-128 "Format of a Resource File" + * + * $Author: djh $ + * $Revision: 2.1 $ + * + */ + +#include "dt.h" + +/* + * print 4 byte signatures in hex and ascii representations + * + */ + +int +printsig(sig) +u_char *sig; +{ + int i; + + printf(" :"); + for (i = 0; i < 4; i++) + printf("%02x", sig[i]); + printf(":("); + for (i = 0; i < 4; i++) + printf("%c", isprint(sig[i]) ? sig[i] : '.'); + printf(") "); +} + +/* + * print out a representation of the ICN# and it's mask + * + */ + +int +printicn(icn, typ, len) +u_char *icn; +int typ; +short len; +{ + u_char *p; + int i, j, k; + u_long data1, data2, mask; + + if (typ != 1) + return; + + for (i = 0, p = icn; i < 32; i += 2, p += 8) { + for (j = 0; j < 2; j++) { + if (j == 1) printf(" "); + bcopy(p+(j*128), &data1, 4); + bcopy(p+4+(j*128), &data2, 4); + for (k = 0; k < 32; k++) { + mask = ((u_long)0x80000000 >> k); + if (data1 & mask && data2 & mask) + printf("8"); + else + if (data1 & mask) + printf("\""); + else + if (data2 & mask) + printf("o"); + else + printf(" "); + } + } + printf("\n"); + } +} diff --git a/contrib/DeskTop/dumpdt.c b/contrib/DeskTop/dumpdt.c new file mode 100644 index 0000000..6257a18 --- /dev/null +++ b/contrib/DeskTop/dumpdt.c @@ -0,0 +1,139 @@ +/* + * dump CAP desktop files .IDeskTop and .ADeskTop + * + * Copyright (c) 1993-1997, The University of Melbourne. + * All Rights Reserved. Permission to publicly redistribute this + * package (other than as a component of CAP) or to use any part + * of this software for any purpose, other than that intended by + * the original distribution, *must* be obtained in writing from + * the copyright owner. + * + * djh@munnari.OZ.AU + * 15 February 1993 + * 10 August 1997 + * + * Refer: "Inside Macintosh", Volume 1, page I-128 "Format of a Resource File" + * + * $Author: djh $ + * $Revision: 2.1 $ + * + */ + +#include "dt.h" + +struct adt adt; +struct idt idt; + +u_char last_creat[4] = { 0, 0, 0, 0 }; +u_char last_ftype[4] = { 0, 0, 0, 0 }; + +main(argc, argv) +int argc; +char *argv[]; +{ + int len; + int fd, i; + u_char zero[4]; + u_char icon[1024]; + char path[MAXPATHLEN], file[MAXPATHLEN]; + + if (argc != 2) { + fprintf(stderr, "usage: dumpdt volname\n"); + exit(1); + } + + if (chdir(argv[1]) < 0) { + perror(argv[1]); + exit(1); + } + + bzero(zero, sizeof(zero)); + + if ((fd = open(AFILE, O_RDONLY, 0644)) < 0) { + perror(AFILE); + exit(1); + } + + printf("APPL Mappings from %s/%s\n\n", argv[1], AFILE); + + for ( ; ; ) { + if (read(fd, &adt, sizeof(adt)) < sizeof(adt)) + break; + + if (ntohl(adt.magic) != MAGIC) { + fprintf(stderr, "bad magic in %s/%s\n", argv[1], AFILE); + break; + } + + printf("creat"); + printsig(adt.creat); + if (bcmp(adt.userb, zero, sizeof(zero))) { + printf("userb"); + printsig(adt.userb); + } + + path[0] = '\0'; + file[0] = '\0'; + + if (read(fd, path, ntohl(adt.dlen)) != ntohl(adt.dlen)) { + fprintf(stderr, "bad path length %d\n", ntohl(adt.dlen)); + break; + } + if (read(fd, file, ntohl(adt.flen)) != ntohl(adt.flen)) { + fprintf(stderr, "bad file length %d\n", ntohl(adt.dlen)); + break; + } + if (path[0] != '\0') + strcat(path, "/"); + strcat(path, file); + printf("file :%s:", path); + if (access(path, F_OK)) + printf(" (not found)"); + printf("\n"); + } + + close(fd); + + if ((fd = open(IFILE, O_RDONLY, 0644)) < 0) { + perror(IFILE); + exit(1); + } + + printf("\nIcon Family Mappings from %s/%s\n\n", argv[1], IFILE); + + for ( ; ; ) { + if (read(fd, &idt, sizeof(idt)) < sizeof(idt)) + break; + + if (ntohl(idt.magic) != MAGIC) { + fprintf(stderr, "bad magic in %s/%s\n", argv[1], IFILE); + break; + } + + if (last_creat[0] != '\0' + && (bcmp(last_creat, idt.creat, 4) != 0 + || bcmp(last_ftype, idt.ftype, 4) != 0)) + printf("\n\n"); + + bcopy(idt.creat, last_creat, 4); + bcopy(idt.ftype, last_ftype, 4); + + printf("creat"); + printsig(idt.creat); + printf("ftype"); + printsig(idt.ftype); + printf("itype %d ", idt.itype); + if (bcmp(idt.userb, zero, sizeof(zero))) { + printf("userb"); + printsig(idt.userb); + } + printf("\n"); + if ((len = read(fd, icon, ntohl(idt.isize))) != ntohl(idt.isize)) { + fprintf(stderr, "bad icon length %d\n", ntohl(idt.isize)); + break; + } + printicn(icon, idt.itype, len); + } + + close(fd); +} diff --git a/contrib/MacPS/Installation b/contrib/MacPS/Installation new file mode 100644 index 0000000..b16e77d --- /dev/null +++ b/contrib/MacPS/Installation @@ -0,0 +1,75 @@ +Installation Instructions (SCCSid = "@(#)Installation 2.2 10/24/89") + +1) Look at the Makefile. There are three CFLAGS options that you can +use. Setting SYSV should allow macps and prepfix to compile on System +V machines (I've only tried it under A/UX). Setting CONFIGDIR will +cause macps to look for macps.config in that directory. Setting SAVE +will cause macps to enclose the entire print job is a PostScript +save/restore context. Normally you don't need SAVE, since most +spooling software will automatically do an EOF between print jobs, which +effectively does a restore of memory for you, but some spooling +software does require the save/restore. Note that defining SAVE will +cause printing to fail on a NeXT laser printer, while it is harmless on +most other systems. + +The options will look something like: + +CFLAGS = -O -DSYSV -DCONFIGDIR=\"/usr/new/lib\" -DSAVE + +if you defined all the options. + +2) Type "make". If all goes well, macps and prepfix will be created. + +3) To create the unprocessed LaserPrep file on the Mac, as well as +creating the raw PostScript files that you want to print, make sure +that either you're not running MultiFinder, or if you are, go to the +Chooser under the Apple menu, click on the LaserWriter icon and then +turn off Background Printing. + +4) For each version of LaserPrep on the Mac that you want to include, +install that version in the System Folder. Then, open an empty +document in some simple application (one that doesn't have its +own ProcSet to download). Choose Print from the File menu and the +LaserWriter print dialog will appear. Click on the OK button and +IMMEDIATELY press and hold Command-K. When a dialog box appears +telling you that it is creating a PostScript file, you can release +Command-K. The unprocessed LaserPrep file will usually be found in one +of three places, in the System Folder, in the same folder as the +application or at the top level of the disk. + +5) Upload the PostScript file(s) to Unix, using some file transfer +program like MacTerminal, Versaterm, Red Ryder, MacKermit or NCSA +Telnet (if your file transfer program feels left out, feel free to add +it to your list). + +6) Run prepfix on each unprocessed file, diverting the standard output +to an appropriataly named file (like LaserPrep5.2). If you want to +allow bit smoothing on a non-Apple PostScript printer, specify the -l +option to prepfix (you can specify as many printer names as you want, +each with a separate -l flag). If you aren't sure the your printer can +do smoothing, you can try it and see if it works (if it doesn't, you +can always re-run prepfix on the unprocessed file(s), leaving off the +printer that doesn't work). If you don't know the product name for you +printer, you can use the following PostScript code to print it: + +%! +/in {72 mul} def +/Courier findfont 18 scalefont setfont +1 in 8 in moveto +statusdict /product get show +showpage + +7) Put the modified LaserPrep file(s) in some directory and modify the +macps.config file to point to these LaserPrep files. Then put the +macps.config file in a "lib" subdirectory to where you install macps +(or in the directory CONFIGDIR if you used that option). + +8) Now when you want to print something, do the same thing as in step 4 +above with the LaserWriter print dialog, except press and hold +Command-F (this cause LaserPrep not to be included in the PostScript +file). + +9) Upload the PostScript file and run macps on it, sending the output +to your printer, as in: + + % macps psfile | lpr diff --git a/contrib/MacPS/Makefile b/contrib/MacPS/Makefile new file mode 100644 index 0000000..27dc539 --- /dev/null +++ b/contrib/MacPS/Makefile @@ -0,0 +1,35 @@ +# Copyright (c) 1988, The Regents of the University of California. +# Edward Moy, Workstation Software Support Group, Workstation Support Serices, +# Information Systems and Technology. +# +# Permission is granted to any individual or institution to use, copy, +# or redistribute this software so long as it is not sold for profit, +# provided that this notice and the original copyright notices are +# retained. The University of California makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# SCCSid = "@(#)Makefile 2.2 10/24/89" +# +# For System V, include -DSYSV in CFLAGS below. +# To specify a fixed path for macps.config, include -DCONFIGDIR=\"path\" +# in CFLAGS below (path is the full pathname of the directory). +# To do save/restore context, include -DSAVE in CFLAGS below. +# +CFLAGS = -O +MACPS = macps.o macaux.o ucbwhich.o +PREPFIX = prepfix.o macaux.o + +all : macps prepfix + +macps : $(MACPS) + cc -o macps $(CFLAGS) $(MACPS) + +prepfix : $(PREPFIX) + cc -o prepfix $(CFLAGS) $(PREPFIX) + +clean : + /bin/rm -f *.o macps prepfix + +spotless : + /bin/rm -f *.o *.orig macps prepfix diff --git a/contrib/MacPS/ReadMe b/contrib/MacPS/ReadMe new file mode 100644 index 0000000..4bee6bc --- /dev/null +++ b/contrib/MacPS/ReadMe @@ -0,0 +1,101 @@ +COPYRIGHT NOTICE (SCCSid = "@(#)ReadMe 2.2 10/24/89") + +Copyright (c) 1988, The Regents of the University of California. +Edward Moy, Workstation Software Support Group, Workstation Support +Serices, Information Systems and Technology. + +Permission is granted to any individual or institution to use, copy, or +redistribute this software so long as it is not sold for profit, +provided that this notice and the original copyright notices are +retained. The University of California makes no representations about +the suitability of this software for any purpose. It is provided "as +is" without express or implied warranty. + +WHAT IS MACPS? + +Macps is a Unix program that takes an uploaded PostScript file created +on a Macintosh (by typing Command-F at the LaserWriter dialog box; see +macps.1 for more details) and includes an appropriately modified +LaserPrep file so that the result can be sent to a PostScript printer +>From Unix. The LaserPrep file contains macros used by the PostScript +generator on the Macintosh. + +WHY IS MACPS NEEDED? + +This is how Mac printing works. When a Mac talks to a LaserWriter, it +asks if the LaserWriter has had a LaserPrep file downloaded to it. A +LaserWriter that is first powered up, has no such LaserPrep file, and +so the Mac downloads it and makes the LaserPrep file resident in +memory. Then the actual print file is sent to the LaserWriter. +Subsequent print requests need not download the LaserPrep file, unless +it is a different version. + +Since a LaserWriter connected to a Unix system usually does things +other than Mac printing, it is unwise to make LaserPrep files resident +in memory so that other PostScript jobs have less memory to work with. +What prepfix does is to modify a LaserPrep file so that, among other +things, it does not make itself resident in memory. Thus, the +LaserPrep file must be downloaded for each Mac print job. This is the +function of macps, to automatically append the appropriate LaserPrep +file. + +WHICH VERSION OF THE LASERPREP WILL BE USED? + +Macps interprets the %%IncludeProcSet directive found in the PostScript +generated by LaserWriter driver 4.0 and greater. It takes the ProcSet +id and looks it up in a file "macps.config", to get the pathname of the +prep file, and thus macps can convert PostScript generated by different +versions of the LaserWriter driver. + +HOW ARE THE LASERPREP FILES GENERATED? + +Since the Apple LaserPrep files are copyrighted, I've included a +program, prepfix, that reads version 4.0 and up LaserPrep files, and +edits them so that they are compatible with Unix, and are even +electronically mailable (See prepfix.1 for more details). + +WHERE IS THE MACPS.CONFIG FILE LOCATED? + +Macps has some special code that is able to figure out from which +directory it was called from. It will then look in a "lib" subdiretory +for the macps.config file. + +WHAT ABOUT BIT-SMOOTHING ON NON-LASERWRITER PRINTERS? + +For PostScript printers using Motorola 680x0 processors and Adobe +PostScript firmware other than LaserWriters, there is an option that +will allow these printers to do bit-smoothing, just like LaserWriters. + +CHANGES IN VERSION 2.2 + +Version 2.2 of prepfix now supports LaserPrep 6.0. The PostScript +save/restore context is now a compile-time option, since it caused +printing to fail on a NeXT printer (though it was harmless on most +other printers). This save/restore is now more intelligent about +clearing the stacks. + +CHANGES IN VERSION 2.1 + +Version 2.1 of prepfix uses a safer method for turning on bit-smoothing +for non-Apple printers. This should get around some of the problems +people have been having with specialized macros in the LaserPreps that +are Apple printer specific. The -l and -p options in version 1.1 have +been replaced with the single -l option, and the limit on the number +of printers you can specify has been removed. + +Also, prepfix removes some other various macros that cause +unpredictable problems, and a problem with Apple LaserWriter II/NTs +(but not other Apple printers). + +Version 2.1 macps has several new options. The -c option allow you to +specify the number of copies to generate (overriding any multiple copy +option that was specified on the Macintosh). The -d option allows an +alternate directory to look for the macps.config file. Finally, the -r +(raw) option suppresses the conversion of 8-bit binary into ASCII, and +is useful for some graphics programs that manipulate gray-scale images, +and produce 8-bit binary PostScript output. + +Macps will even work with a NeXT laser printer, but (at least the 0.8 +version of the operating system) will not do bit smoothing. Beware, +though, that if you print Macintosh patterns at 400 dpi, they will +look funny. diff --git a/contrib/MacPS/macaux.c b/contrib/MacPS/macaux.c new file mode 100644 index 0000000..a708c1e --- /dev/null +++ b/contrib/MacPS/macaux.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef lint +static char *SCCSid = "@(#)macaux.c 2.2 10/24/89"; +#endif lint + +#include +#include +#include "str.h" + +#define FALSE 0 +#define TRUE 1 + +extern char *myname; +int rawmode = FALSE; + +STR * +STRalloc() +{ + register STR *str; + char *malloc(); + + if((str = (STR *)malloc(sizeof(STR))) == NULL || + (str->bufptr = (unsigned char *)malloc(STRSIZE)) == NULL) { + fprintf(stderr, "%s: STRalloc: Out of memory\n", myname); + exit(1); + } + str->curendptr = str->bufptr; + str->realendptr = str->bufptr + STRSIZE; + return(str); +} + +STRfree(str) +STR *str; +{ + free((char *)str->bufptr); + free((char *)str); +} + +STRexpand(str) +register STR *str; +{ + register int curend, realend; + char *realloc(); + + curend = str->curendptr - str->bufptr; + realend = (str->realendptr - str->bufptr) + STRSIZEDELTA; + if((str->bufptr = (unsigned char *)realloc((char *)str->bufptr, + realend)) == NULL) { + fprintf(stderr, "%s: STRexpand: Out of memory\n", myname); + exit(1); + } + str->curendptr = str->bufptr + curend; + str->realendptr = str->bufptr + realend; +} + +STRgets(str, fp) +register STR *str; +register FILE *fp; +{ + register int c; + + str->curendptr = str->bufptr; + for( ; ; ) { + if((c = getc(fp)) == EOF) + return(str->curendptr > str->bufptr); + if(str->curendptr >= str->realendptr) + STRexpand(str); + *str->curendptr++ = c; + if(c == '\n' || c == '\r') + return(TRUE); + } +} + +STRputsptr(str, cp, fp) +register STR *str; +register unsigned char *cp; +register FILE *fp; +{ + if(rawmode) { + for( ; cp < str->curendptr ; cp++) + putc(*cp, fp); + return; + } + for( ; cp < str->curendptr ; cp++) { + if(!isascii(*cp)) + fprintf(fp, "\\%03o", *cp); + else if(isprint(*cp)) + putc(*cp, fp); + else { + switch(*cp) { + case '\n': + case '\r': + putc('\n', fp); + continue; + case '\t': + putc('\t', fp); + continue; + default: + fprintf(fp, "\\%03o", *str); + continue; + } + } + } +} + +STRcompareptr(str, cp, sp) +register STR *str; +register unsigned char *cp, *sp; +{ + register int comp; + + for( ; ; ) { + if(*sp == 0) + return(cp >= str->curendptr ? 0 : 1); + if(cp >= str->curendptr) + return(-1); + if(*sp == '\n') { + if(*cp != '\n' && *cp != '\r') + return((int)*cp - (int)*sp); + } else if((comp = (int)*cp - (int)*sp) != 0) + return(comp); + cp++; + sp++; + } +} + +STRheadcmpptr(str, cp, sp) +register STR *str; +register unsigned char *cp, *sp; +{ + register int comp; + + for( ; ; ) { + if(*sp == 0) + return(0); + if(cp >= str->curendptr) + return(-1); + if(*sp == '\n') { + if(*cp != '\n' && *cp != '\r') + return((int)*cp - (int)*sp); + } else if((comp = (int)*cp - (int)*sp) != 0) + return(comp); + cp++; + sp++; + } +} + +unsigned char * +STRmatch(str, sp) +register STR *str; +register unsigned char *sp; +{ + register unsigned char *mp, *last; + register int firstchar; + + firstchar = *sp; + last = str->curendptr - strlen(sp); + mp = str->bufptr; + while(mp <= last) { + if(*mp == firstchar && STRheadcmpptr(str, mp, sp) == 0) + return(mp); + mp++; + } + return(NULL); +} diff --git a/contrib/MacPS/macps-22.hdr b/contrib/MacPS/macps-22.hdr new file mode 100644 index 0000000..4aa23eb --- /dev/null +++ b/contrib/MacPS/macps-22.hdr @@ -0,0 +1,44 @@ +30-Oct-89 19:09:02-GMT,38908;000000000001 +Return-Path: +Received: from violet.berkeley.edu by sumex-aim.stanford.edu (4.0/inc-1.0) + id AA19660; Mon, 30 Oct 89 11:09:02 PST +Received: by violet.berkeley.edu (5.61/1.31) + id AA10825; Mon, 30 Oct 89 11:07:00 PST +Date: Mon, 30 Oct 89 11:07:00 PST +From: edmoy@violet.berkeley.edu +Message-Id: <8910301907.AA10825@violet.berkeley.edu> +To: info-mac@sumex-aim.stanford.edu +Subject: New version of macps/prepfix (2.2) + +[Note to info-mac moderator: This version replaces the current version +in the info-mac/unix directory. Thanks. Ed] + +I've just posted the latest version of macps & prepfix (2.2) to +comp.sources.unix and comp.sources.mac and it should soon be available +by anonymous ftp from sumex-aim.stanford.edu, in the info-mac/unix +directory. Here is a brief description: + +Macps is a Unix program that takes an uploaded PostScript file created +on a Macintosh (by typing Command-F at the LaserWriter dialog box) and +includes an appropriately modified LaserPrep file so that the result +can be sent to a PostScript printer from Unix. Since the Apple +LaserPrep files are copyrighted, I've included a program, prepfix, that +reads version 4.0 and up LaserPrep files, and edits them so that they +are compatible with macps, and are even electronically mailable. + +CHANGES IN VERSION 2.2 + +Version 2.2 of macps/prepfix adds support of LaserWriter 6.0, and contains +some minor bug fixes. Printing under NeXT 1.0 to the NeXT laser printer +works well for LaserWriter 4.0 - 5.2 (except for the inherent problems +of printing patterns at 400 dpi), but under LaserWriter 6.0, some things +just don't print, and I don't know why (the exact same file prints fine +on a LaserWriter). + +Edward Moy Principal Programmer - Macintosh & Unix +Workstation Support Services Workstation Software Support Group +University of California +Berkeley, CA 94720 + +edmoy@violet.Berkeley.EDU +ucbvax!violet!edmoy diff --git a/contrib/MacPS/macps-22.shar b/contrib/MacPS/macps-22.shar new file mode 100644 index 0000000..c33e31c --- /dev/null +++ b/contrib/MacPS/macps-22.shar @@ -0,0 +1,1339 @@ +#! /bin/sh +# This is a shell archive, meaning: +# 1. Remove everything above the #! /bin/sh line. +# 2. Save the resulting text in a file. +# 3. Execute the file with /bin/sh (not csh) to create the files: +# ReadMe +# Installation +# Makefile +# macaux.c +# macps.c +# prepfix.c +# str.h +# ucbwhich.c +# ucbwhich.h +# macps.config +# macps.1 +# prepfix.1 +# macps.shar - archive created: Mon Oct 30 10:48:50 PST 1989 +if test -f ReadMe +then + echo shar: will not overwrite existing file "'ReadMe'" +else +echo 'x - ReadMe' +cat << \RAZZLE!DAZZLE > ReadMe +COPYRIGHT NOTICE (SCCSid = "@(#)ReadMe 2.2 10/24/89") + +Copyright (c) 1988, The Regents of the University of California. +Edward Moy, Workstation Software Support Group, Workstation Support +Serices, Information Systems and Technology. + +Permission is granted to any individual or institution to use, copy, or +redistribute this software so long as it is not sold for profit, +provided that this notice and the original copyright notices are +retained. The University of California makes no representations about +the suitability of this software for any purpose. It is provided "as +is" without express or implied warranty. + +WHAT IS MACPS? + +Macps is a Unix program that takes an uploaded PostScript file created +on a Macintosh (by typing Command-F at the LaserWriter dialog box; see +macps.1 for more details) and includes an appropriately modified +LaserPrep file so that the result can be sent to a PostScript printer +>From Unix. The LaserPrep file contains macros used by the PostScript +generator on the Macintosh. + +WHY IS MACPS NEEDED? + +This is how Mac printing works. When a Mac talks to a LaserWriter, it +asks if the LaserWriter has had a LaserPrep file downloaded to it. A +LaserWriter that is first powered up, has no such LaserPrep file, and +so the Mac downloads it and makes the LaserPrep file resident in +memory. Then the actual print file is sent to the LaserWriter. +Subsequent print requests need not download the LaserPrep file, unless +it is a different version. + +Since a LaserWriter connected to a Unix system usually does things +other than Mac printing, it is unwise to make LaserPrep files resident +in memory so that other PostScript jobs have less memory to work with. +What prepfix does is to modify a LaserPrep file so that, among other +things, it does not make itself resident in memory. Thus, the +LaserPrep file must be downloaded for each Mac print job. This is the +function of macps, to automatically append the appropriate LaserPrep +file. + +WHICH VERSION OF THE LASERPREP WILL BE USED? + +Macps interprets the %%IncludeProcSet directive found in the PostScript +generated by LaserWriter driver 4.0 and greater. It takes the ProcSet +id and looks it up in a file "macps.config", to get the pathname of the +prep file, and thus macps can convert PostScript generated by different +versions of the LaserWriter driver. + +HOW ARE THE LASERPREP FILES GENERATED? + +Since the Apple LaserPrep files are copyrighted, I've included a +program, prepfix, that reads version 4.0 and up LaserPrep files, and +edits them so that they are compatible with Unix, and are even +electronically mailable (See prepfix.1 for more details). + +WHERE IS THE MACPS.CONFIG FILE LOCATED? + +Macps has some special code that is able to figure out from which +directory it was called from. It will then look in a "lib" subdiretory +for the macps.config file. + +WHAT ABOUT BIT-SMOOTHING ON NON-LASERWRITER PRINTERS? + +For PostScript printers using Motorola 680x0 processors and Adobe +PostScript firmware other than LaserWriters, there is an option that +will allow these printers to do bit-smoothing, just like LaserWriters. + +CHANGES IN VERSION 2.2 + +Version 2.2 of prepfix now supports LaserPrep 6.0. The PostScript +save/restore context is now a compile-time option, since it caused +printing to fail on a NeXT printer (though it was harmless on most +other printers). This save/restore is now more intelligent about +clearing the stacks. + +CHANGES IN VERSION 2.1 + +Version 2.1 of prepfix uses a safer method for turning on bit-smoothing +for non-Apple printers. This should get around some of the problems +people have been having with specialized macros in the LaserPreps that +are Apple printer specific. The -l and -p options in version 1.1 have +been replaced with the single -l option, and the limit on the number +of printers you can specify has been removed. + +Also, prepfix removes some other various macros that cause +unpredictable problems, and a problem with Apple LaserWriter II/NTs +(but not other Apple printers). + +Version 2.1 macps has several new options. The -c option allow you to +specify the number of copies to generate (overriding any multiple copy +option that was specified on the Macintosh). The -d option allows an +alternate directory to look for the macps.config file. Finally, the -r +(raw) option suppresses the conversion of 8-bit binary into ASCII, and +is useful for some graphics programs that manipulate gray-scale images, +and produce 8-bit binary PostScript output. + +Macps will even work with a NeXT laser printer, but (at least the 0.8 +version of the operating system) will not do bit smoothing. Beware, +though, that if you print Macintosh patterns at 400 dpi, they will +look funny. +RAZZLE!DAZZLE +fi # End ReadMe +if test -f Installation +then + echo shar: will not overwrite existing file "'Installation'" +else +echo 'x - Installation' +cat << \RAZZLE!DAZZLE > Installation +Installation Instructions (SCCSid = "@(#)Installation 2.2 10/24/89") + +1) Look at the Makefile. There are three CFLAGS options that you can +use. Setting SYSV should allow macps and prepfix to compile on System +V machines (I've only tried it under A/UX). Setting CONFIGDIR will +cause macps to look for macps.config in that directory. Setting SAVE +will cause macps to enclose the entire print job is a PostScript +save/restore context. Normally you don't need SAVE, since most +spooling software will automatically do an EOF between print jobs, which +effectively does a restore of memory for you, but some spooling +software does require the save/restore. Note that defining SAVE will +cause printing to fail on a NeXT laser printer, while it is harmless on +most other systems. + +The options will look something like: + +CFLAGS = -O -DSYSV -DCONFIGDIR=\"/usr/new/lib\" -DSAVE + +if you defined all the options. + +2) Type "make". If all goes well, macps and prepfix will be created. + +3) To create the unprocessed LaserPrep file on the Mac, as well as +creating the raw PostScript files that you want to print, make sure +that either you're not running MultiFinder, or if you are, go to the +Chooser under the Apple menu, click on the LaserWriter icon and then +turn off Background Printing. + +4) For each version of LaserPrep on the Mac that you want to include, +install that version in the System Folder. Then, open an empty +document in some simple application (one that doesn't have its +own ProcSet to download). Choose Print from the File menu and the +LaserWriter print dialog will appear. Click on the OK button and +IMMEDIATELY press and hold Command-K. When a dialog box appears +telling you that it is creating a PostScript file, you can release +Command-K. The unprocessed LaserPrep file will usually be found in one +of three places, in the System Folder, in the same folder as the +application or at the top level of the disk. + +5) Upload the PostScript file(s) to Unix, using some file transfer +program like MacTerminal, Versaterm, Red Ryder, MacKermit or NCSA +Telnet (if your file transfer program feels left out, feel free to add +it to your list). + +6) Run prepfix on each unprocessed file, diverting the standard output +to an appropriataly named file (like LaserPrep5.2). If you want to +allow bit smoothing on a non-Apple PostScript printer, specify the -l +option to prepfix (you can specify as many printer names as you want, +each with a separate -l flag). If you aren't sure the your printer can +do smoothing, you can try it and see if it works (if it doesn't, you +can always re-run prepfix on the unprocessed file(s), leaving off the +printer that doesn't work). If you don't know the product name for you +printer, you can use the following PostScript code to print it: + +%! +/in {72 mul} def +/Courier findfont 18 scalefont setfont +1 in 8 in moveto +statusdict /product get show +showpage + +7) Put the modified LaserPrep file(s) in some directory and modify the +macps.config file to point to these LaserPrep files. Then put the +macps.config file in a "lib" subdirectory to where you install macps +(or in the directory CONFIGDIR if you used that option). + +8) Now when you want to print something, do the same thing as in step 4 +above with the LaserWriter print dialog, except press and hold +Command-F (this cause LaserPrep not to be included in the PostScript +file). + +9) Upload the PostScript file and run macps on it, sending the output +to your printer, as in: + + % macps psfile | lpr +RAZZLE!DAZZLE +fi # End Installation +if test -f Makefile +then + echo shar: will not overwrite existing file "'Makefile'" +else +echo 'x - Makefile' +cat << \RAZZLE!DAZZLE > Makefile +# Copyright (c) 1988, The Regents of the University of California. +# Edward Moy, Workstation Software Support Group, Workstation Support Serices, +# Information Systems and Technology. +# +# Permission is granted to any individual or institution to use, copy, +# or redistribute this software so long as it is not sold for profit, +# provided that this notice and the original copyright notices are +# retained. The University of California makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# SCCSid = "@(#)Makefile 2.2 10/24/89" +# +# For System V, include -DSYSV in CFLAGS below. +# To specify a fixed path for macps.config, include -DCONFIGDIR=\"path\" +# in CFLAGS below (path is the full pathname of the directory). +# To do save/restore context, include -DSAVE in CFLAGS below. +# +CFLAGS = -O +MACPS = macps.o macaux.o ucbwhich.o +PREPFIX = prepfix.o macaux.o + +all : macps prepfix + +macps : $(MACPS) + cc -o macps $(CFLAGS) $(MACPS) + +prepfix : $(PREPFIX) + cc -o prepfix $(CFLAGS) $(PREPFIX) + +clean : + /bin/rm -f *.o macps prepfix +RAZZLE!DAZZLE +fi # End Makefile +if test -f macaux.c +then + echo shar: will not overwrite existing file "'macaux.c'" +else +echo 'x - macaux.c' +cat << \RAZZLE!DAZZLE > macaux.c +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef lint +static char *SCCSid = "@(#)macaux.c 2.2 10/24/89"; +#endif lint + +#include +#include +#include "str.h" + +#define FALSE 0 +#define TRUE 1 + +extern char *myname; +int rawmode = FALSE; + +STR * +STRalloc() +{ + register STR *str; + char *malloc(); + + if((str = (STR *)malloc(sizeof(STR))) == NULL || + (str->bufptr = (unsigned char *)malloc(STRSIZE)) == NULL) { + fprintf(stderr, "%s: STRalloc: Out of memory\n", myname); + exit(1); + } + str->curendptr = str->bufptr; + str->realendptr = str->bufptr + STRSIZE; + return(str); +} + +STRfree(str) +STR *str; +{ + free((char *)str->bufptr); + free((char *)str); +} + +STRexpand(str) +register STR *str; +{ + register int curend, realend; + char *realloc(); + + curend = str->curendptr - str->bufptr; + realend = (str->realendptr - str->bufptr) + STRSIZEDELTA; + if((str->bufptr = (unsigned char *)realloc((char *)str->bufptr, + realend)) == NULL) { + fprintf(stderr, "%s: STRexpand: Out of memory\n", myname); + exit(1); + } + str->curendptr = str->bufptr + curend; + str->realendptr = str->bufptr + realend; +} + +STRgets(str, fp) +register STR *str; +register FILE *fp; +{ + register int c; + + str->curendptr = str->bufptr; + for( ; ; ) { + if((c = getc(fp)) == EOF) + return(str->curendptr > str->bufptr); + if(str->curendptr >= str->realendptr) + STRexpand(str); + *str->curendptr++ = c; + if(c == '\n' || c == '\r') + return(TRUE); + } +} + +STRputsptr(str, cp, fp) +register STR *str; +register unsigned char *cp; +register FILE *fp; +{ + if(rawmode) { + for( ; cp < str->curendptr ; cp++) + putc(*cp, fp); + return; + } + for( ; cp < str->curendptr ; cp++) { + if(!isascii(*cp)) + fprintf(fp, "\\%03o", *cp); + else if(isprint(*cp)) + putc(*cp, fp); + else { + switch(*cp) { + case '\n': + case '\r': + putc('\n', fp); + continue; + case '\t': + putc('\t', fp); + continue; + default: + fprintf(fp, "\\%03o", *str); + continue; + } + } + } +} + +STRcompareptr(str, cp, sp) +register STR *str; +register unsigned char *cp, *sp; +{ + register int comp; + + for( ; ; ) { + if(*sp == 0) + return(cp >= str->curendptr ? 0 : 1); + if(cp >= str->curendptr) + return(-1); + if(*sp == '\n') { + if(*cp != '\n' && *cp != '\r') + return((int)*cp - (int)*sp); + } else if((comp = (int)*cp - (int)*sp) != 0) + return(comp); + cp++; + sp++; + } +} + +STRheadcmpptr(str, cp, sp) +register STR *str; +register unsigned char *cp, *sp; +{ + register int comp; + + for( ; ; ) { + if(*sp == 0) + return(0); + if(cp >= str->curendptr) + return(-1); + if(*sp == '\n') { + if(*cp != '\n' && *cp != '\r') + return((int)*cp - (int)*sp); + } else if((comp = (int)*cp - (int)*sp) != 0) + return(comp); + cp++; + sp++; + } +} + +unsigned char * +STRmatch(str, sp) +register STR *str; +register unsigned char *sp; +{ + register unsigned char *mp, *last; + register int firstchar; + + firstchar = *sp; + last = str->curendptr - strlen(sp); + mp = str->bufptr; + while(mp <= last) { + if(*mp == firstchar && STRheadcmpptr(str, mp, sp) == 0) + return(mp); + mp++; + } + return(NULL); +} +RAZZLE!DAZZLE +fi # End macaux.c +if test -f macps.c +then + echo shar: will not overwrite existing file "'macps.c'" +else +echo 'x - macps.c' +cat << \RAZZLE!DAZZLE > macps.c +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef lint +static char *SCCSid = "@(#)macps.c 2.2 10/25/89"; +#endif lint + +#include +#include +#ifdef SYSV +#include +#else SYSV +#include +#endif SYSV +#include +#include +#include "str.h" +#include "ucbwhich.h" + +#define CONFIG "macps.config" +#ifdef SYSV +#define index strchr +#define rindex strrchr +#endif SYSV + +#ifdef SAVE +char *finale = "clear countdictstack 2 sub{end}repeat macps restore\n"; +char intro[] = "\ +%%! *** Created by macps: %s\ +/macps save def\n\ +"; +#else SAVE +char intro[] = "\ +%%! *** Created by macps: %s\ +"; +#endif SAVE +char *myname; +int ncopies = 0; +#ifdef CONFIGDIR +char ucblib[UCBMAXPATHLEN] = CONFIGDIR; +#else CONFIGDIR +int ucbalternate; +char ucbpath[UCBMAXPATHLEN]; +char ucblib[UCBMAXPATHLEN]; +#endif CONFIGDIR + +main(argc, argv) +int argc; +char **argv; +{ + register STR* str; + register char *cp, *pp; + register FILE *fp; + register int i, fd; + char line[BUFSIZ]; + char path[UCBMAXPATHLEN]; + long ltime; + char *ctime(); + +#ifndef CONFIGDIR + ucbwhich(*argv); +#endif CONFIGDIR + if(myname = rindex(*argv, '/')) + myname++; + else + myname = *argv; + cp = NULL; + for(argc--, argv++ ; argc > 0 && **argv == '-' ; argc--, argv++) { + switch((*argv)[1]) { + case 'c': /* multiple copies */ + if((*argv)[2]) + ncopies = atoi(&(*argv[2])); + else { + if(argc < 2) + Usage(); /* never returns */ + argc--; + ncopies = atoi(*++argv); + } + if(ncopies <= 0) + Usage(); /* never returns */ + break; + case 'd': /* alternate directory for config file */ + if((*argv)[2]) + cp = &(*argv[2]); + else { + if(argc < 2) + Usage(); /* never returns */ + argc--; + cp = *++argv; + } + strcpy(ucblib, cp); + break; + case 'r': /* raw mode */ + rawmode++; + break; + default: + Usage(); /* never returns */ + } + } + if(argc > 1) + Usage(); /* never returns */ + if(argc == 1 && freopen(*argv, "r", stdin) == NULL) { + fprintf(stderr, "%s: can't open %s\n", myname, *argv); + exit(1); + } + str = STRalloc(); + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: Null input\n", myname); + exit(1); + } + strcat(ucblib, "/"); + strcpy(path, ucblib); + strcat(path, CONFIG); + if((fp = fopen(path, "r")) == NULL) { + fprintf(stderr, "%s: Can't open %s\n", myname, path); + exit(1); + } + time(<ime); + printf(intro, ctime(<ime)); + do { + if(ncopies != 0 && STRheadcompare(str, "userdict /#copies ") + == 0) + continue; + if(STRcompare(str, "%%EOF\n") == 0) { +#ifdef SAVE + if(finale) { + fputs(finale, stdout); + finale = NULL; + } +#endif SAVE + STRputs(str, stdout); + continue; + } + if(STRheadcompare(str, "%%IncludeProcSet:") == 0) { + for(cp = (char *)&str->bufptr[17] ; ; cp++) { + if(!*cp) { + fprintf(stderr, + "%s: Syntax error on IncludeProcSet line\n", + myname); + exit(1); + } + if(!isascii(*cp) || !isspace(*cp)) + break; + } + pp = (char *)str->curendptr; + while(--pp >= cp) { + if(!isascii(*pp) || !isspace(*pp)) + break; + *pp = 0; + } + str->curendptr = (unsigned char *)(pp + 1); + fseek(fp, 0L, 0); + for( ; ; ) { + if(!fgets(line, BUFSIZ, fp)) { + fprintf(stderr, + "%s: Unknown IncludeProcSet %s\n", + myname, cp); + exit(1); + } + if(*line == '#') + continue; + if(pp = index(line, '\n')) { + if(pp == line) + continue; + *pp = 0; + } + if(!(pp = index(line, '\t'))) { + fprintf(stderr, + "%s: Syntax error in macps.config\n", + myname); + exit(1); + } + *pp++ = 0; + if(STRcompareptr(str, cp, line) == 0) + break; + } + if(*pp == '/') + strcpy(path, pp); + else { + strcpy(path, ucblib); + strcat(path, pp); + } + fflush(stdout); + if((fd = open(path, O_RDONLY, 0)) < 0) { + fprintf(stderr, "%s: Can't open %s\n", myname, + path); + exit(1); + } + while((i = read(fd, line, BUFSIZ)) > 0) + write(1, line, i); + close(fd); + continue; + } + STRputs(str, stdout); + if(ncopies > 1 && isascii(*str->bufptr) && + isdigit(*str->bufptr)) { + cp = (char *)str->bufptr; + while(cp < (char *)str->curendptr && isascii(*cp) + && isdigit(*cp)) + cp++; + if((char *)str->curendptr - cp == 4 && + STRcompareptr(str, cp, " mf\n") == 0) { + printf("userdict /#copies %d put\n", ncopies); + ncopies = -1; + } + } + } while(STRgets(str, stdin)); +#ifdef SAVE + if(finale) + fputs(finale, stdout); +#endif SAVE + exit(0); +} + +Usage() +{ + fputs("Usage: macps [-c #] [-d directory] [-r] [file]\n", stderr); + exit(1); +} +RAZZLE!DAZZLE +fi # End macps.c +if test -f prepfix.c +then + echo shar: will not overwrite existing file "'prepfix.c'" +else +echo 'x - prepfix.c' +cat << \RAZZLE!DAZZLE > prepfix.c +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef lint +static char *SCCSid = "@(#)prepfix.c 2.2 10/25/89"; +#endif lint + +#include +#include +#ifdef SYSV +#include +#else SYSV +#include +#endif SYSV +#include "str.h" + +#define CLEARTOMARK 12 +#define EEXECLEN 80 +#define EXTRA (NZEROLINE * ZEROLINE + CLEARTOMARK) +#define LINELEN 256 +#define NPRODUCTS 32 +#define NZEROLINE 7 +#define ZEROLINE 65 +#ifdef SYSV +#define index strchr +#define rindex strrchr +#endif SYSV + +char exstr[] = "\ +%ck userdict/%s known not and{currentfile eexec}{%d{currentfile read\n\ +pop pop}repeat}ifelse\n\ +"; +char *match(); +char *myname; +int maxproducts = NPRODUCTS; +int nproducts = 0; +char Ok[] = "\ +/Ok{ok{true}{save /Pd statusdict /product get def false 0 1 ProdArr length\n\ +1 sub{Pd exch ProdArr exch get anchorsearch exch pop{pop pop true exit}if}for\n\ +exch restore}ifelse}bind def\n\ +"; +char ProdArr0[] = "/ProdArr [\n"; +char ProdArr1[] = "] def\n"; +char **products; +char tempname[] = "/tmp/prepfixXXXXXX"; + +main(argc, argv) +int argc; +char **argv; +{ + register STR *str; + register FILE *tp; + register int i; + register unsigned char *lp; + char buf[BUFSIZ]; + char *malloc(), *realloc(); + + if(myname = rindex(*argv, '/')) + myname++; + else + myname = *argv; + for(argc--, argv++ ; argc > 0 && **argv == '-' ; argc--, argv++) { + switch((*argv)[1]) { + case 'h': + usage(); + case 'l': + if(nproducts <= 0 && (products = + (char **)malloc(maxproducts*sizeof(char *))) == NULL) { + fprintf(stderr, + "%s: Out of memory creating products array\n", + myname); + exit(1); + } else if(nproducts >= maxproducts - 1 && (products = + (char **)realloc(products, (maxproducts += NPRODUCTS) + * sizeof(char *))) == NULL) { + fprintf(stderr, + "%s: Out of memory expanding products array\n", + myname); + exit(1); + } + if((*argv)[2]) + products[nproducts++] = &(*argv)[2]; + else { + if(argc < 2) { + fprintf(stderr, + "%s: No argument for -l\n", myname); + exit(1); + } + argc--; + argv++; + products[nproducts++] = *argv; + } + break; + } + } + if(argc > 1) + usage(); + if(argc > 0 && freopen(*argv, "r", stdin) == NULL) { + fprintf(stderr, "%s: Can't open %s\n", myname, *argv); + exit(1); + } + mktemp(tempname); + if((tp = fopen(tempname, "w+")) == NULL) { + fprintf(stderr, "%s: Can't create temp file %s\n", + myname, tempname); + exit(1); + } + unlink(tempname); + str = STRalloc(); + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: Null input\n", myname); + exit(1); + } + for( ; ; ) { + if(STRheadcompare(str, "% \251") == 0) { + fputs("% ", tp); + str->bufptr[0] = '('; + str->bufptr[1] = 'C'; + str->bufptr[2] = ')'; + } else if(STRheadcompare(str, "%%BeginProcSet:") == 0) { + STRputs(str, stdout); + fseek(tp, 0L, 0); + while((i = fread(buf, 1, BUFSIZ, tp)) > 0) + fwrite(buf, 1, i, stdout); + fclose(tp); + break; + } + STRputs(str, tp); + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: No BeginProcSet\n", myname); + exit(1); + } + } + while(STRgets(str, stdin)) { + if(nproducts > 0 && STRheadcompare(str, "/ok{") == 0) { + STRputs(str, stdout); + fputs(ProdArr0, stdout); + for(i = 0 ; i < nproducts ; i++) + printf("(%s)\n", products[i]); + fputs(ProdArr1, stdout); + fputs(Ok, stdout); + continue; + } else if(STRmatch(str, "setdefaulttimeouts") + || STRmatch(str, "setsccinteractive")) + continue; + else if(STRmatch(str, "/stretch") && STRmatch(str, "eexec")) { + eexec("stretch", str); + continue; + } else if(STRmatch(str, "/smooth4") && STRmatch(str, "eexec")) { + eexec("smooth4", str); + continue; + } else if(STRmatch(str, " checkload")) { + checkload(str); + continue; + } else if(STRmatch(str, "(LaserWriter II NT)")) { + while(STRgets(str, stdin) && STRheadcompare(str, "35de") + != 0) + { /* ignore line */ } + while(STRgets(str, stdin) && isxdigit(*str->bufptr)) + { /* ignore line */ } + } else if(lp = STRmatch(str, "scaleby96{ppr")) { + STRputsptr(str, lp, stdout); + continue; + } else if(STRmatch(str, "waittimeout")) + continue; + else if(STRheadcompare(str, "%%EndProcSet") == 0) { + STRputs(str, stdout); + break; + } + STRputs(str, stdout); + } + exit(0); +} + +eexec(name, str) +char *name; +register STR *str; +{ + register int len; + + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: EOF during reading eexec\n", myname); + exit(1); + } + len = (str->curendptr - str->bufptr) - 1; + printf(exstr, nproducts > 0 ? 'O' : 'o', name, len + (len / EEXECLEN) + + (len % EEXECLEN ? 1 : 0) + EXTRA); + spliteexec(str); +} + +checkload(str) +register STR *str; +{ + if(nproducts > 0) + *str->bufptr = 'O'; + STRputs(str, stdout); + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: EOF during reading eexec\n", myname); + exit(1); + } + spliteexec(str); +} + +spliteexec(str) +register STR *str; +{ + register int len; + register unsigned char *bp; + + bp = str->bufptr; + len = (str->curendptr - bp) - 1; + while(len >= 80) { + fwrite(bp, 80, 1, stdout); + putchar('\n'); + bp += 80; + len -= 80; + } + if(len > 0) { + fwrite(bp, len, 1, stdout); + putchar('\n'); + } + for( ; ; ) { + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: EOF reached before cleartomark\n", + myname); + exit(1); + } + STRputs(str, stdout); + if(STRheadcompare(str, "cleartomark") == 0) + return; + } +} + +usage() +{ + fprintf(stderr, + "Usage: %s [-l product_name1 [-l product_name2]...] [file]\n", + myname); + fprintf(stderr, " %s -help\n", myname); + exit(1); +} +RAZZLE!DAZZLE +fi # End prepfix.c +if test -f str.h +then + echo shar: will not overwrite existing file "'str.h'" +else +echo 'x - str.h' +cat << \RAZZLE!DAZZLE > str.h +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +/* + * SCCSid = "@(#)str.h 2.2 10/24/89" + */ + +#define STRSIZEDELTA 1024 +#define STRSIZE 1024 + +#define STRcompare(str,fp) STRcompareptr((str), (str)->bufptr, (fp)) +#define STRheadcompare(str,fp) STRheadcmpptr((str), (str)->bufptr, (fp)) +#define STRputs(str,fp) STRputsptr((str), (str)->bufptr, (fp)) + +typedef struct { + unsigned char *bufptr; + unsigned char *curendptr; + unsigned char *realendptr; +} STR; + +extern int rawmode; + +STR *STRalloc(); +int STRcompareptr(); +int STRfree(); +int STRgets(); +int STRheadcmpptr(); +unsigned char *STRmatch(); +int STRputsptr(); +RAZZLE!DAZZLE +fi # End str.h +if test -f ucbwhich.c +then + echo shar: will not overwrite existing file "'ucbwhich.c'" +else +echo 'x - ucbwhich.c' +cat << \RAZZLE!DAZZLE > ucbwhich.c +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef CONFIGDIR +#ifndef lint +static char *SCCSid = "@(#)ucbwhich.c 2.2 10/24/89"; +#endif lint + +#include +#include +#include +#include "ucbwhich.h" + +#define F_OK 0 /* does file exist */ +#define X_OK 1 /* is it executable by caller */ +#define W_OK 2 /* writable by caller */ +#define R_OK 4 /* readable by caller */ + +#define LIBLEN 4 +#ifdef SYSV +#define index strchr +#define rindex strrchr +#endif SYSV + +static char lib[] = "/lib"; + +char ucblib[UCBMAXPATHLEN]; +int ucbalternate = 0; +char ucbpath[UCBMAXPATHLEN]; + +ucbwhich(str) +char *str; +{ + register char *dir, *name, *cp, *tp; + register int len; + char dirbuf[UCBMAXPATHLEN], namebuf[UCBMAXPATHLEN]; + struct stat sbuf; + char *index(), *rindex(), *getwd(), *getenv(); + + strcpy(name = namebuf, str); + if(*name == '/') /* absolute pathname */ + *(rindex(dir = name, '/')) = 0 ; /* remove tail */ + else { + if(cp = index(name, '/')) { /* relative pathname */ + if((dir = getwd(dirbuf)) == NULL) + return(0); + /* if any errors occurs assume standard version */ + *cp++ = 0; + for( ; ; ) { + if(*name != 0) { /* multiple slashes */ + if(strcmp(name, "..") == 0) { + /* parent directory */ + if((tp = rindex(dir, '/')) == + NULL) + return(0); + if(tp == dir) + tp++; + /* root directory */ + *tp = 0; + /* remove last component */ + } else if(strcmp(name, ".") != 0) { + /* subdirectory */ + strcat(dir, "/"); + strcat(dir, name); + } + } + name = cp; + if((cp = index(name, '/')) == NULL) break; + /* ignore last component */ + *cp++ = 0; + } + } else { /* look through $PATH variable */ + if((tp = getenv("PATH")) == NULL) + return(0); + for(name = namebuf ; ; ) { + if(*tp == 0) + return(0); + else if(*tp == ':') + tp++; + if((cp = index(tp, ':')) == NULL) + cp = tp + strlen(tp); + /* positioned on null */ + for(dir = dirbuf ; tp < cp ; ) + *dir++ = *tp++; + *dir = 0; + strcpy(name, dir = dirbuf); + strcat(name, "/"); + strcat(name, str); + if(stat(name, &sbuf) < 0 || (sbuf.st_mode & + S_IFMT) != S_IFREG) + continue; + if(access(name, X_OK) == 0) { + if(strcmp(dir, ".") == 0 && + (dir = getwd(dirbuf)) == NULL) + return(0); + break; + } + } + } + } + strcpy(ucbpath, dir); + strcpy(ucblib, dir); + if((len = strlen(dir)) < LIBLEN || strcmp(&dir[len - LIBLEN], lib) + != 0) + strcat(ucblib, lib); + else + ucbpath[len - LIBLEN] = 0; + ucbalternate = (strcmp(ucbpath, UCBSTANDARD) != 0); +#ifdef EBUG + fprintf(stderr, "ucbwhich: alt=%d path=%s lib=%s\n", ucbalternate, + ucbpath, ucblib); +#endif EBUG + return(ucbalternate); +} +#endif CONFIGDIR +RAZZLE!DAZZLE +fi # End ucbwhich.c +if test -f ucbwhich.h +then + echo shar: will not overwrite existing file "'ucbwhich.h'" +else +echo 'x - ucbwhich.h' +cat << \RAZZLE!DAZZLE > ucbwhich.h +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +/* + * SCCSid = "@(#)ucbwhich.h 2.2 10/24/89" + */ + +#define UCBMAXPATHLEN 128 +#define UCBSTANDARD "/usr/ucb" + +extern char ucblib[]; +extern int ucbalternate; +extern char ucbpath[]; +RAZZLE!DAZZLE +fi # End ucbwhich.h +if test -f macps.config +then + echo shar: will not overwrite existing file "'macps.config'" +else +echo 'x - macps.config' +cat << \RAZZLE!DAZZLE > macps.config +# This is the config file read by macps. +# SCCSid = "@(#)macps.config 2.2 10/24/89" +# +# Each line is composed of the %%IncludeProcSet id, a tab and the file +# containing the ProcSet. If the file doesn't begin with a slash, the +# the name is taken to be relative to the directory in which this config file +# resides. + +"(AppleDict md)" 65 0 ProcSets/LaserPrep4.0 +"(AppleDict md)" 66 ProcSets/LaserPrep5.0 +"(AppleDict md)" 67 0 ProcSets/LaserPrep5.1 +"(AppleDict md)" 68 0 ProcSets/LaserPrep5.2 +"(AppleDict md)" 70 0 ProcSets/LaserPrep6.0 +RAZZLE!DAZZLE +fi # End macps.config +if test -f macps.1 +then + echo shar: will not overwrite existing file "'macps.1'" +else +echo 'x - macps.1' +cat << \RAZZLE!DAZZLE > macps.1 +.\" SCCSid = "@(#)macps.1 2.2 10/24/89" +.TH MACPS 1 "24 Oct 1989" +.UC 4 +.SH NAME +macps \- print Macintosh-created PostScript file on Unix +.SH SYNOPSIS +.B macps +[ +\-c copies +] +[ +\-d directory +] +[ +\-r +] +[ +file +] +.SH DESCRIPTION +.I Macps +takes the command-line file (or the standard input if no filename is given), +and prepends the appropriate Macintosh LaserPrep file (e.g., those generated +by +.IR prepfix (1)). +The standard output can then be directed to a PostScript printer, via +.IR lpr (1), +for example. +.PP +The input PostScript file is generated on a Macintosh by typing (and holding) +Command-F immediately after clicking the OK button of the LaserWriter printer +dialog box. +Another dialog will appear, confirming that a file named ``PostScript'' is +being created (the Command-F keys can be released now). +.PP +Depending on the application, the created PostScript file can be found in the +System Folder, the application folder or the top level of the disk. +This file can then be uploaded via some file transfer program, such as +MacTerminal/VersaTerm and +.IR macget (1) +or MacKermit/Red Ryder and +.IR kermit (1). +.PP +Normally, you would specify the number of copies in the LaserWriter +print dialog box on the Macintosh. +However, you can override that after uploading the PostScript file by +specifying the +.B \-c +option, followed by the number of copies. +.PP +The file +.B macps.config +specifies the mapping between the internal LaserPrep name and the actual +file it resides in. +This file is normally located in the lib subdirectory from which +.I macps +was called from. +The +.B \-d +option allow you to specify an alternate directory in which the +.B macps.config +file can be found. +.PP +Most Macintosh applications produce normal ASCII PostScript files. +However, some graphics programs that manipulate gray-scale images will +produce PostScript files that contain 8-bit binary data. +Since +.I macps +normally converts this binary data, these PostScript file will not work +properly. +The +.B \-r +(raw) option suppresses this binary conversion. +(Note: Depending on how the printer is physically connected, it may not be +able to handle 8-bit binary data properly, and results may be disappointing.) +.SH FILES +.TP "\w'lib/macps.config 'u" +lib/macps.config +maps ProcSet ids to LaserPrep filenames +.SH "SEE ALSO" +lpr(1), macget(1), kermit(1), prepfix(1) +.SH BUGS +.I Macps +only works with version 4.0 and up of the Macintosh LaserPrep files. +Because of the way bit smoothing is implimented by the LaserWriter driver, +some PostScript printers other than the Apple LaserWriters may not be able to +bit smooth. +RAZZLE!DAZZLE +fi # End macps.1 +if test -f prepfix.1 +then + echo shar: will not overwrite existing file "'prepfix.1'" +else +echo 'x - prepfix.1' +cat << \RAZZLE!DAZZLE > prepfix.1 +.\" SCCSid = "@(#)prepfix.1 2.2 10/24/89" +.TH PREPFIX 1 "24 Oct 1989" +.UC 4 +.SH NAME +prepfix \- converts Apple LaserPrep files to form useable on Unix +.SH SYNOPSIS +.B prepfix +[ +\-l +printer_name1 +[ +\-l +printer_name2 +]... +] +[ +file +] +.SH DESCRIPTION +.I Prepfix +takes the command-line LaserPrep file (or the standard input if no filename is +given), and converts it into a form that is useable on Unix with the +.IR macps (1), +and is even electronically mailable. +.PP +To use +.IR prepfix , +create the input LaserPrep file on a Macintosh by opening an empty +document in some application, selecting +.B Print +>From the +.B File +menu and then typing (and holding) Command-K immediately after clicking the +.B OK +button in the LaserWriter print dialog box. +Another dialog will appear, confirming that a file named ``PostScript'' is +being created (the Command-K keys can be released now). +.PP +Depending on the application, the created PostScript file can be found in the +System Folder, the application folder or the top level of the disk. +This file can then be uploaded via some file transfer program, such as +MacTerminal/VersaTerm and +.IR macget (1) +or MacKermit/Red Ryder and +.IR kermit (1). +.PP +Normally, only Apple LaserWriters can take advantage of the bit smoothing +feature of the LaserPrep file. +However, other PostScript laser printer using the Motorola 680x0 processor +and the Adobe PostScript firmware can be made to do bit smoothing by +specifying for each printer the +.B \-l +option and the printer's name, as returned by the PostScript +.B product +command in +.B statusdict +(remember to quote the printer name if it contains blanks). +The resulting LaserPrep file will be modified so that for LaserWriters and for +printers specified in the +.B \-l +option, bit smoothing will be allowed (smoothing must still be selected in the +Print Dialog box when saving the PostScript to disk). +More than one printer name can be specified using additional +.B \-l +and printer name pairs. +.SH "SEE ALSO" +macps(1), macget(1), kermit(1) +.SH BUGS +.I Prepfix +only works with version 4.0 and up of the Macintosh LaserPrep files. +RAZZLE!DAZZLE +fi # End prepfix.1 +echo '***** End of' macps.shar '*****' +exit + diff --git a/contrib/MacPS/macps.1 b/contrib/MacPS/macps.1 new file mode 100644 index 0000000..17db230 --- /dev/null +++ b/contrib/MacPS/macps.1 @@ -0,0 +1,87 @@ +.\" SCCSid = "@(#)macps.1 2.2 10/24/89" +.TH MACPS 1 "24 Oct 1989" +.UC 4 +.SH NAME +macps \- print Macintosh-created PostScript file on Unix +.SH SYNOPSIS +.B macps +[ +\-c copies +] +[ +\-d directory +] +[ +\-r +] +[ +file +] +.SH DESCRIPTION +.I Macps +takes the command-line file (or the standard input if no filename is given), +and prepends the appropriate Macintosh LaserPrep file (e.g., those generated +by +.IR prepfix (1)). +The standard output can then be directed to a PostScript printer, via +.IR lpr (1), +for example. +.PP +The input PostScript file is generated on a Macintosh by typing (and holding) +Command-F immediately after clicking the OK button of the LaserWriter printer +dialog box. +Another dialog will appear, confirming that a file named ``PostScript'' is +being created (the Command-F keys can be released now). +.PP +Depending on the application, the created PostScript file can be found in the +System Folder, the application folder or the top level of the disk. +This file can then be uploaded via some file transfer program, such as +MacTerminal/VersaTerm and +.IR macget (1) +or MacKermit/Red Ryder and +.IR kermit (1). +.PP +Normally, you would specify the number of copies in the LaserWriter +print dialog box on the Macintosh. +However, you can override that after uploading the PostScript file by +specifying the +.B \-c +option, followed by the number of copies. +.PP +The file +.B macps.config +specifies the mapping between the internal LaserPrep name and the actual +file it resides in. +This file is normally located in the lib subdirectory from which +.I macps +was called from. +The +.B \-d +option allow you to specify an alternate directory in which the +.B macps.config +file can be found. +.PP +Most Macintosh applications produce normal ASCII PostScript files. +However, some graphics programs that manipulate gray-scale images will +produce PostScript files that contain 8-bit binary data. +Since +.I macps +normally converts this binary data, these PostScript file will not work +properly. +The +.B \-r +(raw) option suppresses this binary conversion. +(Note: Depending on how the printer is physically connected, it may not be +able to handle 8-bit binary data properly, and results may be disappointing.) +.SH FILES +.TP "\w'lib/macps.config 'u" +lib/macps.config +maps ProcSet ids to LaserPrep filenames +.SH "SEE ALSO" +lpr(1), macget(1), kermit(1), prepfix(1) +.SH BUGS +.I Macps +only works with version 4.0 and up of the Macintosh LaserPrep files. +Because of the way bit smoothing is implimented by the LaserWriter driver, +some PostScript printers other than the Apple LaserWriters may not be able to +bit smooth. diff --git a/contrib/MacPS/macps.c b/contrib/MacPS/macps.c new file mode 100644 index 0000000..5eaefd7 --- /dev/null +++ b/contrib/MacPS/macps.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef lint +static char *SCCSid = "@(#)macps.c 2.2 10/25/89"; +#ifdef ADOBE +static char *SCCSid2 = "Modified 6/27/89 by jaffe@rutgers to include support for Transcript"; +#endif ADOBE +#endif lint + +#include +#include +#ifdef SYSV +#include +#else SYSV +#include +#endif SYSV +#include +#include +#include "str.h" +#include "ucbwhich.h" +#ifdef ADOBE +#include +#endif ADOBE + +#define CONFIG "macps.config" +#ifdef SYSV +#define index strchr +#define rindex strrchr +#endif SYSV + +#ifdef ADOBE +#define debugp(x) {fprintf x ; (void) fflush(stderr);} +#endif ADOBE +#ifdef SAVE +char *finale = "clear countdictstack 2 sub{end}repeat macps restore\n"; +char intro[] = "\ +%%! *** Created by macps: %s\ +/macps save def\n\ +"; +#else SAVE +char intro[] = "\ +%%! *** Created by macps: %s\ +"; +#endif SAVE +char *myname; +int ncopies = 0; +#ifdef CONFIGDIR +char ucblib[UCBMAXPATHLEN] = CONFIGDIR; +#else CONFIGDIR +int ucbalternate; +char ucbpath[UCBMAXPATHLEN]; +char ucblib[UCBMAXPATHLEN]; +#endif CONFIGDIR + +#ifdef ADOBE +extern char *envget(); +extern char *getenv(); +FILE *jobout; /* for debugging and logging */ +int Debugging = 0; /* debugging boolean */ +int verboselog = 0; /* do we want messages dumped to a log */ +int havejobout = 0; +long starttime; /* for time in messages to log */ +char *outname; +#endif ADOBE + +main(argc, argv) +int argc; +char **argv; +{ + register STR* str; + register char *cp, *pp; + register FILE *fp; + register int i, fd; + char line[BUFSIZ]; + char path[UCBMAXPATHLEN]; + long ltime; + char *ctime(); +#ifdef ADOBE + char *Debug; + char *Verbose; +#endif ADOBE + +#ifndef CONFIGDIR + ucbwhich(*argv); +#endif CONFIGDIR + if(myname = rindex(*argv, '/')) + myname++; + else + myname = *argv; + cp = NULL; + +#ifdef ADOBE + /* set up for debugging */ + Debug = envget("DEBUG"); + if (Debug != NULL) + Debugging = atoi(Debug); + + /* set up stderr to go to the right place ala transcript */ + + setjobout(); + + /* identify myself like most of the postscript filters do */ + + Verbose = envget("VERBOSELOG"); + if (Verbose != NULL) + verboselog = atoi(Verbose); + + if (verboselog) + { + (void) time(&starttime); + if (havejobout) + { + fprintf(jobout, "%s: Started at %s\n", myname, ctime(&starttime)); + (void) fflush(jobout); + } + else + { + fprintf(stderr, "%s: Started at %s\n", myname, ctime(&starttime)); + (void) fflush(stderr); + } + } +#endif ADOBE + + for(argc--, argv++ ; argc > 0 && **argv == '-' ; argc--, argv++) { + switch((*argv)[1]) { + case 'c': /* multiple copies */ + if((*argv)[2]) + ncopies = atoi(&(*argv[2])); + else { + if(argc < 2) + Usage(); /* never returns */ + argc--; + ncopies = atoi(*++argv); + } + if(ncopies <= 0) + Usage(); /* never returns */ + break; + case 'd': /* alternate directory for config file */ + if((*argv)[2]) + cp = &(*argv[2]); + else { + if(argc < 2) + Usage(); /* never returns */ + argc--; + cp = *++argv; + } + strcpy(ucblib, cp); + break; + case 'r': /* raw mode */ + rawmode++; + break; + default: + Usage(); /* never returns */ + } + } + if(argc > 1) + Usage(); /* never returns */ + if(argc == 1 && freopen(*argv, "r", stdin) == NULL) { +#ifdef ADOBE + debugp((jobout, "%s: can't open %s\n", myname, *argv)); +#else ADOBE + fprintf(stderr, "%s: can't open %s\n", myname, *argv); +#endif ADOBE + exit(2); + } + str = STRalloc(); + if(!STRgets(str, stdin)) { +#ifdef ADOBE + debugp((jobout, "%s: Null input\n", myname)); +#else ADOBE + fprintf(stderr, "%s: Null input\n", myname); +#endif ADOBE + exit(2); + } + strcat(ucblib, "/"); + strcpy(path, ucblib); + strcat(path, CONFIG); + if((fp = fopen(path, "r")) == NULL) { +#ifdef ADOBE + debugp((jobout, "%s: Can't open %s\n", myname, path)); +#else ADOBE + fprintf(stderr, "%s: Can't open %s\n", myname, path); +#endif ADOBE + exit(2); + } + time(<ime); + printf(intro, ctime(<ime)); + do { + if(ncopies != 0 && STRheadcompare(str, "userdict /#copies ") + == 0) + continue; + if(STRcompare(str, "%%EOF\n") == 0) { +#ifdef SAVE + if(finale) { + fputs(finale, stdout); + finale = NULL; + } +#endif SAVE + STRputs(str, stdout); + continue; + } + if(STRheadcompare(str, "%%IncludeProcSet:") == 0) { + for(cp = (char *)&str->bufptr[17] ; ; cp++) { + if(!*cp) { +#ifdef ADOBE + debugp((jobout, + "%s: Syntax error on IncludeProcSet line\n", + myname)); +#else ADOBE + fprintf(stderr, + "%s: Syntax error on IncludeProcSet line\n", + myname); +#endif ADOBE + exit(2); + } + if(!isascii(*cp) || !isspace(*cp)) + break; + } + pp = (char *)str->curendptr; + while(--pp >= cp) { + if(!isascii(*pp) || !isspace(*pp)) + break; + *pp = 0; + } + str->curendptr = (unsigned char *)(pp + 1); + fseek(fp, 0L, 0); + for( ; ; ) { + if(!fgets(line, BUFSIZ, fp)) { +#ifdef ADOBE + debugp((jobout, + "%s: Unknown IncludeProcSet %s\n", + myname, cp)); +#else ADOBE + fprintf(stderr, + "%s: Unknown IncludeProcSet %s\n", + myname, cp); +#endif ADOBE + exit(2); + } + if(*line == '#') + continue; + if(pp = index(line, '\n')) { + if(pp == line) + continue; + *pp = 0; + } + if(!(pp = index(line, '\t'))) { +#ifdef ADOBE + debugp((jobout, + "%s: Syntax error in macps.config\n", + myname)); +#else ADOBE + fprintf(stderr, + "%s: Syntax error in macps.config\n", + myname); +#endif ADOBE + exit(2); + } + *pp++ = 0; + if(STRcompareptr(str, cp, line) == 0) + break; + } + if(*pp == '/') + strcpy(path, pp); + else { + strcpy(path, ucblib); + strcat(path, pp); + } + fflush(stdout); + if((fd = open(path, O_RDONLY, 0)) < 0) { +#ifdef ADOBE + debugp((jobout, "%s: Can't open %s\n", myname, + path)); +#else ADOBE + fprintf(stderr, "%s: Can't open %s\n", myname, + path); +#endif ADOBE + exit(2); + } + while((i = read(fd, line, BUFSIZ)) > 0) + write(1, line, i); + close(fd); + continue; + } + STRputs(str, stdout); + if(ncopies > 1 && isascii(*str->bufptr) && + isdigit(*str->bufptr)) { + cp = (char *)str->bufptr; + while(cp < (char *)str->curendptr && isascii(*cp) + && isdigit(*cp)) + cp++; + if((char *)str->curendptr - cp == 4 && + STRcompareptr(str, cp, " mf\n") == 0) { + printf("userdict /#copies %d put\n", ncopies); + ncopies = -1; + } + } + } while(STRgets(str, stdin)); +#ifdef SAVE + if(finale) + fputs(finale, stdout); +#endif SAVE + exit(0); +} + +Usage() +{ + fputs("Usage: macps [-c #] [-d directory] [-r] [file]\n", stderr); + exit(2); +} + +#ifdef ADOBE +setjobout() +{ +/* So that all messages will go to someplace that is permanent. This is to + defeat Sun 4.0's lpd that uses a temporary file for error messages + (stderr) that disappears as soon as the printjob stops */ + + /* get jobout from environment if there, otherwise use stderr */ + if (((outname = envget("JOBOUTPUT")) == NULL) + || ((jobout = fopen(outname,"a")) == NULL)) { + debugp((stderr,"%s: Failure opening log file (%s)\n", myname, outname)); + jobout = stderr; + } + else havejobout = 1; +} + + +/* envget is a getenv + * if the variable is not present in the environment or + * it has the null string as value envget returns NULL + * otherwise it returns the value from the environment + */ + +char *envget(var) +char *var; +{ + register char *val; + if (((val = getenv(var)) == NULL) || (*val == '\0')) + return ((char *) NULL); + else return (val); +} +#endif ADOBE diff --git a/contrib/MacPS/macps.config b/contrib/MacPS/macps.config new file mode 100644 index 0000000..5abf4d0 --- /dev/null +++ b/contrib/MacPS/macps.config @@ -0,0 +1,13 @@ +# This is the config file read by macps. +# SCCSid = "@(#)macps.config 2.2 10/24/89" +# +# Each line is composed of the %%IncludeProcSet id, a tab and the file +# containing the ProcSet. If the file doesn't begin with a slash, the +# the name is taken to be relative to the directory in which this config file +# resides. + +"(AppleDict md)" 65 0 ProcSets/LaserPrep4.0 +"(AppleDict md)" 66 ProcSets/LaserPrep5.0 +"(AppleDict md)" 67 0 ProcSets/LaserPrep5.1 +"(AppleDict md)" 68 0 ProcSets/LaserPrep5.2 +"(AppleDict md)" 70 0 ProcSets/LaserPrep6.0 diff --git a/contrib/MacPS/prepfix.1 b/contrib/MacPS/prepfix.1 new file mode 100644 index 0000000..0758635 --- /dev/null +++ b/contrib/MacPS/prepfix.1 @@ -0,0 +1,70 @@ +.\" SCCSid = "@(#)prepfix.1 2.2 10/24/89" +.TH PREPFIX 1 "24 Oct 1989" +.UC 4 +.SH NAME +prepfix \- converts Apple LaserPrep files to form useable on Unix +.SH SYNOPSIS +.B prepfix +[ +\-l +printer_name1 +[ +\-l +printer_name2 +]... +] +[ +file +] +.SH DESCRIPTION +.I Prepfix +takes the command-line LaserPrep file (or the standard input if no filename is +given), and converts it into a form that is useable on Unix with the +.IR macps (1), +and is even electronically mailable. +.PP +To use +.IR prepfix , +create the input LaserPrep file on a Macintosh by opening an empty +document in some application, selecting +.B Print +>From the +.B File +menu and then typing (and holding) Command-K immediately after clicking the +.B OK +button in the LaserWriter print dialog box. +Another dialog will appear, confirming that a file named ``PostScript'' is +being created (the Command-K keys can be released now). +.PP +Depending on the application, the created PostScript file can be found in the +System Folder, the application folder or the top level of the disk. +This file can then be uploaded via some file transfer program, such as +MacTerminal/VersaTerm and +.IR macget (1) +or MacKermit/Red Ryder and +.IR kermit (1). +.PP +Normally, only Apple LaserWriters can take advantage of the bit smoothing +feature of the LaserPrep file. +However, other PostScript laser printer using the Motorola 680x0 processor +and the Adobe PostScript firmware can be made to do bit smoothing by +specifying for each printer the +.B \-l +option and the printer's name, as returned by the PostScript +.B product +command in +.B statusdict +(remember to quote the printer name if it contains blanks). +The resulting LaserPrep file will be modified so that for LaserWriters and for +printers specified in the +.B \-l +option, bit smoothing will be allowed (smoothing must still be selected in the +Print Dialog box when saving the PostScript to disk). +More than one printer name can be specified using additional +.B \-l +and printer name pairs. +.SH "SEE ALSO" +macps(1), macget(1), kermit(1) +.SH BUGS +.I Prepfix +only works with version 4.0 and up of the Macintosh LaserPrep files. diff --git a/contrib/MacPS/prepfix.c b/contrib/MacPS/prepfix.c new file mode 100644 index 0000000..b796890 --- /dev/null +++ b/contrib/MacPS/prepfix.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef lint +static char *SCCSid = "@(#)prepfix.c 2.2 10/25/89"; +#endif lint + +#include +#include +#ifdef SYSV +#include +#else SYSV +#include +#endif SYSV +#include "str.h" + +#define CLEARTOMARK 12 +#define EEXECLEN 80 +#define EXTRA (NZEROLINE * ZEROLINE + CLEARTOMARK) +#define LINELEN 256 +#define NPRODUCTS 32 +#define NZEROLINE 7 +#define ZEROLINE 65 +#ifdef SYSV +#define index strchr +#define rindex strrchr +#endif SYSV + +char exstr[] = "\ +%ck userdict/%s known not and{currentfile eexec}{%d{currentfile read\n\ +pop pop}repeat}ifelse\n\ +"; +char *match(); +char *myname; +int maxproducts = NPRODUCTS; +int nproducts = 0; +char Ok[] = "\ +/Ok{ok{true}{save /Pd statusdict /product get def false 0 1 ProdArr length\n\ +1 sub{Pd exch ProdArr exch get anchorsearch exch pop{pop pop true exit}if}for\n\ +exch restore}ifelse}bind def\n\ +"; +char ProdArr0[] = "/ProdArr [\n"; +char ProdArr1[] = "] def\n"; +char **products; +char tempname[] = "/tmp/prepfixXXXXXX"; + +main(argc, argv) +int argc; +char **argv; +{ + register STR *str; + register FILE *tp; + register int i; + register unsigned char *lp; + char buf[BUFSIZ]; + char *malloc(), *realloc(); + + if(myname = rindex(*argv, '/')) + myname++; + else + myname = *argv; + for(argc--, argv++ ; argc > 0 && **argv == '-' ; argc--, argv++) { + switch((*argv)[1]) { + case 'h': + usage(); + case 'l': + if(nproducts <= 0 && (products = + (char **)malloc(maxproducts*sizeof(char *))) == NULL) { + fprintf(stderr, + "%s: Out of memory creating products array\n", + myname); + exit(1); + } else if(nproducts >= maxproducts - 1 && (products = + (char **)realloc(products, (maxproducts += NPRODUCTS) + * sizeof(char *))) == NULL) { + fprintf(stderr, + "%s: Out of memory expanding products array\n", + myname); + exit(1); + } + if((*argv)[2]) + products[nproducts++] = &(*argv)[2]; + else { + if(argc < 2) { + fprintf(stderr, + "%s: No argument for -l\n", myname); + exit(1); + } + argc--; + argv++; + products[nproducts++] = *argv; + } + break; + } + } + if(argc > 1) + usage(); + if(argc > 0 && freopen(*argv, "r", stdin) == NULL) { + fprintf(stderr, "%s: Can't open %s\n", myname, *argv); + exit(1); + } + mktemp(tempname); + if((tp = fopen(tempname, "w+")) == NULL) { + fprintf(stderr, "%s: Can't create temp file %s\n", + myname, tempname); + exit(1); + } + unlink(tempname); + str = STRalloc(); + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: Null input\n", myname); + exit(1); + } + for( ; ; ) { + if(STRheadcompare(str, "% \251") == 0) { + fputs("% ", tp); + str->bufptr[0] = '('; + str->bufptr[1] = 'C'; + str->bufptr[2] = ')'; + } else if(STRheadcompare(str, "%%BeginProcSet:") == 0) { + STRputs(str, stdout); + fseek(tp, 0L, 0); + while((i = fread(buf, 1, BUFSIZ, tp)) > 0) + fwrite(buf, 1, i, stdout); + fclose(tp); + break; + } + STRputs(str, tp); + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: No BeginProcSet\n", myname); + exit(1); + } + } + while(STRgets(str, stdin)) { + if(nproducts > 0 && STRheadcompare(str, "/ok{") == 0) { + STRputs(str, stdout); + fputs(ProdArr0, stdout); + for(i = 0 ; i < nproducts ; i++) + printf("(%s)\n", products[i]); + fputs(ProdArr1, stdout); + fputs(Ok, stdout); + continue; + } else if(STRmatch(str, "setdefaulttimeouts") + || STRmatch(str, "setsccinteractive")) + continue; + else if(STRmatch(str, "/stretch") && STRmatch(str, "eexec")) { + eexec("stretch", str); + continue; + } else if(STRmatch(str, "/smooth4") && STRmatch(str, "eexec")) { + eexec("smooth4", str); + continue; + } else if(STRmatch(str, " checkload")) { + checkload(str); + continue; + } else if(STRmatch(str, "(LaserWriter II NT)")) { + while(STRgets(str, stdin) && STRheadcompare(str, "35de") + != 0) + { /* ignore line */ } + while(STRgets(str, stdin) && isxdigit(*str->bufptr)) + { /* ignore line */ } + } else if(lp = STRmatch(str, "scaleby96{ppr")) { + STRputsptr(str, lp, stdout); + continue; + } else if(STRmatch(str, "waittimeout")) + continue; + else if(STRheadcompare(str, "%%EndProcSet") == 0) { + STRputs(str, stdout); + break; + } + STRputs(str, stdout); + } + exit(0); +} + +eexec(name, str) +char *name; +register STR *str; +{ + register int len; + + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: EOF during reading eexec\n", myname); + exit(1); + } + len = (str->curendptr - str->bufptr) - 1; + printf(exstr, nproducts > 0 ? 'O' : 'o', name, len + (len / EEXECLEN) + + (len % EEXECLEN ? 1 : 0) + EXTRA); + spliteexec(str); +} + +checkload(str) +register STR *str; +{ + if(nproducts > 0) + *str->bufptr = 'O'; + STRputs(str, stdout); + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: EOF during reading eexec\n", myname); + exit(1); + } + spliteexec(str); +} + +spliteexec(str) +register STR *str; +{ + register int len; + register unsigned char *bp; + + bp = str->bufptr; + len = (str->curendptr - bp) - 1; + while(len >= 80) { + fwrite(bp, 80, 1, stdout); + putchar('\n'); + bp += 80; + len -= 80; + } + if(len > 0) { + fwrite(bp, len, 1, stdout); + putchar('\n'); + } + for( ; ; ) { + if(!STRgets(str, stdin)) { + fprintf(stderr, "%s: EOF reached before cleartomark\n", + myname); + exit(1); + } + STRputs(str, stdout); + if(STRheadcompare(str, "cleartomark") == 0) + return; + } +} + +usage() +{ + fprintf(stderr, + "Usage: %s [-l product_name1 [-l product_name2]...] [file]\n", + myname); + fprintf(stderr, " %s -help\n", myname); + exit(1); +} diff --git a/contrib/MacPS/str.h b/contrib/MacPS/str.h new file mode 100644 index 0000000..54e02c3 --- /dev/null +++ b/contrib/MacPS/str.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +/* + * SCCSid = "@(#)str.h 2.2 10/24/89" + */ + +#define STRSIZEDELTA 1024 +#define STRSIZE 1024 + +#define STRcompare(str,fp) STRcompareptr((str), (str)->bufptr, (fp)) +#define STRheadcompare(str,fp) STRheadcmpptr((str), (str)->bufptr, (fp)) +#define STRputs(str,fp) STRputsptr((str), (str)->bufptr, (fp)) + +typedef struct { + unsigned char *bufptr; + unsigned char *curendptr; + unsigned char *realendptr; +} STR; + +extern int rawmode; + +STR *STRalloc(); +int STRcompareptr(); +int STRfree(); +int STRgets(); +int STRheadcmpptr(); +unsigned char *STRmatch(); +int STRputsptr(); diff --git a/contrib/MacPS/ucbwhich.c b/contrib/MacPS/ucbwhich.c new file mode 100644 index 0000000..4dc316d --- /dev/null +++ b/contrib/MacPS/ucbwhich.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef CONFIGDIR +#ifndef lint +static char *SCCSid = "@(#)ucbwhich.c 2.2 10/24/89"; +#endif lint + +#include +#include +#include +#include "ucbwhich.h" + +#define F_OK 0 /* does file exist */ +#define X_OK 1 /* is it executable by caller */ +#define W_OK 2 /* writable by caller */ +#define R_OK 4 /* readable by caller */ + +#define LIBLEN 4 +#ifdef SYSV +#define index strchr +#define rindex strrchr +#endif SYSV + +static char lib[] = "/lib"; + +char ucblib[UCBMAXPATHLEN]; +int ucbalternate = 0; +char ucbpath[UCBMAXPATHLEN]; + +ucbwhich(str) +char *str; +{ + register char *dir, *name, *cp, *tp; + register int len; + char dirbuf[UCBMAXPATHLEN], namebuf[UCBMAXPATHLEN]; + struct stat sbuf; + char *index(), *rindex(), *getwd(), *getenv(); + + strcpy(name = namebuf, str); + if(*name == '/') /* absolute pathname */ + *(rindex(dir = name, '/')) = 0 ; /* remove tail */ + else { + if(cp = index(name, '/')) { /* relative pathname */ + if((dir = getwd(dirbuf)) == NULL) + return(0); + /* if any errors occurs assume standard version */ + *cp++ = 0; + for( ; ; ) { + if(*name != 0) { /* multiple slashes */ + if(strcmp(name, "..") == 0) { + /* parent directory */ + if((tp = rindex(dir, '/')) == + NULL) + return(0); + if(tp == dir) + tp++; + /* root directory */ + *tp = 0; + /* remove last component */ + } else if(strcmp(name, ".") != 0) { + /* subdirectory */ + strcat(dir, "/"); + strcat(dir, name); + } + } + name = cp; + if((cp = index(name, '/')) == NULL) break; + /* ignore last component */ + *cp++ = 0; + } + } else { /* look through $PATH variable */ + if((tp = getenv("PATH")) == NULL) + return(0); + for(name = namebuf ; ; ) { + if(*tp == 0) + return(0); + else if(*tp == ':') + tp++; + if((cp = index(tp, ':')) == NULL) + cp = tp + strlen(tp); + /* positioned on null */ + for(dir = dirbuf ; tp < cp ; ) + *dir++ = *tp++; + *dir = 0; + strcpy(name, dir = dirbuf); + strcat(name, "/"); + strcat(name, str); + if(stat(name, &sbuf) < 0 || (sbuf.st_mode & + S_IFMT) != S_IFREG) + continue; + if(access(name, X_OK) == 0) { + if(strcmp(dir, ".") == 0 && + (dir = getwd(dirbuf)) == NULL) + return(0); + break; + } + } + } + } + strcpy(ucbpath, dir); + strcpy(ucblib, dir); + if((len = strlen(dir)) < LIBLEN || strcmp(&dir[len - LIBLEN], lib) + != 0) + strcat(ucblib, lib); + else + ucbpath[len - LIBLEN] = 0; + ucbalternate = (strcmp(ucbpath, UCBSTANDARD) != 0); +#ifdef EBUG + fprintf(stderr, "ucbwhich: alt=%d path=%s lib=%s\n", ucbalternate, + ucbpath, ucblib); +#endif EBUG + return(ucbalternate); +} +#endif CONFIGDIR diff --git a/contrib/MacPS/ucbwhich.h b/contrib/MacPS/ucbwhich.h new file mode 100644 index 0000000..676feaa --- /dev/null +++ b/contrib/MacPS/ucbwhich.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 1988, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Serices, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +/* + * SCCSid = "@(#)ucbwhich.h 2.2 10/24/89" + */ + +#define UCBMAXPATHLEN 128 +#define UCBSTANDARD "/usr/ucb" + +extern char ucblib[]; +extern int ucbalternate; +extern char ucbpath[]; diff --git a/contrib/Makefile.m4 b/contrib/Makefile.m4 new file mode 100644 index 0000000..b260737 --- /dev/null +++ b/contrib/Makefile.m4 @@ -0,0 +1,82 @@ +CFLAGS=cflags() specialcflags() +I=includedir() +O= + +# Make sure to define needgetopt if your system doesnt have it +GETOPT=ifdef([needgetopt],[att_getopt.o]) + +# for other libraries (like BSD on hpux) +SLIB=libspecial() + +CAPLIB=libcap() +AFPLIB=libafp() +UDESTDIR=capdestdir() +SDESTDIR=capsrvrdestdir() +RENAMEFLAG=-DLWRENAMEFILE=lwrenamefile() + +ifdef([useatis],[],[# ])ATISPROGS=snitch + +SRVR=lwrename printqueue ${ATISPROGS} aufsmkusr aufsmkkey +USER=cvt2apple cvt2cap +PROGS= ${USER} ${SRVR} + +all: ${PROGS} + +snitch: snitch.o ${O} ${GETOPT} + ${CC} ${LFLAGS} -o snitch snitch.o ${GETOPT} ${O} ${CAPLIB} ${SLIB} + +cvt2apple: cvt2apple.o ${O} + ${CC} ${LFLAGS} -o cvt2apple cvt2apple.o ${O} ${SLIB} + +cvt2cap: cvt2cap.o ${O} + ${CC} ${LFLAGS} -o cvt2cap cvt2cap.o ${O} ${SLIB} + +lwrename: lwrename.o + ${CC} ${LFLAGS} -o lwrename lwrename.o ${O} ${CAPLIB} ${SLIB} + +lwrename.o: lwrename.c + ${CC} ${CFLAGS} ${RENAMEFLAG} -c lwrename.c + +printqueue: printqueue.o + ${CC} ${LFLAGS} -o printqueue printqueue.o ${O} ${CAPLIB} ${AFPLIB} ${SLIB} + +aufsmkusr: aufsmkusr.o + ${CC} ${LFLAGS} -o aufsmkusr aufsmkusr.o ${O} ${AFPLIB} ${SLIB} + +aufsmkkey: aufsmkkey.o + ${CC} ${LFLAGS} -o aufsmkkey aufsmkkey.o ${O} ${AFPLIB} ${SLIB} + +att_getopt.c: + ln -s ../extras/att_getopt.c + +install: ${PROGS} + -strip ${PROGS} + ifdef([sysvinstall],[install -f ${UDESTDIR} ${USER}], + [${INSTALLER} ${USER} ${UDESTDIR}]) + ifdef([sysvinstall],[install -f ${SDESTDIR} ${SRVR}], + [${INSTALLER} ${SRVR} ${SDESTDIR}]) + +clean: + -rm -f ${PROGS} *.o core make.log err att_getopt.c *~ + -(cd AppManager; make clean) + -(cd AsyncATalk; make clean) + -(cd AufsTools; make clean) + -(cd MacPS; make clean) + -(cd Messages; make clean) + -(cd Timelord; make clean) + +spotless: + -rm -f ${PROGS} *.o *.orig core make.log err att_getopt.c *~ + -rm -f Makefile makefile + -(cd AppManager; make spotless) + -(cd AsyncATalk; make spotless) + -(cd AufsTools; make spotless) + -(cd MacPS; make spotless) + -(cd Messages; make spotless) + -(cd Timelord; make spotless) + +cleanexe: + -rm -f ${PROGS} + +dist: + @cat todist diff --git a/contrib/Messages/Makefile b/contrib/Messages/Makefile new file mode 100644 index 0000000..33d8ea4 --- /dev/null +++ b/contrib/Messages/Makefile @@ -0,0 +1,47 @@ +# +# Macintosh Messages for UNIX +# +# djh@munnari.OZ.AU, 08/05/90 +# + +LDFLAGS = -lcap + +all: macwho messages macuser + +macwho: macwho.o notify.o + cc -o macwho macwho.o notify.o ${LDFLAGS} + +messages: messages.o notify.o + cc -o messages messages.o notify.o ${LDFLAGS} + /bin/rm -f macto macwall macmail macwrite + ln messages macto + ln messages macwall + ln messages macmail + ln messages macwrite + /bin/rm -f messages + +macuser: macuser.c notify.h + cc -o macuser macuser.c ${LDFLAGS} + +macwho.o: macwho.c notify.h + cc -c macwho.c + +messages.o: messages.c notify.h + cc -c messages.c + +notify.o: notify.c notify.h + cc -c notify.c + +install: + cp macwho macuser macto /usr/local/cap + (cd /usr/local/cap;ln macto macwall;ln macto macmail;ln macto macwrite) + +shar: + /usr/local/bin/shar README Makefile *.h *.c *.1l dot.forward \ + messages.hqx > messages.1.4.shar + +clean: + /bin/rm -f *.o core macto macwrite macwall macmail macuser macwho + +spotless: + /bin/rm -f *.o *.orig core macto macwrite macwall macmail macuser macwho diff --git a/contrib/Messages/README b/contrib/Messages/README new file mode 100644 index 0000000..311ad9b --- /dev/null +++ b/contrib/Messages/README @@ -0,0 +1,62 @@ +Introduction: +------------- + +Messages is a combination of UNIX and Macintosh programs designed to make +it easy to communicate short messages (not email) between Mac and UNIX users. + +The shar files includes C source for use with the Columbia AppleTalk +Package (CAP) on UNIX and a StuffIt/BinHex file containing the mac components. + +For more information on the UNIX programs, refer to the *.1l manual entries. + +Macintosh files in messages.hqx: +-------------------------------- + +'Messages' is a Control Panel document that receives and displays short +messages. 'Messages DA' is used to lookup users, edit and send messages. +Together, they provide a similar functionality to the UNIX programs "wall", +"write" and "biff" for Macintosh users (something akin to "biff" is +implemented in the UNIX version of the DA). + +The messages system was implemented in two parts to allow the use of +'Messages DA' to be restricted in a student environment. + +Usage: Messages version 1.6 + +Copy the file into your System Folder and reboot, a mailbox ICON at INIT +time indicates successful installation. If you don't want to receive a +particular type of message, set this with the Control Panel. At boot time, +the cdev attempts to NBP register the Chooser User Name as type 'macUser' +(NB: this conflicts with MacServe but I suspect usage of this is fast +approaching nil so I don't really care). If this name is already in use +then it generates a warning message to both users and tries 'User-x', +where x is 0-9, A-Z until successful. If no Chooser name is set, then it +registers 'nobody', 'nobody-0' etc. (no warning messages for this case) +and politely requests that the user enter a Chooser name. Messages will be +received but not displayed for a short period after a reboot. + +When a message is received, it is displayed in a scrolling window dialog box +together with an ICON, time of the message and an OK button. If the message +is not acknowledged within 60 seconds the dialog box goes away. Any mouse +movement or arrival of a new message will re-display the original dialog +box (this is different behaviour to the earlier version to be more +compatible with the aims of screensavers). Up to 16 messages can be queued, +after which, as many message headers are saved as possible. + + +Usage: Messages DA version 1.2 + +Use Font/DA Mover to install the DA, and, if required, use ResEdit to +edit the file 'myICON' and copy it to the System Folder. Run the DA, if +necessary select a zone, choose a user (shift click for multiple users) or +check the 'Broadcast' button. If you have 'myICON' installed (the 'Send +ICON' button isn't grey) you can also check this button. Type a message in +the text window, use CR for new lines and Enter or OK to send it (you can +also double click a single user name for the same result). If you send a +message across zones, then your zone name is sent with your message. + +BUGS: + 1. messages are limited to about 550 bytes less 128 for the ICON. + 2. it should have a way of receiving messages without requiring + use of 'Messages', probably in version 2.0. + diff --git a/contrib/Messages/dot.forward b/contrib/Messages/dot.forward new file mode 100644 index 0000000..2315404 --- /dev/null +++ b/contrib/Messages/dot.forward @@ -0,0 +1 @@ +\djh, "|/tute/staff/djh/bin/macmail djh" diff --git a/contrib/Messages/macto.1l b/contrib/Messages/macto.1l new file mode 100644 index 0000000..f081809 --- /dev/null +++ b/contrib/Messages/macto.1l @@ -0,0 +1,71 @@ +.\" troff -man +.TH MESSAGES 1L Mac/UNIX_Messages CAP +.SH NAME +macto, macwrite, macwall, macmail \- Macintosh Messages for UNIX users. +.SH SYNOPSIS +.BI macto +user[@zone] message +.br +.BI macwrite +user[@zone] ... +.br +.BI macwall +@zone ... +.br +.BI macmail +user[@zone] ... +.SH DESCRIPTION +.BI macto +provides the equivalent of +.BI to(1) +for sending one line messages to users on Macintoshes. Usage is the same as +for the +.BI to(1) +command. +.PP +.BI macwrite +provides the equivalent of +.BI write(1) +for sending multiple line messages to users on Macintoshes. Usage is the same as +for the +.BI write(1) +command. +.PP +.BI macwall +provides the equivalent of +.BI wall(1) +for broadcast of messages to users on Macintoshes. Multiple AppleTalk +zones may be specified. +.PP +.BI macmail +provides the equivalent of +.BI biff(1) +for informing Macintosh users that mail has arrived for them on a UNIX machine +and for passing the important headers of that message to the users' Macintosh. +.PP +These commands provide equivalent UNIX functionality to +"Messages DA" which is used on a Macintosh to lookup users, edit and +send messages. +"Messages" is a Macintosh Control Panel document that receives and +displays short messages. The equivalent UNIX command is macuser(1l). +.SH FILES +~/.myicon \- the resource fork of a Mac file with an ICON (ID of 1) to be +sent along with the message (just a little bit of magic here). +.SH SEE ALSO +CAP (Columbia AppleTalk Package) +.br +macwho(1l) \- list macUsers, see also who(1) +.br +macuser(1l) \- Register a Unix user on the AppleTalk network +.SH DIAGNOSTICS +Incorrect invocation diagnostics, aka usage: ... +.SH BUGS +1. The messages system was implemented in two parts on the Macintosh +to allow the use of "Messages DA" to be restricted in a student environment. +.br +2. messages are limited to about 550 bytes less 128 for any included ICON. +.br +3. If you want to send a message between UNIX users, the syntax is somewhat +painful. You can use user@host@zone or user%host. +.SH AUTHOR +djh\@munnari.OZ.AU, May 1990. Updates via FTP from munnari.OZ.AU diff --git a/contrib/Messages/macuser.1l b/contrib/Messages/macuser.1l new file mode 100644 index 0000000..84ca672 --- /dev/null +++ b/contrib/Messages/macuser.1l @@ -0,0 +1,53 @@ +.\" troff -man +.TH MACUSER 1L Macintosh/UNIX_Messages CAP +.SH NAME +macuser \- Macintosh Messages for UNIX users. +.SH SYNOPSIS +.BI macuser +[-dadpn] [userlist ...] +.SH DESCRIPTION +If run by the super user (from 'rc.local' or 'start-cap-servers') +.BI macuser +registers each of its arguments on the AppleTalk network as +"user@host:macUser@*". Ordinary users cannot provide a user list +- the user login name is used instead. +.BI macuser +then listens for messages sent from Macintosh +or UNIX users and forwards them to the designated recipient (writing to +the users' terminal if logged in, or by sending email if not). +.PP +.BI macuser +provides equivalent UNIX functionality to the +Macintosh Control Panel document "Messages" which receives and displays +short messages on Macs. +"Messages DA" is used to lookup users, edit and send messages from +the Macintosh. UNIX equivalence is provided by the commands listed in SEE +ALSO. +.PP +.BI macuser +is a messaging system ONLY, it +is not intended to monitor login activity and cannot be used to check that +a UNIX user is actually present. +.SH FILES +None. +.SH SEE ALSO +CAP (Columbia AppleTalk Package) +.br +macwho(1l) \- list macUsers, see also who(1) +.br +macwall(1l) \- send stdin message to all macUsers, see also wall(1) +.br +macwrite(1l) \- send stdin message to macUser, see also write(1) +.br +macto(1l) \- send argument list message to macUser, see also to(1) +.br +macmail(1l) \- notify macUser of UNIX mail arrival, see also biff(1) +.SH DIAGNOSTICS +"No permission to register name" if run by a non super-user with a user list. +.SH BUGS +1. The messages system was implemented in two parts on the Macintosh +to allow the use of "Messages DA" to be restricted in a student environment. +.br +2. Messages are limited to about 550 bytes less 128 for any included ICON. +.SH AUTHOR +djh@munnari.OZ.AU, May 1990. Updates via FTP from munnari.OZ.AU diff --git a/contrib/Messages/macuser.c b/contrib/Messages/macuser.c new file mode 100644 index 0000000..60c3361 --- /dev/null +++ b/contrib/Messages/macuser.c @@ -0,0 +1,431 @@ +/* + * $Date: 1993/08/05 15:54:23 $ + * $Header: /mac/src/cap60/contrib/Messages/RCS/macuser.c,v 2.3 1993/08/05 15:54:23 djh Rel djh $ + * $Log: macuser.c,v $ + * Revision 2.3 1993/08/05 15:54:23 djh + * change wait handling + * + * Revision 2.2 91/03/14 14:20:25 djh + * Fall back to log(), don't write non-ascii on a terminal, + * handle Sys V utmp files. + * + * Revision 1.2 91/03/04 18:20:11 djh + * Add USER_PROCESS test to fix SysV style utmp, + * make sure we don't write non-ascii characters to the terminal. + * John Sellens + * + * Revision 1.1 91/01/10 01:10:48 djh + * Initial revision + * + * + * djh@munnari.OZ.AU, 05/05/90 + * Copyright (c) 1991, The University of Melbourne + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of Melbourne makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * + * NBP Register each of the argument list as "user@host:macUser@*", + * listen for messages sent from Mac users & forward to UNIX users + * (write to terminal if logged in, send email if not). + * + * 02/12/90 Fix unregister/biff bugs, add some security, make debug work. + */ + +#include "notify.h" + +#include +#include +#include +#include + +#define USERNAMELEN 34 +#define DEV_PATH "/dev/" +#define DEV_TTY "/dev/tty" +#define UTMP "/etc/utmp" +#define MAIL "/usr/ucb/mail" +#define LOGF "/usr/tmp/macuserLog" + +struct userlist { /* map socket to username */ + int skt; + EntityName en; + char user[USERNAMELEN]; +}; + +int mailing; +int numusers; +struct userlist userlist[MAXUSERS]; + +char host[MAXHOSTNAMELEN]; +char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + +main(argc, argv) +int argc; +char *argv[]; +{ + int f; + int debug; + int cleanup(); + char *s, **dbgargv; + char logf[MAXPATHLEN]; + char realuser[MAXHOSTNAMELEN]; + char *cp, *index(), *getlogin(); + + signal(SIGHUP, cleanup); + signal(SIGINT, cleanup); + signal(SIGQUIT, cleanup); + signal(SIGTERM, cleanup); + + debug = 0; + if(argc > 1) { + dbgargv = argv; + s = *++dbgargv; + if(s[0] == '-' && s[1] == 'd' && s[2] != '\0') { + debug = 1; + ++argv; --argc; + dbugarg(&s[2]); + } + } + + if(argc >= 2 && getuid() != 0) { + fprintf(stderr, "No permission to register name.\n"); + exit(1); + } + + strcpy(logf, LOGF); + strcpy(realuser, getlogin()); + + if(!debug) { + if(fork()) + exit(0); + for(f = 0; f < 32; f++) + (void) close(f); + (void) open("/dev/null", 0); + (void) dup2(0, 1); + if((f = open(DEV_TTY, 2)) >= 0) { + ioctl(f, TIOCNOTTY, (char *)0); + (void) close(f); + } + + if((f = open(logf, O_WRONLY|O_APPEND|O_CREAT, 0644)) >= 0) { + if(f != 2) { + (void) dup2(f, 2); + (void) close(f); + } + } + } + + abInit(TRUE); + nbpInit(); + + gethostname(host, MAXHOSTNAMELEN); + if((cp = index(host, '.')) != NULL) + *cp = '\0'; + + numusers = 0; + while(--argc > 0 && numusers < MAXUSERS) + makeuser(*++argv); + + if(numusers == 0) + makeuser(realuser); + + if(numusers != 0) + for( ; ; ) + abSleep(400, TRUE); /* 100 seconds */ +} + +int +makeuser(name) +char *name; +{ + EntityName *en; + nbpProto nbpr; + int i, listener(); + NBPTEntry nbpt[NUMNBPENTRY]; + char user[MAXHOSTNAMELEN*2]; + + userlist[numusers].skt = 0; /* dynamic */ + if(DDPOpenSocket(&userlist[numusers].skt, listener) != noErr) { + log("Couldn't get dynamic DDP socket\n"); + return(0); + } + + en = &userlist[numusers].en; + strncpy(user, name, sizeof(user)); + strncpy(userlist[numusers].user, user, USERNAMELEN); + strcat(user, "@"); + strcat(user, host); + strncpy(en->objStr.s, user, (i = sizeof(en->objStr.s))); + en->objStr.s[i-1] = '\0'; /* just in case of long name */ + strncpy(en->typeStr.s, MACUSER, sizeof(en->typeStr.s)); + strncpy(en->zoneStr.s, OURZONE, sizeof(en->zoneStr.s)); + + nbpr.nbpEntityPtr = en; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + nbpr.nbpAddress.skt = userlist[numusers].skt; + nbpr.nbpRetransmitInfo.retransInterval = 3; + nbpr.nbpRetransmitInfo.retransCount = 2; + + NBPRemove(en); + + if(NBPRegister(&nbpr, FALSE) != noErr) { + log("Failed to register: %s\n", user); + DDPCloseSocket(userlist[numusers].skt); + return(0); + } + numusers++; +} + +int +listener(skt, type, pkt, len, addr) +u_char skt; +u_char type; +char *pkt; +int len; +AddrBlock *addr; +{ + char *q; + int i, fd; + long secs; + char timestr[32]; + struct tm *tm, *gmtime(); + + if(type != ddpECHO) { /* drop it */ + log("Bogus packet type: %d\n", type); + return(0); + } + + for(i = 0 ; i < numusers ; i++) + if(userlist[i].skt == skt) + break; + + if(i == numusers) { + log("Couldn't find a user for socket %d\n", skt); + return(0); + } + + if((fd = findUser(userlist[i].user)) >= 0) { + bcopy(pkt+1, &secs, 4); + secs = ntohl(secs); + secs -= TIME_OFFSET; + tm = gmtime(&secs); + sprintf(timestr, " at %02d:%02d %s ...%s\t", + tm->tm_hour, tm->tm_min, + days[tm->tm_wday], + (mailing) ? "\n\n" : "\r\n\r\n"); + if(!mailing) + write(fd, "\007\007\r", 3); + write(fd, "\n", 1); + q = pkt+5; + i = strlen(q); + write(fd, q, i - 4); /* delete original " ..." */ + write(fd, timestr, strlen(timestr)); + q += i + 1; + while(*q != '\0') { + if(*q == '\n' || *q == '\r') { + q++; + if(!mailing) + write(fd, "\r", 1); + write(fd, "\n\t", 2); + continue; + } + if(!isascii(*q)) { + *q = toascii(*q); + write(fd, "M-", 2); + if(!iscntrl(*q)) { + write(fd, q++, 1); + continue; + } + } + if(iscntrl(*q)) { + *q = *q ^ 0100; + write(fd, "^", 1); + write(fd, q++, 1); + continue; + } + write(fd, q++, 1); + } + if(!mailing) + write(fd, "\r", 1); + close(fd); + } +} + +/* + * Search utmp for user details. + * + */ + +int +findUser(name) +char *name; +{ + struct utmp buf; + FILE *fp, *fopen(); + struct stat sbuf; + long now, idle, minmidle; + int idling, off, fd, doOpenTTY(); + char tname[MAXPATHLEN], mostidle[MAXPATHLEN]; + + idling = 0; + mailing = 0; + + if((fp = fopen(UTMP, "r")) == NULL) { + log("Couldn't open utmp file: %s\n", UTMP); + return(-1); + } + + minmidle = now = time(0); + off = strlen(DEV_PATH); + strncpy(tname, DEV_PATH, sizeof(tname)); + while(fread((char *) &buf, sizeof(buf), 1, fp) == 1) { + if(*buf.ut_name == '\0') + continue; +#ifdef USER_PROCESS + if(buf.ut_type != USER_PROCESS) + continue; +#endif USER_PROCESS + if(strncmp(buf.ut_name, name, sizeof(buf.ut_name)) == 0) { + strncpy(tname+off, buf.ut_line, sizeof(tname)-off); + if(stat(tname, &sbuf) == 0) { + if(!(sbuf.st_mode & S_IFCHR)) /* not char */ + continue; + if(!(sbuf.st_mode & 0020)) /* no grp wrt */ + continue; + idle = now - sbuf.st_atime; + if(idle <= minmidle) { + minmidle = idle; + strcpy(mostidle, tname); + } + idling++; + continue; + } + } + } + fclose(fp); + + if(idling) { + if((fd = doOpenTTY(mostidle)) >= 0) + return(fd); + } else { + if((fd = doOpenMail(name)) >= 0) + return(fd); + } + + return(-1); +} + +int +doOpenTTY(tname) +char *tname; +{ + int i, fd, dummy(); + + signal(SIGALRM, dummy); + alarm(2); + if((fd = open(tname, O_WRONLY, 0644)) >= 0) { + if((i = open(DEV_TTY, O_RDWR)) >= 0) { /* disassociate */ + ioctl(i, TIOCNOTTY, 0); + (void) close(i); + } + } + alarm(0); + return(fd); +} + +int +dummy() +{ + /* a dummy */ +} + +int +doOpenMail(name) +char *name; +{ + int funeral(); + int p[2], pid; + + if(pipe(p) < 0) + return(-1); + + signal(SIGCHLD, funeral); + + if((pid = fork()) == 0) { /* in child */ + close(p[1]); + DUP2(p[0], 0); + + execl(MAIL, "mail", "-s", "Macintosh Message", name, 0); + exit(1); /* o, oh */ + } + + /* parent */ + + if(pid == -1) + return(-1); + + close(p[0]); + + mailing = 1; + + return(p[1]); +} + +int +DUP2(a, b) +int a, b; +{ + close(b); + dup(a); + close(a); +} + +int +funeral() +{ + WSTATUS junk; + +#ifdef POSIX + while(waitpid(-1,&junk, WNOHANG) > 0) + ; +#else POSIX +#ifndef NOWAIT3 + while(wait3(&junk, WNOHANG, 0) > 0) + ; +#else NOWAIT3 + wait(&junk); /* assume there is at least one process to wait for */ +#endif NOWAIT3 +#endif POSIX +} + +/* + * Log an error message. + */ + +log(fmt, a1, a2, a3, a4, a5, a6) +char *fmt, *a1, *a2, *a3, *a4, *a5, *a6; +{ + long now; + char *ctime(); + + (void) time(&now); + fprintf(stderr, "%.*s\t", 24-5, ctime(&now)); + fprintf(stderr, fmt, a1, a2, a3, a4, a5, a6); + fflush(stderr); /* especially for SUN's */ +} + +int +cleanup() +{ + int i, j; + + for(i = 0 ; i < numusers ; i++) + NBPRemove(&userlist[i].en); + + exit(0); +} diff --git a/contrib/Messages/macwho.1l b/contrib/Messages/macwho.1l new file mode 100644 index 0000000..bd4b1c0 --- /dev/null +++ b/contrib/Messages/macwho.1l @@ -0,0 +1,40 @@ +.\" troff -man +.TH MACWHO 1L Mac/UNIX_Messages CAP +.SH NAME +macwho \- list macUsers for a network of Macintoshes. +.SH SYNOPSIS +.BI macwho +[zone ...] +.SH DESCRIPTION +.BI macwho +provides the equivalent of +.BI who(1) +for a network of Macintoshes. Usage is the same as +for the +.BI who(1) +command, except that there are no flags. +.PP +Output from +.BI macwho(1l) +gives a line for each user on the AppleTalk network (who has the "Messages" +package installed on a Macintosh, or who is running macuser(1l) under UNIX). +.SH FILES +None. +.SH SEE ALSO +CAP (Columbia AppleTalk Package) +.br +macuser(1l) \- Register a Unix user on the AppleTalk network +.br +macwho(1l) \- list macUsers, see also who(1) +.br +macwall(1l) \- send stdin message to all macUsers, see also wall(1) +.br +macwrite(1l) \- send stdin message to macUser, see also write(1) +.br +macto(1l) \- send argument list message to macUser, see also to(1) +.br +macmail(1l) \- notify macUser of UNIX mail arrival, see also biff(1) +.SH DIAGNOSTICS +None. +.SH AUTHOR +djh\@munnari.OZ.AU, May 1990. Updates via FTP from munnari.OZ.AU diff --git a/contrib/Messages/macwho.c b/contrib/Messages/macwho.c new file mode 100644 index 0000000..90ea549 --- /dev/null +++ b/contrib/Messages/macwho.c @@ -0,0 +1,77 @@ +/* + * $Date: 91/03/14 14:23:37 $ + * $Header: macwho.c,v 2.2 91/03/14 14:23:37 djh Exp $ + * $Log: macwho.c,v $ + * Revision 2.2 91/03/14 14:23:37 djh + * Revision for CAP. + * + * Revision 1.1 91/01/10 01:10:57 djh + * Initial revision + * + * + * djh@munnari.OZ.AU, 03/05/90 + * Copyright (c) 1991, The University of Melbourne + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of Melbourne makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * + * List Chooser Names of Macintoshes running "Messages" + * + */ + +#include "notify.h" + +extern NBPTEntry nbpt[]; + +main(argc, argv) +int argc; +char *argv[]; +{ + int notify(); + int compare(); + int num, i, j, k; + EntityName en; + AddrBlock addr; + char *zonelist[MAXZONES]; + + zonelist[0] = OURZONE; + zonelist[1] = NULL; + + for(i = 0 ; --argc > 0 && i < MAXZONES-1 ; i++) { + zonelist[i] = *++argv; + zonelist[i+1] = NULL; + } + + j = 0; + k = 1; + while(zonelist[j] != NULL) { + if((num = notify(0, "", "", "=", zonelist[j], 0, 1, 6)) > 0) { + qsort((char *) nbpt, num, sizeof(NBPTEntry), compare); + for(i = 1; i <= num ; i++) + if(NBPExtract(nbpt, num, i, &en, &addr) == noErr) { + printf("%3d %-34s[Net %3d.%-3d Node %3d%s%s]\n", + k++, + en.objStr.s, + ntohs(addr.net) / 256, + ntohs(addr.net) % 256, + addr.node, + (*zonelist[j]=='*') ? "" : " ", + (*zonelist[j]=='*') ? "" : zonelist[j]); + /* Sigh: much nicer if we could use en.zoneStr */ + } + } + j++; + } +} + +int +compare(a, b) +NBPTEntry *a, *b; +{ + return(strcmp(a->ent.objStr.s, b->ent.objStr.s)); +} diff --git a/contrib/Messages/messages.c b/contrib/Messages/messages.c new file mode 100644 index 0000000..31c6036 --- /dev/null +++ b/contrib/Messages/messages.c @@ -0,0 +1,204 @@ +/* + * $Date: 91/03/14 14:22:25 $ + * $Header: messages.c,v 2.2 91/03/14 14:22:25 djh Exp $ + * $Log: messages.c,v $ + * Revision 2.2 91/03/14 14:22:25 djh + * Revision for CAP. + * + * Revision 1.1 91/01/10 01:11:03 djh + * Initial revision + * + * + * + * djh@munnari.OZ.AU, 05/05/90 + * Copyright (c) 1991, The University of Melbourne + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of Melbourne makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * + * Send a message to a Macintosh running "Messages" + * or a UNIX user registered with 'macuser'. + * + */ + +#include "notify.h" + +main(argc, argv) +int argc; +char *argv[]; +{ + int i, j; + int msgtype; + char zone[34]; + char user[256]; + char *progname; + char icon[MAXPATHLEN]; + char *userlist[MAXUSERS]; + char message[DDPPKTSIZE]; + char *cp, *index(), *rindex(), *getlogin(); + struct passwd *pp, *getpwuid(), *getpwnam();; + + progname = argv[0]; + if((cp = rindex(argv[0], '/')) != NULL) + progname = ++cp; + + if(strcmp(progname, "macto") == 0) + msgtype = MSGTOARG; + else if(strcmp(progname, "macwrite") == 0) + msgtype = MSGTO; + else if(strcmp(progname, "macwall") == 0) + msgtype = MSGWALL; + else if(strcmp(progname, "macmail") == 0) + msgtype = MSGMAIL; + else { + fprintf(stderr, "%s: Unknown Message Protocol\n", progname); + exit(1); + } + + if(msgtype == MSGTOARG && argc < 3) { + fprintf(stderr, "usage: %s user[@zone] message\n", progname); + exit(1); + } + + if(msgtype != MSGWALL && argc < 2) { + fprintf(stderr, "usage: %s user[@zone] ...\n", progname); + exit(1); + } + + if((cp = getlogin()) == NULL) { + if((pp = getpwuid(getuid())) == NULL) { + fprintf(stderr, "%s: Who are you ??\n", progname); + exit(1); + } else + cp = pp->pw_name; + } + strncpy(user, cp, sizeof(user)); + + icon[0] = '\0'; + if((pp = getpwnam(user)) != NULL) + strncpy(icon, pp->pw_dir, sizeof(icon)); + strcat(icon, "/"); + strcat(icon, ICONFILE); + + userlist[0] = OURZONE; + userlist[1] = NULL; + for(j = 0 ; j < MAXUSERS-1 && --argc > 0 ; j++) { + userlist[j] = *++argv; + userlist[j+1] = NULL; + if(msgtype == MSGTOARG) /* just the first */ + break; + } + + switch (msgtype) { + case MSGMAIL: /* read stdin and present in 'biff' format */ + processMail(message); + break; + + case MSGTOARG: /* get the message from the argument list */ + i = 0; + while(i < DDPPKTSIZE-1 && --argc > 0) { + strncat(message, *++argv, DDPPKTSIZE-i-1); + i = strlen(message); + strncat(message, " ", DDPPKTSIZE-i-1); + i = strlen(message); + } + strncat(message, "\n", DDPPKTSIZE-i-1); + msgtype = MSGTO; + break; + + default: /* get the message from stdin */ + i = 0; + while(i < DDPPKTSIZE-1 + && fgets(message+i, DDPPKTSIZE-i, stdin) != 0) + i = strlen(message); + break; + } + + msgFormat(message); /* format for Mac display */ + + j = 0; + while(j < MAXUSERS && userlist[j] != NULL) { + if((cp = rindex(userlist[j], '@')) != NULL) { + *cp++ = '\0'; + strncpy(zone, cp, sizeof(zone)); + } else + strncpy(zone, OURZONE, sizeof(zone)); + + if((cp = rindex(userlist[j], '%')) != NULL) + *cp = '@'; + + if(notify(msgtype, message, user, + (msgtype==MSGWALL) ? "=":userlist[j],zone,icon,1,3)!=noErr) + fprintf(stderr, "Couldn't deliver to %s\n",userlist[j]); + + j++; + } + return(0); +} + +/* + * act just like biff/comsat + * + */ + +int +processMail(buf) +char *buf; +{ + register char *cp; + register int linecnt, charcnt; + char line[256], *index(); + int inheader, cnt, statr, len; + + /* + * Get the first 7 lines or 500 characters of the new mail + * (whichever comes first). Skip header crap other than + * From, Subject, To, and Date. + * + */ + + linecnt = 7; + charcnt = 500; + inheader = 1; + + while(fgets(line, sizeof(line), stdin) != NULL) { + if(linecnt <= 0 || charcnt <= 0) { + len = strlen(buf); + strncat(buf, "...more...\n", DDPPKTSIZE-len); + return(len); + } + if(strncmp(line, "From ", 5) == 0) + continue; + + if(inheader && (line[0] == ' ' || line[0] == '\t')) + continue; + + cp = index(line, ':'); + if(cp == 0 || (index(line, ' ') && index(line, ' ') < cp)) + inheader = 0; + else + cnt = cp - line; + + if(inheader && + strncmp(line, "Date", cnt) && + strncmp(line, "From", cnt) && + strncmp(line, "Subject", cnt) && + strncmp(line, "To", cnt)) + continue; + + if((cp = index(line, '\n')) != NULL) + *cp = '\0'; + + strcat(line, "\n"); + len = strlen(buf); + strncat(buf, line, DDPPKTSIZE-len); + charcnt -= strlen(line); + linecnt--; + } + return(len); +} diff --git a/contrib/Messages/messages.hqx b/contrib/Messages/messages.hqx new file mode 100644 index 0000000..d64d36c --- /dev/null +++ b/contrib/Messages/messages.hqx @@ -0,0 +1,545 @@ +(This file must be converted with BinHex 4.0) + +:%%ePFh0KCf9c,M%Z-bjcDA3!8dP8)90*9#%!N!4PH3#3"0mZ8dP8)3!%!!"PHA* +-BA8"!*!(!J!)6@9cFf&RCA0[C`#30@0NCACYB@PX)3#N"ABBT!9f(!!!4Ld!N!B +d+J#3"*(H!*!)al3!!!3)#)")Mi"$$JU%4l#K`iF3)5*S8QE1R$"R+Xi43(!-Q6* +ffS4*``C%3'!!!$hd#&)N5C-!8+U-5*08!6XB(KTT3E1Rcjp!J`SG5P5S"Q&N![* +`+!1!$3!k!2J!)#5"LbT0LM"T-T*0dBCJ!NS)#i!#@3YN-C!!eB"%$e8`'0S+N!! +'4S*FHQ!Bb08'aN$E!95Fa%&b,UD3!$-[%,a)!1)0!!P1)-(i`16E!!"BRL"!)4# +!!6-%AZ"!`-p*(F)3F*K`M)'%B`T+9*8pBd!)N!#`!Z48HH4%e`4KNf%!1%-!YjM +G[5q)#Mkm1!!"8RT$8'8#J(4D,kcVJN!+#Ci"A+5l+J'N,*4(CjkEH+'LV(4ml0e +[P`C#69N58)6BYe5$ch-!D1J(J"T#b)C*J3#!))3%!VSK)"`#NL'J'3K+JL!'rIh +c$`#+C,JK)d)%F4i-!5!"6$Q!##%,!L4)%33$33!!34X!r101%2SFJ%%*5!$!J4` +!P"$%!&!)13!6hJ'JJA3JN!!('"ESDF!HB0+a-@9JfaP4(f!P3((NH8NZUFX$pML +*"C4R5+R#!&K)*m58E@l(`CCBG%RNH9NmmJ)24#4""4-r,!9)+U#!m!&j!""`"JE +a%5#G")hfpJ!lMDBL#CjkmT!!a""21"&S5S3DLLJ#LmD(J+6NQ#UT,kCDLZQHQhE +kD5#K(PSH!k@fai#NYX5hkjL3!2MUkL0jlJQ&&%m--@ZY*!a(a3X#5-S)Y*+L3Hf +B1)J!`#5i"-#!#$Ki#`Fr`J!53M$HA[X!#&"Q*jUh+,a*3@m03&)#(Yi#`'Ldf+V +,!l5TM2,%1`N@b`-DEma"akH$&QUVM'F`%4m%NS)`F@m1k&1IM#"!%F-DqRVfJJ+ +p'5#$F!-$`!!E#(bU3#V)J+%BbEVFe'jl%+5LbX5T`-+c*#m-J('!3%$j3hNF9-B +"!L"mFCi6d&J-!02C%@$Ed5ShrA68f9'Gk0@hDXdE0)3"!-0U-VUQ-XDYDHH!#0P +K,%&XCD%*`mB8L!!"$#XSb8"j3E3"JKX#b2`h!'5BJCB#,`63@`#3!%!*"Z-,02k +i$1GGf`!eYKPG(J@9-C!!S00MPhehGK#Sc3$G&+3(!$eiQ`#&%L"EFSB$Zrh3Yb@ +p5D$-C#DqX!+9ZM4JM[$%0i!1mPJfJ!RbF6EJ$26d3N0pmB!)RbLp[IEYD2'iD2p +p!pEfI@VaRQKrIJ1JU%m[&0VrfS!KmG1,52h&ik!paF@lX6qpF2KIm43NK*3K!3B +#-"'+P$!,&TeKE`Nm8ICQ`#*kU3"+8K!#!i3J)aVpJa[aQ9Ia9""#HV'JK,TJ!$G +5YN%JB!edha#Gf+"''"#!3(@0N4%01--"*!J$",Q"`$K3d5`"#%*$!B%"CcaJ)J( +-4%1%!-3C1(#a&'ULA3Qb$`4HX%9qUC!!KC8KJ+2Ui!3Vr)-IK,NE#G``YGJ!aJ` +'J--NF11,,9("6N``3q(Fd4X'X+!kIE4"h&*S!Z6S`J(mJ)1-2J!"c+!R))*JJ(d +iJJD[T%3+pN%$$0U$KLS!`3`"N31FqZL%8DB3IQZ+NqJ+4+3`p@B"kM"6bL#3!#C +6-S!'YR3!RH`%KFc4E!!(G#6,A!BcQBfX0`*`K15H8jY-KL8!rYJ5%j5!KZH!%J# +'S"XQA'F(ZY%b)'lB'!4%!)@0B8)#%%"#)*!!%!`ir+&FF2#!*!$4"LR!`3#`#-3 +EQ!##!%!$%%U)!`+')i40)Nk"!&d4#FiJ!B15!D%[N!"""A@a!$G!bD%),B'"@)4 +*a!R!2Jp!())`dCr1U1'BZJL!20T&X`$`33Q!J0)eB@-I0D3R!&L3!#3!&!!!0p$ +)F3*5!pd`F&-Kd%e4!Y%"@8$4![X3i)a)F--CA`N%*EMLM'J+b2dBji)caJF$[8P +!0J("`M0Ji8&@2H-CZ$T9A5J!&"GYMaNJ+P%5S23!$C8VA5HU!&-BG+i5"*+3!!! +JL4)%JU2fd50)!@!'&X+PT!1"3eVjF3B2[+#VTA'V%55A9mCQ9!iX)L`Srb%&*GJ +($YId!pe!`-d5f-FNNQKUSU!UeF`U3!q644-@4RCCVlB(V,T!J$"J@PBC6(DY[Fe +X!X"aX`5Ke!"DL!m)`QS2`FDSX$%#J@-KF&bf+YF9l4+D,K+J#%9#L3RZ)!!$rJ% +2*hM"0'@%kMPXZ-DTkC5RER#$!6VU"M!J)DTR6)8F*)P%45L1`"mbJ`0U8&X1-IJ +IrJ!!)m"!!KKJ&KEf885D,!aK$%[iJ3rQKi1P'Q&'`#%!B"#!)Y%VaRr3`lfQUH% +0(E-k9E`aMR--L#2XL%FpJX%GTJ@!*H!)Ke+3!+@E"P+`G`5JK,"#!&'BJ*)fck! +&D3S"&BQ5CJcfXe-Ymi(*(&2$N8*J(db8j8Up3B!kGR1P$b`P!'i@#-D3!!%mB!J +(63BJ*M,3(&aJR2FEBS5['HP"'"rJS,i)F#-@L*aM)ql55e$`X6X%C)RG)'%r3MK +&(ph"(0S8'6GDd--rm08"raM!aDAZk6rUNHShr--HU4E$2ql4kRrJ)p9Nq%Fq8Tf +(IqK$#&hGKcKK)H,K2%F$XNX5#*!!!$)@!2M&`B9#RJ)9$"#N3KH'DPCCAA!Y"%! +JALDJ!!U'S!3+S)N#bQBf!%iSAJ4i3!K%B1'e$N!2+42KJ5H15[!!J0)!%1-m%1L +EX`&K)LPN$`#A&S!je-f#hKb!(!%(!*))$Jb$CqI5!d#!ZTRJF'C%R!Y)S,M&%5j +Q&DJl2,Si!#dXV3D&5dF(GGj5IV"-!*N234d+$d%5TQCcG3#!%cV2QTJppQ8PK*P +)%E$2*3"`!CR(`$m$NB$-)q!I!ZKVk[lK5!DQ[43#T#)AKJ)"%,L)LLJ%!HIQ-!% +#K'#%(KJ"`LNM!9R#PdU(!i$0U46##54P!HCN&3#8-+8"a)&h0ZQGAVLSmjdIF8d +l#(ki4*!!+34XSfpJZ)YQ2cq2%NLX1"!-!ChUC+Fd"L'&3)!J#"$J"3"Di(NU3)! +"E%`!K0qJ!Fq$ITh"'$f(r@%L1L3$%S)i233!!4IETa2hZZmUli&""aK!3a"J)!F +I!L!&%L["#'C3!1`"3!))Hp!(JZF"+U$!J#))4+B%J--!`1!!*43"0(!SA'eZSq) +5!31p(1$$2kJ"Bc+D%4q&GQKXK!!di!)!i!",eQ3eJ`iXi%KQ`!"k*NHiB9bb!3% +q*JrLP'a5T3mP3`*ZB3)'-'iBS(SJ)!@$!!F%`"RZ!N`Ef"X&!!l@3!JJ3!!'T`f +!X%%#-8YQ)!E-&J!md"X%)!+FJ89"di-r@$1f%)2M*J38B))c@)-%Y`BZe4X$`!p +$U#Im"Na5b!FZ#!NaU!f%d)1m3)AJF)8V+)BZ5!Ga4aD-S'kF3)A!B)CCk)BZ5!9 ++1!4-k)3d#!JfU'lF3)@D))G&#$*r@$-Zm)9Kf'`ZL!`-4i9f))KDZ'iZU!#0@$0 +fJ)MUPPN$)!@3!*L*3'J1+B-$-@J#3f!"UYF!*NL+3M!"-dJ&I%K`H+!N3!J'eI& +hSK!d31J+Z+J,"+!)ifB#9+!%3R!Hed3(,2GcRa)!bCK`#`Fb[m)!*U"iM*8I*'C +T**CTD@B,dQJ'1#!%Uj!!+$G3G90c!b5f8cGJIK"`!NC3&Q`f!(%@!)FhAQ"3ClQ +MI'NQ$h@fL`3J*H*&!)`MAJ0J$#b%84+N"!V&8!5*)K%e832J$(&e8"*8!SD!@"k +e@'3!@31#IrU($IdAAfei$X)a1&0$!JDi1PKQJ6Mf61!!!Y8%!4f6*1NN9Gb!"0A +%!94S#UR@!2r!$MRj$qh3Nj2@9Hq3!'SJ`&j%q3raF*6b!'cr-!p-fBC54@K5a3L +2S`r%46#YBc94#@$f`''%CRN)*j12B`pAU6+J&!!5FJT*)J!FCJrlKNa1)!@#N!! +N"!"Jr(!Y!q!"@#!)ea)!FP!G,6!%YJBY!)B2EIQ@+381*pB#3'"V!!B2KAQBB2N +iaJ"Jc,#@b8@&IL&9c2!i+-#BMXQCNGP9EMQC+@8*LpQB!#K9N!!T9BC*QSMT8TD +j*aUL)CQT#`+J$CEC'`$!$D#jQ[p`QDiTQB-8!%U3!*UKZC1Mq@UaH6G$)!@)3!h +h`bm!i!QrD3F!N!!bL!!'&1"jd5!-$0")#Q!'6$-%hjPk!)#+Jf!,9i82(R3m#65 +FX'QD!+!1b!QFl,#FT6P)!!!0a+NG!8!#+C-dlU!brd!)rCG'0S3kF(-IfB%"f%N +#3j!!&b#J"0%3$#+J"0-!$"!3#9+J#,8J$"i%$f'd+h@3!%2T"`"J3)1)B("+B)% +eJJq#&K!LU4J'5!'A*5-jbJ#*%4RH)')8melqp`r!%!6Z3+3K-)#2!3Tj`dB-)!0 +BCJ&1S!m!S!ZmU3Vaj3a'qJr1N!#N8r-B-F"'$J!!&L!%R+%"CJS!'h"P2J)+!#! +#E!3"8+S[8`S!L"!&`C!!BP!3$&QkT9dDT`N5TMZP,h#U-L%3"G`R"0A%!QcU!NS +3!+!"#S8M!+!`!)Ak,9L'!LE5N@DNT8IU$),+8aJJU'2+!PlD5#i!#N5`U8,DTjm +DU[S##[EAIbr30BpM!CPK'Mr!Ej`"'5QP!jP4CrfR5$%!$!*J3kG5"k'`*2VJBLJ ++#KX`TIqJ$L#!"`*J#Y+kNP86#L)JVGM`V#-JVFl`V#8!8fTJ!*!!J!c5@NG!)+f +f)$8%%!SH)+fZm+`I)+fNm+`T)+fDm+`U)+f3!2#X55#YK[#X6GDXI#!ehq)B61- +B"!!+,L#YDS#L(3-`i8!#(b!#$LX#L,#T*qU`))!,!Q#Ld&+V8r-"f6%"*PZQCj! +!#ZeJX[`$!&b3!"NBDaSPfc8S#`!UQad@m#N#i,)`bj[!qJ3eHk*9F`BQ)(BLJ!3 +3N!!"P58"%5!"(!UTq$!*b!!##6!&3-!!-e%MEf!!),!8!I%2``H[TJ'[$#9fLJ3 +"%,#L%)!1!K%&b1"V3Z#59@-DAj'hHVZhI+Zh"1!#6G",-qJ#38!&jG5hL*ZiLPX +fB$!%cDS!3(!14X!2e3%#8B!I)%!U!L!#NQ!%h`"6a!%+)`!'jP!C"Q!GG@!#(q! +$J#!#3Z!%dI"dVkB[MhS'#"!#4XF!T&!F$qXSNQ!D*)!%,!N!-N"1-e!HP2U[GE! +BS+!#j"5jNeXG)U!,rU!+MZX2TX#aRLX#Af!D5Y!H5'#lQ3X$r[&@,l!a0Y!Er`! +0hYXH2L#q#%#q1p8qk+ZqZ'!Dh+S%Rr5`+D#rTJ%NU5%!NE!'#X!4!2!!8m"BZ3& +R-6*'Kk'qj'!E5[!"CL!$%S!$!V`'kIF*B)!#U4%!qf$!#!`!Ej!!ITi!&jiJBM+ +JAGH,#8(()*ba!@lkSm5,0@0N"`3J#d0!"J*J!`'`"2VJ$ek`!N[`($3!6%`)!%S +`"-6K"*6!M8F3a!"J"`Y!#c-J(&H`$,jQ"LT3$NZJ!i0J!R-!!aJ!!JY3@5B!-J4 +J!P0J!hd$!@PF&NJJ"0BJ$*E`ZUaJ6B@6V0MT"1c`(%[-"J3!!c``$q-`-SlE$rj +3"2V3$l)J!a&!!R)!!@AX9"dM!j`"!C)`"$%3)a*J!QR`(#43644!#beC`MXN)e- +J"'f!!"8`#bCK%Q"!$b6`"6'!['Q+!"'`"NL8!,X-B3HFTKL3!(5GN3%d%!%b)!$ +NX#Q06!8-d"i$-3"Q!!(%c"%1S+JUB`*a!!*U`3+r,')1m$'kTM+dXfVDc-eRB!( +kD`B5%!B"J30RX$TpN`"R`!*20`rKm"`JS-E0U`6QXeEM@llYXk*3%!q[0`+6`-h +l[&1pi3p5HKVR!!4!)!4$F(06)`F*%!BD)!+NM-Q2%3EF034k0!9$B!RfB3#9)3$ +48JHf-3@SP`(@!!6MX!94N!!-YQBDMXX2V#!(#f!,-G")9f$6(%K'ZY!2F)#ah#X +##3"(e`X(5Xfp3@-"+5!3+8bmfK8"3q!M)4!M(["d!)!25L+lpK#V",$10Q!"2$3 +"@4d!11)21K!,`@!'T+J2r#!&-V!8#k!%2`d!3Eh&%$!"Jp")3[$6$&!155!%X8$ +*+dV)re!+0#`$8pS2*0!Er)!2'$XC89fG+Rc9@Fd"ARd2BDerBid"S%!!MfedCJ! +$kUX%A$cBJNhBKJd#F8$*L%h*5iZeD4[+$kX#)*!!"+'!!L)3"C5Y$f#J$P2+$m4 +!fCL3!0-@)!+I)!%I3!%Id$5Q!`VpqdN"B3IiXE3B+3!8S(S$!!GDd"[pB)&HN!# +j(4-#j@%$35F$4`#h1Q!$3$S%F*X%*T!!$$EJFh$XFa3J"&YJ!B&!c!Q!'F5m!$k +bC5S$!&"!c'0+"F3F8PC3"2#0Z`$J!4(!34hJe@1Y!Bcm'*60!k($I6U`$iC`h#C +h$S!!$T6GVVV!$fK3'3IJ1%5p$qVJZ2[!"XiYhA-XhG4p$RYM!NKJhHXF%(T!b22 +`$IKK!HeaaQ,3d$aJ"Pc3hGrp(($J!*3GD8U!hP#JhMXP"*!!%!*&6!!A$J!*--G +M[RD%63-"8!lk+lQ8#`"(%!rFV!563"aX$!EiB!+)i,N5c"N)S$KkrATRS!m'96J ++d"39qY[05b+KJ`#X+`*kS!r5!,ZmU3&HR3p+8YU2EF11U`r-F!A4B"VFfa[lJ!* +9[F*2)0Cd'JffdHQZS!34--f%!3BTFJEUk`Z684N"B!$q5p6kB0NI!0e0Sll@B!C +H80hAE3C3X0e))1@UPhiX[JpbB!BkS19FcYil"pQkJ"PQ-'j`b`%bi(-B)!,,)!) +qpk6k,31b!E8aiJ0H[3p+dZ&@VJ[k)!`K2UAk3!Z%NH+pS3r+N!!B-f!Il5&epU% +$rDi-`dl[q!$M-NiB3!!15`X(Bp$[rM$0)p!Me3i&3%i#pS%!BE"f3!$M+cd%dJ# +elU`[&Ld#*Km3$Q$44,$bfe-(%*!!$91!PN2GlaJ3"!4c5i8-$M*!9%BJ#eJJ"N* +!6Ljp"K5J"BI0K"&!$6(36di24&B`&m)!"ZL!SM1IpGN`me+3!!4d-!"5`c4!!,8 +cAr0NS!m'd,8LX-TQ`!lF5m-3i!1#31QkN!!2Z)#a`Ykpe$YN!3%(b,l1&!B&h!e +ecc(PFF6LK'i'@Fid@llHlHh`i'!#pX%!B&lK8!#h)R!"pR%"!(!!3E!1C-la8k1 +q5I-0"b!!#*i6("!"lHl9r4$[9$V[q3!(pki2qF#$++lLGBm([@(h$!pIG5m#)M$ +G+"X!)d-#,0i2r("0+S!Mq)!+$Bd-Tml#[ki*3GI9rL&LPLlDC2hBY@c$H0[j-J! +'H&!!A2%5!F%5GJ!2*S%!#Q!&C5!(Fj!!"Qm36M$3&'3a&J&K&J'"&J'K&J'",HL +"!H!0i-*I`!Yf33m3!,5J&`BJ2qJ,EB%!c+["8"L!`@&)$)XKKd!'b8!C,!0Qd!b +FB5"mKY!`'NT$(8""JZBIS!2#`!&`!!J`3+QM**N!!h3q#)!+i!a!#*GS"`*!!T5 +!)%!rci32E*q"SJ%Hd![i(ZVPa$`6B,"jP-3CU&##`(9J!28$#k*J3)!!F1!I9%( +AJ3"16"8%$Fp%%M6")KJ3C)%2G"d+J!S+JQ[5(@,4VaJ!e-!%X$J$%)dFN!!,-'0 +RJ)3X!#VN$el!(Y3&!d!Gq%%UC!a)`"3S!9Q0!$!B-M8e-*)#F#c1!3#B!i1"-"3 +'`kJ9*L&0N!#!l1'V"S!ZH!'Gd"5!3LSN#"`,h2L$!d!4$%*!U!G@iISjJP6)"jb +B@8)@i!%X")4hF!'3!!9aB!+%J#(F2Kb"$')62*MG")3#8!+5i!b3!)%5C!+fL!6 +3'D#`Cd4$S0%*GF!S")3@S!5T!!M!CJS!+P!$Nk%(0+BeSbHmi4S)KidT2dN9GC! +!*-M!Z)%"$FJ[Q#B"`!HZS3#3!!If8"cB3f53!$)XaKm8!,kJAA`2!D!*&!NBF!, +EaqS8332J#,I23#L#'-$#k!X6)3--N!!&B#H3!#!3Z)Xr#!"i`(RB2THK#,iHqc! +3aT0Li)Me4Nm-a#`R!+!%Br)4q!F(V#rhdM5q3E&a$qc,M%!QN!"d0`D3!%4m'k5 +L$')Z+5!l$NF!S"%!J"3F$J2!4Rk1LCJ#J#!3L*JH4Vb!5Hi*"$!!Q!#$I`)$+!M +#!3C'!"$F!%#3!"-DK3aS$r&,+ri6+3!%T)%,+4%N"X$S!`!M$e*")mL+*Z+I+)% +j-&#)Jd'X2le&(qbU9$!'c#*Da)YE%4"3JEEi&J%-lpP!G0%Z)NDpb"HEJa*8-Ib +QU`K'!%!Bd3X$@#mUVPATa$65%hP18"3b3e&f'%1+CKLRaQ3%"'c4,Ii!Z!KKj'* +N[)YU%D"84VqSSJ#MCKb-KE%p%)#ck"Tcif+8ME6a-IUDfrJDpf*I6$mS"M-'4Z! +S"BB!!mKU!m#$C!rKQ!V5J'F%MIdRl-%V(-%2hNZ,X`*J)fZ3!!kS)4kV4RTN!$1 +%Dr!FpqK#`XCkP)pH`fV84r@i0GVMepL2m2%qZLSZ"DY'PCK5%PB&BN!!qZBM4") +@Y!!3bdq90PCP'SL9X8*@q)YC15Z("DfN&E@b9YLU@@QV4-'Y["@i%PIk5PS4!hm +PVE!0KaaBc3S@'#aTC3T!!4Ni@`j,#9JV4k!'j)('5K5%BA9K,@#!#`D!"SKR)J! +)J)!)3#50T0KK0-p%$+#!*&"Ze%$k@34J3"k`J#3!!P$!'@Q5'U!6!!-6B!@L!#, +3'm+J5'S!&a!!SX!K8#3A+KBXJk33!'M!LhSJ**%&$#m#%!fF`BX#'3%!")L!"j! +!*TfNY9)&NJ!5N!!!X@9eJ!Jd1!5bJ*jX,J#3!!9PJ5"!L[b!!!J#JJ!)*)%Jq!6 +"S+STZ5H`!Q)%!PJ"q#)R5)!3i!`#3"QM!P%!#DL!J%8"`13+Z"8EJ!V)L*-($C5 +!"1"L+UqI4!&!i!X!J4J!!C!!i"$3"MV!!+TJ'FL'9#"$6B0UF`!Q!6#)"+j!C8! +X%-!%3-%+!!0S`$qF2'[JcJ)!"$KjP#"BmJ3#%!6D!CQ5PX(!@R)$3)!YYD@M8TB +q!'Y"!Jh6h!"!&!J%NZ!3P!&FB3#B*5d)"'@JjP!"--!+0L%6L!CQ)e`P#K&J##5 +!kKNl'3f5R$aH%!D!!2G#!K!6"S!"8a!&%N%8b*E`-3(Xbc-J!$BK#8J#*+!DF-Y +)i!a)!$*!!BS!!l3#'4%#h+8%F*@+j"Zi5`4!`ba!CH!$q+"B(DZce6S8!*J81l& +b44NT!%!$d+5D[*15*'p8%`)`$IcN,P-!`5Y1,S-b)!lk3La`P`aJ$,5A%m86%!! +X1!4Yi&JGJ#(J,U&P0$J'N3!BH!*!F#XR*J$BP[cN!%5U&A#fS%#XT!!3!!S!!K! +!###!))J'`!!%")*TJ$,2PU!)#+#!ip3"')$"PJd`#(r!K2cK$MH3!!CD3"XS!fc +J@*krNN!8mKpCi(pN+LfXKED!PJbJ(JJ!GU!Zd%j-N!!AD+FIJ)#h%a3)KV*K!4' +$BQ!-MQ%$jMS2Q"Nf3fFBJD+"0+6!RHJ#N8N+dJi#`!%8*hpJ-)j&XVL%$U0CS!N +Ha5m#J2ri@D-!5P$#K,%`"!@c!!+dS0Li$Vl88V""[d%',H,5U)c(p$LB#'[D%d4 +!#PL"k*!!8XCNGL)B4%8*l+)!J!S@+#EJ69T!1*b(BE)8ANE-k%CU#5`Y8&LJ2cF +6HhNFUi1XB#ADS'H#KUA)#jr#,"#!6q%"B-BY$!!fi,bN&`l`$d*MM!&*h#GFJ,Y +`)3hB#I[T*bK#l(`R*3!*I)`$Q!+"`!3%!$M`!@$")2!Jk%!1r!-5B5)U`pS`@[0 +aHe![cJ%Jik0re)pB)d$faki"VpkM&b@Mp$'-RY%X#NEYBhrF@!aV48%R+C!!#$V +8D*&4rSG'$5$V&LMBAk$3%)8+!33h*a!*DP6cSP9hDp[4JXb!SRjN&m3!Kd&ZHC8 +H`5#b``E3,Qj+"V`#IE(Tq%ef-!#mb4B%U`rJX5cNXFTF'9*D`3i1'DfDeBHm9YN +U46DVEm8K$aDAJKDCaQ6aU1`J3Ta#+JJ&3@[EH3&'qJ'+eXfD'MmJ1d!+RX@ERQ% +U#+C3J@EG+bbU+*+@af4D6XX$3#fT"3'SPYA#@PU,Da'%Ir#e`YBi,9YhLjUQ,5# +`YYS@!AKEF@YZr4Ul&I0B(JF3R9@!G*T1e+NkZ`*-%!UZFrqG"GNC!0[#!X!#YR- +"i!(GU3F@J#ESR3f9q2L&KLS+KQF&[)$(8`0'KZ9j'CTR#23-S#&kQX$iFTR1!3q +i'c4`"UU-qJ)"BP%I%3,YSBp)$1h!!(L!RU',U)!1!"KZm#Q-`+G3!i%L!&5#6d% +08)'Q55%SB*!!K+HJN@Pb+3qS6ImJ54LrTeUEN!#!&0LTf)LLJ),U%$3qK3Ge5l9 +&I`S#ZcJbB&*623@C4UTD9E'kLa3!2d!8k-Bqb)($F"L#kK2iU8EePE##3E)!1%( +33!9c!-#JJeFL$e,'EU%C"i!GT!*p*eA)`5[a"L+JUk!$1-!$T!%JN!!&9i8FR)H +I!JGb"`m)!(l!-U8#1,#f'-P`8!BJ-6LC!5-3@EQ8!!J%ij3+"J,A)3&DUc0iVE' +e!H36-f"A"X!bNLTJPECm%,++@BK*&m!Acm3"$%(9UMhr%6,4!HFK,2b$6[))41X +I++f0a(9)!fX8R!V6BdS&J!#l$JIY`dB#!([a-E"Jqj`DH$"D6)j88Dab!-"3L[B +J3K3!#*!!S)q%E"d1#Z""%-$KZ!"Z3!*!!b!!!hB!!2!'6J$HF33XJ"NDE%iT$i% +!P2L$&@JI"!%Uf!0TpEfLN!!%3!r+%UiJ!@F(!2!PLf%afS-Mb$4iTbbNJRP%5Nd +6!B!%+51Z!J!jF'("J!U)BYE"$mVA$HX+(!X!m!H'`#'UeKr,"r4-@2!(SU5Vb!2 +!DN+YJ!NG"#qJ!)39G!!PCT!!ChbUY)"6b4F2!!*8UJ,!!65`(D8#-B!1)BbBCE& +,S9J-J&43CBVT4#X#B[A#`J$emMQ3!+)Qq+p084)-f!*lB"2XIaJSJ!3!N!"CH3J +9hd$m)V6%4cVk9Xii"QiR@5#cbSFLqS9J8(VbLD'&!I,3i25&h4-m&LeK&$9J!!G +J@K1a"3$"TC@(T6E&'%32`'Q"J3bBY&SJ%&MD3fXLY)"%EE9GPG'HfFR3&(J"%+J +X)AE%RUrii!KJ3!(B0qc%FJ"#'4"Mp5H0h3-X"JqS3*Vi"EM!f)J[qZiFj%Z9bL+ +F9*j!"GrU!c5J&f!!8%%Vp+e[aM&b*3!M$1cLTd#h!'!!S)*0`'lr+$q`YkJJ&E$ +E#eY[P`)%q"33!"A!JQ58M1CY`6d%Ri)"S!*&8(!4!3TS$j*!#K##VVS8MQeF"+l +XeLlZfce!PqML,83!TH@U&0DGjcVN3DV4!Ip!'6#PC3"JX)1566-24DTi!`kM#a! +6!SLjqiI$m!+E5`8!M$*)-k*!hdJ9rK0F-!%0#%[r!-%'&dG!$B#!0Y!JE%3"r!0 +G%(IB!1ji"*m#"U##ZrJ"MZl6h6j5PaGBAFpU0VEZC2#k"D@V-#*Afe@)!8B-"$, +Jf!D$M"J9hS!8S!%&3"`)!4dJ!!V32b!'e+!)r!#fe&@%J3'i&EX+&8`#X$Yef8# +H@3TDGa!!'+9l!)$6cddjm)$amS)*qRM,VZ3GZJi('d!*,%!)$J-UZ"r`0Ml94S` +V9GTY)pLiqhB8r)#fkhF0,`H"3!*!mGl#!k!*EQ%"N!!$"JFE!!+AXA!TEVa9YaM +A2ZJ"MDYm,f`*)!)!`"%%ff&EBU-[LY86p(A&jL2Y!!$F396`[5)Jq'B2H1Y"PD0 +EUV$-9m4Hf2,M'IVKT6ff`+!,*&T&%N)'BaRS!N@JK"P%X#@fDN6pjBaG`2M@TB[ +,R&U[aQfp&hCM5!*[drRU@C)J1"[S&KU!B#4#cC)2B,H"-48XJF"B-K5!j%!"#TK +YK4`6)3Iq!$!)"(,!!d6+l)1"dD,-D-%U3`-lf`YV3Y-!Z88&HH!'C`-@ShrHN!" +Sr!F%jacm!"!!CRH+$6"!$N!&`-!PYLKNJ&98YS0-$J5!5)PRE--cN3C4&JJK!(( +iDS6!"Z)`Fc%a#J%lJ!!%!GZ4(%B!"QJ*M4-jJ8!d+!EKDDI39M@FZ8b%%)A$MD3 +"Q!%4S)C*J-Ej)8,J$GHYkZ0V`("H"!5[PTf8RKj`@!5"&$!#[!#iXS')Z)EYX"X +Z"RH5[")[Aq0"2-'h$EI)Ef5BfkK5&[JY'k%!U#!8!"Kh-([*9#ZQ!+eB!l4L$'" +89c("I62j&X#B!e5`#(S!Dl)e9-!eqH*rB!iJ$!p32Ya!UVJ$Y`0KKM%l1#-F"Ke +!!kPL$Sla'H%'3)!Cmi0MF+63L$+ZU`0!&,q$3*EdL!#(i3I"3-38J)S)BGaL$`J +#%'D*`C&9M!TNEeI""l-A`LMH1AZ)B3!C&J3Z31-F!a"`"0j`Mq*&iQ$k5!iD)!@ +)J!p32YkJY[5!)`"KmM%q9MllZ#iP4RpFKJ-b%KM)"ENB('3#`!S`L"'3!-I+G4d +C!*D"#cD3!!IK"@`($,$D5Yb'hr#,8M"dQ!hIBI5N!"ja*(ie(J5%+!!F,)UcJ5' +q2qQ&(rb$'rQ$Cp)j`!%#U)hB'#VJB`JHPr!5H86qd!d1!#8&!Mq3!#E8a*SX"'p +b8lM"P"%)e%#FN!!!Pe4Q,%!kNEc$`*dB!d#3!))Y35$i+EMJRMK4aa!!C13!q#H +Z+k$d4B'`(0V$3bP)"dNJ5"EC!!+ZL5Y)3&6)"##+C90EU"!@`%k5,d&B!"%$!P# +3!!VL0`[!,P%K#R!09Z3HXJD!)&iJ!Dr`KP)+'0!@+!#Rk!RMY`"8-fY'*ZkJ0!Z +"5hUD8l-3@-d"),-%J'`"!'*cZk$0YMNi)a0M8*U9d'pQ!`,J#!@!IQQFCl-IY-h +4'CQ3!!,G2!&L8!h)DQ`J&0Q!pX!'N!$!-0JB)#!D$!-JCdkd`2di(`"!'h#'MPY +Ec!Pd1XJ#B+l)T&b+@C!!L5hBUEc*&BJ!!M"kS%%J%+H!V"q@jb2S"p[cHdBQ,U! +dEqB%mB3!!@S'd0X1$m"QfG`iG+aB`Nd)3%+,Q!D`LLSdDNiC'!)maiF(QTK'LTk +4((cB-'H8MG)Ll!-2XdJM"51G&-E4(Qc5YL-&j`(rd0$N*U43P%&N85i+4YddDJT +6S$-4!!4R#ieq$E4+TFpUEk!(eX!fa&JeHKqrU"RYdP+kMI,(XH'PZbLBCU0IHQZ +8%6dk0AC8C)!5Y%S!j0,XX%[26(E`d3%!&15X%j!!60G0RjB"I4S(p'R`Naf%3#T +i&VcdFF!#BQT-j64Lb!lf0APbN953!0TDE+1qJ*!!@J0b%C1P!9+"*V(8da5ARJ* +GDV*`e*eq(#jJ6rIT%j)G-"X!f!%QLe!$!#13!!Vk3+Pq($k!88p68iSKPC@'A+@ +*SN1kdQS&5d8NXFC5Xr4%0L[j'+rQ9E-b"[C+@[%#KH9)ReA%DPDUJ',4DB!a$T! +!'X2L@"k,BSNX%`@[FSKLq&Q&-&3I+c*+B`j$+R!("eR)5&1E*3"-&J,)@6[,&5q +&Rr@b8!G[SJ2cHRR9DeZelD$8a#AB!%!,"'aFqUJCfVDc!+KJ@N"UhT5VRd#G+9T +SbjSZVDEeY+,@e$)$9HYUCDfYeEA)+ImpTiXcRD)YUF01CB3lKDGY8Tl@,E09"`K +#XdJ3a'X0J!!aN!!(k%"&8&`k)!G3!643"N"!2Nd$GL$qc6mkN!!('S-C!!%8)A@ +qJ6SJ"pa!'@J!A'%-T!%h3!F5"KU!fKS")a6Y+C!!"a4''4!F-F!&f)#6jb0FJL8 +4#*[##Bb!KX$qM)!8+!,VL##`[i!`%!,#!%!*(4%(a&YJB(AfG3!!Dc[!!)L1%-! +!$-!!#!8BB!!)J&$36CG!,ZLQHL!3G&-PS,P,K,Rdh"!J4J`!#*!!ccK!k!i!i+# +EHJ!CFEJ"`!Ii"``f)-6Z"YX4BlF`L0d#)!X`K"kJ"4U*#2!'L#$HIS2b3!!$`JI +i"J$J"fJ)Ik#mJCP$q0X"i3H!Y3&`4T)hK$'['Z)!e+B(8*YHYiE`h[m!I)[[lNf +q[lH'i!IH'abmlJMcZX(Dkij&!3&q!`$ic32JYc#!h`)J$c#%(k!('XN)m!I"QhS +6l`MMZMG%mfEHbaXL(!$epa#HbSBJ#233UL)4Kh!9XX*@k!S3J5*B",(0"0k!kfJ +)62%0L)%h3!Eb!1)k!#*K$&5"14$r)N)!D!q++iBrK#X3"UVfeRk#CN!1[!("33H +)0Y6Z#Lp!Jeq%M!!#b,EC&K`K)(&PJDNY"d$!#4J###0Ka,m6!!,U!!Y[iQiJ$*c +1D[F3b%FBQ!-lZ`b8JA!L"mV!'8J$CVZ-,d1H$3,Q`!i[!r@[D+01&Qk!%TF8+!0 +aS!j8"$V3Q(8i$rIKUc1)KfdLEX4p0K*2A&#!$C5",bl(ZAEm!`&jJ)Q$!$EJ`EI +fi0$L4IX002&)6V9MH!1B!e3lLjp1%'$*HhM4IZ*4r)T6m4Aqb*d!*UrML@X+[(% +3F$ShH%B!if0JDV-"-Z!'6J!I&`0&1iqA!6fq$-1!'dJ+LUX"h)(i9l6*J!iRV@@ +!$0c"S9fdLABBq!Mb$`3Xmc)1!T)"#'J!$8#'"`5HJVhj'J!B!r#))!#*dp831%2 +`2JLBi26"!q(3%-T$m+i#!##H#`"i%!-F!QG!"J$J)'!$HGi8kMN!!1MiI+$[maR +JcmeP3!q8m[cSeR1(AJ8JqMk[!3jK)'3"!"!'!J!3-&E`i#N%"-lRcJ0k2ZIE+$d +J',#(`2RXqGdjkCdKT[m(L%$5$lT*a`BT[@r2G*B1!-S$3JF!1$fQkr59lK")ZN2 +[!B%bTd2`L0$5+AT5&qT,r5%F!(J`e6[$3("Rk#!#j'N4@a)EJJ#!"J)!#*LCeDh +6XET9jqJ!!'i0K+ZHePAk1(J1Eed![(8$m2S!!$b!@`FJ`J5!"q!!m,THK`F"!'j +Y!!0h`!)#)2J!!&d!3!!"%'%3J')2#!b!DI`0H*F"H!S!m!-(b&J0&-8ZBJj!)j% +4$'"&!3&ip`%fJ%!B"&-$#!L!"H!!")!%-!EJ)KFB!ab!#"`!$P!!Si!3i)%i)!( +@J32B%+8phJB!3)!19$X$L!3#B!))2b)J$23')SJ!%#!"N!!!)N!%%X%%11l!hE5 +I,Q#!#P3l!N$ZY98%%!&"S0LI1`4J%G3p%8L!ljlG%`8!N!!#lX#lI`,A,J)S!"m +)"-)!!TKh"(!#L%&eAqqIS,eE(5m!#V`l*+$[&!!)Y#lp$YeC"!(il`J!%JMipel +J"F"hTqm)B,`lp`BrhDZl4)N%%ei+p(3,l`J#J$J3!3J!"q"h"S!((-"j(`'k(3j +)!`6J#$C%!bJ!lPh%!i'a0MAU3AV#mHqd2!!$(Sm)I$`$X$S%!!F!J32JibQPMdF +!5"i#!!%m!,HJ[(Xr!X5pmed'Uq-(#X-"Z!cDfaS8"Jd3B4U!"3J)610,UB4'`MK +&c!!i!'[qpC(j4((Q93BBL$!&B-d$J"J!#0`m!LJ2%1$e"IF$9ZGaqb(!!,&J'"b +#-km#(%%,K!2a!"JJ!"U2!q+!'@M[CX-5"!6TMJ33!6))3jQ!qd3#mDlH-pGhK`# +*)"0X#!m3B6#!*I!2#'!%)!-%J!M#8!4)%)88#13"UA-!%!%N3284!0-r!#Y!#%B +5%U$eK1!)h(S3m!PDS(8("`M(%8#!I"$XAEGTRaUh3"3BHf32"#*"JMM`"%#p"`4 +e!J%#h+T[pIF!'KKl4&$VNB#h"`'MrVrAi8J!l-qp[VJ(+L(@(`0D2`B1`E*hp$J +!$d6k!h!)(!%$L!29(J*BRFjh$i#"2,L#",c22iD"m!$m`BB!"23!!"3!Kcie*(i +h2@!@2bA-JhJ,l6ZIJShV+0mc!)!r8!iHq1PLq3rF!@#k4$(c%F$-[r-(D1B6J*T +rmb1-c!m)1ar0prbF$r4jIN$!q6qIjJ[pSqrcGEl49e&0[qJ[IDKr`*cqdPmriF$ +-6!"-*dDZ)!'SkcBrhSB"UD1cZ2l#Rr4J(p-0J$"`!46*eNm8#rqGTRfa6rEIIU+ +)q(0rlC2p$'"eY$H%L6"G(`#`J8f3!2I([NRS!%KHT40`!S$h%m9!3I+[Erf)Jm2 +IpeNHi)rlN!$Jp4@!J@*J0`!J)1!i(r%R#T3J!birQNF%QRqJJ!0J!!E!!HL2-+* +rN!!"!'&JqQ8%)8Mp`efL`B$A$`"L2`13!0be2`!!!pbI)"SRlirp!i!"-(DiclH +"!1i2P,rqq#GqH,$m!lr`*rk-%`*)IrIq()+rmpIi#'!JC2rY[r#VIrN(!GMIq$2 +phTriZcrcMlGJ!2F6(b9*rKArqkG8U9r#!!%Y8IrGIq"I2rRI-3!-m!%i(p8AqpP +rJGqaN[mTH'*(rfIqUA6S(rLhj`%#I0lfjrIj!hMH`UFK!!1!hd$a!EKZG4h5Cr9 +0$80IdKIdNB$VR`Rip1&m$m!)'2DK'&P3e5IeSB!S"L+`)E5!-f$BYhiX!LS"$UM +dSB$VKb*3([L!*k!1#!B)J6,J$fJ%)S&%S0'hIXJ"jB%+12@*J$PJ#HJ#@S&9B!T +i"@U"@5$9*`@LJ&lJ&NMeF3!4aRRAC3&p&3kDpfG91+p2i6!'PS'KR3&MGGakA`T +(-&"%!'fJ,!2l44JA3&mK"U#"%3!,B!cN!)a!HR!(aJ"Q3!`!V4Q!I'!#N!!#D![ +ZA9I6[dJ"NX#A%J%N!BP!%T!!!ZL"[4mIb0p"JR0J#(!#+!%1`"mi8(J!HB!"N!! +%8('-)01"!$b#J'!)d,p)!",!Ph+kDB+FB!IJ#F+#lPd%)!XU!C5J,CJ%*!#iB#Y +i!Ab#D##AG3cN!)k!1c033!!aJ!%3!l##9+!5L!9DJe`J0NMeZ5Q$`!2AB1&i$3% +8%!$`&'4!!'!%%!!#`"03a$3%9XG!33%%!)V63j!!%``8e83md,83"#P!!-$j*!% +"`&JN$`B!BdS*i0(GJrq2A3FJk(5FNYm'Z2%!JY[I9VJp"keEiVDi0@k2@q3fZ99 +ZPeX!N!#jE@kG'qI@65%"jT,S4VVj#+GEkKB!V'lGP2X'Za%%Xe[XaJ2BEVLElKE +3p@j[#[!Q["&[hS$aKV`GF!FF4%!34J3E)2Gf4RK[#*b'm!-B!aM!2r!(2!0+f5q +!$C`D[`!X-!$m!4a-!&!X-32-`!R'#$!$B%!JS"+!!B-!!m!-`!"Ki9L)fTN0C5& +C+"E#!&P0a,3@IJ"Vi3#J%UJI'J*JTb&!HcM!!F#h`&"V'QMQ9-9Y)d!VpM)i$%q +3!0KKh3!jA%3V"Tpe*dY!mj)B"!`Q32IL"EJ!Ri"T!!EB!r%&`L'%p4"5"3#3!&- +"'!3!+[!'F%i*!!X`$Q!"N!!,""!#`!*iJ&@`+$!!Z!!8%!E8!@b!!S!(f#iaJ)L +"!*5'I8%-J!mS!(j!DELP$$+F`C&A(QJU""B18"Vq*U!*2m!8*3J3aR0BP&!Z89F +BB!13!)BX3%8d09K*Gdea"Jd%!F9+DAJDTSBK3(MS!S#'U)#YeP9iKR0!CB!$P!E +R6E8e0L3"4`ShJ!)X!JB!*F!Tj3$LL0@3!-FB!#Q!Ia$,l"3[32PaT03PNd4EX'i +8*Zp@TS%+X!-%J(jSf"@)qP-#i4E%*,C'-Z)JXJ2K$d%`)2BY()"9H-!%8!-835! +)-(!kaX$L*a!%QS$hY0B!!+E!(&#fZ38"J5rJYK%%+TF63!3`!35"03!B%J6LJ0f +'Ya%%lN!+9dd%"2T!*83(m'e-N4``"Lb*!S%%d#Im#3,"56J%1!&8iXCR!d!"Qi+ +Z*"$i!E'#%e!P+J)!3"d("8!%HjYpT53B!`j"#9!@Q!3K`%d8%-"@A&D3!0!M0!5 +!J)CJ%JJ"Pm&$!))"!$2!NGJ3k)Q!)L4!Ya8Pri"*-!-i!iFLS6J$&#Kk'k*S%Y! +!$eqN5#M5!(8GTd3SeJ$JB%$!rK#+0d!Kd#JQLJ$!$6!*1!4j)UN)"(`#G"X-3#J +UKh4E$2!UhQbHSJc`+ZiUHYX-m#TU!A3E$I!UeQak@`h`+KU+HTX0m#Tq)hJLS6M +D135"!+%B"2`"$B&9J"9S"DZ6Y'M"9BX9(,AB&9L,fL**`#eHF#3"!J!,6@9cFf& +RCA-J4%%!N$4%4NP-4%e29J%!Sm8k"k2&1JF!!$YD!*!',-F!N!6RM!#3#1f5!!! +%#!K!akU!13i'I%@`SF1(%#%ZD&*QcT``CbU#)"*%!%%L4T)`)G,NL4@(3"U#&%R +5*-U)-!'-+UEM`--G@Q,Uh-QcTmqI3)-qY+'16%!H!G33P!(!4N%!2J!)38$4)XD ++3KZ##5KK+`!+ALeiaH"9!a)p8X&J1"[!'KJ*E2'"BF$@'aJ$C`[BF")(b6N!`)5 +FHB(J43)3E`")F!)*aJFQh`B!`2)%!3U"!!bB)I!#"`*q6ZSiXI+2Rem)-%#i!-# +!K"X!#*5S!Z$KM!%K3,B'F10%9`aE*J$dMJ(XKA$IX(V,)%$LH!aFaSH$'Nk2&L! +SMpi`'-iTZQp'@"iK`D0"$rF5+5Q%2b13!!-50k94dAQKiU[b`[8T$!G(hlj[A5! +S4B%*8!bRa!P!$-!%(!!JL!!*$U,J)!MM!D$"F&!ib-)6l`!!`4ND[,!#!L`-*iU +)*!l("`KVN!"i!K3)J)"GK4Ik"L%3#0#`ASJMdM!F(LMkk*X8,1EiSQ8ciJ%!$Vh +"S)q$-1b))Jc$16(PF#J8#F14*#3j8*2N)(J!+q0pU3X-f)J*#`ceV38-%B"-!FJ +kJ!!!"$!!h*!!S""C2))+0L"mJ1!!A,b`3'm0)')SSZk!-%8-5Q%!!"XP)%(ESVS +3!!FU8,cJ3RacF2M9#`-dL39ZiI@3!")%6,M$!!,r`11%&b"mJCd6d)"JA)c'%B$ +E$bNa81ZYZHkU+`#q!J%XDm-qJZYSrl"c'JkZX@E#Dc'ZmC8*%+!`a!X8S$)(QaB +bN!!5%'d%T)Zj!#6a'J#b3-+(RG!!!F-1Q6Qa$`!H83$!C8k*!S!C!!MmQL!!-!+ +!*U3'S3i!(,`!3@m!')-EGPkaJUN!cU$kL&HH#!'I1e@)0c)*3b!KaA[rq*-+)!a +#m!(+XJf!a4N2IUZ#cDPS3fT[%bJ$"M)rm!#!!UNJ!`B0+02(Xc9pTX*2S,h03!8 +B)$5p-aDT9!1'1P9cS$A2fi#4Xp0FAb2#%##NBE1-$+#",!"H[#("fQfr$38-PhQ +""$"C!!+'!!aUlH%2!+45"QkUHYJU"l"b-fZccj)'Memi`(!YE09ZTiX,hPcQ0"1 +pZD"0$hcmScSr*5ca&3`6K-IZ!1N'S!Ul#,`@J#MehKZ3!,lm!J"&!*B)caVa!+J +!JJ5`(#3!`3&)mS)%UBM`JJ+p13$'p+Q-%*jZILcI2,r3di(!#J3pF)B#D+H#M@j +LY*q0EUHH)X3TerGfJ3AB'CHr,KB!!!M3*`!"e#NJrj!!``"N!3K4-F!-9TKHd36 +#-Rq`$"mXJdFU'Q'#RA&%(3&!4D8'3)6r2F!85J$%HUch+Aa-%'PGU"$fG)%"4E9 +3-+RS!ZS%8TV`P'!)*25+clEQ2V5K)J)`3*m$rQE!J!b%23"3``$je83%bQ%-$(3 +$!GVJ!6#i3!j$b'*!a0!'!B"""(+`!J20B%BdBQ'06J#$#H4JKb`DSBaJJ)!Fm," +'(S#"!ZhcfQ*mSi6fPFd%lB2D)k4'0GpSiBm"HYeP###!P2`$FHhM4S$d&VHje3d +#Qm5#M2K'Ylm&ER!-kJm%%+Fie+QZ0(NXQD&8aM*q[#aQ(f!!I&Ch"Y4mbM6&!F$ +r!J#1rK'J0ba`4hKDa3!1r%0bAJJ0BT!!"3+j!A*LZRJ!&m)6'Vm!)$A'J3!*%X- +!)Fc'!j%F%"4+pS-9()!@[6Q"&4cc0a'0#CRFX'GbG-%#DU#S42cdacpld`*KMJL +J,%"&N!#3!'Q1KI)6(3jP!55Za%pR8*3&d,JSN!"@-!!b)0-@)ZSS-Y%38L`Jda3 +P688c3XS&9'6M"40)a5$kSapq1S+Qb*aSIT!!L30cIJ8hSL,#XLJ!'3B)d&E1JSC +I3*!!0A+@V$FNJ!!Z)6!`#ca9&b4!JQ0J!)$e[6"TAU(&*MX&!94%33i#$!!d!#% +%15!!!b"3+b!F&N)K`!%"!*!!3L!B%!L#)#0Gra$(raMJJF&Lihm3S-(r('#"qe8 +S![qM!"$k*m`C6N!&rlX!'qjh[mJ5k"(qXb`%rVF!06K@53@)V!)Sqcm*d10r'N! +!Cr&R@AL`GSB5m0N-&d!288%"-J3iTQK)``frq!!'e8+!$9EMJ"*)JCT+)3!5j&E +@2FcfIa0`a@el)`&-`'!!$Q4A!0*9!0B%kc8%`%I[m(A-IAN%!`(!!J%%)Yq!d!% +![L!!1r5VA`!d4`Ir1i!I[SFXC6")#@SB!!qZm#B!e#""22LI!83""R)F1-%qB$! +4(*`J(dM3D(5"M`AKS`q@Q@1$#"k!$mkJU3#J8TLIdJFVal!9!ZLKIB!5%!1J)"# +#%3!0kCcZd9!4Kh55FSR!U+*(@!`(&a01Q&Xc6T!!j6C$&lLJaL`SJ9)J-!!`X-` +HUC!!`jHG"SEHT-!8A-81H`J!!AC4)&d%3!#l,Z!'#D`A!0i!RNGb`QF!D)&GJ5$ +B!0!Kbjf")49JF)a6(!XIHj!!fFad5+%$f6-!BK5kbiKfM#@!)*K3M[)b'!M'-$# +j094d"3$r%-%4p%%!9@aP!)5BB!J2-F%%S%)4,(-(+KBK'&63'X5heX'Rk!'$6m% +$'L26"K!B-!6b'3!%p8%!'!J!EGDJTmX5(%!&@9EL"-0KJdM`YUaPMBT8K(X!QpV +$9J6JRND(qGa`q$)bGC!!3M96XGMrX-IIC,#cH!F$%S%)4$&``*TQ2mm!$J-!@3K +!9a%LJB6-*Km#')5,"12J,K42X!kid1!8RN%%%5F!p+!c3`@i))AV%BJUK*!!i!M +2N!!!RJ!$%8*ZKYLd((U3!+#j$5Sq!##B!3C4Ud1J@!!!6UK#fd%2e,@4!!F#`-) +3E+JT#R$!#d#`J3(r+i!'#"`!+KMj-NK@mX!`1m-%i!,PMcK$BFXZMC!!KkIQrd1 +!A3`3G`Z%*p[E*M'm`5eZS`@!h1Ef0LVf))-#$)-Bd[!fblcaJjh*i3GrhjNGB&! +!D!3"FDJiaH*43BT,(cV4(pLhG)-"M8#X&34!L)Bk5,KDJ845ZY3Gr)FT+'*ZQhL +$rdY!E&5BGNd"i#iJ-"S#2I82'5HZ#a&rRKE'+M'cXPc"raZ!&Z,ZKpD5iV3!51d +-(m#1lHV#!GEiA`A8FGhY5m2l$Z"&J+dK+KC-Z`C%k(R*8M'(U3i-eI5BIrdpp!' +U%U`If["k3PCG5IF"5qF9VY*X!f!'ckB#Bc*Yd(B!X("YI""a#pL!X!'"pF%!ecB +A#XL!#EG`$6G#*53"2k"YYDGh#BB''h3&3l!CF'"'K--C*JJ!H9GmHpF)ArCZMIC +SZK!FDCCf!D%'EYB'BM"RGACRHHCH!'!d6YL%J'B'4-"SqHD$*H!0NYBK$(!',R! +rRTGTS4G-T+80El&U!M4,2@KS[9%#V1"!R-*-U3--Xa)Dd')Dji"1*V!D#S!$Ub& +E45",!j!!#Q,J'+V5-S!)KNE6*k55#XXh-rC#!%8B%*U!K!%K#8USCl!"!-(K"!! +`L(J"$(,b"JJ!!jmS*bp'+M1Q"bif4[KQ3F!J!hJ4$(S&#i%JLU6i0e)J1!E3L[8 +d!+Ri$f#3!$QIH!@!B)[$+$K2"JBH`)Z[')Y@%!LdD)arB`@kb)bSQ$KMF)LJaa5 +m!!4R3!'89dmf'!`[%!$rS`$%i%#Y3J"im!rS-$PI`!9*j4GiD"`DJ!2'J3'(1)J +IB$6Dk"Jk!!#+')L05!+2')N!!!L8#!"iF)P-L&FFX#3!`!6K#)V&1)S&m$HQQ)a ++F)Z[Q*'b#)feL*'i+$J%i*&H9*(%D)XC#3c%q'*JS!%H@AK)%!c2')dN#3c81'd +HHBfTN!#0S"@)S+F*R(B'RVBhS"B-a8"U0Q0U`)8(Y5+2P4-VIT%Db98$UI-22GG +"*$5#$dF%!KKlHh"I-CPL2-"LFZ"ND!8#!V"@$4F$JB"A8L!)E$)!1[!hX#!)Vr& +-XlF!0eKLm#%2+0Cb61CNU44M-cCl!I#A,#1BMI!rqS9f62ClcbCmU%CmaTG$N!$ +CA%"%"-[N$T!!q!rN-)Ge8*90j5&Fq8%K4)*Kk5'b0d-@3!-r4%,,p!f3!"JD,b! +!di3!%6![+M"I3S!#""!!3S!'!p"6aUN$aDPJbqN$3S!+0[1F(48$@GPcdSN%DbP +AG[9@FE9@3i!1G48(FVPAI483Ii9UiP#Fle3bdr91pp-E('!-p!3-*2!"G%JDk)! +j@B-YI9)KjVKE-[))L00C22"+rj!!#LXb-i"J(1)bHbMB-VBAQ,LR!"8#!E1&IF4 +*@lfK!24!@Bdj2Ed4!U#!(V!aHaQCJMJSSBp*S8TLSIEcS[lC@59R#aiDQ##U#b% +J"L5+G5CS0!I!Q#Tk249kSCUR*"PkMRK3SrmJ$cFD!L4!SU29SjN"T%Z+He++&bJ ++Q2qJ$Z!@Q"0%!)$(-ZS`ERjAE[dCQ'+D#Y+JG!Q#"Gi4!Ua`P05*'8SJTr1L!-* +MTkbKF(U+TaV3M`*K4"1!BhhbTDQ3!!YX1J"38+KqGkL*5JAc4`Q13A!9`KPPa`Q +8"98fi"5L3JGYi!++b!0*-!42i!5bPJUJN!#SC0",6N-'U5!p1q041+S!iA'#UD! +*2d!!UB!*XaGL%+Th+PScUqTl`'HCri#CV03&`QS'+0#I4TTlUb@JLE-,UYUUVpU +U[3%#U("h-k5ERdN!cS30T,P8TbNX+b1@h*-'`k32N!"*#rmM!*E`2aBLVp3!!ZI +UQRXJ+N4PQm)&,4Kd$PD*,IBkJ)1RC@-b!(b3!*SK5)*D"K[B*U8#`*MHYN&H#&V +4"SELQ(@#m'$(1CY%S)kJb3$4-UlR`&6Ma"Ub-5B`q(Xm%#"M)J*U-#C5N!!)3U! +BJ!!"J$#`k"TY1&3'rB%!TcS'2cY""pS&rr-25P#E`A@Irr#[!3XE1SZ["JX,#+Z +`)14`*05`$2#`AbUak%DaTe"SV)'aB[Kb`X#a1Z#a)"ZDl%#b*NY1+3X,+`X',4Z +cX!#c%MLc05X"0jZcpeTGr9&1Ck!iJ4ZdK@Xd4AZd5IX)`#9F)T!!',k(HY'!$0' +3!!iNT!438*KJi!#jb!!("!$Rq3rQ`#$5",N-)J*!-!h*-!hS3%*#%!@GqlQKb`l +8U6V@#Ce)`,5A8l,l@D*5ZTKCfTJE0%'qD$3%J!UEd*K&m`rlX,cl8'l+Dc5V*,f +S!!YN+K$)1hX4'l`UfTLS%!Uc*ehF@k8k'*MI1hYBqUXT5VlH'`VV5!$Z5*V3!Jb +B)d$B)M%6Y%3MBf+,ek9EQUe!@kDraKUhTJ5ZX+8T&`"kd!(cSJ1a)J5I%Jm-r%e +,#X(r-!m@6,r`N9'I!MDkm!(B`$,%pLR@))kp!F)AK'mK(%`RM!beK'rDB-)IM!X +CP+d'3+R`!4SIc!mdB)2`J3mR,!p1)F)RM!l@!!68-!6B)!5kG$6r`(iG!J0X`!( +XC$3`J!U0i"Jp[-50b6*bU!X(3!Fc!Krdi-@pF3"QN!""CJc'IrBD#[!2f#!U%$$ +&9Ia0@+c&!-$&D!SIAh`!0$$'6E['"f!#DXc(Ck`"6Ic'fL$(G#bJ9Rc((l$&3Y$ +&KU`,"Y#K*P-DDf`!jS"[j,"[R`)103P`bc(+J8"`f2%'+N!$"*!!RMJJ!$33bY3 +J9!+`A2rJ$3E3E!a`JUJ`#CXh#Q[X!G)3(Vdf`!a`Dkd#-Im!6p%dA0&#VXPe3@' +'[Pp@5lRQ[qj`3EhK!F43EfQR!*Y%"FdA"B2cKU!*[k2Cc0#bZdq,!-k&,&DlQPp +C)3MJRp&R$pih!'K3)3P3)3)3IGk3!-p3)%-9-J$4C`cjM)p5kTI!UXR"fR+L3J+ +Dqf6%a`q*+D@qUU@QmG!U0Y'(@4SA,@X4kY$Nqcrm)!+55Db954$(qN[*1RX0-0) +EcEif'UZTX!68h0!clCM4+a"KDVl!A0-m-`dHZiiLQmlhD8&qm31pL`"m#!!1X&b ++860m`)#iS4ZJS!4$F!B5m&dbA!"#-!9!X"jQ4!k-!`6qS!3"f30&!%[fQX2l"T+ +PPiXbm%Xe+3f"%!6ZN!$@-3!)NK+3!*[%"a8C$F@"!,e"!'l!9HZK!%f-$+8"@-* +!EJ0XDlJ'(qT3Ph)J"FVb6IfQfBK6PhBJeTqp-h3JfJ#3!(PL1RMHZ`Jp!"rX8(a +D3')mm#Rb`!f@hGT0fc,i4Jr)YU3qm#Rqi!e!d!0(d$,(i!lU5hjRF)*1de,[)!4 +&S09AK`9%d)VP!!6F-0E4rB)"B3LT23TH`3FrJ*J'd-X`)&h!i*ClhC&h"3!Z8-r +(!!*(S(S)5aLpX3(Jm)@JpcFLm%dY+3f!J0FJ%!6d635BZaiN!)Z5X-IrS!hM(5f +3!2IJm0#+PSGjQJFIfX"j@Nd`#LF&4'$E,5-&4F!!4C!!I@b!!,L3!-1!T3G4@eh +&2%((V!Mi"NcVb!rq8#,UE!AqX,Xid03SX"S-N!#((J,"rL"M!JcMYcCl&-UprX$ +6EY$NJcIHrM$#rQ!2@E$C[D`%Jh!')9EN2k-,'S$3*J2PSr!#a`6QCIcNqY!E'X! +1,6jiaB`GkmJ"rJ!1mU[M20l83Xi!6rfl[SENLY!IB#UqkUZP6VkN86jXq'EP@1l +,32hP'T!!Xp&&X0Ce"VlfVA4Zjmdm633J!mDK!(`c!*0-[k3A#(q$Q`T`XU0S(!k +J"$jh'cXM1"3#$,PS(2Bm3rq!$"kDB)"`hdcJ3!ic!$Rl)&r36HGJ0)Md&BTP)IK +)'l4!"'r`L1))!a%VKU@L#dd4N!$R2HdY+5IqFqhr)*mY@H[I91hIhK[r)!X4$4P +0D1`YHl)BX1`HB!2')3*H$3cP-&IU3!#%J!)`J&H$-`9#3!Y$%1eQhKZ,fGph&`$ +8rMIjlM$mrJ,rIK`-IhI#G*,9RZp$X1q%m!)aJ&HpN3(deN!G-XHSaJX%RaJ!`!X +0MqrkcZmU-2%Yrr!F,`2D&JcP%!JEcqmK%!Z5JPIh6[-mA`J!,`"5-!3T,`3(Mqh +%S2#JaI$Dj[)3hr%66c'XB2&Q([5!X2-Grr((J3'f)!IT6[)H`KlrS!XTE`-crr+ +%%20!Rr&XEr-eQI0Fhr-rrde4,r5%%!*%$`!%raVqJ!mY$e`mB1aFPH`-8)$@J)b +T3!X&D!Z!X*Id`1lE)A53!2Zi'8J!8S!)-`X"N8!`69Zkb(,9QXrj3+!%%!$k'#5 +(TARXCrJ9eU0`3M!%10"P4Mm%-$!%*&$2`J!#3N$BU!rkm%!$!h$d-#!%+P$2a3! +#48$BV1cK-S$mr9`-)P!%da!-RJrki-$ZK@rjb),jdKBE',!C9fd"%!!"X*!!!@B +[$qH2")$`0a)3!NKJk[pQkU`2!mCK!+l1"Tm)*i2c0kHZp9QPa'#KK%!(H8!1i-X +KV9bdq+5!%!J2,@qY+!'h-[lN6-d+AD*-5BL!F$0I[K`&!!IUb$C92[edXLM!XY- +!X`m&1#"BX,Q@RMrJ"lN)'`##SaFHE)%31(%4*h6P*b8""$KJ#6Pc&%!6K%"`aE6 +XN!!2S"EB3MBm#!B%"bE"ZS%&l%R%Q!!em##3!)#fQ#%-!!0XPDj#!0B0*CL#$q+ +U+)#B&iDU#+Uj!-'##B)hIN%(`#!*Z!$c`J!!J!EJ"Nm!(l!*"-%$Z%%KS#f@b,1 +D)3C!"`3K&V0Z*TN'j)!f5!eJ!#DK#bC!YjN[qX-p(%)e3!!fS#*NK"0!(("!%D! +r#)!G#!$c!VSG!AAJe%M!!K!9)-!0JJ$*!!M`5N"!%,4K$rE"@pFE#S!c')4G8#" +B!cHB4-SG%&!GbXB'C+4K)!8'J4[%!HM$24!$%%!!S)%Km&`"JDU`!3c!$6SHCiK +BPa"SQ!&U3!K-J"!3!K-J["L#D$JT-!#*!eeX3!+S3[6K!Jc)2b!)CSm1R%0Nm!B +)J!i`'XK!')l#8VJ!j!J+%!)BJ!)J!8+!")K"EZS0&S!)6!(FPrF!`5MNGbG!%1! +9CTL,U!([d@Tlb4Vm3b&!!HE54J4(,5RI284#)!4Q!9kj(aA*fe@@hV!2N!$!l(- +JpfkYY*@hF[4b)'"a"bK!q!b%Q%3IlSNZN!!!@X!!%!)RF['i#a&!$f1LIcJJ9X" +GP%$9#3pl#4Z3!)#P12Z%J"%!!ej!kih%&c!$3*iZi!HX)"GG"U`R!,6He*0iAP% +#$$4+L2FFRN2NH&c4+r)$ET!!KEk#'A!!2'!!a%4!8!0iJ8"i+pc`k0Q#)J"B2)( +[%`)3B#iGaMC6iJS!5rSE@`!CTC*S`dV+!!-`!YQR$HbL6h5++#-ffJ*&S"ibL', +&dSE@mF%0Iq0aT!jS8J5A'K+%$8S3"6""*mJ[p-"9H3"Pm30`&8,),p4!"CJA2B! +!6+(I9!3UK#8m!,E!"rD'hK4ZN!"MEk!!E-!i0XF3TL6ScJc*"jJJ8qQ##+!&TZ0 +c9!,5-@Bp`Z-3!5DCH(b1-L"!@)D0Kaa$3"+!@P63@j!!3KcJ(TP&I+419-9$U%F +8-+H3!)&Lf)qpD@l!PIJB)#9$"LJd18))R)$Xi3QfS+L!!E`a!0##m8"9HJ-%m!0 +%jqdB!$K!#E#D%$KJr-!%2*[i5%SN3#94(34("h4"1-#HU'#2Limri$+B)1N5Xj) +M&H3![H%"q,X`a&A-!"-BKFJ4!N`!peH5[KfQJ!"k`&Y3!)%S#*!!J$$iFK2!%Nb +"kSEHp&i)'!36dHJ"JXA(PR+4P5N0hJ!3Y)$LaJrNQ")J!RZ*%CL!)3!1Lq'D")F +95Hm*!9K`%Np"5Pa3+e%Ai!0&3!*d)J"`)$aa"HC)@@!!%!CQ1)MCK"-J49LJ +3MK3&HDJd3$kSL'US`4583%+!#!5ME5Iei',9d`Ak3!!d"kbh!$LPaS1,ALp(SS* +V)!b%`!DB5`l%`q%#`SKUL)'V")F[`!$N5"k3!#NK)(Tc5eGV1q@9QE0K3TFk1)H +1-5G&aXl)'LTMi-)KB`"CLNE+&(a+i`C+9NUJ#-!JB24N8U-li!$m!)kjVA)9'fH +MEQ!"9i8"U!%mCKZSR@i`#j-315V(lb-YDZ4b,#laNMTQK[pa$a#!GR3!bX!lENG +G%"i2`(M-(V+J1Dkjl5J+pL0lY!Ae-8E%aihRe1UMX,L2I#!rJK+-L4NN!)#F&r0 +P3&U'!JN!$U6&5T!!#h*G1!!mpL!r6XZcG8VL!2`2Hk!([%mp)!28$P!kJIfS"15 +'"(!$ZU*!-5r6%$0RTK1`QCQ$"q`$IJ!'S%',G'Vl-8BU"T&@)f&@M%!#DB!ep!B +'-(R#81-bI+F*!Y!l!'!"P#!*J%&1%!aF&36!$VEJ'6!M4))+SJaeX!MVimCm%-% +*!)J!p8J#jY4m)3&m-`Bd'a2!0cGHhAb2k9&Z8LF"8,0X!0pm6XJ#34krP+N!r-" +m!Lk9V`lj"4l`'Q1$+SJ4jK)!$!!+8#l0L$``5dcQ!k!5%&!"J-!LV#8jNJT8+SH +a#$FIBB#GZU!"5!#c*-GX!cqE0M,NIp3$&d"CD#F1d!++`V$TJRXJ!4K9-!!"U3" +!I)$JB'm!J%'mGXI-@jJ!+U!%BSF3!J#+iP"N%iLb!)`RmV4[bj2ZB%p'X!N4B@q +i"kH2l'%!,P5KrJFp%!E&NfiHcq6C'qb"00L%jT0rkJ*l)!cN@)9S!2D6%Z624AJ +qPDFp)!@,5(TFZ`8!!V83'-!Xf&-$I"9NJ(f`cJbK"ec!qm`$)b$(G)-i#+$S8pm +%*#j3")cRB!-'K1bdr)&M485LJ5U"PSU)L#b$3#%kcY`#8!8SJ!LB!!S35N3!&&! +#@C-(B!Fc3!)f+2C*J$0N(T!!!qmM$bb+$a8FNXjQ'&%BN!!#QY$Ik*-"JA(5T'" +!#D#4)(J$$X#*&LN!m!$!J#mSSRZMLr+!M,8![Y`#i!*`&!BNdGhR*(eI0&J'%-! +[0S!m+NIr"M4B"+KRmb('6`JS18!HMATV"3Cm2#q6hK!T%)J!4U-"($diDJZbTKF +!,)IPc#N!6C*JX-!@PD08!!JSQa6M!pD$$#"a!8!BX!'L8arm'Jj`S5f*X"'b68) +'Z%@&@&"ep0ST!%Y@iXKH14N#AN%1l-@'S`6DfaR)LP%[Y25'HU"qCXJq@*Gr8JY +*)I8)!L$&1cNDh"4b8KXUf%e"*Rb-%6'Ja0e0%'!ikH0lT!!)NJ8!+Z+8-JN!-*K +2JP(M!"C&m$J+P+E$6E)P-F5)9iB02*H"iR)H*dB!J3mJ0c)!!$J-aDi1C)@*5P% +VUNAp#36!"63"+!!&Q+%,#!*8S+0He*&+8LfU0`%$3d!Ir!-&!!61J4%)N3)N#R3 +*%)!!hUB)N!!%4Z!EH"`#!!T'!"J`"j!!33k'"K2`!A`!)"!"3X!*4)2DC3m8MK) +)!$JM"#J"eN!+ZL"225D5)$4NP@S#!'5!%Cd"+8%!J!)9J*X5`&JeSLheT3B(%D! +,r)%U5+Rq`"5)!%53!&0&3%49![8"#H!-QJS$lK3SH!'4C#rSJRm!$8)$AS8+Hh8 +8q9A!UK3%kcr!"D%K&+!dRm068i#V#`eS"3+mXNL`"K5!4f#M8f"J#)#dp!'#!,, +3UKp!-+!lFD!%2N$0J3%mB"k)!bM`"#!*(K!!f-#ZbJ"8B#(XkZZD!$b'1!%"SiT +8PDTU(D`fm25j9KNJ!6EV'Y!8R`!-S!$0'J$f`@Gp!+&90-BF$1!*q%&A"3&2!+k +UJISS!I,Q"J!&H-DlbS"P)9cX!!'3!!9$J!c8XJ#`"25"2dJLVJ1@D4Z1#!#d'SY +a!T6!$1#!9G-2T)%G@!#dB!C`P5Z`$)U2'9!"j@!*k!"M1#lJbJ*31bC!@a!!%c! +&E!$kJ!!CpLZi4'XJ$#a"8Q8&j-1-',BkB!H%!c[J&p,Y*-A@FA!p8QSrJ!C&3"r +d!d)J!b)!#C!!!a"J3&)6+#!$,J-%N!!%3b!'Q&CeQ$9&T6@K"G5P(Y)!)b[`fJ! +#U!#c!!3)N!$I3`p)`"H)!@%eEr+Q0D!k(QS%),2qJ)hQ63`3!C6#3-J!0#!#b!! +"3!j'PBie!3bJ2Jb%"33"fQc`F!$&b9S8'E,!!XjXDB"U-B$-jJ184@EVJ3NBY'H +JE2SF#4!'!J)13+MS)`'F!4C!RHC"11!A,!*CM&8PJ$jU+MK6V%IMVdiE+"!2fX` +)Q!3J!+jF`Gl3$pj4k`-#R'B)E$cB)!F53"M3!#,!QK4Ca4!')-#'%`!,d4)S"3- +!'3T)D-!08b!)3)!-F-6'`4D)!XNJp0@"P-S2r)!F@!#f)!C3P5Z3!'hAR'M3"Ie +!#03RZbS#%J!$JUY`S0hD99*K!ETS!1LZhr8*4)"Q``&#J'Re!(-+(eJ)TUT`3!% +"F(9Q`!CBJ-[!!5C!X`N!3F#qkS"B%!c-`*V8"r`J`4Q0"D!%a#d!),F19NJ1!US +L"-3Y!bJ(58!)a))Jbj-+9#R`"ZV9#@"FHp!Eq)%bU%q1JGi#!(bE([8Y[je6pf$ +JTSkQLJ%-,XcYUP2ejk!l*I"J5bl*0ENS&`6%J5#VFS2X"J3"QYCMCNfHUMb53#K +!!5)J#Y4FC[!eC#irX!3e9`fNe(f!$N6!*j!!!"q!!Rb!@Z&0B!!SX+`q*b$BJ5k +a!E%5CK!!&-![SKXY8(2&J4QJ&6%##S5!P'!$kU--1!,SS#$B!1m+!Ej6Zc!"bB! +SS+D(33'%`"D`!)(!cbD!bH"R&`$%J"4l#JVi@3F!!+L!Rhd!!-!+&)()U`1N+Qd +SM`#J!mbTTUS"FUaLU,N1S+Mi&af`$db+2PLDIJ%3f,Q[1%MiJ3k!$!FJ!0!KAE! +2H-&a"BY+i!Z`eqQl$e5"#*JC(`!1%)'D5`4k5IDY&9meV",AT"S0D+U-@,a0)3* +mJB*J9ae[-%J#GrIqKYdC-#m'!Z1m$,ShGG3$K9113X-`3!4Mp3!VJFZJ!lJFBHL +Vi$3!r09D`98&kcl!"-B1!N!#'m!$'+F*b*Sb!!C!938!#)B"0%3erq!0'$dJ-!& +53JS!"Yl9Fq`$AF"%YbS8'!'@!K`a['%!#iB!'b!!B,CPJ)"Pm!""3$T`EpCJ'%# +#)H!'d!'$J!bkjl+Hfhq!!b5!Ac`!3U#,MJ!%d%8$JJL`$3JJmTDA$K!#XLDHmJ" +jd`9%J+aC!J"!#eM$$8+B`1%ZbJ,+-'e3$$%JDl*H%@#(U3S*X-2qJMr1LhHS!4C +`36J$B$F"T04[BM&&3""!!,ae)(5!UQT8c3!'X!bBS31F!A``!q4'!0%!CX2J'Y9 ++QaQ+Dc3)YPe9hND"6Z`Kq)83b*SUB!,)MG8`)LC*lV8#4hJ%-)!)!!!1`"Q))![ +J!hq&%D!Ym%SA1Re0pZFB#QLJ,3c!&,J-,N#,Q)![J$8B`"TJR*Q"Ur(IHG!0B#` +$CN!rJ!p`BpKJ$@)`'*!!"Q!9BJ5$*4"UJ8"[d!FiB"e2BlKU(U*"Er!(*X!+!i! +&N!#&!F!)3#XbS)[D!!B3"#3!"+L(-S!('16ri3r`J1d&!Pi@i4i6qmS-dV%!B+m +Ji!T`94N`9Q[&eafV2GAh23YXT`(Qe+,9!%CA[E,AP*S2'-!9X-Ge`+lfKRb!"RJ +ZH#@i'-#i!S'8LJrFJ4+)!'B!![J&--"@cJ#km`@1!6)%!2hRMXpY2U!@(`$ZeJT +dC`d1EpfpZ`JA#["G*1"hr@rJ44C``"hVJR`J#mb!$R#rLTIa1Pk+J3@BPHAP!$, +JB@!!%E!-4-$$B!"af82)J0NJ!5+!DI8"FfTIp&jp!09QXJ!3[M)A(j5aFk"mCc, +l3!!c3#R8"iqT&(5!Bjl+BaN(3&rTHf["`3D%!f0J*[Z!S)b$#i+-)'3N3#NJJ$! +J@i!!p"8!S@%)5)1q6)Sa`+i9!E3j)$L!A8X%F[0TV3-3)"Y-J3"!"XaYEm!(@#! +)D#%D%&["J3bBRdC!&Q!"-5!%M'Ldr8CD)19ba!K!$@*!A1R1E-N+#!"T)!c!!$V +JG-%j2@H$i#`&NJ!G'!$(3U(fjH!mR)Zc!IKF)N$JQ3&fB&H2,J6`!B*!UHDQcQ) +FX)ja!#6'!8IUJJ&J$ck!F6J"2m!iX)!)h98T0!kJd"l'1!L"9#$1$[4K-`#8)9$ +8*pc%1!I$TjXQ%N"LT)+I%iC'G"dBd#BDR#D'&$eDl&%U3!2cb8@$J&FQ+qS!Jb! +1ZSQQ4YB,S9,T!DF$"4Y!jQiT%("E6B'5"JG-@J#S!L80+(U&C&A5Z3*bp93PE3b +1"3%)"4j!5I1#)ffI9+SX10*5&4#S!3-!#C!!JC*ZQ1jBT@S#81")ir3rN!!%4cS +&+'P&F+6*UNV0@9Sk#5KT1h#NPB#5CJ2(JJ'![b2Y!T5d&J!&(XAl(GcEkJM8J$c +!I0,P("49V!X-F-%!d!"F$R@"J!M3U6mekJQG!8!-S)!NS!3S!#@%!iX!$-J$&T! +!"%!!#LJ0T9S$G!)BDJ@L!#)3!4"!'(KU$H!#!N!81!3-)KS%JeL`$)a#!+!"U+q +AZ&N@`&8*3$4`"UK2@`3!%#!#(S#`0Y@h949)!NJ!4ZF'@i)'Ki#"P*(q"JI5R5" +)&`#!(a!!3jQ3!#5")2J%`3$I@J!9m!4@J'Np(hJJ!%L+q1F-"$B)S!*4!!QS!%J +!!LK!VPi"`@)$8!%23BUKJ4+3!!!2&MI(P5J!#(`")"!$)!!5(!,E)!rc8KQ`!!F +ET9dr%(!!*J%`L!5ZJ$@!!KF!!TJ!+&J"B!!0c!Y5E!dUE3#!!+5B%QMXJX*`fJ( +C90R"`'9c!d!!XfAfb4RC2J$V3J*&%'Rl@a3)"*,J%&K'NXd!D%%J+!28jQU`!K) +!#*K!02JQ)i!698*$S)q"J'%JaB+!&21#-!!%l#S510X`!!bBJLL3!!LL3-`@&JP +JDVp0XNd#NJ!*U!Be1a)i!a+!$&#!)X!!VF"$K!#M,3%%0S2i"NCElRKA#`!Cq!! +q!0)J3*T3J2@4Ue&2`TifHJd!d!"JrDd,mBG3Pa3!&41!DB#YcfbUZp[,HKQ8!A& +`&f+"d@B!Bk"(Ji##JJ"J`5%S)bal#"KYP%d&SX%aL!6!`"-!!SMpr3$!c+lC,(X +ch'aT!J850J@!!&!JCpdX38$B3%!JQ!Cr@jSBM3-##NL(T!B"Z%!!9$i[[CYb8bS +`"Lik0mARD5+i8S%l3*m!!"T3K[aGSPr!Tc2J8c0"8fJ4m&AD3Bkf6brkRqd+LX% +'8%((Qq$BMK)-F!J1Sa(i$"%HU)!4H(#+J3FS``-Rd3TbQV#2'UdB8%%VN!!C4*) +qFI!#rZNSKJ9)"CGJK'1l9[S%6VM3k49RJ($@E53`EG91AjB!`!!#"!!cJ!mQ!6, +!ZQ,Y"+1D0l$5%!M1mY,5")Kl6#!3-b$!Y)%!NGGB1cCpJ"LaH!52!NqJ-bJ!Gm8 +$U%!4`!+bNjbNJR5,"a3!,!J8Dm!!%!FlcL!f1)PQdQ[mlQD"*m!JB!!1X12qSif +rFA%'!9+"2`$NY+!MYqJCAVpPKR%B9-EKKP-$&cdD!N*Z'Z3[J!G-!5SJ"8E!H)0 +UU%S%B!XK%!&%p!I`1&N##)`5$&$A&--Ei!#b!!Li!3c!)+kc!3LbK!d-f)2cYc+ +B`XafZX+KQ4'%ca869!F`H!#Vi`1S$Rr`!el*$c!''1!Ir)&R%#jr!6B`!2rJ&e# +Y2a"G!i$(CJE-!"J%!NC!GJ0"R3!$Ji!"-!-B%-rRq5!)#24FRZIcHGjXd,BqcbA +cA0J"!$J`!&3(2!J!UJ-F,!Nm5&%9HJ0`#*!!i'F$!&)3!*K!F*N#CF!0')@'S'@ +TLKF!!(%!)QKC"[(4Q6P"d,+)J'i!J#2m%,4Xj2hS![FKa!`!%,0CZN0J%*%hCXG +dKd"9U)U@"33Q[4@RG#f,$i,k9U!U$++S2`3d%!$`5X!'!`8J!E590a!'b-!B#!0 +cJ!i`L)B3X2%+)R!a84fMDh33-+T+PGaS#2"&bq+!!!!%3)&('!J0!3M!pH(K&3K +##-JV"5%!+!!*6"6K1N%)"RZP+Pb%M$!(ZUN,B!S#JHK)!!m4"1!!('!$CB!+K!% +fX!C!J"Ci!fkJ)ZJ!!e!&jN!CN!!$Fb!J(3!Gd!Eb3&Ph!K+!#Eb"0l!'dS!E-%T +Qi!h)!4"3"c`lD%m'"k!0K)&XC0Xl3'Irl#$!$E`"1J!#j%"&J!1BR3bipNd,!Mk +l(*!!l5#J"bb2+Z!'eS"`[`0!%k4f91FZfeh!"CMU9IfUCh83)0L[!JJ`!mqp$B! +!"d!%dN$p#30dB!`!'I6q"Y4l#+!#D-#pYih#(JC!!"fS#(5!@Mrf-S$9bd$E1!2 +#hELV"LZ$-X+!'cJ"a0fcMr8f8"%'HaQJeX+GRD+"e@lE3hFBS2!KJ0TB'4F`!Cc +!'`!"&0iUC!6rIZ)P2"N)!4bJ[+pir8i(9MX)B!1Bh5M9H"$JiN0mf`#DUAfeYrE +AIYkKZal!l"8K'9c8L)3`K+-8X!,2*5#SJ001%'`!%8J#9)!*%)4-qHZ1!%&`!U1 +mP"1%R'"8YZa$!1a(ScRB!(I3%0$m"0#b0`#Yr2@qiNLAa%QBm`#J"+Me2G!34Uq +-V`K8SF*IK6N3k&8mS2rcFq!!0)(GlLjL1`)$#NePFh0KCf9-EfG"!*!d9%9B9%e +38b!"!+2&1JHMa6S9!!!"RJ#3!cd!!!%C!*!$1T3SXc%!N!EXc!!!"!JS%!Y"J`' +0"!a!Td'6-'15Z$(c"J3-'5pJh"K)--*#GjmB5B#LS%QC1A2#R#R$j-fC-!))8LQ +#K8S6+&0!%0`TNkC0R$Tj#Kh+Fe3a(3F)pPMS#3D0&cPJU1!Bm-0#H*j#jG-K5JZ +aCcaE!*!!&!#EJ"NF!3%!&!$32ecr5K'G5eFJR3S2dl!"3841'MYPj)#3!2(8+95 +T9!&B&BK9+eH[B(H+*@Yf"N%%!4@d!E!dCN!N!")dHH-'iKX!IcJV&[$Rclm!h36 +iBHdkJ$F"I@5l"Q!!!!%!8!#!J)dEZ($LI85piB@"S!lR1kP+4`L!ZN)$cA!-F%@ +3!!-!K3(DA*&#KD#%Qe1N%233m0mrSJ2DZ5I)!-#"CQJ!&d,KFVBXUcSE)`X0S@Q +fPZ[*BV'XPQ5"ElQr@m5Z%CIKf%9[Vi8pIah)Z(C8KBDder`CBqCT8Cch!!)!"Qe +j58026Q9-EfG"!*!d2j!)!3#Ma6SYSm8k,3!!!EB!N!B"A`#3"&KZ!*!)(-i!!!3 +)+*!!%%'$!@Am!lD`)80JI`$p!$#aBN!%!$!'l!"!@)!%"YVN56,NLC-f!JMq@-P +b*F'!J&iZ3"1`CFZA!',Lh,Pc9$%G,AB'X282'C*c(r!`F!0J!!!'E!*53q,Q(cY +8Q&"jBS$Racm"MUDiqkG1$"m398"!qB%#!*)I2!5L5N6eRc09'HSL8k9"b#NK*ek +i!1!!b4X!"P34SI)%a"He)'JLD)(NRcG3+9T!qDI0,f!36"fSIG'@J'(%US3`GJa +CXPSBE@(L(!![i!Gq!Irm#rM[9mar2i!&m2F"h!"q$ic$#`$[J$-1F!jFdJ((J#2 +S"J!"JL*!1j3"J8""#4!)"*J!`F!*Aaq!A!*J!1lYJjr[A%`m"hlr!!IJelr[RrM +$#!&Im$-)(QIJSpd)qH38J6JAT36!!EJ*49!!#!'3!+%-!"63"3F"k%%3"`"`#!" +**K'NJ%$rl2C5!Fk!![&k!!!: + diff --git a/contrib/Messages/notify.c b/contrib/Messages/notify.c new file mode 100644 index 0000000..5db4701 --- /dev/null +++ b/contrib/Messages/notify.c @@ -0,0 +1,275 @@ +/* + * $Date: 91/03/14 14:24:09 $ + * $Header: notify.c,v 2.2 91/03/14 14:24:09 djh Exp $ + * $Log: notify.c,v $ + * Revision 2.2 91/03/14 14:24:09 djh + * Revision for CAP. + * + * Revision 1.1 91/01/10 01:11:09 djh + * Initial revision + * + * + * djh@munnari.OZ.AU, 03/05/90 + * Copyright (c) 1991, The University of Melbourne + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of Melbourne makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * + * Low level message delivery to a Macintosh running "Messages" + * (handle lookups for 'macwho' as a special case). + * + */ + +#include "notify.h" + +NBPTEntry nbpt[NUMNBPENTRY]; /* return lookup storage */ +ABusRecord ddpr; /* outgoing packet */ + +#define LINEWIDTH 226 /* pixel width of Messages window */ + +int charWidth[128] = { /* char widths for Messages 'applFont' */ + + 0, 8, 0, 0, 0, 0, 8, 8, 8, 6, 8, 8, 8, 0, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 3, 4, 5, 9, 7, 10, 8, 3, 6, 6, 7, 8, 4, 7, 3, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 4, 5, 7, 5, 7, + 9, 6, 7, 6, 7, 6, 6, 6, 7, 4, 6, 7, 6, 9, 7, 6, + 7, 6, 7, 6, 6, 7, 6, 10, 6, 6, 6, 5, 5, 5, 5, 7, + 4, 5, 6, 5, 5, 5, 4, 5, 6, 4, 5, 6, 4, 9, 6, 5, + 6, 5, 6, 5, 4, 6, 6, 8, 6, 6, 5, 5, 4, 5, 7, 0 +}; + +int +notify(msgtype, msg, from, user, zone, iconfile, lkupCount, lkupIntvl) +int msgtype; +char *msg, *from, *user, *zone, *iconfile; +int lkupCount, lkupIntvl; +{ + int msgFormat(); + int i, j, err, fd; + char *cp, *index(); + long timenow, calctime(); + + EntityName en; /* obj/type/zone to lookup */ + nbpProto nbpr; /* NBP record */ + AddrBlock addr; /* Address Block storage */ + char ddpt[DDPPKTSIZE]; /* outgoing message */ + char host[MAXHOSTNAMELEN]; /* local host name */ + char header[256]; /* "Dispatch from ..." etc */ + + abInit(FALSE); /* Initialise CAP routines */ + nbpInit(); /* Initialise Name Binding Prot */ + + strncpy(en.objStr.s, user, sizeof(en.objStr.s)); + strncpy(en.typeStr.s, MACUSER, sizeof(en.typeStr.s)); + strncpy(en.zoneStr.s, zone, sizeof(en.zoneStr.s)); + + nbpr.nbpEntityPtr = &en; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + nbpr.nbpDataField = NUMNBPENTRY; + nbpr.nbpRetransmitInfo.retransInterval = lkupIntvl; + nbpr.nbpRetransmitInfo.retransCount = lkupCount; + + if((err = NBPLookup(&nbpr, FALSE)) != noErr) + return(err); + + if(nbpr.nbpDataField == 0) + return(-1028); + + gethostname(host, sizeof(host)); + if((cp = index(host, '.')) != 0) /* discard domains */ + *cp = '\0'; + + switch (msgtype) { + case 0: /* special case for macwho */ + return(nbpr.nbpDataField); + break; + case MSGTO: + sprintf(header, "Dispatch from %s@%s ...", from, host); + break; + case MSGWALL: + sprintf(header, "Broadcast Message from %s@%s ...", + from, host); + break; + case MSGMAIL: + sprintf(header, "You have new mail on %s ...", host); + break; + default: + return(-1); + break; + } + + /* fill packet */ + + i = 0; + ddpt[i] = (char) msgtype; i++; + timenow = calctime(); + bcopy(&timenow, ddpt+i, 4); i += 4; + ddpt[i] = '\0'; + + j = strlen(header)+1; /* include null */ + if(i + j > DDPPKTSIZE-1) + j = DDPPKTSIZE-i-1; + bcopy(header, ddpt+i, j); i += j; + + if(i == DDPPKTSIZE-1) { + fprintf(stderr, "Bogus header length!\n"); + exit(1); + } + + j = msgFormat(msg)+1; /* include null */ + if(i + j > DDPPKTSIZE-1) + j = DDPPKTSIZE-i-1; + bcopy(msg, ddpt+i, j); i += j; + + if(i == DDPPKTSIZE-1) { /* ensure null terminated */ + ddpt[i++] = '\0'; + fprintf(stderr, "Outgoing messages too long, truncated!\n"); + } + + if(iconfile != 0 && (fd = open(iconfile, O_RDONLY, 0644)) >= 0) { + lseek(fd, ICONOFFSET, 0); /* MUCHO MAGIC */ + if(i + ICONSIZE < DDPPKTSIZE) { + ddpt[0] |= ICONFLAG; + read(fd, ddpt+i, ICONSIZE); i += ICONSIZE; + } + close(fd); + } + + for(j = 1 ; j <= nbpr.nbpDataField ; j++) + if((err=NBPExtract(nbpt,nbpr.nbpDataField,j,&en,&addr))==noErr) + err = sendMessage(ddpt, i, &addr); + + return(err); +} + +int +sendMessage(q, len, addr) +char *q; +int len; +AddrBlock *addr; +{ + int skt, err; + + skt = 0; /* dynamic */ + if((err = DDPOpenSocket(&skt, 0)) != noErr) + return(err); + + ddpr.abResult = 0; + ddpr.proto.ddp.ddpAddress = *addr; + ddpr.proto.ddp.ddpSocket = skt; + ddpr.proto.ddp.ddpType = ddpECHO; + ddpr.proto.ddp.ddpDataPtr = (u_char *) q; + ddpr.proto.ddp.ddpReqCount = len; + DDPWrite(&ddpr, FALSE); + + DDPCloseSocket(skt); + + return(noErr); +} + +/* + * Calulate the time in Macintosh Format + * + * Epochs: + * UNIX: time in seconds since Thu Jan 1 10:00:00 1970 + * MAC: time in seconds since Fri Jan 1 11:00:00 1904 + * + */ + +long +calctime() +{ + time_t now; + register long diff, mactime; + struct tm gmt, local, *gmtime(), *localtime(); + + /* + * Do this by determining what the given time + * is when converted to local time, and when + * converted to GMT and taking the difference. + * This works correctly regardless of whether + * local time is Daylight Savings Time or not. + * + * - courtesy kre@munnari.OZ.AU + */ + +#define isleap(yr) ((yr) % 4 == 0 && ((yr) % 100 != 0 || (yr) % 400 == 0)) + + (void) time(&now); + gmt = *gmtime((time_t *) &now); + local = *localtime((time_t *) &now); + diff = gmt.tm_year - local.tm_year; + diff *= 365; + if(gmt.tm_year > local.tm_year) { + if(isleap(local.tm_year)) + diff++; + } else { + if(local.tm_year > gmt.tm_year) + if(isleap(gmt.tm_year)) + diff--; + } + diff += gmt.tm_yday - local.tm_yday; + diff *= 24; + diff += gmt.tm_hour - local.tm_hour; + diff *= 60; + diff += gmt.tm_min - local.tm_min; + diff *= 60; + diff += gmt.tm_sec - local.tm_sec; + now -= diff; +#undef isleap + mactime = now + TIME_OFFSET; + return(htonl(mactime)); +} + +/* + * format the message to fit into the Macintosh Dialog box. + * + */ + +int +msgFormat(msg) +char *msg; +{ + char *q; + int i, j, wordWidth(); + + q = msg; + i = j = 0; + while(*q != '\0') { + if(*q == '\t') *q = ' '; + if(*q == ' ' && (j + wordWidth(q+1)) >= LINEWIDTH) { + *q = '\n'; + j = 0; + } else + if(*q == '\n' || *q == '\r') + j = 0; + else + j += charWidth[*q & 0x7f]; + i++; + q++; + } + return(i); +} + +int +wordWidth(q) +char *q; +{ + int i; + + i = 0; + while(*q != '\0') { + if(*q == ' ' || *q == '\t' || *q == '\n' || *q == '\r') + break; + i += charWidth[*q & 0x7f]; + q++; + } + return(i); +} diff --git a/contrib/Messages/notify.h b/contrib/Messages/notify.h new file mode 100644 index 0000000..1505c16 --- /dev/null +++ b/contrib/Messages/notify.h @@ -0,0 +1,51 @@ +/* + * $Date: 91/03/14 14:24:51 $ + * $Header: notify.h,v 2.2 91/03/14 14:24:51 djh Exp $ + * $Log: notify.h,v $ + * Revision 2.2 91/03/14 14:24:51 djh + * Revision for CAP. + * + * Revision 1.1 91/01/10 01:04:54 djh + * Initial revision + * + * + * djh@munnari.OZ.AU, 06/05/90 + * Copyright (c) 1991, The University of Melbourne + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of Melbourne makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUMNBPENTRY 200 /* maximum users we can look up */ +#define MAXZONES 200 /* maximum number of zones */ +#define MAXUSERS 100 /* maximum users to send to */ +#define TIME_OFFSET 0x7c25b080 /* Mac Time 10:00:00 01/01/70 */ +#define MACUSER "macUser" /* NBP type registration */ +#define OURZONE "*" /* Zone for this host */ +#define DDPPKTSIZE 586 /* maximum bytes for a DDP pkt */ +#define ICONFLAG 0x80 /* indicates ICON included */ +#define ICONSIZE 128 /* mac ICONs are 128 bytes */ +#define ICONOFFSET 0404 /* into a ResEDIT resource file */ +#define ICONFILE ".myicon" /* the ResEDIT file */ + +#define MSGMAIL 30 +#define MSGWALL 31 +#define MSGTO 32 + +#define MSGTOARG 64 /* an internal flag */ diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..9b3a5de --- /dev/null +++ b/contrib/README @@ -0,0 +1,32 @@ +Contributions +------------- + + snitch.c - a mimic of the "responder" INIT for Apple's Inter*Poll + : Contributed by Rob Chandhok, Computer Science Department, + : Carnegie-Mellon University + + cvt2apple.c - convert from CAP's Aufs format to AppleDouble or AppleSingle + cvt2cap.c - convert from AppleDouble/AppleSingle to CAP's Aufs format + : Contributed by Paul Campbell + + lwrename.c - a daemon to reset LaserWriter names and types + printqueue.c - allow a Mac DA to access UNIX lpd information + : Contributed by Ed Moy + + aufsmkkey.c - tool for creating global key for distributed passwords + aufsmkusr.c - tool for creating non-cleartext authentication password file + : Contributed by David Hornsby + +The following are not automatically made with CAP and some have not been +tested on (or ported to) all of the systems that otherwise fully support CAP. + + Messages/* - send short messages to/from/between Mac and UNIX users + Timelord/* - set Mac time from UNIX host (maybe using NTP) or another Mac + AsyncAtalk/* - asynchronous AppleTalk for UNIX hosts & UAB + : Contributed by David Hornsby + + MacPS/* - useful tools for manhandling Mac PostScript file on UNIX + : Contributed by Ed Moy + + AufsTools/* - tools for manipulating AUFS files under UNIX + : Contributed by Nigel Perry and others. diff --git a/contrib/Timelord/Makefile b/contrib/Timelord/Makefile new file mode 100644 index 0000000..abfd267 --- /dev/null +++ b/contrib/Timelord/Makefile @@ -0,0 +1,17 @@ +all: timelord + +timelord: timelord.c timelord.h + cc -o timelord timelord.c -lcap + +install: + cp timelord /usr/local/cap + +shar: + /usr/local/bin/shar README Makefile *.h *.c *.1l \ + tardis.1.3.sit.hqx > timelord.1.3.shar + +clean: + rm -f timelord + +spotless: + rm -f timelord *.orig diff --git a/contrib/Timelord/README b/contrib/Timelord/README new file mode 100644 index 0000000..f7a09b1 --- /dev/null +++ b/contrib/Timelord/README @@ -0,0 +1,34 @@ + +Copyright (c) 1990, The University of Melbourne. + +All Rights Reserved. Permission to redistribute +or use any part of this software for any purpose +other than as originally shipped must be obtained +in writing from the copyright owner. + + +PLEASE NOTE: in the netatalk-1.2 package, the file + + etc/timelord/timelord.c + +was distributed without appropriate accreditation. +The original copyright notice should be inserted +before use ... + +/* + * timelord - UNIX Macintosh Time Server + * + * Provides: + * Time Server + * + * written 1.0 May '89 djh@munnari.oz + * revised 1.1 28/05/89 djh@munnari.oz Add Boot/Chooser log + * revised 1.2 16/07/91 djh@munnari.OZ.AU Fix argument handling + * + * Copyright (c) 1990, the University of Melbourne + * + * You may use and distribute (but NOT SELL!) + * this freely providing that... + * + any improvements/bug fixes return to the author + * + this notice remains intact. + */ diff --git a/contrib/Timelord/tardis.1.3.sit.hqx b/contrib/Timelord/tardis.1.3.sit.hqx new file mode 100644 index 0000000..11052bc --- /dev/null +++ b/contrib/Timelord/tardis.1.3.sit.hqx @@ -0,0 +1,337 @@ +(This file must be converted with BinHex 4.0) +:$R4KFQ4TFbia,M-ZFfPd!&0*9#&6593K!!!!!$ip!!!!!"Y68dP8)3!$!!!q2A* +-BA8"!!!!!!!!!!)!"R4KFQ4TF`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!&*%49C868a%)3#Ma8N@T$TKr!! +!&aB!!!!!!!!3j`!!!!#jD`!!!!!!!!!!ll-!!!3)#+!#Qi!8$JT84E#K`iF3)4U +J%dB1Q64ck!JJ+)9)%5Y8QM!K!U)KN!#'$N`&l2Jaj-L5"%p'R$QU@"),$brJR-Q +cTmqI3)-+(3SdJ#X`H$bN!K%M4iiB)+Ui5@1RM*`jDHMN!I('$)JQCGL)H90(MTX +b!-!%P+!@!)@f&YTLD+X"L4i!U-"JX!Y!&aJ*I,Q"BF"A'"J$I-NjLB2N(!"J3Xk +m32!L!9F!%Ta!J['"bEF"!,!m3B"#)!!$CJLm`)'!Rj-kM@%!F+)VJ!86XfZ$H*% +l!)3XMel`J#,PbC!!(c`!!%S(%U%IRA(a3J"Y!+#UTaS&A,L6)P5Z)&I1h$P +d,0)G#$P9rESSlD1J""p`h4Kkc`!B[0i0!!&r!V6p!iN332a`%J-JI#'I%p$`jap +["""SB(i*,YJJE`m#%''""eEi#)-1rLGKK`TqH'&r)R*)BBN-1Q&&3##id4mS+5! +($`M)rI12#$)L)%)86N35ii`U11(&Dbl#f#10,lL3!"q2rIdBj*!!#)"5j*&e`!& +!$-!)!!))#,`@LJC1k2-22CF4!-S'CIkM$JKi#'"+Qq!%Y!!P5#R&P&0358@999K +TaC9AB)P&PPPSYF8@3R$*4CGG""LfPai%82-AT2B-"UPJL&(Ucf+024ECC*9GPYP +QRAd@fQLP$B5DDUbj"YXj-)"J!Qm392'K,JFB`"N5`-KQ4N$Hk%VE!6Vm'L`!``, +JM,'k*!#'XX)'j!X5H"#!#fd%)%-#%,6`%a"kC`5N#VB!N!#TLjSPR'3'HQB`m)) +#h-+#APZ1S)X$Yi5d#i!Dm%V`JJV-FZ[([0`b)KmBDJcm,lH+1'`'Yi+!S-Dr*8! +"J"`@-jXa!'iJV!X!6-Mfa$[jaI[#!#),`-[#"SbR3#V)5-`Y&3kV`5d60Ur,3Fl +F8L!b!5+Fl*CR"1KAKi[rB0)B#$#3!0#M%#lmS`R9rr##Y6I!fIK"+VSiC`)3YSJ +,`"NH[,!!E32)Jki)5,KKp3X6X)f2fQbMBc3&AI-!$`"JLdfff@MMVFX!`V`GGpC +dXehcfSIMXRIIm#J9pJGMPae3iC!!$b#*iR*ldrMKQ"Jq!#*l3e#e*NJ!!U`8J2# +'K"5,Dm)f(UTRhIVVX31!a-9`bmd,ff$NlXhZ`-!Zqm9%,1i0fdMd2!!,(C[aX4X +GZa'#%LKHl-B3kJ"J4`K*81Kp$(aX"!!%fD0[QJ6C4m#(KJ"J%2rm"3#3!!'d"e# +KE%P'3m%C#0#@KK&XBVS3J$*d*6%KR1"BI[KIG-iJ!$"%li#d%B!UXUF%0'!'2@l +`&3rf`3m`Z%"A33LI(5BM!Zrp)!@B'8m!CYFa15$"Jr65"3)X)%%+JN%!"!)@%pc +"!!,mJa&'HXh63%##0q5("V`4'-&dPN!PG%`0(q2BaH3!2J'-VhcXfk,m6#1#'VU +2!!!JJ4Q&!!!6e,#,A`5!$-bB2MED!&S%50B(D$$"#SS!D,9K"`1Rk%$D'-"AH`3 +!)!0!$IR!3!"#r!B"!,3d+rb$(e"#!,S)%!4hA()b5%L$&fN6!''!S'Vm-*TqX-3 +dCX3QDMdbJC-BJ$9Qp#dB)!JFjJDhZE4",J"f3"F-&XH-d3@!$iB,!"[f4S'UZ9) +38K!#-8Q*L%F#LcFTp+)@%!&+Z6'$P%aBR'YUBi90CY-18Z#Q*Z8fcJ$8DJ!#!!4 +"rP%")(KaNIkj'"BeTX@0@4-B@J"%$%lLJ"S+!48!m!#k"#!bIa##MdCMJ4RFZ%8 +i-!!@#(A"'3a!Ja'q!J`rbk%rp0%#)(`5PGFKKh`-kN%1r(1)$'"!eT*BKa""L$E +lX-1)9Q3KQfTST`KLdBNbY+%*"E@R'%U486d%SU4##+K-KBD@Z13P-1d(3U%33CY +Zj%3eMD"0j[K2+$c3*QkN#43ID*-dcTU#0L(MV%8bNbri``!410%rA3@&#pV%LM5 +"!"F#80Tr3+%$B&e93fH!(a#d"!%)J)%!%%#(3++!M(rS3`MXZfNGL-,CcRVfXk! +P#!&Fd!3S3!%%S`d#&8iEfYDkpV@K!X-3c+3!)*c$#2c!$3LL3),6)S##)T!!K"' +qS34!$"!8)`#$16aMJ0R8`33Im!%J4#!%*d3$IIq`4rf8%)!c)'"lq5%&!G8%)%Q +mKJ4)!)%(C5!#+-cJ*!+`dQXUBkAffKDhZ"'",[bKLYRk`a3L3-4`4I#&ebL"B$l +`,TKJ-$m&C+GM0JJ30-3N!L8!)68dX["Vj,!q(!JJ%QY3J2SH-!9Q#8!1!IK!%$6 +dQXJ%L"`%8X)(c#!$#AJBa!5!`bI!J!))i#!!qaKaLGq3!'02k-86iTUMFI6K$eL +3!!mc3LM0"N!"!'mNH8+8$*!!!PbXLhr33JPI`$+5pSX2%6cR!h!J3S$dF!B)J$P +"lAd[!!3JAHTD&d`JJ%))6Q+$#(`"!$SJm*0P%)`Nd+M32jV"r!B#a0*d!,[eU0m +,![#DB5$#5TC@3QPdF!Cj)B$"!&!!#!,`i#qS&`!4hUmjP(J1#%$#"M`!SJR5)%F +BG&F"J"J'!q3*!"fp33"5!-)%6T)#B)K,2rZ&KaP)J0imM`!*EV%e$)B"Lb'`J3! +$rBFr3,!-)@!""1N!J!ZX-3a)$-%0k0#5Ca+!!!d[VFXiN!!!,`"`!#(!F!3)J'& +!4,"4"%JfIad)!Dd9N!$3+)Xl!V3Z!3"DJ(!!R%#4$BFK#`5H8-c%J0B1!)!)+!k +"0&+F!J"!JI[qN5j0!rS-+"""!QB,!"JmZDj"3!"#"p)"m8VA$"JJM@NkF!Cmc-# +$10%!'+T%!1Q')5!'X(-d4,"HP5IDJad()Ue9-!%21QN&1NGM"k`3lK%`)!,d2S- +9e$CVYiaJ$Id4GiaT,3!c`%"Yd%#l!DC3'KHiJ3!Qq!)B3-#!03$a0)P9p$bk-@H +6km!--H2$i2YMM@1$34TbjN!`PJ!#Y!1"0[d`a16clPmp@*FfrM#"[!'`!(X$B!3 +FPJ%-EF#!)%J!!N5@!3pHhe!mJ"F)*2L#K906*Rm`3mi#`$))VR"U'9JT3D&!JC@ +3!!Z#kNjiC"T`Acl5"3S#@&Q1@*iY2l4`K@LmKX#diBFYN!!X4a!m!E[DaB"e#D4 +p(LJK!QD!3'2!!!JZrm-AR2&-!!cJl[!EJJ3I)!%IN!!J!@)0CZ!&S3)$'ACK80" +EF&-$LcCR&$"[1ACjZX!2k'!'1S"RHXCRJhBG@'!'+$!%NX8"-K!q'#!#bb!#iF- +!*lJq-X!3%K!"+qB$lV-2k9)%qJ!!$K"q81!C$$!EqX!2dR31J!!1iBF&Nc%$&d- +`m(-a1T!!K!4iJ@lJ'3G!DE-#"1!!0h!`"Z&("r(hE)#@CbD!"#4`-3J3"JK!)&C +S(A8`"0)`JdGA2q#MFA)B%!j3Kd4`Kba@"a#3!!e6%!"NS!pMYJqQ%!3S``!G03r +J)!-BF!C')!YB)!C#d&j6%!4R3!&DN!!%3L!%&"!"e!!9M!39!Q!&!L!0KS%1DI+ +(V*J0IbJ&58!(!a!L3$#$IaL)JfJ![+Ca8b!%CX!1"(Cp%1!$JQ"G!6%!j!!'`a) +!8"!%3l!%Ib-3L3!!H*!!&Q[4&Qm4%(%4%(-4%(@K"`*J!RTK&`+J!jF#MPbJ+H! +S")G"MQ3!+Si"'C*"'CEK4+H5IkSL'MVR+UQa'Uf"*&EJ$pM3'"`!!LMJ*"!J5rP +"!Nk#!!`J)`2aL!C!)'TK&"N%"`563AV!'aQN"M$J*"U!(Ja!8'dJ%)B`NJ!`!4$ +T"p!!"$#`!`#J$Nk!JaX4K*B!!-`3!#L'BKb!0GT!)$eJ8[`3P0T'19pc1Gm5,Vh +8-LS!1[qJ$5q3!"N*p(Bjj#A-9$ADJ!5#X$MDN!""+U!%@%NZ!L%"ZX+92`"%Tb" +1+j0"9-"B(p"aj3)!p,!biS3ZP&BEN!#J5Qh"0BrJ0ES%!VJA2C6K*"53!!S(-#q +TS!U8BcR1!CMS45S%B*L)U3TYJ3Tai`rDi$8bP%%-B*4r'CMGK*Q5162RiJDBUCR +*B4fe)3k,qCQ2Z8kB#3B!*J!#%!Mc"!F"!!Z"B!C)d$FDNJUjB"lJ3MJ#Nd-"S!& +4L3T4)$,(+4rG`32I%4lMX4c0m4aR)hr9'!$Y-6+5!"rbm3)&F"h53#$S%C3!`!& +$4!!ii!r)B#3*mJeQiaC0K5)3%J4-*J%`X)BUFP3QiP0&45*)4CmrYCp4jCp3*93 +'5U"#P53!3#9--NZC*#9#XL4A-Pm#!!'mX3#A89HimJ'+K+&h#3#D!)#[-99GmL9 +K8JGMdLCSNPGXBLC[%LHU3#GXe5EB!&GYiJadC9FSNPGlC5Dmm"q*"CL-j9L3!#9 +C!8"CPS9Crr%DB8!$B5!$B3!$BH!#BF!#BD!#BB!#B@!#B8!#B5!#B3!#BH!"BF! +"BD!"BB!"B@!"B8!"B5!"B3!"BH!!BF!!BD!!DKJ'"K!'""!'eK%(Xr82bZ!FN!# +3!!M3!!J*mJ1+K!#YF!i-QJ92)"a63!95-!)ri#3c!`T33NX4i"aRm#9!N!"R-)! +"-Y#3!,)!"'iJ!9TbL4X&!4J3$F!!"[E3@-'Q9qiT9!4K!c9!"@K3"P%a&99a&9Q +a&9ha&@%a&Q9a&Jh3"'%`"QRJ"R6`"R1!"L"!"@R3"X"UE@P3"Y,+&#i`!`6"!5j +""!34!%N`"%i`!Jea)dBJ"89J"1d+%r6'%LrL%"NR!!N3"Ql`"Qk3!!GY3"Cc%!! +%%a!+X+rpqUm"@`Gc3"!#3,!%`@j58!CcB"9933B%83%8-!9K83CM3!GNHUhC@JB +F+`I$UJ-4L`!F#l,BUUdTZl)Lkl)"`@iY@`C-m!B@iE!+3(!"B83Nea!5-'F!S#A +i!!#%%!!S5l4'Ll3"-4!1%3$!%""H%K!ri!m"m3Fr+drbp![q)%qJ%%m!`,9DUaa +KflAB)3Mbj!Uk)%qUS!TTZlB!d,C[klA#3,Gc#`#Zi,Ci#lHJJ,ClblCkUlCHklH +#'lH"blGS1`!k8LGN+a"4#`!$`,K9Hl8rUb2bC,N!!!j$F,Rr`,QHffZG#lUILlQ +N'lUP1lUQQlUSZlULflURUlMr`,J"%'(hUMEV)4cTZUkC'QVPm3'K#TJd8SB[!!' +l'bBM3`%JX!4@-KRENAG'iJ+Im"UdbM5qF`ir`!',JaGdX$J%J!T[!!-iN!!!,$! +1@&"E%"!#X)!(#D"4'-!!Z!!&B9!(E+!!H1"G-5!Z###qKa%$q+!!IL#qRI32"!! +$T9&%*i%#51#5iFX#*I82q0$!r'!!-J)#fLE"$AT**L!MHQS$i#ZqTpSIDN!!XKG +bd"!%A#+qj'Zq)A$#,X#pU0!(68*bU$!(RS%$VZ%&#F)&*T)%RX30+,!)"N!*"!% +)16!rD"3KV(!D+6!r*9%5#[!#$&!%RN3![c0RI-%#Li-2ic%!ki%+l%!!3K`3X8- +B&`0*Gb&rFU2&b4%!AF`1Vq%36GaD("!!&m-!5H!%58!&"+%ci(%&"'%(cIL-"#% +)j8S3MP"G4-!%"+%*laU["'%+PAUT"#%,N9`5cH8,4q!%8U$)BpN'cqT"!Z%"EL! +((pZd+T!!Zq`U%$C!(-E4Y'bNF!#J-4!K6`bJ-3%J#qh+M4hA!0cM%$T5%K%J!r2 +kb`!3!3S4%$G#c"(!#1hUS@T8c*I3c!X(c+R3VKj3IX9m#qeU!-VF#qeD!-UX$1h +UXm!m$Zd+Zm!FMFJ-ZX$F$lP-c",!XmK-`Ip3%Urhc[@-'4J`c2NX!8l5%2*d!b8 +a!F*-%!Y`3Ne3"%a!%4D"%3GG"3Qpd"9a%A2`d"(0d"50!%43"QB3[fa!"h-!)'' +JV35J"IjD"LU,"Qp`"dN`&A3!!J!)9'PYC@a[FQ3!!!!!!!!!!!!!!!!!!!!!!!! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Bf4PGP454&-K!+2 +&55@N1f)"!!!i[`!!!!!!!#UI!!!!!%N0!!!!!!!!!!!mpJ!!"!J)i%D)J$B1#[4 +%X+($Ka!K)U#5TNdC0QrNN!"T)i$J'$*Pl+Q3+L)JH3BSND4+Pbi!#U)`UPU6 +%3abrAZVFbE1Rcjp!I@U3!%)QS!+(-J$B!+!$J!m!3K+iU0+N#"-UEGJ8$4S36%! +*AJ&3#'XK,)D`'T!!k)%+"S2D!0l!5&$E%3b$YqE!'+$Va8NF*1F!!"0bjJ@#&`P +![!%J`3NN'"qBI"X!!-X6"#J%!M"JKX!,(!Mi1DN$'!)-%iXTN!"B$+(+)bHk+-L +!$!2!'3*H!qKa$CZ#'GUfF3Y8`e[A"8I!EqI@!VZ"-"-!QM0l%9eA!ej)m"5Jd0a +B#5"LS6`k)p$&#a9LQjXlRpik-""Ua*+!3SYI32(N!cJ3%Tm#I`!-r!F"(!"m"i! +("PU3!"d!1$6RKi%DB$'H"ZaTd"`S&6DR"h`!D&!#&!"B)0k#&PT(KB%L5(J'KHL +*d*`Gl,PS(43FL[$KJH*PmFJ22!!!5#UjJ2#"J3LF)3*l#$6("*,0NF'NG4iJQBS +N6l`$S)i[m)$''h23`D12UB!LT)%%R+%&H`3d4`'DcE(!TLi-Q--K!5"!!BYp!&3 +*i"Nba,!'!"J!S)!5D!"3jTS+`$D!2l9*D-CKLHTL!$d5STQ++TE#BUNN,``!'`0 +i#!'%K$q!Ki&N$#!!`KIL13%0#05T5Kd"STB+i+UY[KSVV)E@#Ki$Z,i'$@!!R-E +DDJ!USBTBRBN+KK'IHN"#G3bJ30fR'-#`3SF5-J"H%'f!i)B!B#$J,3"NQ&'@!Lq +`Z`"X!A"4EVX[[+X,!0L)pi)!X#eJMULNJNF"UJ!%kbUa-2!+!E)-+-ZX!Fi'K!Q +(&*J!K5eiQN%Z1rA#9S!9,faEiJ,DK0aK[mLB2$)V*XZiJ$!Ypk[TYLj6BR+DZLb +!iEBi,b$+cIhbBA+515-bG,q+(*fcNqKaPc-Fl$QpJ"a4pmZ%%-[k"i5H30Jkm$F +-&-bUX)$"!!1b&#J'U"+S-%ZZ"b!8@[(&'C-,J4+V'+TaZ5$m5B!*%(3'ab4Jq+$ +MP`3!+53)30`Cd'd[3)"+&#!-%80m("KkaTVf#X""SiqB!8((ZK3!PJ'N%f#&K!5 +LbX"SJ)%J1hA!XXBfXm)"m+VFmp8A8'GK!9-F"*B3#-%(%0J@%#!-a&F!Bf`%*)J +8m8N!!hS5E!'%'3(4%50X#JMb[5i+30dLq&4JI5#*i2Z!STi3V$Lq!Pl-Md100hU +JEk3"L!'k'Hb+&!'`)#%`81",#NJ&-Z`LS2q!*5"88%*m'2!'"NaKJK)%N!$'8!% +&KfPJE`VJN!#(3'3"`4'1"4bb!+%!)!!cJ-"KCG,!R1VN10XiB)9PJK4X!-3k!%L +'!'QUJa1Xm!pqa!i%*(!$!"!J!JAX63)H"+%)Ee4#!``Z,,")iHdm`$d!F-*K4A) +!K`iMZ5M8J!m!`!1!k%#!)DJ$!*!!d'+K12!'GL@JAE!KJ!&'j)BLpXYQ!N"#I## +"K$k+4KF()%F+iF#!%)$("5Pd)aa$N!!%!-JJK6%3JP*5'!*0HJBp*8U!!&a6)5' +F!$B5m!&NN!!!M0VSL3*R--#A++$!$1N#!GbS9+3'!!%*-F%G32`(2Cc3&p+FJ`F +i3*EXR#JF!GLJHCS*(-5!i"8"U-"K)J!H#q%@(aZ"L)[0K-"rr"-I%`J"25Ji*`" +53%VdQ%#3!1e*hLTED4XVPL)hFSS2!@UB(faB$'1r%di!Q(%i([aL(+R3KC!!TR8 +'#B"!#%U!"(N%*3)KJ#!+5JLF!1J!!#8)`J`-+'J`3*!!d)9f9""mfKGX%%#"G#U +"%LVL3%8[QY(0,3J%52J6&0,j!UFKJ!0kBS"+%8QT4d"J@e"!!L"B+39!8!HH+XJ +T!+!!Q`2JikJ!J)05QHT8!-#6#9+&3eA"JG@P,K8B6AeUI0`J98"8&4NFmS!N98$ +**FE9M3+3!%CGJ48I$m5!$`,3*!6L'S(i9!!!&iMVAcAMd,j'!)dG`3"Kd8J!!'5 +JS)C5h!FB9d-pN3"bC6c#'e8JK#(JB!$M1`!,#KJ31l36!"8pT5iLS!h)0+8cGJJ +!',M!!#)!)!&[N!#!"#D!K%!J)4M5@!3-#-"+1L3$%S)`S"+)m!B,$,HiadhZFTX +,!fJ)!J4"J!!J`+!#"NcJ3%Udj(@0Lpa&5#%3i)8!,`$3J[9QGa&8J'pij`X$+Nb +!*%SB`U0k#`!5Y!%!ra$Iq56P#3i'f&%%J)2GP&!%-aMJM0'%33(!))$"X9)b(1! +"l-i"JV1a"J41Y#,K![,&q%JcBR"dc3qD"KX(@'#Hij`62`-#KhrLk3aQ%""@NhV +@Y(Se2P(9+@`JKP@Y&VQVA`hVNP93eUfL&FTVEHZ5*H!DU-)Q!MB!MQI23#i$H18 +IF)"$!$i-YL!HN@%SIQ)8b4A#q)``4#B%!aBbk*X)hp-*Q1b2AH0MJEm'&J"L*,3 +N)9(AdB(5Bh"iV3Bqd+-!0!F3`&%4Fi&K&eX@`!N%mZ8hJ$LD)Ik$#V&,Q")4N!" +%!)N!!AZM`1d)X$GJ&HT[G2ZGLVdb!&9d!)ecq)FCIJf!-[cM$-3H`cr331aJTd% +),[L('SMGJ(q`!pVrD!Ha3I!2Gf$l(GJ'K4$k#)m&3D#3!2rJKP5"N!"(HTJEhB6 +m%c$b5)jhph'TDaK)(UPKEc6R0!!Jb#-aC&aTG&ZKUTUBTbZY"!-c1)!"EL!!"2k +KK6BBS!Gp*--ri+%%FT[",H4'0bJ84B6Di'F[R'BK1p!Y$aK%Zpc!H'SIZD%S)#J +"%%(PRJ#F)B46,%J!,[q(2&Kj,9d-!"2S2U3"k)"ZDIcm"G&@1L+D$KX"Q+)&3"" +Qdp&0Mk!22HE9'B!)e)aeVII4(9ch1Y($MS#QCqNIF2m(e2dS+5j3A4F#d%,ChGe +(TrHakp(qHY%&B!fbCjh[h8jli0GHG9iBhZb*rl[D`9je86`Hm@LAr1)TMhG%+%% +#HX+$'HCbEhL4BqrS(ZqjqbK@A34!'UKR[H+&cRLm1`&qPdGhjS8jqF($)2GRRlh +JUb-!#la3#jk(J*i`-(Ti`mX0XIm(*!aiI+-4,3"k2hce$52mfJH!'[#M!`8Si3T +KJJ%%`)mmlcGIp!#i)UK6%)4(PCMZp1mHm,6RI!#QVNEQpK%I,b3&ePGeDJ!#84F +*jBF2EKGYq*!!4ij`G`"J$(Y("`T!IX,N@HGRIpfRIc!!Ir)R#25RERfh3iJ3IAk +hI[RAIK%@!0&hIleA(3#J$ZKQ"M[N"0&R"KYBG!#J$1QhFTUAJM!)#bm),`5J*cc +`5`a!!2q!"-3d'QS6B3!!"J3JJ%f98Gcc$`eS6!QM-%FL(p5"!AC3B%0J&bm8$F% +J!NS`$F!!!C%J"BT3#m*`B"[h3kmM4%3%$%(J$[m!$#'`DS`"#K3J!NV%!$,30KE +J"2S!!,U`3kTJDXk3!)Iri!apZ%5-%30+j!!K)J5CS3'D#!!E)!4Y``'J!&Y+"!' +&##L)#!#)%!A"`'&3%!b1#)Q5@)S&BiQ#!LL###!K%!8&*J5&`J+J#!!ZS!3"d"Q +J3#i#!!TL0iJLd$BS`%T0D)H4+)ZfH"3BB)ZBb!+6Q$`Z!!T%!)h&&)Ykk!c9##L +JX'Ec9N`[%#Z+`J#@-4Sr!!![N!!CM@&d-'!Cma50"")$`#!!XT-NG4!+&U)2`[5 +%S,!"L2J2kJ!#H'"e#3N1[%)!SH!L")N0"MN##HN-"TN##8N-"UN##DP3Ld%!S*! +!"!QC45-*#NU3!*!!TX!V$#!#Lk%U+HN##8N*S%!'6ML55Y#3!)kJ"[)!NiB#'"r +J!b#3!!$!J!X$S!&Rm**!!!)4F*4*b6J@GN8#)3BSN!!%5N!"DK"KL`!'mX!#53! +#+&"%8+N"R3!-*Q!&8B!))J!"`S#8'Z!#!4!&Kd!JCKJ,be!8!8!$038"m4%!+(4 +VdH!-'I8R!#F#$r#@8GQ3!+SJ#C!!3!)pBLJ&)`$3F!Lb!!JF)3*Cp3qb)!J("J$ +m3!##3"#!)!Q#m!R"i!RfB3%Um!3V%!4,Y!*i%!#")J%Ki!bd#3*8%!9)S!*a4!& +PZ3+rXJ&8!!!3%!B"!3fIC`BU)!)10CH!i!Z!)!BJ!!Q(%%Yd`!#`)!KPB(a8J)E +6-&)(-!R!J)!!!JU3!-3%S,!#B)!'D)5F!'!0b"N!aaN3P1#F)0!#KK)%l4!Li"N +-j-N0J'#H$)#H5R#G4*N!N!#J#'GJ!CSC"B%J#BG3"Jb!R3a!#i&3"R4#"@$!#L3 +!#%`3$F@#NBBL!SBJ!I-&")J"Ri)!Rl`3"N!J!Pq!"#)+!f"J#P'3!!K4i!UhSU! +-+J!I5J**3!,9!!*-%!R13!,)J!++J!'YB*`Kd*m53*X%mJhpL3$HX*U5`3Ii`)r +qk)5`T!"Pb6Lm+B9j#!!dd*D+U3%ee6b"H'[63*J4X!B+3!)d'Jai@3ELS"Had*m +--!E`i)6kL3#`F!JF!3)(-!6pkCh4F!b4!!bH!!M#@D)!J*j'LUM'Z!*1#!@m53% +3!!@!!!+!!!'#%!h[%3M6`+0e#!)p!JJ"!3T-N!#6KJ)#Z#!!GEJ[[#+6!%)BUH! +1KV&$I'!C*2!"Sl%[klK%(d!G%h#X&X#VlA#X%,"$A##Xa&S(aKSVb3S!bdSG&[! +P!T!!#Xp+(G&k,cT!V8ii+fGJ!S`M!NJ!!4P`"KiJ!4%J!@YBM2J`#A#9!&-!"!c +JUJ(a$fpJ!+a+%2mJAK&jVTVM8%"J2"!JK4#!$J)4"FM`$rSJ")-e+k1aN`,J#'X +J!M$3)j!!-!T'`+iI#`#IF!T'-![)F!B3)+1V!KQ)!3U3!'!V3I5%*4X!l+SA!c! +"#b!!!e'b%#!13,#[3S!&%PY%-M"I!0H3!+i`!hc`$rl!3Y@#B,pe"K#9'3MJ"L$ +JY($(3KV,X6$JY*SK!$Zj!#+J"L3JYKe",K`J"'NJYCR4%9NE2`JJYT99CQ*,'B( +eYJ6!!$M!3J(`!QkJ!"#!!i%V"8!`!H"a!&ql"Jk!!(p,!'BE5a1!4J0K,RM`!rq +`X@*J!*@V'3P3Z!,J!8&!N!"E)!-pXNmJF!A*8%45%!3c!*Z$JJ-#%!PVJ!%5B,M +li,-!m!!--"$&'%)0b3d68#J(!!!0-!-0Q6`hK!F$B!pY3!4ZLfLS#`!m3"K4d,V +m!!*2%!0iS!"B!#K`J!#L!!CQ3!)a8#Jd%#+LF!"QS!%K8#KD8#`K`!G&)!9*X!3 +!`!&U9Vi@0J1&BJ')PU8!X#&*)+0Fm!3q`![U3!K1)+*F%F%52-%8h"-%i!*0!!8 +dFX&"3!8d8X%J(-)86#aJ-!3%U3"!F!j'`!r3F9(c!3*&)J!L)!P'm!dhGaZJ-!* +JB!k5B3$483FQ-*5!8&%2r&IrB!pV%`"RJ!!KS!3!3JUi3C*T)JQMBDGaBdNL!!8 +c!"l*#*,@QJ#Jd*a3N!$#+``G)U!,rU!+*Z`2TL!#L&$$-TUak1%$5`c$-)"'#S! +K(')$X2%2d$!D%kN%fd15+5$)Sb%(aNQlYUX!(G'l8`!!'L-(!I!"X"P%K0((j#! +U5[!"CL!$Z9ZlDa"KR`!'+&#i!E#l!I%!Mr`'%HB*ED'DPX5pD``,G58"REJ"SdM +!-N#cTDB,rk!!Pqc,Y+!%Am$,3S6'q#!#(c#XF%!%ID`(,%[-Uj,&@ma#2M$%3[$ +!-&`RMU38%I!&6#'MG58$`C!!"+#3!!,P,!*4),B$%9LCd3&'A!q!mJ+@9JI$J!K +KI-p+N!!C1V#8KR((JJ)#!D$(Ah$&I)c'jM"L%!!*0X!$J@8#EcXE5U`!J$!-rIU +[!#X!L+Zik`3-p[%kD!`2jf[&8$!#5#!@-"!!-$!-X$!%E%!!-C!!GIi!!XY3Y## +3!!l#D!h$!!P$i!ESd$VIN!!!#'$)G8$',!`!*Y`2YV#,eZ['06`D4JdG**c8R[! +#%5!%&d!#Af!B@,dUYB!-&LB#bm)$B+!"[Q-EJDJ%,j!!!YD,e9Vpe@E!$L-3(ce +bK0rJ`dppc2f!$F-USmS-bjI8[@J8Y4S3ci!5!@ml%"`3!V$T!D"!!-@l[`+KbpG +-a0%!'rd!#M*+!mQD!)$0[BZ9L)ApY2+-!9Hp[iqYbc4EZ[d!!&&G-(EbBa55'6a +!#jR"!5$e9r1`$FBT!SAb1T'`+Q!!$RN0'rl!"#Lk4%,!eL23LF"B+#k!f,qe[c( +`YTJS!L(`YXQ$[QM8fQc+c`eP`[Zl9mD0$9"mcHAbf0F-R`CJfEk0a3QJcJ"XR#b +%'3$`2"eJ"6JY!L03+"iJ+21YQ4c3hiaa)*(0!S!bhbl3)I[-&,GKh*U`aSPbfEU +Je-NYe'`G%#-35`J!XCA9!G+G!12pYNIK!GNY&J#!!Y,0eL`JhIVC!YAG)F+i@2E +ah3iZ!H+pf*8%d[lJ!ZFpAJLJhS$!hZjG+$)J!['pcS@52,BD!4SN9+T5+1bQ#rb +J#+[5i2f--rjJ!%PY"`q-fFijA`b!b$,!eXm8"")!!@qJ!4ml$pj`cG8X!+'!!Q' +F`b#3!!4c(XB2j5Ti91&Ld-5ZVG@#E!ED)!&XR3)6!*XYN!#3!#b`3k-0!2R3)DP +Y(lX-(QfN$rZ!"9G`fA83alU`$l6`fB*pa+JB$D*L`[ZJ!dS3!D)$''!!#-(m$li +!'C)4!!C!e,#a$i3`V"+`@9r3apCJ"Pk!-1Fmk&!`(qb+BHe-!I-9B91q$qCJ"MU +`c9$3c3P456+`3eKJ"LJ`""$,!6,`4KJJ!XXJ!Qp%L'pNLXXLVl$T!iZe$ae5")R +S!,RZ"!5$L2X!")!"#1#3!1XJ8lG)aKJFSJ2q[LUjcJD5F3$e$"K!!!lX#JGMN!$ +VFL!k*Xd8GI*1*"!I#"!'##!U#Fm[G6!%dL#[m)N"NZ5F%3#I$L"*4%$b!4&%%*! +!$9-3!'5J$lfX$k6!kljZh2E349S9'$"3l0X$"XL1"-V13Xbq'A$`l,5Q"04ZlH2 +-m1"J!K-8![UV2a!V!KF3(aF!!!F3"1[`@aQr4(h-"apr1B#b[a(3lS[PhI+1D,# +K$ea`lrTJmr[HllUJ$cLCpib3!2$e(2F1S-a#3XrY3J*6lJpFd%8U8,Vj3!PpM!+ +LEX,jB!Kej9GS910'M-3B-1Q@T0A'E"6m!!EDJ49DF4)!m"%K!3mRJ3!+B!9P)!G +cN!!'Ek#e-"!$8IJ9B6%@!9%@!A%@!C%@HP!!FY!@DP%!L#!AaDm+GP(mdlFA`5m +,IN%XJe%BKj%BLp%BMa%CNe%CPj%C!m%CRJ%DSP%(F""%TJB1J2%j"QLF*,$J$'! +##cl8(f9&Ybm!EJ"aP2K"*N"`!S&ZH)m!jN$K3,+(%h%Q$MQ`1'UQQJJ"--!-1Nk +(-")HCD*J!$J`!,E64"N3rm!#"J3%S'DfNi8"!H5#K@!!Mq%*d)2(3!9&T`"J!JF +B)8+(3'!$&0!$#J3bX#"!@J'!"*R"83L96e-kj!#"b!dP!fF8!%`K"'A"#a##J-! +%%!%5-!A1@Xij%N)34JK"2A!%23E)i)%&J!B3#2L4'c6"Ie!!*@!)')SDS%3%'!, +!%PU#5hL*9K8Q6)SCi!"9X(6)J$KB!'`!(C3!,i!(%J"iN!!!GF&3+3!Ui!r13Aj +41JV-Gj%#2LBJ"*@*SJRkR!!J"qY,3V!"#%!,rS3#1!-+)%$m!h$`dQk!*KN83U# +,i!'T!J236D!b1TE!&'B*)L!&V)!8@$,f!$V!Mc0!"DS1'eKIE3!*G)SmiJFpK5i +J!*DJjm#!3!*fGU'N3$Z@d"3U#QT`Fr3%"pJFaP!d481U-3"5`5J3$f#!A94$55# +&1S8ec)0j4!@!J0`K#9#!2eU#8!$rGB323J1qS"+3!!6b3`*FLKFJ$c9&25)!-)! +HjK%D3!qYSEl!'35!AI$!!I$6TXSM!!2T8)P8PJpbB5bAS-"r!q'$C)X&jaD!J3a +BF!SL'%##3%!GqTce%JriMc*m%!J!63D#%p%Kpm)H9)U"D!P!S)6!1[X,#H'!Im! +-L-QUq!Ci)Mhm-5+LENM-f9Je,JI40*'*-XcJB8"`"5!!%HBDj3%!4)%-i"RRSSA +3#VqaVbc-$A#)"%!@q)f*SQ!-aI9B)SJV(U%EIi"Zp!'ki3DTS"%SJDfiJMJ-JCJ +lqL!HTB)aJ#DqiN6S'PkPMj!!a6jL&QG1@Pb,QJ-+ZFA!&@hL)J#BLch!Er`$FJ! +B,b()@B`,JJaS#d&4H`5!(3JU5%J*rE&`4%6+M8pX0DVL%JB%2I!1*`SG5)U*N!! +TSS%[@"I4!`))LhN4DT9&P[-Af@)#l$$bU$$+4EU)([#KDk3#H(%XcXBq)JpUBf$ +mJ4f'-&+Xh@JTdN"QA)c4U'`8'--&lJbAdfP&BL3!P)03"35LJ6#)+([$!8b"3'! +#!J!Fq!#`B"$))A3J"rj"5Q-c!',%E+%6!fXQ$#53!#,(MMjN$!-JbF)#(6!"e*% +'@%FN)!c#!KB!JGZ4F85$B4"4cS$6%`!*XMX1Jba'8b6+Y)X#-#!c%!"f!!33!%' +)*4HP6R#B%%NMLN4!m!"`3!+B+Zi6(f$!$N%"0dFm2!S4S0%#!4%)"'"!2!S#'RN +HB8%JN!"$iLB)[%GJ3)GN&3%!5JM!A9NCZf#&KKSJ%"a4L!28&!8`"6#6!BJ&#B! +mr!0d!!(1c9Qa6GK&)`B#)iNNT4!95J4Xk!TPS9`9N8UA2P!'F8mBq)TE-6CF"CZ +F&A-5@04*A@&Am'6Aq"8'Jdr+LPla*qPNVVL6K0*@k-PFFDa`aJ"3!CEK#5&*1)! +"X0I%iJF[i+6C-ZU`!EMA+*)"V`"3U$Cj4"d-J+*B,8mJ(dQM4c51bY%eZN3-MNI +&$`MJlIDAZ$"1F-!#S#GCp0M!d@MB4rhS(`'bJ953!&)53L*)#kNKQB+(&*%Q8N+ +b5#N*)a%NMC551"*"mNJT#534**&N+%V556*)+iNJQB+EC#5rPNm#5XcP(!bPSP5 +@PT))D%T2#5j*T9e6PDj59YT+F+!VID@`0*Ei39Nk5fPT,DNTZ#5Ak**G`P0j55$ +`T9RjP`+6S4K-K8NJJ!$%Y+BBNJ"S6)mT-P8@%&LC,P0QfNbGk61&TY'd2%`6DS* +PV-Ne`5B%)*YS%f1i6EPT0r@Qha5FKP0aUNqkBcNeTqF8"D,6G+T1efRTD#IZj*h +q%k)L6`6+8UNRpZ5H!!"mNNm#!@IH*iHLR`J!Ir*2D3K!!3-"P65(%B)U5JZU36f +S#$@K+T63a&!D5MGeU!m9SND8%bK4*bT&V5MTiD*JP)bL8FAL4Z@S(G@M`LD3!!) +%3ST)'5NNTD5BP*1#8P++5J%!+l9%#*J&f&*G+PL#+8`iTS"!QE+DlJ"0$FbNe+E +8`*Xb&(&UeG%T1h@APS'HiP0q#P!*UL95U!j9SPT8ZUP42DT)0DQ1C+9+6jL+*'Q +U1P!R1Y@R#P@MUP5GUP39(hPRUhT9XFTD"CC6F+b%#R9`'NSK&B3#D,9$[%#NA'E +9kPSYN4p!(I!JYpSKmM!98%pbCDji*lT59c'UAEfVH$@[kT8CZ&IjDPpGY(m9X!E +@[c*B'-Ym*Lc'`E!F&X5D5a1VBPdX3j'aH&,(+PNKDf3KJC*eXP,@bQTC"Hd,`#` +`0V-XRC'm@6R,!1bXR[@cHN63'PT!S'JG,Ak3!,3N*Y2L@P(,9Q@'Ie#eVYB5d9T +FLi@5,3-DYKcL$$9ED%YYX4!`d,EHPJTP)5q8EYQY(@S!mKB,Q9jpkfrj,-&&Z!c +A#pKSLkYa2Dl)0ENmPd2%A*U,FeP4d#@k5*ITUPkULh@j,YJPZj5!)VYGZHZ8m5l +I"E`!L-8NAXB,H5N[!F#mGY,cLPl6b`&8VqYe"V*A%3&YhbYm68$bCEl3PrTLAqi +,IXN[qQ@rm*IqiPm"`(m9d3!f`1b$!80J#Sb"16!)0J$+&al`!+N!"-5!(*!!!f) +!#+J#EL!0f)(A&r[S3"j3$#kN#9`%-I!'kS!FF!0Pi2BaKYa(&X`#@RJ,TS$ikB% +!J!Z3!0m`e3E,EjKb'ZFA!-4"p!X-dmm`)!DeJIeShIDc$2EYqh@'ca!D5Jd4@6R +R!!Hi5,b6)BQ2$KJm8F@3!&3G'T!!(8j#eH%"D"!+5)%R-!5qa)m3%mXX6eL*#L9 +8#1(qf4I@8%+J`5h4*HaT'r`!*J!)!"5Qb!P)K`!3!!j9!c$"XiBI!S)FJ#HPX)q +`JaeL$0!0aq!"V0!9`JYr-#eQS42B)ACJ+E3")6"8i%*,e3A#F!#XRCCU$q#*![# +Sm))%0%-VJGY3alILKS(3Rf))I`S*p-@h'J@N)`!iJU6U"qU&2p`425+"T!bF%3" +XJ#pa"`b!!r`$E"!Gcd&Y)!(883B-51`Sd#+NG`52j%)mNNIcL"l9)i*KMqi4(8Q +'iLNTBj+C4!45!%e'!M@*X!DPPT-%H6*3)NTD85JATE!BV)&e6b,@`LTBGi@I9*5 +"FSJ%K&XCj&)!Me"p2!,Zj#)Q%J@F3("E0@%X'MA+(5)1)Z9)QT59XSpHbNc*'&L +U*r+8PL48BS"4HDa-jEdJ"rMS!d!MmJF![*5`$%M%NKiBT'1TN!!XjV)N51!J&(5 +!9r1%3!"mB6F%L8q5*1Mk$q"+Y8a)[Z!M*54DB*!!6"*"8JAJ-L%0eCl62)m9p,3 +"dR0m!J$Vq33@5[D%U,'#Hc)'CJ8q8i(i&&FlT&`pJ@'&X-S%qQ4AlJTHb5Yk"3( +X&EiU5[,6Ab'BqKQC%!cq*+$kXd`SV2iTFIlRd4+J"kX1%)4T86!!J"`S!h2JpDe +5%)!$eJ!)%!0jJ!k!f+$!!8C#55!)!5!*$!%R-!))3L"3I8C!#K3")f"M94p"X!% +eJ!UJJ6*`5P2T+S9pDF#9`P)3)%[C!#feTELd!65"-$!'dS!ES!0F!JhSTST!C+H +!L(epSY3&f,k!F!"3AaJ+"$$f)33!B!!660m2L&S!i!q`8"p"Chq"2h"9amK9f9P +Aa@F"`*kY9(F@!,J#Bq#U9)%JF&@Z3"88@NN!#`5Y*)!'PHSBM#*3B!a1JLX!"Qc +@&3`!4(YS+j8`3,5I&J#SJP$V#L3"RM@eMVE3ALC"#`2`,"i!"!-!lS!$Kl"Q!m) +!#!Y[0L$)@BcQUZ#1U`)()k$ArJ0K5f`4c,!eYXA@eaVE4JYhi)'aRE@beYLb@@9 +,EBpYY8ffeMEEBPYB'fde!kePXbc%cF,CA8YRp@bJcE0q0Y$ff6m,#J,YS#fdR6E +4,YT'@fSK,5L3!,59UY)+@N`VD$HYS1fdS)$8KYT4LfK4,5K!YD9@e5,D9PZTAQf +XhB6IeYELfR%lCkmY!!#ffaECDPb,@ffClFCjYL!AlNaEEEYa55l(0ERDpZ(1fS" +!"!)!)(J!m!!#))!,J!TNlJI!"a%!%93!9*!!FhGZcq@j1KISrJ"qi(*KlXdpZ[J +!k5VGT-YdPql3e3b8iH9KY)#J@`K#!BJlF8H%ZB5T8"@Z3PEB#Jk")PJ%M+!4Q-! +EQ#J%`3"i@6P`C#[B41#bC"IX0S3!J"kdVYd0#$c!$E`"@NS'mX"6Z,X86!LmJ6I +!88,B%%!$F`#4!9klHa3#30Hk!UF[!'L5J*!!&"SC3G"2SJ!U!!"CF!!%!$bS$3d +"0Q9H+l"j1bmm',0T0JXic3!!"2S42&J+!D(VB9l0+`ZSVZf'KkiPH!%"kDkq +h(3Lipb%F!")`I*XL!'J&!F"R8B))-0!!J#!`L3f"-JN!))!*$S6a43A*0`"8hmb +,I*@[mHfq!B!5`)4a`%,)V`!`[qLhGm&C(%!2!S)$L&S"J!1!Jd58!6aNR*fe!F! +3')V@jYUL9J(!!!0J%!##'!!)k+rpC35m4[mL!2iE#2`["LJ!ak#h&Q$%iX-!!!) +@#)B!hJ'!4A(I(R$B)m#*k!0XJ)"3S6j!rTd"M#!%I!!EJ!H1!6-S($`J!-b""a! +,F!%q-!Bfi!am!"BUJNR`'SJ"qEF1F))j%![`3$%B"K`!$)`"$"!!4J!'L!A0S"% +B!cb`$Q)!$al"!')1b)$mQ`-B33b3!!8`!"KB0#A-K!G!"SJ"MB!4'!-BS)@[-!N +H!c4!UFB!,d`$B!!H'-0,1!!N*65XKQ%!(1E"(H!0ii&!B!PL!5Z3!!8i!"B`J'A +!!X""(Ki!2'!1T'&R!!['`+X&!$dB3)b"@"!!6N%-D!Bd)!EJ!%B`$CU!q5V$@+! +@C!*-F!e``#Cf``aL$2`hBa!$(!%1m!"fH"`B!M$!!c#!"Rd%13!HL$3rl!&B+!+ +3!!Fl&&$)Xf2-3Nc&-NE'!X"933"jeJ$Q`3BqAP%)6rJ!H)F!`Ti!1!$$eMjdBaB +LCchDEf%('cJ!N!#$6@JI%J!k(J!(S)q!JhEmMMp!#[J$jKJ#),%"m,If&l`E!$R +J%1`Kqe!"i&d!1!$j)'VGfS!!!rja2IJ#iF!FB`#$M!#%J8)1#c!JDKQ!MK!!@KF +(N!$)&"NBA'5'V*%I!$$ST2cJ!i!"qh!"%Y%"X'MU-Jc%JeN!$c6b"`!("b!Gi)- +CX*)4LdXq"KhJ"iq"$E#'0I)2`!F1i"ZJ!c23NeZbeaX'$N!,Mi%-B!JbmQEi!bR +j%H#$&mb5AE*&Jm06Z5TVC+M9!ei",X!$66N44B"K`!)Xm6QJbSVAmIk$29#`-!! +3k-U#BKJ`J8hmJid",D#krZ!IF'!`F*F""EaE!!'B%FML1"!1"N%rAK5#f6k!!h2 +F!*cY!4J!r!$Zi)-!-!`SXf8q!*Pji`3!C`!)l%0PpRT@4FkbJ`&`#%ScmV,--&G +cFB-"i()rQV-e!#CCFc%#K3I['-"YrJ#!B!Fc!!F`!(VcEBj(ErEK&'F!FC[p3*c +P"`k!!aJ!H2F!iX&[#3$fi3mJC4b!L@"!Dk[1[`8c)jKmi!N!'MricYBj!A$JIc! +21!%Ff-cq!$f(ChrJH-d")`!(XbNqpkld[#JFlcP`"-$JErf$eMB"lN%UDmF"`"m +-J$p`$"i!)&!M!eSX''L9"JaD@`*`[,(@%4`!eabK08$83J$a+!!%!Q$JFTZ510i +(i(JhF`$kV*mpp"*aFMi#2!!#%le%B!!ihJaAG`$!JpD@!6id"B#"m#!)(!0!-+V +Jh3'id4jJ"aq#(@eC2M5i3`5)3"-4D3KJT"p!e%V5rq!CJ!-HlD0&!"j!"0jZ5PI +T+de%[X'@EY*,T'9"D5'!!-5dehX!r!!3'!!q8)kjp)YfBe%D"FaS%'#N2i#r6#1 +DUd+MD4"p"qT!2)J(a`!B0+8qR3rB,!EB`B#JY8@!#4f'iCd"Z,N-q3%8%H0NS(Y +A)VV8cYBf3+f2CL9NLE1G12Z!(pL("i#-%m$+r3210J-8%AaJ(b#!09B!"q"9cpS +0!,9J`+UfaJRJ!DcFIa!B`,%rS-l!qJF-Dh23Zk!@[)-!l9F"1&lmZaLEpE%f6Y% +D(&0V0VZ$rm'cMYBlf0NkhRkNZEbeYM,3$H!"")4`,4$q36!`"RmJ---l#Q#J(8! +BMV0Bk&m0!f1!KF,arJS8"k+bL'J5[DIME+V@eiiA([KV$Y$)%N$8NJ!c1NDIK"m +!$&LSicd'fKSIf`F1!,!4!#IJAd&k5)mUkcYX`I%cd05!3'2rk`e)#2#dP"lC2H) +2J!00l3afm2hGf"fE%(#!,afQ4rEIJY8lQ"eSjjZpXTF))4!!DlT0MqcDm!2JJ1D +D"a$h"m#$MEd!!N)#1**V@NmhT44kP11eB!i)4jPURdK+BkJ4YD)'!9',!0aF1@Y +R"F,0Y3m&@L#%BBAXTq&1D`X!F0Sqd1Y8eYS'30h@cr!iDUeSii5-"m!$U!G&T'r +(@ZX9Y5"!iGE8@+K[MfYm%,9Bp69ZY[C"!-KCbYfl#MFiCXFX4(29j-kpJ4qZ14B +!QKSF4#d(!)dI,VclaX-fDM8!D1aiqB&"2J$kHAB$VSeMN!!(!$jSE3c!1SrVhXf +N&i"eMV1*5!qIDH10Gj2hQQeYjlIm5ZrS2BS'`CbYUT*9(Ld!mXS$C#b0r3%,,S( +Jdc-J1i$!1AXRN30mNlelN6D@3"Jc$0E3"2`k,q!#2X&S!!2f`04iPA2`!cJ!ZJ% +!U)$Tp"%#J!VH!!c!!3Q!"B`$,)$#)%!)J!9i3#UF!3c!!(!"&!J$GB!0+!!mX-4 +LJ$K1i(SK"Z!$"H!(%MJN`SHC)3Q"KfF%"!ji!Lmlq+$Xm!-$S%5icCDVi8A%"#J +4"4!'`J`#C`%AFBPXTHU&!U""%1"(#Ab"0r!3J-4G!,SKi(eJlJ$`15!CF)"Sm!+ +VJJX)Lb5JKlJ"#PJ%"N$m,SmF3&Pk"5[B$#N!MCb%Nm!Z'%!4d%2-*6i%NVA!!Y! +02[J5!k$RS!*f3!$%Z)pi!AG"MUX&if6([i6N239lI$3iK$9H`6K!!!K$$b#N5J' +#)!M-,%&`"#BT#45RJ+!*jN!HQ!0V)5#BJQa'"*J!3C!!"5lfN38%Aj!!BhFX39! +'hV['$J4V-!5UJ"43j3$!(E3"+PXS!S)q)+L&epDj!6N`"S+j"#!#QYb8#`5jiJ5 +SJ#N2!(3A#XMB65jjj5NpTET[8cp0&BMJBip#Kb#p$F%Q8!!383,!Z823(bNZ*`) +!e3Gh6N)+i,!0SCf[%pp#%0Mj2h$RcVQHFjYl[Ni5J8-!"2*F"65#Ibl29d!ND!M +fr#5XJ'a!d2Rj#JJ(#(fIRi3@8!3LZMa[!EKmRF1!Lql2klN9jZFYJ"3JG"Y`dA0 +#3`$Sr*`'"!D#)"@SJP8JI@5JTAGGQ#l6ArVAVHPH9bX!!J!,9'PYC@a[FQ4-EfF +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!!9%9B9%e38b!"!+2&1P'Ma6TA!!!"RJ!!!$8!!!%F!!!!08h&SQd!!!!!!!$ +N'3!!"!JS%!Y"J`'0"!a!Td'6-'15Z$(c"J3-'5pJh"K)--*#GjmB5B#bJ%UD0QA +B[*&$KXQE-`))8LQ#K8S6+&0!%!`)D1I-QMGclJ63FkK4Sk1+kBK#X-C#6c"S[-J +"3`A(J"m@`[-8+Tm189U)24[D!S#N!0J%c1$B%e!!32p`r5YeY+lGR3`V2%c$"J3 +41@RXP*%$3SE8U&1VAJ@3!&AJeUjI`ilG@ICXfKN%%348d!C!$`!a!b)"N!#Jb4X +h%0m!q11CXB!rIri&k#E!cq[B!E`*k&-l0J!$!!J!J!)!a1cG`iXIlb2U$5qR!@8 +3P%l`UR@%!,!V00!-a`"A"$N!8"LJc48T9!K+##U&S)H!rrl9(G!Z2N%'!!iiJ`% +M)'0[F(NJG'KTFb"dEb"dD'8JFhPcG'9Y)'C[E'4PFL"dEb"PEQ&LE'8JE'pRCfP +ZC`dM$CjN!!!: diff --git a/contrib/Timelord/timelord.1l b/contrib/Timelord/timelord.1l new file mode 100644 index 0000000..33169e8 --- /dev/null +++ b/contrib/Timelord/timelord.1l @@ -0,0 +1,32 @@ +.\" troff -man +.TH TIMELORD 1L TIMELORD CAP +.SH NAME +timelord \- time server daemon for Macintoshes running 'tardis' +.SH SYNOPSIS +.BI timelord +[-d [CAPDebugFlags]] [-l logfile] [-n name] +.SH DESCRIPTION +.BI timelord +provides the equivalent of the Macintosh cdev 'Timelord' +for a network of Macintoshes. Timelord is a time server that can be used to +set the Macintosh time at boot or from the Chooser using the rdev 'tardis'. +.IP +The -l option allows use to be monitored in 'logfile' (default is /dev/null). +.IP +The -n option names the server, the default is the machine host name. +.IP +If the -d option is used, Timelord stays in the foreground and +prints debugging information about activity, +otherwise it forks and runs silently in the background. The -d flag can +also be used to set CAP debug flags (l lap, d ddp, a atp, n nbp, p pap, i +ini, s asp, k scheduler). +.SH FILES +None. +.SH SEE ALSO +CAP (Columbia AppleTalk Package) +.SH DIAGNOSTICS +"Bad Argument" for unknown flags, -d option for more verbosity. +.SH AUTHOR +djh\@munnari.OZ.AU, May 1990. Updates via FTP from munnari.OZ.AU +.br +Copyright (c) 1990, The University of Melbourne. diff --git a/contrib/Timelord/timelord.c b/contrib/Timelord/timelord.c new file mode 100644 index 0000000..cc59c10 --- /dev/null +++ b/contrib/Timelord/timelord.c @@ -0,0 +1,353 @@ +/* + * timelord - UNIX Macintosh Time Server + * + * Provides: + * Time Server + * + * written 1.0 May '89 djh@munnari.oz + * revised 1.1 28/05/89 djh@munnari.oz Add Boot/Chooser log + * revised 1.2 16/07/91 djh@munnari.OZ.AU Fix argument handling + * + * Copyright (c) 1990, the University of Melbourne + * + * You may use and distribute (but NOT SELL!) + * this freely providing that... + * + any improvements/bug fixes return to the author + * + this notice remains intact. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "timelord.h" + +#define forever() for(;;) + +extern int errno; + +time_t now; +int skt, debug = 0; +int doRequest(), cleanup(); +char logf[150], obj[33], type[33], *index(); + +main(argc, argv) +int argc; +char *argv[]; +{ + int f, err; + char *s; /* argument parsing */ + char *cp; /* general char pointer */ + char requestBuffer[atpMaxData]; /* A/TALK packet buffer */ + AddrBlock addr; + ABusRecord abr; + atpProto *atPtr; /* pointer to ATP record */ + + signal(SIGQUIT, cleanup); + signal(SIGTERM, cleanup); + signal(SIGINT, cleanup); + signal(SIGHUP, cleanup); + + strncpy(logf, LOGFILE, sizeof(logf)); + strncpy(type, MACSERVER, sizeof(type)); + gethostname(obj, sizeof(obj)); + if((cp = index(obj, '.')) != NULL) /* strip domain names */ + *cp = 0; + + while(--argc > 0 && (*++argv)[0] == '-') + for(s = argv[0]+1 ; *s != '\0' ; s++) + switch (*s) { + case 'd': + case 'D': + debug++; + if (--argc > 0) { + if ((*++argv)[0] != '-') + dbugarg(*argv); + else + argv--,argc++; + } + break; + case 'l': + case 'L': + if (--argc <= 0) + usage(); + strncpy(logf, *++argv, sizeof(logf)); + break; + case 'n': + case 'N': + if (--argc <= 0) + usage(); + strncpy(obj, *++argv, sizeof(obj)); + break; + case 't': + case 'T': + if (--argc <= 0) + usage(); + strncpy(type, *++argv, sizeof(type)); + break; + default: + usage(); + break; + } + + if(!debug) { + if(fork()) + exit(0); + for(f = 0; f < 32; f++) + (void) close(f); + (void) open("/dev/null", 0); + (void) dup2(0, 1); + if((f = open("/dev/tty", 2)) >= 0) { + ioctl(f, TIOCNOTTY, (char *)0); + (void) close(f); + } + + if((f = open(logf, O_WRONLY|O_APPEND|O_CREAT, 0644)) >= 0) { + if(f != 2) { + (void) dup2(f, 2); + (void) close(f); + } + } + } + + (void) time(&now); + log("timelord server starting\n"); + + abInit(debug); + nbpInit(); + + addr.net = addr.node = addr.skt = 0; /* accept from anywhere */ + skt = 0; /* Get Dynamic ATP Socket */ + + if ((err = ATPOpenSocket(&addr, &skt)) < 0) { + log("ATPopen() failed, code %d\n", err); + exit(1); + } + doNBPRemove(obj, type, ZONE); /* if one already running */ + if ((err = doNBPRegister(obj, type, ZONE, skt)) != noErr) { + log("NBP register error: %d\n", err); + exit(1); + } + + atPtr = &abr.proto.atp; + forever() { + atPtr->atpDataPtr = requestBuffer; + atPtr->atpReqCount = atpMaxData; + atPtr->atpSocket = skt; + if((err = ATPGetRequest(&abr, FALSE)) != noErr) + log("ATPGetRequest error: %d\n",err); + doRequest(&abr); + } +} + +/* + * process a new request + */ + +doRequest(abr) +ABusRecord *abr; +{ + char *q; + int request; + unsigned long mactime, mact; + register long diff; + struct tm gmt, local, *localtime(), *gmtime(); + atpProto *atPtr = &abr->proto.atp; + + (void) time(&now); + + /* request data depends on the user data field */ + abr->proto.atp.atpUserData = ntohl(abr->proto.atp.atpUserData); + request = abr->proto.atp.atpUserData; + + if (debug) + fprintf(stderr, "Request %d from %d/%d:%x\n", request, + htons(abr->proto.atp.atpAddress.net), + abr->proto.atp.atpAddress.node, + abr->proto.atp.atpAddress.skt); + + switch(request) { + case GETTIME: + q = (char *) abr->proto.atp.atpDataPtr; + /* + * Do this by determining what the given time + * is when converted to local time, and when + * converted to GMT and taking the difference. + * This works correctly regardless of whether + * local time is Daylight Savings Time or not. + * + * - courtesy kre@munnari.oz + */ + gmt = *gmtime((time_t *)&now); +#define isleap(yr) ((yr) % 4 == 0 && ((yr) % 100 != 0 || (yr) % 400 == 0)) + local = *localtime((time_t *)&now); + diff = gmt.tm_year - local.tm_year; + diff *= 365; + if(gmt.tm_year > local.tm_year) { + if(isleap(local.tm_year)) + diff++; + } else + if(local.tm_year > gmt.tm_year) { + if(isleap(gmt.tm_year)) + diff--; + } + diff += gmt.tm_yday - local.tm_yday; + diff *= 24; + diff += gmt.tm_hour - local.tm_hour; + diff *= 60; + diff += gmt.tm_min - local.tm_min; + diff *= 60; + diff += gmt.tm_sec - local.tm_sec; + now -= diff; +#undef isleap + mactime = now + TIME_OFFSET; + if(abr->proto.atp.atpActCount > 0) { + bcopy(q+1, &mact, sizeof(long)); + mact = ntohl(mact); + log("Get Time: %-16s %6d secs (%s)\n", + (*(q+5) == '\0') ? "" : q+5, + mact - mactime, + (*q == 0) ? "Boot" : "Chooser"); + } else + log("Get Time\n"); + mactime = htonl(mactime); + sendReply(abr, ENONE, &mactime, sizeof(mactime)); + break; + default: + sendMsg(abr, ENOPERM, "Bad Request"); + log("Bad Request [%x]\n", request); + break; + } +} + +/* + * send back an (error) message. + */ + +sendMsg(abr, code, msg) +ABusRecord *abr; +int code; +char *msg; +{ + char buffer[atpMaxData]; /* room for translated message */ + + buffer[0] = (char) strlen(msg); + bcopy(msg, &buffer[1], buffer[0]); + sendReply(abr, code, buffer, buffer[0]+1); +} + +/* + * send a reply back (fill in the BDS junk and then send it) + * + */ + +ABusRecord res_abr; +sendReply(abr, userdata, data, length) +ABusRecord *abr; +int userdata; +char *data; +int length; +{ + BDS bds[1]; + + userdata = htonl(userdata); + bds[0].userData = userdata; + bds[0].buffPtr = data; + bds[0].buffSize = length; + return(sendReplyBDS(abr, bds, 1)); +} + +/* + * send the BDS block + * + */ + +sendReplyBDS(abr, bds, bdslen) +ABusRecord *abr; +BDS bds[]; +int bdslen; +{ + register atpProto *atPtr; + + atPtr = &res_abr.proto.atp; + atPtr->atpAddress = abr->proto.atp.atpAddress; + atPtr->atpTransID = abr->proto.atp.atpTransID; + atPtr->atpSocket = abr->proto.atp.atpSocket; + atPtr->atpRspBDSPtr = bds; + atPtr->atpNumBufs = bdslen; + atPtr->atpBDSSize = bdslen; + atPtr->fatpEOM = 1; + return(ATPSndRsp(&res_abr, FALSE)); +} + +/* + * Log an error message. + */ + +log(fmt, a1, a2, a3, a4, a5, a6) +char *fmt, *a1, *a2, *a3, *a4, *a5, *a6; +{ + char *ctime(); + + (void) time(&now); + fprintf(stderr, "%.*s\t", 24-5, ctime(&now)); + fprintf(stderr, fmt, a1, a2, a3, a4, a5, a6); + fflush(stderr); /* especially for SUN's */ +} + +cleanup() +{ + doNBPRemove(obj, type, ZONE); /* if one already running */ + nbpShutdown(); + ATPCloseSocket(skt); + exit(0); +} + +doNBPRemove(sobj, stype, szone) +char *sobj, *stype, *szone; +{ + EntityName en; + int err; + + strncpy(en.objStr.s, sobj, sizeof(en.objStr.s)); + strncpy(en.typeStr.s, stype, sizeof(en.typeStr.s)); + strncpy(en.zoneStr.s, szone, sizeof(en.zoneStr.s)); + return(NBPRemove(&en)); +} + +doNBPRegister(sobj, stype, szone, skt) +char *sobj, *stype, *szone; +int skt; +{ + EntityName en; + nbpProto nbpr; /* nbp proto */ + NBPTEntry nbpt[1]; /* table of entity names */ + + strncpy(en.objStr.s, sobj, sizeof(en.objStr.s)); + strncpy(en.typeStr.s, stype, sizeof(en.typeStr.s)); + strncpy(en.zoneStr.s, szone, sizeof(en.zoneStr.s)); + + nbpr.nbpAddress.skt = skt; + nbpr.nbpRetransmitInfo.retransInterval = 10; + nbpr.nbpRetransmitInfo.retransCount = 3; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + nbpr.nbpDataField = 1; /* max entries */ + nbpr.nbpEntityPtr = &en; + + return((int) NBPRegister(&nbpr,FALSE)); /* try synchronous */ +} + +usage() +{ + fprintf(stderr, + "usage: timelord [-d [CAPDebugFlags]] [-l logfile] [-n name]\n"); + exit(1); +} diff --git a/contrib/Timelord/timelord.h b/contrib/Timelord/timelord.h new file mode 100644 index 0000000..04f2bd4 --- /dev/null +++ b/contrib/Timelord/timelord.h @@ -0,0 +1,25 @@ +/* + * timelord - UNIX Macintosh time server daemon + * + * 22/05/89 djh@munnari.oz + * + * Copyright (c) 1990, the University of Melbourne + * + */ + +/* general junk */ + +#define ZONE "*" /* NBP Zone */ +#define MACSERVER "TimeLord" /* NBP Name */ +#define TIME_OFFSET 0x7c25b080 /* Mac Time 00:00:00 GMT Jan 1, 1970 */ +#define LOGFILE "/dev/null" + +/* available commands */ + +#define GETTIME 0 + +/* error messages */ + +#define ENOPERM 10 +#define ENOLOGIN 11 +#define ENONE 12 diff --git a/contrib/atprint b/contrib/atprint new file mode 100755 index 0000000..becba8f --- /dev/null +++ b/contrib/atprint @@ -0,0 +1,31 @@ +#!/usr/local/bin/perl +# +# Matthew Lewis +# April 1991 +# +# usage: atprint [zone] +# + +open(PIPE,"/usr/local/cap/atlook '$ARGV[0]' |") || die "Cannot open atlook"; +$a = ; $a = ; +while () { + $index = substr($_,0,3); + $name = substr($_,6,41); + $name =~ s/\s*$//; + $net = substr($_,52,7); + $node = substr($_,65,3); + $socket = substr($_,73,3); + $list{"$net:$node"} .= "$name\034"; +} +print "\n\nNode list for network $ARGV[0]:\nNet Node Names\n---- --- ------------------------\n\n"; +foreach (sort keys(%list)) { + ($net,$node) = split(/:/); + print "$net $node"; + @names = split(/\034/,$list{$_}); + @names = sort @names; + for ($i=0; $i <= $#names; $i++) { + if ($i != 0) {print " ";} + print " $names[$i]\n"; + } + print "--------------------------------------------\n"; +} diff --git a/contrib/aufsicon.hqx b/contrib/aufsicon.hqx new file mode 100644 index 0000000..4f22a8a --- /dev/null +++ b/contrib/aufsicon.hqx @@ -0,0 +1,340 @@ +(This file must be converted with BinHex 4.0) +:$%&eCR0*BfpZ,R0TG!"6593K8dP8)3%!!!!qk3!!!!!`UP0*9#%!!`!!2ZPb6'& +e!3!!!!!!!!!#!!K"G@Cc5@0[ES'15%#$'#B!!!!!!!!!!3!!!!!!"J"144i!5%[ +Z!%9bf!")YCS"%`!I!3!"%`!I!!!!!!")6!K"8&"-BA9*3b%!T,aUDD5mDQi!!$F +5!!!!!!!!+aN!!!!!Q83!!!!!!!!!!*NR!!!%#!L!KSf!-`i#%-!0!!'#(2lpFdM +3KN5+!FeFI"K3N!"%"%(UQ*Q6C-`E0fB%%!`#"3U6-(@5$!%4m!A"Q`"BZS3TNbB +!QcL$#J9!LTHD0%&[5,KB)#!#FN`$&T!!%a9!!9j9$AM)LLJVXkS(4&9PS(&L8`! +,[&4e3+RU!a09)E#V'[#LJCYJ!KUK4!b!N4S2MIb!%T!!!#5#+SF+%1!LB!8J!#! +!m!#La6j6C`L!!1)%8LTk6R40i"%DJ")@6DDdqHGY"jq&rlJ*@@e05+[,Y[HTbZd +UYkcCqm3"*`HPh6jcaIHGLf)F(I0pk@$`1!!YG!3P)+#X3,*2P*-k@4kpi+'PL*3 +R3XkN!JALJKN#@"jp*`%Nr(JL3DJ%5EmHa*AX)Z#4!!)N5!''!c*JBiB#-QJ!KJ" +#C"&&-[r`XdXbCVL6bLJEfXF$%9)8`34rl&d"5JRCL3-%#3f!BB!-'&46Lf44)2- +22KYqKi3HNIh!Jd#TV+-MM`D!)3p"$5#aQM+SZ)'+2kP)N!!+$+N`Xb-!(+5b"`K +2L0"'Gj&%!dS,S8PJKK`*J',$K"9qppN,$!Ka5QJ&8!,#&bJJ!@FU&Hcj!CbK"F! +'#PrFk33dB4K!!"9Pi%%(!#"mm*d69[cM$a,RI)!"#Qi!J)!*E!#!L3XV!#!$#k@ +U`#-+TEU!a"S1S6)+$+8U!3fX'[3!K+8rP-V%$`#J-JQ2B'M!bb&[#2!D#!C)B3J +)X$)!$55E-5"C!bQ&b!XKC`4NKbk`cUC0AJ#`F@bbVm(!!J!``!)V!Td#i)8*45T +Jbb&R@'!,Ya*)-3Ld!$!!!J63r!Z%Y3"JD`i)!d$bE,3[32#$!F#L8JJ,!fal"JV +-1JZ`Y03HI+dC%Y!+!!E4Z-(VMa5lB+Q`fjSKb3QK`J!!"8amJ`%q6RJaDD@ARK- +!$Tak#UUSkEe3UJQTJ!($"c+8+S3N32J3J"*(R#(%*q'DM%%`amLTkm[Bh%N#%Um +kP0i2,Pm+"5V[[+!!+RN!USXrc-3(`T*)S"%C+R[)R8S+FU156FiF!0,cR8qmip# +KP2j$$UB!%0dTJD&LNKi5+X165J33I+!%!0d#S%JZl[`MMaNG+FN21+M3!5FUFq` +TJTbSP2f&#@LrV3F5DX5k"r#aMQ+h2eES[@3,IX-J14+K3J$iRQN%N6Sk3l!6'3M +CF!q$bqiBX'XmBk[$q6rS6-p!+J5mX!$GlKZqpcr+K+!b1Hbqd-,VY1mj!Z)!@0` +A'[HikP3+(jJDJ1@-PMRSr@d20,ZCTdJ!+a1!3!'P8i1k!'!"+3"KE2lBS!A%p`r +bl8SI'k3!#H8a0Rl!i4pM``F)T!!&4!`KG6,XAMCZS$*p!%!"*N-!*'"&!#3!)a$ +V1*N2!(!$b1JJA8H)44)K`!BJ(J%%TB)!*"$a)lP0,"9Pm"VBEZ@3!#CkUJI[ZY( +mP0%$[e'J3QJ%J!(qSBmi+X"5jm1(V"!(M*lp,##8``!),SF#[f&##D`!J!R1J!2 +FkBjh52!G#%*P35Mdc91!Qk3LXh0*"-K+NjAXC#T3%,qk[Hp`1JZJcqT!+8#HSh) +Q)'5R'%##"VD"Il*M32mBd+Z&r'!#r0Q!hIl"KrLBc!"$A%M8(#+*+F!*@$m)`!G +q&)"8G1"iP)L2hIS"KQ%b)6i1K-!SpFHr13"3J!4%!15Xi-U6&3dNlH#()AKR!NE +!`3"QC!)F#Q"'*C`!9U-M`!e#CB%I$P5##$$!'[i"MamUP+'H%X)U)T-f$D"L%5B +$!4Ne3%B1Q"%$rc60c8bQJ@M!LJ)5"B!,8US#'-cb"%J`98TYN!"5(U68#%+!&4" +b#J!K#+&84%LT&H6NdS#p3*fkF!!0lJ66J)&J!6R$J#TrpJplB#S!'"##brja2R[ +ik)GIPB'2!N#2X@)!&A25a6li)#FPlF0m+V1(6GEf)`6)DCS#N9-U*R#R2,N1GVQ +N(GX#KST$$(CZLXJ"(rlJMh)0&J((3`FA(Q%#)-$**UR!3"#65B!P`Z#+#%$LbFb +S!c%Q83GN9),i!2#!*6jcA4Ei`3(id`%4HV#&)L5K#@q8`K@164m[(*Xpj!5RVTl +[(hJ03(U@HiD[3J!*H!#!&9*""DE'e!5pr&@YK%91f-d""!b)UZ+m`$M('F!&LM0 +JK3+*!J&C``4I%J8BC-!!0M#fA'B`!5)"B)JcJ%#6KQJ"&0UN#he``5B[F"Nr3X- +!"8$K%8S!`R[!#`B2S#"8e#+,-Z685`6d8J'pC%"Sr1')1i%!!9&9jCh3ZdjA(Q# +"#(L0$YDee(@j!"D*9%%AJ%%%!#!"%"i3k4N%&LS'b+!)di!'#C*3,"J8)4EDFm% +E$!!#'i1"!%iqX5C"4TmY$``D1DiX'"6!J!NN,&Y'N!$#)QkKM$,)`mX%5k3,k+1 +%!q4VbbD!`TM,I'B"5#(0DflcQi[mC6PA#`8*NmJCr'`%'"JK"6'&!1,!i8G@!Je +6$eJJ!hc3'!J-dQL'r"J*S)"&8dd"%1X5JXVmSB*@kD+r9#k!&!$a)$J!!0&J3!% +Z*US#-aJ!$J%!i3@K8BJJ8%-+M"!"%#3!#483!"H`"J%"CPhV@l2V!&-i3S60!)d +6&%'P5PLe'5!!"KG!q`a8RMDY"@"V4!qE%0@k9QC`83JcL12FkDBfZ`&`!XLS`(d +M&S8aBD@$8QX4%@B8JK,#a3B#m&3)L)0(T51($Nc$q0---)(I6%""8I&!"5IlDqc +L0iHNJCaTBRMD%NfZb+Bp$4CCJqc*CCZ+-6Mb6Le!3S9m8#SBq#*i#'K$32KJ[Aq +`B`MUF!Mh['F!)J$KI1aBfdm8$,HEIb(Rrk"(JV1H0ZPpBqYD,a8'`)i+Fb60CI4 +!K6K#0cScF'$Vr+#E&))R")$ViKlXL-m8k%im!N`2lBEcK'"+*GB$#%X*UP"Nk3! +"Ge6SJH8SIeU%ci$58q31jcT(i0[UjM)pCJ-')"GLm)Ti4'2S!!!eF'*dR4b,T&1 +4!9N'qF&RMB1Ic*`!B)4"!B*K$'L-2[@HHX(YD`k#i)A5E`JB*pAcB'X)8!"B$Jb +!hIY"$!,1c3TI&8#J`0&h`$@I!XqNjK5!8,�$iEhdI#6Tm1"'J`BM20K`!$))- +Y"RK$-#b6VE$!B)(dVerplJGrN5%a`6))*3!V0J0qqq0GhpF#Zi)1Hp-G2S"S$S! +eCb!"Dh-!je0aS4)!SC%!a[!d%5J+%aJC&SL"cD@"+X1"!['"U2!d%BG1MU01"P3 +6$2"13T!!5"#3!!+K-MS8X%%P&5SS*@Fj#!!U8)3dP8Ji&5Sl&5SqY5j%)(+b3`' +did"q0b[VBLZKSJ%rX#kr%Lb6d&fd3bJ6adk3!()j),"a,i"SQ"!Drk!)m9''0h* +9JN4)'hFdQ*!!ID8K#8S`85jJ"JJ38M+J8PFf"Ali-9pfL%$J!4#3!"i3S)'!8$' +0m$54!#X@B$)@N!!$N!!K!lrPL*!!+)P2!`dp"%)Q3`%G48BJJ&ZP3J(*P#ZlJN# +h"&Lc-`Gj*#YLD%ik-elP98#4Jd#[Y#RZ"9kPJ`ReG9pX`$S!i!Kiq$fH8KUfJ"L ++*%F5T!%JJ!-"S!D3!$"40J!,Z#!!p2!'CR!J5J"8CZ!&,M+1!%!%Cf!&k%J%9m" +M2JB)&`!C4(!#H(#0!'!$F@3"P[)pPM*q[e@+T`JVUEJVr&#+V4K$8ML,Y8J+YjK +L-VJ3Ki)T`!!))2!'!#!",i#4$*!!NI%R!cB"!9-3"10A"J)!#8'3!$-"-!#6p4e +h!L-dJ!'`S'S+J**YB!$3!!4"d&!#!!-3F!Eqi!30j@2R-!$`j!pSN!!RS8%!q1! +#$m!#$P!X[N!*E%!"[%!*Ep#)Cr!#&"!SpL##9)!#N!$`)#K!"8T!#ID@-a!!$)B +#$EE"$r!!E"X!"2a!$d+`2r8!"`d`Pr`!A!'`!h6T$R**PrP`Pr``A2Yc$iD*$iD +j$m!'Q2b!$VE4$`)3"1d!!*+`(IcJ$jB*!&+`(IeJ!()5'[P3F&m!QJVJ1Z33+*V +a"BFbQ5a`Prd!!i2C$c'JPh2C$cE`Q%$3$bT3Qc8JQcF!"`)!Q2e!!TdT#k$T!j1 +T!jhjQ8M3$ck99[K!$RF#QNDJ*2d!!UAK#Qhj!J%J!MB"@4Mj%*!!"4QDB4--m!* +"%$#B%J-"T![`N6-!m(l5"aN-)!)I%#NkFJl!!!`JH6-Ab8%LJ!5f*J,!J+!+QU! +-ZU!LJ*%D-**Ri!&`m!%S+3j*X*-"!32C`#EmN!!c!c!!,h!&2f4pha%"@3!!(`! +'l%#4&SQ4%!!$0N%"F[!"jS!%9#1M0b-!N!"a!"XC-"NC!$i*!#V*NNr`4cpaJi6 +8#NLj!Nb!P'4)AJ2N1!63BJ!!"`H3!*YF!!G-N!#EA3!(8j!!Qei!"e#3!*YI!!G +%N!#EB!!(4T!!Qf&39!K`BS(!$`i&'6#3!*XTKiDK4DHT#35VGDHp13D@f3pFF') +4b4MTe8T%'3!)m%j+%#S%N!!CYJB'4V!$S@)!rU!jCk!NrJ!)kE'8ZU"bTR%%CU! +#T`%!&h!'*-"Kki)"ELJ'eiN%rL!*Xq%2PV!jEG#TRbUIRL#U-(!!DUQ,8qS32T! +!AXEd!9*A'I[J#QG!M8#JV,+`D$E4'8T`"3[#"1h3$IL`$lC`+-!L(3F3$D%K!!L +`)1!U$B(L$fF3Te$3'8kJ$r#!"9GL!+[`$jr4!&*3"8j!"8R3"%8!!ZCa(P)!*bL +3!+YS!!CaXT8LTJ[r`!*28jEJC3E`3#KY'6RQ)c50HMRM+JAcYa$V+Kf6i35A+3L +RDLB#)3RRS`ka%aV`i!r0"eXGLbd#SC2Yd!pNX"hq3!CIjB&U"3blZJqkS!5@N3j +Ri!,MUJ#MF`BQ!'`GX#Z#f3"0q`pa``$Ri`kKJ3mmS)GUj3JJH`!L#`##3&F5F6k +H!`GX4cUd`!#G-J!-YDjUDa8-C3DN`,41k`%Z8`mGF!45fh`(d,%$B!Bd-+KN%![ +YN!#cCS!"`*B"N!!K!9Ul$c5`G5Sl"q4#$A3&!!-!ACDlP&,`0'#`Qj(4Yj!!8Di +L),L%Ql0Ni!"6`+4NB$Iki!4jm3mim&8&N!!+b2#k,X#h-HYfHj1c8U#kHKQel`! +"M%He+Q1eGqF'B)#FK6X'T8X'B`!!rX!2ZdX'[CZc%(!)a9@mSA%26*!!&rj!$S+ +,XcVl)pTAB*)`[G9,"K!!!BS,!$V3[KE!S`$`$lRV!)N,'43!"b$3[MT`C8+`,[N +E!1dl!C!!5c["@QNH`J463'V!dKq&SNjeN!"!4jQ851#8,+"J8NQ99SQ9@XQ9SG% +2$VZIB6Q@!P#@CjQ@hl#@E6NF3VX2k'!'5Q!E6FS(q+%I,VX#I+!%6i!%6Z!2G#$ +$H&#VE*@VIR#ArU!*YAUV6-S)RDN*fk%ERDN)6f`+JqR#SD%2k"!Dqf!%E3N$,4! +Eh`%"Af`0hk%%J+!'(+!%JD!'$%!%J%!%J9!D)!$(hq('T@%"ENc(GD$'DU!!G+` +,FQ6(GH!#2c+rrb!,J$!'"L!#9aT!G3!,Kb!'$!!)`D"qJbJ%l2FGXK$*'Y!BQ+a +qP+aq5(!%5c6+MK!-[cV+-Z#eG8!#LK3!iS!)iT!!#*XX"[K!bF"`#$)`!-)!$#j +`Z8IJ$-!`bY*`#'@!!*)J#'*!!(SmIQ"JRHAjS*l5I"LJIZ`,#U4KDHfd+CF65`% +$!4-J!)"!%2m3$BR%!-dh!HVhbHfh#*Za[[!V[r5,R`YL0rF3Vim!"T!!YCi,%"V +ei"+T0(&e)!+&mJ'J`!6SC@X[N!!!-3!-!X#GG6#TpU!%H#!!MU!'+J!#p`J$3i! +2!8!"*`B*N8!($!!,JP!'&L!#84!#b2$4)Z#T%4!J!U!+)S!))#!)dC!!#J(c(D! +`"Yp"d*%5#RD,#1rk$lB!e&m!#T+L$rr!#N$p!D%J!Lj!e%`0#NJ0#LP3e*C`e5T +3e1rh!8KY%k#`!brpdem31RIb(U!3!!p##J&3V%i3$350ePqJeJR3eJN!eq*D+Fj +3G-jJ2`$J!"NC!l2%38+!D"T`f!#`!8+!#PJ##J!J!TFM!ik0!EU5N3,N$Abk6[r +3ekRM$)6Y+5HceTHY!!*%"I)"$9%3$!m#"F%31ClGfB#0!*!!%YU5J3'5(6!K%!8 +!X+Tq%jZ1l3*+%!"U,3!2!JS$F0N5N!$EqHRB+0!c"+hC2a6AN3--43F-J+d!Jhd +j(!3+&A$C$J$GAp#5Hrd2eTdk`"$DY'd"fCf4S$!"i#hHj"djX&"dX!$B%V$GN@( +BL!d+#,"Y+18h(!!+$$!%k!!T)Z!1RL)#VK!`S0!!Pfd"iLhGi5hA"3d$5Id#HNd +(!A!R2b!!4p8+kN!(fXI8p"!3%d!)L+'S#HBTAf@[AG!c,[!*G9`%36!%r'S&3F$ +LM@&AkK(MAM$Mhh(M1CiI44!jl0$L(NC0UI#P"Q!681F%V3!!fU$Nr!(N3Pi(4X! +%-V%%jL%%21lL61l638lMG6!&)P)%1#i&1fk'9UiHC*lP4&!%D-i%DNi&N@-0,h! +!'(G$k#-#f!$S3R!!9I!%Aj@D+Q-0U3!(5[!"C`!2rQJ0j1)'j`-lF9$S@`F1RqC +TjDTp6qFhdY-+MEiaQ1ibQYiTR*i56[$TIp-+TIi2h,H#U0!+([!"cV#L,L#lY!X +'&Z3bf#$1j#`($3!,JI!HIX$@M2XMXeZl#M"qC`!*93!%Cm!06N!%GGBYrq!'5Mj +L5GjTU6!),L!T8NT!)JB00li%6[!%9f$RLTS&KXlN[l!#Aq!2[U!rp-iI0@$[pIi +K8Q!&8T!!#NhJlYQA#[%qlm*Jlm+!l`,r)`0!m2,Z$mCJlmD!lkJ!"3[[%+Q3!!X +2V`bTF!1ZN6$qS!q&RJU8X!2c[Q#VK1C88!93i+p1J14lcJ"#&`$qB!eQ`!$ZEZr +JN!!+24$ZjHE&hmZM!c&r!f%'(T!!mbhJ$pJ`(J%a%&)1!'S3l[Vl#j%)!+R!D5X ++)BF!!KS)"*2RF1U"!eb[IKJ(!aCj16"`#1%Z3&"`T0"`"%9!"81!"%mJ%c#[T*i +5XClH!e33-&'DjA$2VrYD"%F31H+!+CU5mar#"%p`")qe(LE`"6E`a9aJ#DZ""8j +J"jKHb!ZJ#Qi)#cc`!6M`a9U!#DYK"CQ2#8D3!2P2THV21J8Rj[Tj-Q0bT!QKSRe +fi!2V3J#HJ1Gf!!-0Fq4fB%4cZK$3F2QCViN,!31'8IM$Id4d1J$(r`qSE`I3X2T +fJ!1!X%%#!!h#Vr`'!!1Qk2c+6`"4a6055J9Z2`4(6J8K3J4*3#Nq[1GMa3*Zb!- +,(`#aH3S[K&Pmm2$Q`0kCJe6`*j+H1D!Gadi!bTCI0Ak#!#m!!!IJ9q8N*'!(!!! +hB)!kk3&'`!03$Tj1"3a9f#B$3X"IG3lme+iBIKA3kM8#+A!%'-!4m"4[J"GX(9r +!1Ib"(dJ&N!$JU``!(BJ+0X(jm!-qSN)%`EL6#RkJFh%ZU!!@+$kl0`*f<`!#h +J#r`$BA!q$-(@%3DTB!RiJ3'!!4hJ#15!PZ8Jk))DH!-K34I8J#63"2i$22!$&-) +Cr))PN!#%k)%f'"#%"4P%"B5!"ET!'#J,"1!2R)'KJ4p)JE@h5SM!%pJ26k!+9*I +)B3ZZbT+c#V4VV"LYZi%!mYp+iArr`2mp2(GJlc4%!I3(lJ!"5TqNj`i@B!MdJMf +*1RK!!'!1hQ!Ul)!8%!"J!eHB"Q-K,NJ&+l!&[X!"m!D!`4)F!NIUX+b(+2Jm9!B +df$VN)!XU[LRJrN$!i`-&K)*Yd!pTb!b%KGjEG9cT'J)"2+J$HD!29"R!B!J1394 +3"-(K%I`4dJ-@G-,c!3bf$V96+e,JJ*@jla!LfTi8S!,Q3Ij*JcfhZj4"l20d%H- +IVMT8%!A'bJX)K2JJrld!1G(rV0lrQhIb`0l*!`)S%8@K4$5&$I!"#LP9'![4`4[ +FL,#`!RU$MeJ,+b$(bi9l8!$d3TN(m[5"'D!![p!*0-%I%3L'S43d"f"!!X6%Q3L +fE1)rZ&%USmUjM!'i",E1iA-$rN!Di%"T5!fNS6@`JdK4'Z""3PM(RX")XJ,aVr# +jZD3h%3G"aDXdqXS3AXAi9bR!`ClM0#j1!r%)1&$Tp0#8N`19lZRTJ62J!(l9RmT +mTDecbCBV%B98"MK`L`!!,[T&Z8Kjl#*N#!1CMi6B!l`)'45!LqJ"V`&B+!"#H#I +8hk')Ie4!"L#")S!&3%$dq`FX$Zr&+4EJ$eM"V-Pm+3!)P-C*iJq`3kGEG9@Q0"B +r$f3("Y&)'J+mB!)FJJM3i"V!r)T5Gi),Z$dU)!5ZJ"4)![L3!$YC!Aq3!16ih#b +j)IiJ,[d!3&FM+N6cF3(489F`af2(kfSJ2*!!,8p2#j!!!HdM"'b0pBL1-FV@!)Y +Yal$N!!Y-!`E!&b"&G[!'U!&Pr!*Yle!34q1)(1PFD(`R9d),$"0'81LB%G@#IMd +L2[N$GEL`rS%++*!!MH&!&VrR3Ri8T#8B*Xq&4fL!B3)&J10i'il&m6ML3b%J!&J +!#)KI-!!25,K+`3N2J"mi!,E'3$i!dZ%![JS@B)rq`"NJV+m#!R!N--Jf@)-0@)2 +l+"ceBiMXMdH+48U`Gm)$)Z59D)F,5qN*J5X5+Z,8Dha@T1C+`J!$d!%chp[,I&l +`(mL"M'3c!!"ZR&r4C*Jd#eeSZGk!-EJ5F1"id#SUQIHZC(B3L%"J5hC*kqIm`Z5 +B,"N"i8cqJc6C)&@!(Yb&E[*+Q)$Mm5Z5RMlB*[K!,I84+98NS3&ra)GFcJN-!"$ +`Ea,!IrQ826+M2E36'6RFJ5-%N!"k3%JG$p&%*kfNklZ6@[)!V*B&B`Im**N-P!p +`8%UI+'N$$Z9#H!2)i%UJJfdL$!UG*R'9RJj@ZVlQJfLi#S)N3)&`(bkXIX!%M'@ +9a*2+dY-abrP&r1J8K33@JG!K68X2F#@'e`Ic!PH#$@`6iYJUX@@@G(f'%3$35PX +*+-eNVL58(m`Gq%TH'#aj"!RB*K,$8GS0IX!,j''@Zj3LJL4YbNi*+[q,m!%"Sa* +&UXL)+Iq8!"fU9G,J"b3pIT!!#[)3$i!(L&%Ai!1Bq!6Z!q0cI"mc$%#q3V%!ALA +XZj)#aKrF51`SVr5!!##A0Y-"Y"@Nb!@NA!#3!!"+!$YbJ@i4!&3"5f`!)Fm-J)" +#9`20!0[`"f*J22#%)6!Hm#%@`(1Y)!!S!*1A&#X-dd5+6K-l4NdH8!IF3"V!!e@ +6-f*0VFNeTB%5b&A5i"h*[(m!$C,L&3J'J#!!%%dMm$Ap3GL%QMA3mddjFG!fhfC +5C"d"J!0FXIT3-J[C"#"U8diA8#`'N!!C@X#6LQ'ZS$6ZL!"J"&j!H&-VIe&JIXj +p`!JfjlmFRFa!BiBmckR&JTMMme'INamJ!erj!Gi!2,J5m)"eUK96S$SIjHL8!Rl +!'F,13#J+I+82X*fkXaqSJ5HT1rI"("5G@X`%&,URU3TU)#X!&&01(K41Z(Q"VKJ +FS#X"B"*F,9S6%,5GL*PbaG,3B8I"#3$8`IC-LQF!!PbaS0*FH%!!S!4AL`U!!5& +3k$L-f25Ca@RHZ8hZ+6m,M!F!RjAJDVN!-+!i#da8XJ$J8a*F,3i`CX"R*,KDmR2 ++L3(a)Z2-(-+8#8p!!25"(@!""%DS"!&0J#3Y!8lj!NUS`maS)03*X&!,S!%D"4a +i!h+!$S!!-C!!"qK!'3!"-'%NT!'6i!Df3"G)45"J$eJ!"4"%jm!323NZB!a)!!a +J"Y)!'rLKCJ#(JJ!Q#!*Q`Kai!h9!$Sb"-P"`*%$NZ&%BkjhN[aE!%$1K3haikX$ +HU31+Q23L9`+XSaK4""B!#IKd(J8iH)0l&!`LJ8F4JNlK!`bN*A#3!')QA1JV$8! +[I!%XJ$9!Mb1f&#-T-HK#rm!Bf%%fF-6`B(d4#,-+%%(5Id$'G)%mH%'N84,!!Nj +U#84T*%9ATY35G%1'e`1Kacr3"1+3!!G8#(*S5h%T1N5#X%"e3JP-X"1ld+akJLP +JhMN#eFNa-B%Ye3@3!*6%C%'$DHC!4"#i!RAZb#e(4d$rNYi`hAa-%"9!Z(NR5cG +Qa``hYXS-k-ck'3!J3@L`"f!J,`3!G`!C3DNN'#YN3KI3!c@J1rh"Eb"dlqk(F#T +&aqJF(D4,HT*1),J#6QAT-&h5dh5j5KhJb8X5kNDG32!"$Y8Ia,TE1ZY'(3[B1XS +J0-5$BeK([ef#)6&26aQF9%HJaCc"5Rekqr"cqJ-GN!"40kSSk+L1$J*X(@FJ8LQ +"(2efakkQRJp9-1Zqakb#"8M4%Z`BB1$SV-$@JDAa3)0J4dRJ34MMV"+Q(E1B6Y8 +R'!9*$$2YQ1F$QQ*(4j!!"EG1+Bd(2fLU)Y9d#PPF"Lb&"qC!U+S-@6$VhKE0N`4 +Hi$mjZS$3"ee'+B8(+'5UMKqVHNqpD9CYR%c`D8S#VUT-[kSc94PL9CS@4EI+XNL +"A3fPUmZ[XL`m!!DdJ+kVA9+J-LJpB&FAK"fa-`-Z!+5b,%E`8`I"5jeb+R@XAM& +&m&S"3%`0K2$dq5LlAFF!R"hdJJ,4lJc!JQThlD$A"40hhb'EiMLl9bN)J33lGLf +!$mL"15"'rm%brCKJ)$6)!c+Jq)M!bI34m!!-V)I*q5UV#m`F--25C93F(Z%%1)` ++XUilmapi!TqT!B+QI&99rI-85%05m26d3HMlQ[q!(&i#VKQ8G%%rm+b'940)bNU +MA2R9%-"Xbj%9*"!!-&CJJ%MP"[N2"V4466M[l-%Gp3GHC30Q4-[&4f-K1AL$A&# +3!&C!DB"L5b)!F!@-P%d5J%HDp1`"(3@aY10S*Nd&B"4$`c[J![TXkhJ$(bX&CUR +PUU8U`aENdPeD$ShD1I`EkY"PD)-DD!Z@SXX)Kbj$'%"&If!,m+!3-!$6mdHNTPa +&8%IG3Dejj))+jUU'@M)IkZkb"a-9e1(8-q!'j'TRQl-@3-L'"RI!$AUX,R!(Z1$ +M)8f4efBek[R3T+,1dH9CPb%1pU`Yd,0r9K*m2!83mZaXPdfdC`!+Q08p5`6XRBd +G"(b!VIQ!cpUj41Z['fHPGGJ91ifN@IrXKUbahbl8JJ(G#JPV9fq9G[1,"`4ACN" +F$3#f``$dD9)QelKR"*k!&%LKmSmCT!*D%'E(&TPGG+1Z&#,8+`%8m3CJ&)aiJc" +#!jC)mkc"1l)*GTBGT!)@)!2fd`HS!YFd'!!$[8Qeb1c@%EHc!!b)!NlP$$K9GG! +&lm!$'%jRm1b!RSi-#'3!k%%$IFDbX-$ViJ216ZXN29qJ1U&"$45hI(1(cFG8B![ +DjZ[5!J+Ar`4#H[#kQ-!(m`1[L`KmKYIP!jbG1A"fkL#i@LdLm,TB`1Y#!Fj1%cJ +lBq"Eri%'U(D[L`+m,[Qj5ZDHQ[0b9Z$EQ,BTT`1H(TLEFNT!8B@'`M9Lc)%IQA) +bS0)%J5TJ"+C!$'ekNN%Jq!3#i!@DhNd!!4-!qj!!Ve3!$m,Z`"Y+1i[X"Sbdfe! +b*T0V+0b!l5i!%X"f'3!SU,[LJ1dfJ+EiGKd!!@#l$J!+X0d(`!J'EcKmZ`r!'l" +GhVKic3(EP3"jpqe+!+rbGLH!,+fmSS$Y6J"FS(Q&JHC0RCAA(l"G#Q!$4UrB"4, +`J!*`JY(,F0mZ"IL,ElF#X!#f@`&+lpXP#2N0!+$Da%!4%J!!)!0#3EYjhH&,%++ +*lL8)!b!'Z!!B3!18,``!!DPJLiB"0`!#N!"!'*!!!h)J$C5"lf)#'N$$E*K*S!h +!J6+3!(h$!"[SSQq!$9c4-e!'6'3-b!%j!$i&"4)`03N#j``44N!J%)4a0K!#!42 +8[`'"r`+!3%!3,%!&+*YK3!aF84"!"pi!#"J$FU!-K!%I#J+Uk"8P#"4J!L"J"Ia +$'r"&'Vr8e`+A!B,J!6V!"Pl!([J1C&m+$%Ce+"G0!bJ")cN'#R##1l!$(J0Xi!h +-!5aU48G`3,J!&U!'-f!(E!EB3"fB!fJ!"0L"p&X(fS!2"J!V3!A3!64!IRrSpIf +KEX!"Fp%)r%A$k"Mp,M#B!D1"02"G4$!)#!%%i3$S"#B3&!k!eHal!H!2T1%hM"- +'3X!)!10XC`!$!&!"#X$LNJ2d*aP!KJP!![D`#+!!N!"K"e##4,b)!B!LKJb('"+ +6J"lc!E""%[!8J'"d)!"1`!JJ)!,)"!XK!TJ#8,U(bC8Kq!--*4&!KJL3!!LQ!!P +`"C+!#Y3#HN!+U!!,)!A%J!U8JRU!#&aa+9!"N!#K!(!!90`2D"X!L!3"i3$%Jm* +!J#(J4,M$H4JIl1%1d!rfm!F`Ek`&'erM2I`$X(%hjXEH1"b$BfZ-M4p!1Ii(iq` +!S'2,YBi$`$TQ@i!!&D0MH4b2*8)pRXIfQ"l,B`#`MrHa3*!!`3KJ4U)&1H`Tl,$ +q0FGdLVEG"GV@&'JE!3!%$KN)1136!`(KJ6''!"FC!@a*M$`(aJ&'TJ)9!#-RJ@5 +!N@p!#F$)c'!5B'4%%!8`XMYSb3MJ*EYNQ-b5-6)#L!3Bq3q%!B`X!J)!6jB$'*N +$L!#EE!3`mJ(3"cCj+'INJ)#4QE)jCLJ)14XhPUHFMG(a8`B'6aNF2'9iX*@lXN6 +JbPrC+d2PX%b@al*C"XYR@5bMjE@XPYYb@@E,ApRV4Q@&,0SDXN1!b!j")M-!cC! +!!2$")B!(#b!I!!1-[!R!!34)!#Q!%%#""C!!Q"Gc#LJ%LcN031B&)*NAFa+Bc)N +!-LH!49!+$V-r+!C3)-B-jS`-")J!&%J!q2!d9`'@P`#L!'YHcDUC09F!#U#DUF" +KCJP016*FC+p-Tmca9,i)6aN3A1@Xl*E6mPXZcR$C1#[Rj-bFNE0cMXX"34bJ"Cb +3!!RXm#S)!&#!!"#!*`##Em*$GX3%!"S!J#E!%@k#,,$$a!!l%`!$-!5QlaJ0&6I +K)"biiEB&81e0Q!*fZ!bSj`*3"04!'4J$M`)RX!%lI1bbF`%J!YRA$McKLJ#NN!! +"+a3+"k%F!!!(E3q%`L+3!*Ie+3"3!IYF'#M!c5!!H3!!d!&%%"6Z3NI5!&31%Ab +!+9!'VLL!"U)cC2Z#86(k3ie!$al#(lJ-Z)(6Ja-1!&EK0d-$BZSrJN#Lc`*"3!T +E!!$d!3"3#GBcLhE4!CSJM!!!N!#NE`#6AXrYf3fmCjb3!(p&!3$3!KC31cZ"4K' +Pp3*VqG)!S!S3!!3!"5+`(8J$B(31i!6)i$Ib!L)!"5U"3e1%933!LJ%#Z!"2qMr +Vd$!`5-V!(3#L)J%-%e%H[D%@!Nk3!!cBS,Ip6!)J!*l!%X!*Y8Hl'B(`C)a[JRE +6EL"(9"lIJ0"M$(*!b'rD$3hI!d4!!UC!'(M6EX#r61%IbNA*`!3Qe'(JZhM4'6e +'3B!,q08i!3%%J)H!!J)!#4$53#&a3N#FB!N!`"d!!)PR&QMR*l!'`N#)[JP0S5' +8!(NTT0F&YJB!d58$!!"DJ!"!3U)Z#5F"*daThhX$M$8#5!'"qN92hbdk"'3L%2d +ZK$S#``%f%!E'U"0f!i-k8B1!*aS[ES+9lVUAfKNJ!!L3!!6QYIA&[YVhZlcIq)X +6,M80!!"I!!$J!`63!94`'U!$2T6kKQ%UJ!6Lh`U&!VPk$*KI*6`$PZmJ`JNC1`) +!J$N3!$B!JILL6KJ%Y1Jbm+rT`(H*[P-J$l5"kHY$a`$kP3-h9!j-B$KGX!1#R#i +9Kb!!N!#V$a"p`qri,ErRG`LNhrAEIN%!%cJ*C1!NJ)#*,Ar,TqALdJ#!-)"T46# +YUr@e*JK0B3(`B3$`$"#""iMA1R4@Ak3k3!IJ!0qZ`$FkLmU"S8d('(9!F'89m#! +8!`13!!#UJ0R%!b!!"6#"4c0r*FK"5"3)S!RdkmJp"+4!#L!)&3!Rd))K(3"-YIh +eeSi"*dL$!-!&"),T$JMKp5D%lT[3L4Y+k4B+!D!&b'kF!!A#Nq['hBd"G11%&N# +kAlG!!!UTqbB!Afh`Zi1#N!#k#EVEH"ZbIl!22PS%#!*LS!@$J*!!-",3Y4[ieF' +lkHPZS4"6"X"3N!!)rb!3%)!L3!Bq0J&`h'%E63Z%m4d3"X!3i0X!!!X8"Ud0"d, +dJl8++(X1q&$T)K@'`"@p[N1Kp["HR)#qV`%"X0&AG$YhjbF3%!b!%F#KK0X[L'p +X(3AU`-F1#,cY*KKNJi!3&!*$U"b#J"B8!"-!%34!eh8!0D`p"33p!#*%"%%`"1A +K2"!%53!-jjbMpJ3+M033"'&JGFX$3A!'#RUk%J4Y8(m$JYQTGQ`i)-J$CKKDV3, +d5M0&)2m+!-ljIlHi'S"r9)!0&i!!i!LmDq1c#J(!'VLm+V!3EQCGq!HGZR,3JpC +Yb)caAF!"mi!C[(&MV0h`Z)1f`iKQ[q(a[L!!*-+2U!"pMi(r!d5$!KT#'SB'%X& +$Ti#'JJ2S!5"!hrq!*UL!1ci26T`lcZ4'b%0AFUPLb$3jLES*Pab8Ui!NM32U!4S +)!-Li!'Mb35$(q8%!N!!%6X8)G3)42JN!3!63j,9!K&-#9+A*Vi%)V`3!S!0SmU( +8p!l$#+!*+d"ZSh+*!-e26b@("31BQN2KEh-62!)SA`'%BCVrJiH`!PJj25!-J8! +L2!3@N!#aZlNk"`!Yi(N4K2#Uc9e!YkMN,#"h!`!6q41DJ!K[$$1!*Vb!E#$#E8) +1S!N``$HHEJ$`V0J&'lHr0S1,!B!B8!G%q,TS!UiX"V`52!i0k$NSIpRf9hI$2P- +KVZe[Bp!#0%%'J26M$3$!3%Ure"#GG+4di#[#!B!EH!JbB%PAFZ"V[ZN!6Qp`0d% +Pj!'FlN-)!J(3h$$B!%"J#Ha$Cb`),J!HfiF@!"bXJmY!!5M#4aJ0)!!Zc+[,J!( +!fr`Cqq,3VrkUBI!C+!$BZ`9EF!`qJG&kpZEEAheR!qL`,JI'ZUZ'eHMQJJrZYKi +'i-#q,J2VZhhlG!KH"J!#!!acB@e`E'9c,R*cFQ2#!%KjUJ!!2`Jr2!!33)(A*J" +)HDT!J*S+!%KjUJ"$#b!!52rr!%0#q!"144iJ"!"$!%0bFh*M8P0&4!%!T%FZak4 +(,eB!!!Uf!!!!!!!!"[%!!!!!L$`!!!!!!!!!!!LU!!!%#!K!!BQ!#3i'P#6N&#T +f),jii`I!J40S"!1#!!!$!!i!2!$i!2"$*%J'Fm+dJF1Qc"`AFZE)'60(!-'B-k9 +-+8)N)`"!!A'1dFR6*e#I5*'51Z,L'&)&YJJ`!k-Ji"!L4B`391$%bMpN5-jp`-2 +!$B!"!"L`#8J0LCYrl&"K3Z@*!CiIr`3iQZ,ZRcSaI%"8!3(P"`S!5(k%$)!UNGY +rcP4PH)a-PBD'3NkmF&%4b4X!"P34SI)N)Q%3D!!JD)(NRcG3+9T!qDF0m`N3CKd +3IR'BJ'I3!3F+2#S!5%!#'e9$#)J"88!523')%4%`8J5"L")"%N$%#"!#BNL!34! +*!k3)'0#,),'HL"$hL!,&$a5"[K!4pk&$Epim!BC%%"K!JK%J#$!G%!&B"e4f!A8 +Ad(S"S4F3!XX"J&a!a39h9!"($3"-3!H!%p!$m!6d!6i"r8!4!(ri%p!rr`J%)b! +$`!M-!6##m`#-m(`!)cjirF22(c$k!q144a,jMcp"mZ2M2rMXq!mm12i$6Shr!"2 +!M!$!'&#,+DjiiSJP!K"L3"jU5*!!F"aLD*b&b9()R(-!3"I3G09G&d"ffhAhAAM +MPAGHHKLXepjl3X3hAhd4h*FI%IXKdYpr!3jBi(4J*"J"+!"NabN44j3S3JR,4C! +!3BN)4!$&,rcid!3!aB(b5cqrG$!FQKqD+5)!**U))NNVJYPPM&[q3k109HV)ij0 +!`MKNN8JLU556cM)V*C8jBJP1X5,Qb1+8*296iJIjP2K!22$m!L1Y-2BbDkh"*5A +[[25f#HZE&kB&58!5)"&3#JU4BFP#53L8JJcE-8%#-!4)NJ3)#B3J#33)*%%)*!d +r2!!*-L$)"#9!14a3'LBm4`QrrJ+3!%"(F,UT*Vd`afa[QVU5k@ZBAlSiV)c'BRP +MMYGHQkc2a3,P*BXkUhJcVf@HH9DZpJVdFSBYki[b[`%2$)$)!4bFm-)C3b`aa4C +Ml$!)'hFF`-FK&``!bFpT,B%5#1P`R!SBdLf3!#)%I8$3G4(f69!&!+K,6Mm"9D9 +Z2)M2R+[6[3)`*V!j[dMXM$iR'c523pYBp-j)TkMc"cSrS2-"lU$T6(#1#'kLkj) +,VZirl1l$,Z2aaUcllVTMq`('885!#32Cc)))!SL)`N`'M*K$4!3%%'('$#B`)F, +e#-aJaKcT%4%&""N`N`d'b11$!!1B!-!%!FX4B8"(jJJ`!MkL$'"'"V-%m(`%!6M +T0qm!$#""I1Hl+2hM&`EdKC1@a!FImF-2H'N9AS"%*(ami!rIk0%r[Q&!+%Q*(e8 +#BC'`j)X"q1-$[qMI"ckJ`[m0a(I!%alaM)Fmj6(2HG#6([@XKchYF5m#hJ1Iq-L +(#21K6hhX!i$li#FrqYN2IrU,!2rmPlLY@,'+@)aGi,DS43!3cR#)+dMKrX%i!Z, +S4$Y#i)i8k#0r0&")%"55$bEiJ`TH-)-qiU#81LK#)9A*(b3d)3TEf-+!1)!JKc3 +N)KIjZNCfmAqaQehYEKG'i3J`4EqLf)33F"a1`XU6!I"NT`ML,aqp!Kr,JB5FMVF +F3XK*%()+a#[Pj-VPX")!UM6PVe)f5S&i8J#H*)!R%H"*6C,N9jFmjY)Hm+X$r'S +![`S!-T'*"mNGd),rq!3f(i(03f"c%0J-a$HlZ8eXDY0(#!a)03-5c@FfmeI-A0S +2N!!TR(N'a*M%l#5'3#P+1L''*2mij8C8ZC(MEF59'i(P4Q5C8!3Fe+'GJUJUmA, ++J#6K6BN`6J!5B"`"F04#(dA!!N3%!39Xj!Ik5-i!GL8!@3QT%m%"JVVmd30P5Jk +Hld461`P#6B!L%#rDa!XhmH*0[)!6,q)dDV1+qSqKCV0Ck34!(R+9M`m&S+TR`HS +"X2U!IBMS!eiP59M2XUZ95R*fICKT6D@@PMCK!"mIUN!"M#-"14LR!FN`cJ3@"J! +48-!i1k!%B!8,J-!Dakq(YG-(X*%%2#!!%%T`*5FBmBpc)#!6J*!!3!6-34pr!%) +3%N#!)3Ja!8JN!K%8L%-L*%%**dJ#%"B!!LRq-3p)%+-!"YM6!a)4!"8m0K)FH!! +J)S%!"3`A!,)J8Mm+3""FV+ZY3(RVKcV3M`rjk%-lXQk@!2SK[(4hZplP,N#*BFe +Ni'&(bZ$(MLKlMRpNJVEr-!FCM39I3m!h%Iqiacr`Uep*j2FIXjh(2iKa!"`KBVI +r8)&`ra&F32bMZ!j'VR+C'a$RdLjh"9&)3JJL#3!X)!j`3)!&#-+"V9Pd#%iBJ@V +%k1!BU@BM#i$$4`*a0))X`!eN!)!JMKB!!RMi#ZJ!`##1"X`IX`-!K$MD!"MJB6F +`!J#&1&S5&p!'E!$![LiZ!!8mh)BR(q*S"X!!PfejY!18q-D3!!!#!`jKBQpeG#" +"G@Cc5@0[ES,"KN##`C!!3),"S)$%!!-r2!!3pQRc"!")5qi!4A,i!%LeQJ1D!3` +!'`!"!!!!5%`)G(4bEh4dH(3"!+5kp%LNZapb!!!(d3!!#9S!!!9r!!!&qJl$[pd +!!!!!!!#[23!!"!J)i-#NJ!B1"[3"`%f!(B!)%R3!S!!!33mM5J5!!-!$!"3!5'- +3T)kC18R'[((MJX`EP3))dU%Mjme-2(3'%Y3iXqE0R")eVNLfX5K"8VVS)3QB3%[ +!!p(%P*%$iSdC%#VC[+NMCmk,-h,+j*N$`X`EURI3T"Q$"N5HV5$ZK(&$"`5G0b$ +#cTPUT`b)0QADR-hMJL!AJ4ZG!2J#3!b!8J3%2&Qb83'!T8)!k%1d)`YF0'(krJe +$aLrEZ@I+N!#P!lVZRCA9kY)*XbDJ!'1h0ji!m#a#J)'@*HjqKJQ!"YfmN6p6,P" +"6!`$fJ%3!"h!JJ%!BX3$j'kkGN$A!DK3`(hkH2$B5C!!+bp!2AS!'b5aMrrH4", +fpJ1&*i+#2IrhGr6AR3!"!LMJG!@'9q#!#@,hhi$m"9*"!`!3J33L!3@Jd8F!i)$ +2G"BQ-L%!2m!L5BBDI4#3!)I6P6M*L"%!3%P!!`!6%!FVILK!M*A!Q!-'00S)!)i +GkKM"Mb-k%)%-6i&cBil6+5Q$"K)!3-!)6$6*!3Gr$!!1$M8q%)!hr8ah*4-E6!$ +!!2eSj!!F@rla!$ii2!$2$m(m!iJrdl%*b!B(@"N"!`'pk3m(#(M!!`iHi*&(122 +XD@B%$@4``$rrK&-P!'rb``%$1(#!!acKK#-NTTS#'Y%)QiTkU!-HG)J$!1$%%Bb +3!+Y+!+JT5A4"`Sdi[2S!-$M-@ZZY!I(D43ND'*!!bKVT,09K2a`i-!#GaYSUj,2 +4!ST+&NPJ5#)rV`,!4l'dDK[3Yq&DQXS5kd!3d"rqH!TUYXJ#m1ik%@b3!!#[5P! +4%"`1F-$$SD#+5QUq!&Ha33B4+6!V!!3l`!F(LM,UD$Mj4Sc$`jMU)bqY"ccJkCa +ehKP-[L(h'e-!)[$C!$J($-!0"ek##BbBYr)T!-`!11H410)*m)!i3FH8J!"&,af +!d!ri8h68!3K`cR4A%i"1KPTRD$A@B2p$Bc%$&,04&3#Bi4Y`CkGG(%85S@e'fh2 +(REC!#23M0Mrj"!5&!AS$`-q(HHrG0dJ*"-k22J)*!%MKJKmH`J)%r$2)$2mX,Y! +!BLK3q5IcC(ii!jlrmmN-Kr!GN!!1#(`q``b$"ehj)DqV$N!2TCmqJ1d"Y'lk$$r +%R[X-1pK1`I#T(ri"j,E$N!$ihSaAMFAcJNFI!$c-(aj!!IAm%d!q&!3+L$eiPb% +qqEdcF,j!$#c"J1-!i,'2P30JJ!HKldI%c2`%%%!+-`e```-+S$pmi!%II1VIraK +!K!CS#!$pB%B#Vm8-"k5"!3q-B*N'!!S!3Q'!qQ-'2b4)2ha8m)-%"!!cQ!%-%LU +3!"N23!-)9FJ-BVM3Im`Je!`2L!FAeJm2&P6!!$3#M,i&J&"#*+)4'I#"p4fa)!! +B(rZUi%5+1*&39q38r2"4*J2!B%dB`!F$m1%!q!'MLerd(c$'q)!'4!3BmEJ&2hL +!"bZ4BShiD'0%JT%*BjM"#&qmeXiqS8F!a#-6KM#$$li)!&$!3iGZ"!!I%fQ'31* +MCc-Sj#608%NlVK%"QN3N*p0ia`IXS*!!F%bN$i"82f!iS"p9K'88dGH!HMMa)eQ +mS[TQ+C!!!HD5PdqmiJ0dH895A*%!JDU'2DS3!#liiiVjF')$GLR&!&J4Q!`!a"8 +Gm-YU-S#Dk'1!-E%*a@Sk`!F1)%"%kQL--K(JK`d`4$Ve"`$!@3Q($!M'2'Ri$(I +Ld)hlA'%l5jM$0UU6KXEJajSkq-f!-Q1Jlc3K44`+8A`#JU,qA1!"pSN(2Jc$RI! +N(I`Bd$F"-1#DhY3Q0JmJ63C`chYm#Y3(l1%pIJ"#E)KjLN)53K#'k'!(R$#!*`L +#)iB!!!T*'),!J$-!G'!+"",4`5)!-B"d2(8kbJ#!9"%"!&,TLRh&-D$Cl3&0Te8 ++KH#LDNN8c9,P3eFPeYKDEFX0d"H3!%PQLE88Sp+9'Ve1ef1hKGk6CP`+UQ&!+-9 +QY5'@%ZPYc5!@e3l%,XND6bJ5QC*[1#FcT3U4C,+-AaM'*#Pp88JNh'5qP6`DDFL +[jIGmmkVjU2q)bULJjM8heZ!ki+DX9"A9a$rSAbk2+[e6%qQR1(pbNjZ1D0J1USr +L@2$`SdBE!rB-G&pkYc2pp"*Xmk*HcrQaI8GSrak[Q06G)bYNG5Kre*6aQA8H3Md +XAhNa4qc#3TI*I1(p5KrdC9f488(1(p'Hre-PR$q5-VR#cjUU[BVk9cKr#$44AQi +GhpiHHDY3$kjPc'T[ZL6Qir*IF5CArZ`Ba)I,qQ[PPmf2,jjb2V4M&mBaM'-"9HS +lEEE4J+VecB[[*LMqT3rk-Ul)U+$U2)4k@,lbBSrH[d5pRr[Fi0"*Le,G%Q*(r&p +j-8IhpSmPCUk5E1(eer2mAc,U2)4k@CHmfT3rk-Ul)U+$R$qM*&pkTAY&"h,ANa4 +r*I4,fIql"MVNP`cMYYYYYY(#qIi[R$qNce'I8SIp6*F-qY`JdGdr)AZSdPUa8&G +di"VR8@$')0(8bA$2N[R$qlqPM(I-[Hp@jRqqJNfHGd*p$1(p*RU-qT3rkJMKcB3 +D1rY(NY*DK*Ge&-I[H5eZ0ImfiPC[8dIle-UqZD)Fem)5V4R5@h+6%RPCK)Ie+(r +F'ZqjrR*P[6j&rHHG@+JVNA!F,bSdGa2IkQ5cKr,mI[GLJadAphjdi4r,16,Fl1N +VRR1d2p8GSrB-GBU#ZVNP`clMf@qE8SIp&Ph2pGZMqpfKI&c`Cjc,Q06IG%R0aqN +Xj-YkI)[lccUa8&FLlQLX8'iR[kr8%F-QI&i$-K)S11LrZr1R#2jCbCERCdPE"MY +YY'-5&,kST"*Z-AJ-b&pmf%'MK3jVk5cFjk2ccL-ZPE04I*I8PreqjQI)Vq[IpkQ +5bbYXCiimSSapfPh*1CR59dUH#`Bk,i,Ah2rA2phr[5ZEG$%R`DDG`d3S[haPlmY +#`AIq0#+bY@G9QA[3X&hrMG9C@UrFc2Pp($jDMZ),qHR,*DMa9j)apbNjr"r4CGc +r"mrH,le5YdjRr)ZCB03D$8b@CB-CAQJaGi[ZBlhddied8()pJaY''Xd$rf$r(Up +afffffffffffffffffM%Kr9&)*0aU$AdNJ++ac`DJ51rVNA#H[SL)!`SeiXIG(8f +9S#Vpc-qpHrlaUYcp&"aI'H+N2rHS)iGd#lC@ZTXd)dF2PU$39q[2(lcrak[I-Al +P08")#&m,G#`F-Vr(FT+U5CdPE-!R$j8%[pINI*IFTUJ*!3[KD"GCeJ&U,,Z[BL6 +ljPle(LVb4MlP*cq$pIJ$+pbYJadDG"*Yh+p-qBjHhF'KV0$0cNcj`rZ8R0ac4X" +e9(m5aiH&'M$B(kr1(ed%Qc&qS0Gd8(35BXkFILpZd8(1&,f$'*!!rJ-e0k-CA8H +3!)p,*I-ZEYURCdPG01F2iA)3f#led+,0#`AIq-,'rL9&c8!T%15qj69!5!KI#d# +kcUqV`!3SH%)`cA5-1[eCbVIA@$'*$qNN"4@2MT!!rAlhqqQR1(p4j#25[NA1(pG +Z+MrH`VP(8SImaIPRGbEQqj69!5!KI#h1(d,&pk[LkG(kD#q9mbpj`rU2)4k9maI +UC,,+faRMMbLM(aVrT8m&JaL3!+Ae+(r8'[SPl2rHS)iF*pSK%q+[ZJ#X!Ydm5pR +NY%,+$rHHFlLSVe&JafffM'b`EQ54KmfHpIZFQ!C[HqCHpc-$DAe56hccV4MSXZk +pL*2Z-@I8a2ak[TTh+H+M1LJMSm9H5-IFT1I`I'"GA@Y#Bhq[6Ad*'IaJ'9lPG#` +RkVG6eeG4eLSrpi!)S2Fm$',!-AK3$kh)'Ypc'a)f0'`d25@3!$#!d1aSf*'cZBe +YEdVlfPjfJXD0KSBk`dDZYb"VD4lQ0PC@FK65@XXVXd10GT(ZBefJ4`PCIIVFJDf +D(BdE%MCh-4MYYY'-5&PKk#6DQ5d%Q,1R(jPlc1rUk5YJH8S!!!: diff --git a/contrib/aufsmkkey.c b/contrib/aufsmkkey.c new file mode 100644 index 0000000..02f1011 --- /dev/null +++ b/contrib/aufsmkkey.c @@ -0,0 +1,162 @@ +/* + * $Author: djh $ $Date: 1995/06/26 06:06:02 $ + * $Header: /local/mulga/mac/src/cap60/contrib/RCS/aufsmkkey.c,v 2.1 1995/06/26 06:06:02 djh Rel djh $ + * $Revision: 2.1 $ + * + */ + +/* + * CAP AFP 2.1 Distributed Passwords + * + * Copyright 1995 - The University of Melbourne. All rights reserved. + * May be used only for CAP/AUFS authentication. Any other use + * requires prior permission in writing from the copyright owner. + * + * djh@munnari.OZ.AU + * June 1995 + * + * aufsmkkey.c - modify or create a new global key file. + * + * usage: aufsmkkey + * + * The global key file stores default values for minimum password + * length, maximum login failures, password expiry period or date + * and the global key used to encrypt ~user/.afppass files. + * + * Note: Changing the global key invalidates all of the passwords + * of the existing user base. + * + */ + +#include +#include +#include +#include + +#include +#include + +#ifdef DISTRIB_PASSWDS + +main(argc, argv) +int argc; +char *argv[]; +{ + struct afppass afppass; + char abuf[80], *progname; + char pass1[10], pass2[10]; + extern struct afppass *afp_glob; + time_t when, then, afpdp_gdat(); + int afpdp_init(), afpdp_gnum(), afpdp_make(); + void print_date(); + + progname = argv[0]; + + if (geteuid() != 0) { + fprintf(stderr, "%s: Permission Denied.\n", progname); + exit(1); + } + + /* + * get global key parameters, if file already exists + * + */ + if (afpdp_init(AFP_DISTPW_FILE) < 0) + bzero((char *)&afppass, sizeof(struct afppass)); + else + bcopy((char *)afp_glob, (char *)&afppass, sizeof(struct afppass)); + + /* + * minimum password length (0 - 8) (0 to disable) + * + */ + printf("Minimum AUFS password length: [%d] ? ", afppass.afp_minmpwlen); + afppass.afp_minmpwlen = (u_char)afpdp_gnum(afppass.afp_minmpwlen, KEYSIZE); + + /* + * maximum failed logins (0 - 255) (0 to disable) + * + */ + printf("Maximum failed login attempts: [%d] ? ", afppass.afp_maxattempt); + afppass.afp_maxattempt = (u_char)afpdp_gnum(afppass.afp_maxattempt, 255); + + when = ntohl(afppass.afp_expires); + + /* + * expiry period (0 - 10 years) or expiry date (0 to disable) + * + */ + print_date(when); + printf("Password Expires (NNd or NNm or YY/MM/DD [HH:MM:SS]): ? "); + if ((then = afpdp_gdat()) != 0xffffffff) { + afppass.afp_expires = htonl(then); + when = then; + } + print_date(when); + + /* + * global key, up to 8 characters + * + */ + if (*afppass.afp_password) { + printf("Change Global Key (y/n): [n] ? "); + fgets(abuf, sizeof(abuf), stdin); + if (abuf[0] == 'y' || abuf[0] == 'Y') + afppass.afp_password[0] = '\0'; + } + + while (*afppass.afp_password == '\0') { + strcpy(pass1, (char *)getpass("Global Key: ")); + if (strlen(pass1) < MINKEYSIZE) { + printf("Please use at least %d characters!\n", MINKEYSIZE); + continue; + } + strcpy(pass2, (char *)getpass("Reenter Global Key: ")); + if (strcmp(pass1, pass2) != 0) { + printf("Key Mismatch!\n"); + continue; + } + strcpy(afppass.afp_password, pass1); + } + + /* + * set defaults and write + * + */ + afppass.afp_numattempt = 0; + afppass.afp_magic = AFPDP_MAGIC; + + if (afpdp_make(AFP_DISTPW_FILE, &afppass) < 0) { + fprintf(stderr, "%s: failed to set global key\n", progname); + exit(1); + } + + exit(0); +} + +void +print_date(when) +time_t when; +{ + time_t now; + + time(&now); + + if (when < SECS_10_YRS) { + printf("Password Expiry period %d day%s%s.\n", when/(SECS_IN_DAY), + (when/(SECS_IN_DAY) == 1) ? "" : "s", (when == 0) ? " (Disabled)" : ""); + } else { + if (when < now) + printf("Warning, expiry date has already passed\n"); + printf("Password Expires on %s", ctime(&when)); + } + + return; +} + +#else /* DISTRIB_PASSWDS */ +main() +{ + printf("CAP not compiled with DISTRIB_PASSWDS\n"); +} +#endif /* DISTRIB_PASSWDS */ diff --git a/contrib/aufsmkusr.c b/contrib/aufsmkusr.c new file mode 100644 index 0000000..946d915 --- /dev/null +++ b/contrib/aufsmkusr.c @@ -0,0 +1,475 @@ +/* + * $Author: djh $ $Date: 1995/06/26 06:06:02 $ + * $Header: /local/mulga/mac/src/cap60/contrib/RCS/aufsmkusr.c,v 2.1 1995/06/26 06:06:02 djh Rel djh $ + * $Revision: 2.1 $ + * + */ + +/* + * CAP AFP 2.1 Distributed Passwords + * + * Copyright 1995 - The University of Melbourne. All rights reserved. + * May be used only for CAP/AUFS authentication. Any other use + * requires prior permission in writing from the copyright owner. + * + * djh@munnari.OZ.AU + * June 1995 + * + * aufsmkusr - modify or create a new .afppass file + * + * usage: aufsmkusr + * aufsmkusr user1 ... + * aufsmkusr -f batchfile + * + * The .afppass file stores the values for minimum password + * length, maximum login failures, current login failures, + * password expiry date and the user's AUFS password. + * + * It is encrypted with the global key set with aufsmkkey. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef DISTRIB_PASSWDS + +char *linep; +char *progname; +char *batchfile = NULL; + +main(argc, argv) +int argc; +char *argv[]; +{ + int c; + char *cp; + char user[10]; + extern char *optarg; + extern int optind, opterr; + int aufsmkbatch(), aufsmkuser(); + + opterr = 0; + progname = argv[0]; + + if (geteuid() != 0) { + fprintf(stderr, "%s: Permission Denied.\n", progname); + exit(1); + } + + /* + * get global key parameters + * + */ + if (afpdp_init(AFP_DISTPW_FILE) < 0) { + fprintf(stderr, "%s: can't get key from %s\n", progname, AFP_DISTPW_FILE); + exit(2); + } + + /* + * process command line options + * + */ + while ((c = getopt(argc, argv, "f:")) != -1) { + switch (c) { + case 'f': + batchfile = optarg; + break; + default: + fprintf(stderr, "usage: aufsmkusr [-f file] [user ...]\n"); + exit(1); + break; + } + } + + /* + * process users in batch file + * + */ + if (batchfile != NULL) { + (void)aufsmkbatch(batchfile); + exit(0); + } + + /* + * process users in argument list + * + */ + if (optind < argc) { + for ( ; optind < argc; optind++) { + fprintf(stderr, "\nSetting AUFS password for %s\n", argv[optind]); + (void)aufsmkuser(argv[optind]); + } + exit(0); + } + + /* + * do single users + * + */ + printf("AUFS user: "); + fgets(user, sizeof(user), stdin); + if ((cp = (char *)index(user, '\n')) != NULL) + *cp = '\0'; + + if (user[0] == '\0') + exit(0); + + if (aufsmkuser(user) < 0) + exit(3); + + exit(0); +} + +/* + * make password file for 'user' + * + */ + +int +aufsmkuser(user) +char *user; +{ + char abuf[80], *cp; + char pass1[10], pass2[10]; + struct passwd *pw, *getpwnam(); + extern struct afppass *afp_glob; + time_t now, when, then, afpdp_gdat(); + struct afppass afppass, *afp, *afpdp_read(); + int afpdp_init(), afpdp_gnum(), afpdp_writ(); + void print_date(); + + if ((pw = getpwnam(user)) == NULL) { + fprintf(stderr, "%s: no such user: \"%s\"\n", progname, user); + return(-1); + } + + bzero((char *)&afppass, sizeof(struct afppass)); + + /* + * get current values or set defaults + * + */ + if ((afp = afpdp_read(user, pw->pw_uid, pw->pw_dir)) != NULL) + bcopy((char *)afp, (char *)&afppass, sizeof(struct afppass)); + else + bcopy((char *)afp_glob, (char *)&afppass, 8); /* not password */ + + /* + * minimum password length 0 - 8 (0 to disable) + * + */ + printf("Minimum AUFS password length: [%d] ? ", afppass.afp_minmpwlen); + afppass.afp_minmpwlen = (u_char)afpdp_gnum(afppass.afp_minmpwlen, KEYSIZE); + + /* + * maximum failed logins (0 - 255) (0 to disable) + * + */ + printf("Maximum failed login attempts: [%d] ? ", afppass.afp_maxattempt); + afppass.afp_maxattempt = (u_char)afpdp_gnum(afppass.afp_maxattempt, 255); + + /* + * current login attempt failures + * + */ + if (afppass.afp_numattempt > 0) { + printf("User \"%s\" has %d failed login attempt%s.\n", user, + afppass.afp_numattempt, (afppass.afp_numattempt== 1) ? "" : "s"); + printf("Reset number of failed login attempts: [%d] ? ", + afppass.afp_numattempt); + afppass.afp_numattempt = (u_char)afpdp_gnum(afppass.afp_numattempt, 255); + } + + /* + * make sure user afppass not period + * (except if disabled) + * + */ + time(&now); + when = ntohl(afppass.afp_expires); + if (when <= SECS_10_YRS && when != 0) { + afppass.afp_expires = htonl(when+now); + when = ntohl(afppass.afp_expires); + } + + /* + * expiry date (0 to disable) + * + */ + print_date(when); + printf("Password Expires (NNd or NNm or YY/MM/DD [HH:MM:SS]): ? "); + if ((then = afpdp_gdat()) != 0xffffffff) { + if (then <= SECS_10_YRS && then != 0) + then += now; + afppass.afp_expires = htonl(then); + when = then; + } + print_date(when); + + then = ntohl(afp_glob->afp_expires); + if (then > SECS_10_YRS && when > then) + printf("WARNING: Global expiry date is %s", ctime(&then)); + + /* + * user password, up to 8 characters + * + */ + if (*afppass.afp_password) { + printf("Change %s's Password (y/n): [n] ? ", user); + fgets(abuf, sizeof(abuf), stdin); + if (abuf[0] == 'y' || abuf[0] == 'Y') + afppass.afp_password[0] = '\0'; + } + + while (*afppass.afp_password == '\0') { + strcpy(pass1, (char *)getpass("User Password: ")); + if (strlen(pass1) < afppass.afp_minmpwlen) { + printf("Password is shorter than minimum length (%d)\n"); + continue; + } + strcpy(pass2, (char *)getpass("Reenter User Password: ")); + if (strcmp(pass1, pass2) != 0) { + printf("Password mismatch!\n"); + continue; + } + strcpy(afppass.afp_password, pass1); + } + + /* + * reset defaults and write + * + */ + afppass.afp_magic = AFPDP_MAGIC; + + if (afpdp_writ(user, pw->pw_uid, pw->pw_dir, &afppass) < 0) { + fprintf(stderr, "%s: failed to set AUFS password (same as UNIX ?)\n", + progname); + return(-1); + } + + return(0); +} + +/* + * handle bulk batch file + * + * each line is expected to be of the format: user "password" + * passwords containing spaces must be enclosed in double quotes + * + * NB: the expiry date is set to now. This requires the user to change + * their password when they first login. + * + */ + +int +aufsmkbatch(file) +char *file; +{ + time_t now; + struct stat buf; + FILE *fp, *fopen(); + struct afppass afppass; + extern struct afppass *afp_glob; + struct passwd *pw, *getpwnam(); + char *cp, line[96], user[32]; + int afpdp_writ(); + void getfield(); + + if (stat(file, &buf) >= 0) { + if ((buf.st_mode&0777) != 0600) { + fprintf(stderr, "WARNING: %s is mode %0o\n", file, buf.st_mode&0777); + exit(1); + } + } + + bzero((char *)&afppass, sizeof(struct afppass)); + bcopy((char *)afp_glob, (char *)&afppass, 8); /* not password */ + + if ((fp = fopen(file, "r")) == NULL) { + perror(file); + return(-1); + } + + time(&now); + + while (fgets(line, sizeof(line), fp) != NULL) { + if (line[0] == '#' || line[0] == '\n') + continue; + if ((cp = (char *)index(line, '\n')) != NULL) + *cp = '\0'; + + linep = line; + getfield(user, sizeof(user), 0); + getfield(afppass.afp_password, sizeof(afppass.afp_password), 0); + + printf("User \"%s\" - ", user); + + /* + * user exists ? + * + */ + if ((pw = getpwnam(user)) == NULL) { + printf("does not exist - continuing\n"); + continue; + } + + /* + * and has a passwod + * + */ + if (afppass.afp_password[0] == '\0') { + printf("has no password set - continuing\n"); + continue; + } + + /* + * which expires NOW + * + */ + afppass.afp_expires = htonl(now); + + /* + * then set defaults and write + * + */ + afppass.afp_numattempt = 0; + afppass.afp_magic = AFPDP_MAGIC; + if (afpdp_writ(user, pw->pw_uid, pw->pw_dir, &afppass) < 0) { + printf("can't create .afppass file - continuing\n"); + continue; + } + + printf("OK\n"); + } + + (void)fclose(fp); + + return(0); +} + +/* + * output date string + * + */ + +void +print_date(when) +time_t when; +{ + time_t now; + + time(&now); + + if (when < SECS_10_YRS) { + if (when == 0) + printf("Password Expiry disabled.\n"); + else + printf("Password Expiry period %d day%s.\n", + when/(SECS_IN_DAY), (when/(SECS_IN_DAY) == 1) ? "" : "s"); + } else { + if (when < now) + printf("Warning, expiry date has already passed\n"); + printf("Password Expires on %s", ctime(&when)); + } + + return; +} + +/* + * Get next field from 'line' buffer into 'str'. 'linep' is the + * pointer to current position. + * + * Fields are white space separated, except within quoted strings. + * If 'quote' is true the quotes of such a string are retained, otherwise + * they are stripped. Quotes are included in strings by doubling them or + * escaping with '\'. + * + */ + +void +getfield(str, len, quote) +char *str; +int len, quote; +{ + register char *lp = linep; + register char *cp = str; + + while (*lp == ' ' || *lp == '\t') + lp++; /* skip spaces/tabs */ + + if (*lp == 0 || *lp == '#') { + *cp = 0; + return; + } + len--; /* save a spot for a null */ + + if (*lp == '"' || *lp == '\'') { /* quoted string */ + register term = *lp; + + if (quote) { + *cp++ = term; + len -= 2; /* one for now, one for later */ + } + lp++; + while (*lp) { + if (*lp == term) { + if (lp[1] == term) + lp++; + else + break; + } + /* check for \', \", \\ only */ + if (*lp == '\\' + && (lp[1] == '\'' || lp[1] == '"' || lp[1] == '\\')) + lp++; + *cp++ = *lp++; + if (--len <= 0) { + fprintf(stderr, "string truncated: %s\n", str); + if (quote) + *cp++ = term; + *cp = 0; + linep = lp; + return; + } + } + if (!*lp) + fprintf(stderr,"unterminated string: %s", str); + else { + lp++; /* skip the terminator */ + + if (*lp && *lp != ' ' && *lp != '\t' && *lp != '#') { + fprintf(stderr, "garbage after string: %s", str); + while (*lp && *lp != ' ' && *lp != '\t' && *lp != '#') + lp++; + } + } + if (quote) + *cp++ = term; + } else { + while (*lp && *lp != ' ' && *lp != '\t' && *lp != '#') { + *cp++ = *lp++; + if (--len <= 0) { + fprintf(stderr, "string truncated: %s\n", str); + break; + } + } + } + *cp = 0; + linep = lp; + + return; +} +#else /* DISTRIB_PASSWDS */ +main() +{ + printf("CAP not compiled with DISTRIB_PASSWDS\n"); +} +#endif /* DISTRIB_PASSWDS */ diff --git a/contrib/cvt2apple.c b/contrib/cvt2apple.c new file mode 100644 index 0000000..f442df6 --- /dev/null +++ b/contrib/cvt2apple.c @@ -0,0 +1,396 @@ +/* + * cvt2apple + * + * This program converts CAP/aufs style files to apple single or + * apple double format files (primarily for A/UX - you drag files + * from a client via aufs to a Unix volume than convert them to + * apple single format, then launch them using the A/UX launch + * utility). + * + * cvt2apple [-d] cap-file apple-file + * + * (if -d is specified an apple double file (pair) is created) + * + * Bugs: doesn't support icons from the desktop file + * (doesn't know how to find them :-( + * + * COPYRIGHT NOTICE + * + * Copyright (c) May 1988, Paul Campbell, All Rights Reserved. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. Paul Campbell makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * History: + * 4/23/88 Paul Campbell, submitted to CAP distribution + * 4/23/88 Charlie C. Kim, clean up and modify to work with + * byte swapped machines + * + */ + + +#include +#include +#include +#include +#include +#include + +#ifdef USEDIRENT +# include +#else USEDIRENT +# ifdef xenix5 +# include +# else xenix5 +# include +# endif xenix5 +#endif USEDIRENT + +char *prog; + +struct entry { + dword id; + dword offset; + dword length; +}; + + +struct hdr { + dword magic; + dword version; + char home[16]; + word nentries; + struct entry entries[1]; +}; + +#define HDR_SIZE 26 +#define ENTRY_SIZE sizeof(struct entry) + +#define VERSION 0x00010000 +#define APPLE_SINGLE 0x00051600 +#define APPLE_DOUBLE 0x00051607 +#define ID_DATA 1 +#define ID_RESOURCE 2 +#define ID_COMMENT 4 +#define ID_FINDER 9 + +char dir[1025]; +char file[1025]; +char headers[1024]; +FileInfo finfo; +byte *comment; + +FILE *fiopen(); +FILE *fdata, *fresource, *ffinder; + +main(argc, argv) +char **argv; +{ + register struct hdr *hp; + register struct entry *ep; + int dbl; + unsigned dlen; + char *fin, *fout; + FILE *f; + register int i; + struct stat s; + + dbl = 0; + hp = (struct hdr *)headers; + hp->nentries = 0; + ep = hp->entries; + + /* + * validate the flags and input/output file + * names + */ + + prog = argv[0]; + if (argc < 3) + usage(); + if (strcmp(argv[1], "-d") == 0) { + dbl = 1; + if (argc != 4) + usage(); + fin = argv[2]; + fout = argv[3]; + } else { + if (argc > 3) + usage(); + fin = argv[1]; + fout = argv[2]; + } + + /* + * pick apart the input file name + */ + + name_expand(fin); + + /* + * try and open the CAP finder info + */ + + ffinder = fiopen(dir, ".finderinfo/", file, "r"); + if (ffinder) { + if (fread(&finfo, 1, sizeof(finfo), ffinder) < sizeof(OldFileInfo)) + error("error reading finder info file"); + ep->id = ID_FINDER; + ep->length = sizeof(finfo.fi_fndr); + ep++; + hp->nentries++; + if (finfo.fi_magic1 == FI_MAGIC1 && + finfo.fi_magic == FI_MAGIC) { + ep->id = ID_COMMENT; + ep->length = finfo.fi_comln; + comment = finfo.fi_comnt; + ep++; + hp->nentries++; + } else { + ep->id = ID_COMMENT; + ep->length = ((OldFileInfo *)&finfo)->fi_comln; + comment = ((OldFileInfo *)&finfo)->fi_comnt; + ep++; + hp->nentries++; + } + } + + /* + * try and open the CAP resource fork + */ + + fresource = fiopen(dir, ".resource/", file, "r"); + if (fresource) { + if (fstat(fileno(fresource), &s) >= 0) { + ep->id = ID_RESOURCE; + ep->length = s.st_size; + ep++; + hp->nentries++; + } + } + + /* + * try and open the CAP data fork + */ + + fdata = fiopen(dir, NULL, file, "r"); + if (fdata) { + if (fstat(fileno(fdata), &s) >= 0) { + if (dbl) { + dlen = s.st_size; + } else { + ep->id = ID_DATA; + ep->length = s.st_size; + ep++; + hp->nentries++; + } + } + } + + /* + * now pick apart the output name + */ + + name_expand(fout); + + if (dbl) { + + /* + * for a double file copy the forks that are + * present, if nothing just give an empty data file + */ + + if (hp->nentries == 0 && fdata == NULL) + error("cannot open %s", fin); + if (strlen(file) > MAXNAMLEN-1) { + fprintf(stderr, + "%s: warning: output file name more than %d characters '%s'\n", + prog, (MAXNAMLEN-1), fout); + } + if (fdata && (dlen > 0 || hp->nentries == 0)) { + f = fiopen(dir, NULL, file, "w"); + if (f == NULL) + error("cannot create data fork %s", fout); + fcopy(f, fdata, dlen); + fclose(f); + } + if (hp->nentries) { + f = fiopen(dir, "%", file, "w"); + if (f == NULL) + error("cannot create resource %s", fout); + write_single(f, hp, APPLE_DOUBLE); + fclose(f); + } + } else { + + /* + * if a single file just copy it in + */ + + if (hp->nentries == 0) + error("cannot open %s", fin); + f = fiopen(dir, NULL, file, "w"); + if (f == NULL) + error("cannot open %s", fout); + write_single(f, hp, APPLE_SINGLE); + fclose(f); + } +} + +/* + * open the file "dir""ext""file" with mode "mode" + */ + +FILE * +fiopen(dir, ext, file, mode) +char *dir, *ext, *file, *mode; +{ + char name[1025]; + + strcpy(name, dir); + if (ext) + strcat(name, ext); + strcat(name, file); + return(fopen(name, mode)); +} + +/* + * print a nasty message + */ + +usage() +{ + fprintf(stderr, "Usage: %s [-d] cap-file apple-file\n", + prog); + exit(1); +} + +/* + * calculate a file header, write it out, then tack on + * all the contents + * + * on return: the header and entries are all converted to network + * order + */ +write_single(fout, hp, magic) +FILE *fout; +struct hdr *hp; +{ + unsigned hsize, offset; + register struct entry *ep; + register int i; + int n; + dword el; + + n = hp->nentries; + hsize = n*ENTRY_SIZE; + offset = hsize + HDR_SIZE; + + for (i = 0, ep = hp->entries; i < n; i++, ep++) { + ep->id = htonl(ep->id); /* swap */ + ep->offset = htonl(offset); /* swap */ + offset += ep->length; + ep->length = htonl(ep->length); /* byte swap */ + } + strncpy(hp->home, "Macintosh ", 16); + hp->magic = htonl(magic); + hp->version = htonl(VERSION); + hp->nentries = htons(n); + + /* must do as two writes because of padding problems in way */ + /* hdr is defined (double word aligment comes into play) */ + if (fwrite(hp, 1, HDR_SIZE, fout) != HDR_SIZE) + error("error writing output file"); + if (fwrite(hp->entries, 1, hsize, fout) != hsize) + error("error writing output file"); + for (i = 0, ep = hp->entries; i < n; i++, ep++) { + el = htonl(ep->length); + switch(ntohl(ep->id)) { + case ID_DATA: + fcopy(fout, fdata, el); + break; + case ID_RESOURCE: + fcopy(fout, fresource, el); + break; + case ID_COMMENT: + if (el) + if (fwrite(comment, 1, el, fout) != el) + error("error writing output file"); + break; + case ID_FINDER: + if (fwrite(finfo.fi_fndr, el, 1, fout) != 1) + error("error writing output file"); + break; + } + } +} + +/* + * copy length bytes from fin to fout + */ + +fcopy(fout, fin, length) +FILE *fin, *fout; +unsigned length; +{ + char buffer[4096]; + register unsigned l; + + for (;;) { + l = fread(buffer, 1, sizeof(buffer), fin); + if (l > length) + l = length; + if (l > 0) { + if (fwrite(buffer, 1, l, fout) != l) + error("error writing output file"); + } else break; + length -= l; + } +} + +/* + * print another nasty message and quit + */ + +error(s, a, b, c, d, e, f) +char *s; +{ + fprintf(stderr, "%s: ", prog); + fprintf(stderr, s, a, b, c, d, e, f); + fprintf(stderr, "\n"); + exit(2); +} + +/* + * expand a file name to directory and filename parts + */ + +name_expand(name) +char *name; +{ + register char *cp; + + strcpy(dir, name); + cp = &dir[strlen(dir)]; + for (;;) { + if (*cp == '/') { + strcpy (file, cp+1); + *(cp+1) = 0; + if (file[0] == 0) + error("empty filename part in file name %s", name); + break; + } + if (cp == dir) { + strcpy(file, cp); + if (file[0] == 0) + error("empty filename part in file name %s", name); + strcpy(dir, "./"); + break; + } + cp--; + } +} diff --git a/contrib/cvt2cap.c b/contrib/cvt2cap.c new file mode 100644 index 0000000..1eabbde --- /dev/null +++ b/contrib/cvt2cap.c @@ -0,0 +1,303 @@ +/* + * cvt2cap + * + * This program converts apple single or double files to CAP/aufs + * format files (primarily for A/UX). + * + * cvt2cap apple-file cap-file + * + * COPYRIGHT NOTICE + * + * Copyright (c) May 1988, Paul Campbell, All Rights Reserved. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. Paul Campbell makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * History: + * 4/23/88 Paul Campbell, submitted to CAP distribution + * 4/23/88 Charlie C. Kim, clean up and modify to work with + * byte swapped machines + * + */ + + +#include +#include +#include +#include +#include +#include + +char *prog; +struct entry { + dword id; + dword offset; + dword length; +}; + + +struct hdr { + dword magic; + dword version; + char home[16]; + word nentries; + struct entry entries[1]; +}; + +#define HDR_SIZE 26 +#define ENTRY_SIZE sizeof(struct entry) + +#define VERSION 0x00010000 +#define APPLE_SINGLE 0x00051600 +#define APPLE_DOUBLE 0x00051607 +#define ID_DATA 1 +#define ID_RESOURCE 2 +#define ID_COMMENT 4 +#define ID_FINDER 9 + +char dir[1025]; +char file[1025]; +char headers[1024]; +FileInfo finfo; + +FILE *fiopen(); + +main(argc, argv) +char **argv; +{ + register struct hdr *hp; + register struct entry *ep; + char *fin, *fout; + FILE *f, *fd, *fx; + register int i, j; + int resource, data; + int retry; + char rname[33]; + + hp = (struct hdr *)headers; + bzero(&finfo, sizeof(finfo)); /* make sure clear first */ + + /* + * validate the flags and input/output file + * names + */ + + prog = argv[0]; + if (argc != 3) + usage(); + fin = argv[1]; + fout = argv[2]; + + name_expand(fin); + for (retry = 0;;retry++) { + switch(retry) { + case 0: + f = fiopen(dir, NULL, file, "r"); + break; + case 1: + fd = f; + f = fiopen(dir, "%", file, "r"); + break; + case 2: + error("Cannot find valid input file '%s'", fin); + } + if (f == NULL) + continue; + + /* + * Read the header + */ + + if (fread(hp, HDR_SIZE, 1, f) < 1) + continue; + if (ntohl(hp->magic) != APPLE_SINGLE && + ntohl(hp->magic) != APPLE_DOUBLE) + continue; + if (ntohl(hp->version) != VERSION) + continue; + if (ntohl(hp->magic) == APPLE_DOUBLE && + file[0] != '%' && retry != 1) + error("Apple double file name must begin with %% '%s'", + fin); + if (strncmp(hp->home, "Macintosh ", 16) != 0) { + hp->home[15] = 0; + error("Invalid file type '%s' in '%s'", hp->home, fin); + } + if (fread(hp->entries, ENTRY_SIZE, ntohs(hp->nentries), f) < 1) + continue; + break; + } + if (file[0] == '%') { + strncpy(rname, &file[1], 32); + } else { + strncpy(rname, file, 32); + } + + data = 0; + resource = 0; + name_expand(fout); + for (i = 0, ep = hp->entries; i < (int)ntohs(hp->nentries); i++, ep++) { + switch(ntohl(ep->id)) { + case ID_DATA: + fx = fiopen(dir, NULL, file, "w"); + if (fx == NULL) + error("Cannot create output data file '%s'", fout); + fseek(f, ntohl(ep->offset), 0); + fcopy(fx, f, ntohl(ep->length)); + fclose(fx); + data = 1; + break; + + case ID_RESOURCE: + fx = fiopen(dir, ".resource/", file, "w"); + if (fx == NULL) + error("Cannot create output resource file '%s'", + fout); + fseek(f, ntohl(ep->offset), 0); + fcopy(fx, f, ntohl(ep->length)); + fclose(fx); + resource = 1; + break; + + case ID_COMMENT: + fseek(f, ntohl(ep->offset), 0); + j = ntohl(ep->length); + if (j > MAXCLEN) + j = MAXCLEN; + if (j > 0) + if (fread(finfo.fi_comnt, j, 1, f) < 1) + error("Couldn't read input file '%s'", fin); + finfo.fi_comln = j; + break; + + case ID_FINDER: + fseek(f, ntohl(ep->offset), 0); + if (fread(finfo.fi_fndr, ntohl(ep->length), 1, f) < 1) + error("Couldn't read input file '%s'", fin); + break; + } + } + if (!data && hp->magic == APPLE_DOUBLE && fd) { + fseek(fd, 0, 0); + fx = fiopen(dir, NULL, file, "w"); + if (fx == NULL) + error("Cannot create output data file '%s'", fout); + fcopy(fx, fd, ntohl(ep->length)); + fclose(fx); + } else + if (!data) { + fx = fiopen(dir, NULL, file, "w"); + if (fx == NULL) + error("Cannot create output data file '%s'", fout); + fclose(fx); + } + fx = fiopen(dir, ".finderinfo/", file, "w"); + if (fx == NULL) + error("Cannot create output finder info file '%s'", fout); + finfo.fi_magic = FI_MAGIC; + finfo.fi_magic1 = FI_MAGIC1; + finfo.fi_version = FI_VERSION; + strcpy(finfo.fi_macfilename, rname); + finfo.fi_bitmap = FI_BM_MACINTOSHFILENAME; + if (fwrite(&finfo, sizeof(finfo), 1, fx) < 1) + error("Cannot write output finder info file '%s'", fout); + fclose(fx); +} + +/* + * open the file "dir""ext""file" with mode "mode" + */ + +FILE * +fiopen(dir, ext, file, mode) +char *dir, *ext, *file, *mode; +{ + char name[1025]; + + strcpy(name, dir); + if (ext) + strcat(name, ext); + strcat(name, file); + return(fopen(name, mode)); +} + +/* + * print a nasty message + */ + +usage() +{ + fprintf(stderr, "Usage: %s apple-file cap-file\n", + prog); + exit(1); +} + +/* + * copy length bytes from fin to fout + */ + +fcopy(fout, fin, length) +FILE *fin, *fout; +unsigned length; +{ + char buffer[4096]; + register unsigned l; + + for (;;) { + l = fread(buffer, 1, sizeof(buffer), fin); + if (l > length) + l = length; + if (l > 0) { + if (fwrite(buffer, 1, l, fout) != l) + error("error writing output file"); + } else break; + length -= l; + } +} + +/* + * print another nasty message and quit + */ + +error(s, a, b, c, d, e, f) +char *s; +{ + fprintf(stderr, "%s: ", prog); + fprintf(stderr, s, a, b, c, d, e, f); + fprintf(stderr, "\n"); + exit(2); +} + +/* + * expand a file name to directory and filename parts + */ + +name_expand(name) +char *name; +{ + register char *cp; + + strcpy(dir, name); + cp = &dir[strlen(dir)]; + for (;;) { + if (*cp == '/') { + strcpy (file, cp+1); + *(cp+1) = 0; + if (file[0] == 0) + error("empty filename part in file name %s", name); + break; + } + if (cp == dir) { + strcpy(file, cp); + if (file[0] == 0) + error("empty filename part in file name %s", name); + strcpy(dir, "./"); + break; + } + cp--; + } +} diff --git a/contrib/lwrename.c b/contrib/lwrename.c new file mode 100644 index 0000000..d7b4592 --- /dev/null +++ b/contrib/lwrename.c @@ -0,0 +1,666 @@ +/* + * lwrename - daemon to hide printers on AppleTalk network by resetting + * AppleTalk type value. + * + * Syntax: lwrename [-t min ] [-s] [-r] [lwrenamefile] + * + * Options: + * -t min Specify how many integer minutes to sleep in between each + * sweep of the network looking for the monitored printers. + * Default=2 minutes + * + * -s Make a single sweep only looking for monitored printers; + * wait the sleep time; reset printers if "-r" option also + * specified; and exit. Use for testing. + * + * -r Reset printers back to original types before exiting. + * Only has effect if used in combination with "-s" flag. + * To force normal daemon to reset printers and exit, + * send it the HUP signal. + * + * lwrenamefile Pathname of the file containing the list of + * printers to be monitored (see format below). + * Default=LWRENAMEFILE compile-time flag or + * "/etc/lwrename.list" if no compile-time flag. + * + * Copyright (c) 1990, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support + * Services, Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * Revised Dec 15, 1993 by P. Farrell, Stanford Univ. Earth Sciences. + * Add "-s" and "-r" options for testing, and ability to specify + * printer list filename on command line. Add optional 3rd field + * to printer list records to specify new AppleTalk type for each + * printer, or default to new compile-time flag LWRENAMETYPE + * (value must be in quotes, defaults to "LaserShared"). Add + * documentation and lwrename.8 man page. General cleanup. + * + * Use lwrename to "capture" LaserWriter or equivalent PostScript printers + * on an AppleTalk network by renaming their AppleTalk type to a value + * known only to the CAP host, which can then run a lwsrv process as + * a spooler for that printer. Because many printers store AppleTalk type + * changes in normal RAM, they return to default value "LaserWriter" + * when power-cycled. Lwrename automates the process of watching for + * such printers and forcing the type change whenever they are found + * back with their old type. + * + * The list of printers to be monitored is stored in the file specified by + * the compile-time option LWRENAMEFILE (default value /etc/lwrename.list). + * Comment lines are allowed in this file; start them with the # character. + * Include one line per printer with three tab-separated fields in this format: + * passwordprinter_NBP_namenewtype + * Password is an integer value, either the factory default of 0, or a + * value you have previously set by another means. Printer_NBP_name is + * the full name needed to find it on the network, with its original + * (default) type, in this format: + * name:type@zone + * Use * for current or default zone. Newtype is an optional third field + * specifying the new AppleTalk type to be used when renaming the + * printer. WARNING: any trailing blanks on the line after "newtype" + * will be interpreted as part of the type name! If you omit "newtype" + * (be sure to leave off the before it as well) for a particular + * printer, it reverts to the compile time option LWRENAMETYPE, or to + * "LaserShared" if that option was not specified at compile time. + * + * Lwrename sleeps a user-specified number of minutes between each network + * "sweep". It runs forever until killed, unless you specify the "-s" + * flag to run one sweep only for testing. If you send it the HUP signal, + * or use the "-r" flag with the "-s" flag, it will first restore the + * original type to all of the printers it is monitoring before exiting. + * Any other kill signal just kills it without resetting any printer + * types. + * + * Lwrename does not need to run from the root account, although you may + * want to so restrict it to prevent private users from battling to + * control printers on the network. If your AppleTalk access method + * requires special privilege (e.g., the packetfilter under Ultrix), make + * sure lwrename executes under the appropriate user or group. Generally, + * you would start lwrename from your start-cap-servers file. + * + * There is no way to dynamically update the list of printers to be + * monitored by lwrename. To change the printer list, kill lwrename with + * the -HUP signal (to restore the current list of printers to original + * type); edit the list; and then restart lwrename. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef USESTRINGDOTH +#include +#else USESTRINGDOTH +#include +#endif USESTRINGDOTH + +#define ATPRESPONSETIMEOUT sectotick(60*2) +#define MINUTES * 60 +#define R_BUFMAX PAPSegSize*atpMaxNum +#define LWRENAMEBUFSIZ 1024 + +/* + * LWRENAMEFILE is location of configuration file specifying + * LaserWriters to be renamed. File has one line per printer, in + * this format: + * passwordprinter_NBP_namenewtype + * Password is usually the number 0 unless you have reset it. + * Printer_NBP_name is the full name needed to find it on the + * network, in this format: + * name:type@zone + * (use * for current or default zone). + * Newtype is the optional new AppleTalk type to use when renaming this + * printer; if not specified, defaults to LWRENAMETYPE (see below). + * Comment lines are allowed, start with # character. + * + */ + +#ifndef LWRENAMEFILE +#define LWRENAMEFILE "/etc/lwrename.list" +#endif LWRENAMEFILE + +/* + * LWRENAMETYPE macro is the default new AppleTalk type name to be used + * to hide the LaserWriters. Specific types for each printer can be + * specified in the LWRENAMEFILE list. + * + */ + +#ifndef LWRENAMETYPE +#define LWRENAMETYPE "LaserShared" +#endif LWRENAMETYPE + +/* + * Structure used to create linked list of printers to monitor. + * + */ + +struct lws { + struct lws *next; + char *passwd; + char *name; + char *newtype; +} *lwhead; + +u_long atpresponsetimeout = ATPRESPONSETIMEOUT; +char lwfile[256] = LWRENAMEFILE; +char *myname; +char renamestr[] = "\ +currentfile\n\ +statusdict begin product (LaserWriter IIg) eq version (2010.113) eq and end not\n\ +{save exch 291 string readstring pop pop restore} if\n\ +/ASCIIHexDecode filter /SystemPatch statusdict /emulate get exec\n\ +85f6ba98b8147bdb3c41fc154e390200521caba043febd65f48e008d42590001cd0f62e4c9f2b841c6c1c85660f30002ba262234d72494f203c119951000000376b481858e01bff2db172cf2ecfe000446e2f3ddca7b1fb2d27814e1c22e000598f64cae7bb9897afb760a5d81ac0106>\n\ +serverdict begin %s exitserver\n\ +statusdict begin\n\ +(%s) (%s) currentdict /appletalktype known\n\ +{/appletalktype}{/product}ifelse exch def setprintername\n\ +end\n\ +"; +char resetstr[] = "\ +currentfile\n\ +statusdict begin product (LaserWriter IIg) eq version (2010.113) eq and end not\n\ +{save exch 291 string readstring pop pop restore} if\n\ +/ASCIIHexDecode filter /SystemPatch statusdict /emulate get exec\n\ +85f6ba98b8147bdb3c41fc154e390200521caba043febd65f48e008d42590001cd0f62e4c9f2b841c6c1c85660f30002ba262234d72494f203c119951000000376b481858e01bff2db172cf2ecfe000446e2f3ddca7b1fb2d27814e1c22e000598f64cae7bb9897afb760a5d81ac0106>\n\ +serverdict begin %s exitserver\n\ +statusdict begin\n\ +(%s) (%s) currentdict /appletalktype known\n\ +{/appletalktype}{/product}ifelse exch def setprintername\n\ +end\n\ +"; +int s_time = 2 * 60; + +char *newpsstring(); +char *newstring(); +void reset(); + +main(argc,argv) +int argc; +char **argv; +{ + register char *cp, *tp; + register FILE *fp; + register struct lws *lp, *ln; + register int i; + int sflag = 0; + int rflag = 0; + int cno, ocomp, wcomp; + /* + * buf is used to read list lines + * and set up PostScript to send + * + */ + char buf[LWRENAMEBUFSIZ]; + PAPStatusRec status; + char *malloc(); + long atol(); + + if (myname = rindex(*argv, '/')) + myname++; + else + myname = *argv; + + /* + * Parse the arguments + * + */ + for (argc--, argv++ ; argc > 0 ; argc--, argv++) { + switch ((*argv)[0]) { /* option or filename? */ + case '-': + switch ((*argv)[1]) { /* see which option is given */ + case 't': + if ((*argv)[2]) + s_time = atoi(&(*argv)[2]); + else if (argc < 2) + Usage(); /* never returns */ + else { + argc--; + s_time = atoi(*++argv); + } + if (s_time <= 0) + Usage(); + s_time *= 60; + break; + case 's': + sflag = 1; + break; + case 'r': + rflag = 1; + break; + default: + Usage(); /* never returns */ + } /* end see which option is given */ + break; + default: /* doesn't start with -, not an option */ + strcpy(lwfile,*argv); + } /* end option or filename? */ + } /* end parse arguments */ + + if (argc > 0) /* leftover unexpected arguments */ + Usage(); /* never returns */ + + /* + * Open the monitored printer list file. + * + */ + if ((fp = fopen(lwfile, "r")) == NULL) { + fprintf(stderr, "%s: can't open %s\n", myname, lwfile); + exit(1); + } + + /* + * Read the file with list of printers to rename and store information + * in linked list. + * + */ + ln = NULL; + i = 0; + while (fgets(buf, LWRENAMEBUFSIZ, fp)) { + i++; + if (*buf == '#') /* allow comments */ + continue; + if (cp = index(buf, '\n')) + *cp = '\0'; /* change newline to string terminator */ + + /* + * Find the three tab-separated sections of the line: + * passwordprinter_NBP_namenewtype + * Last field (newtype) is optional - will set to LWRENAMETYPE if + * not specified. Use cp & tp pointers to mark off the sections + * so can then copy them into new variables. + * + */ + if ((cp = index(buf, '\t')) == NULL) { /* no tab after password */ + fprintf(stderr, "%s: Syntax error in %s, line %d\n", myname, lwfile, i); + exit(1); + } + *cp++ = '\0'; /* change tab to string terminator & advance cp */ + + /* + * At least first 2 fields exist (minimum required), so allocate + * memory for next element in linked list. + * + */ + if ((lp = (struct lws *)malloc(sizeof(struct lws))) == NULL) { + fprintf(stderr, "%s: Out of memory\n", myname); + exit(1); + } + + /* + * Fill in fields for this element of linked list of printers. + * + */ + lp->passwd = is_it_a_number(buf) ? newstring(buf) : newpsstring(buf); + + /* + * Look for third optional new type field, and make sure it is not null. + * + */ + if ((tp = index(cp, '\t')) == NULL) { /* no 3rd field, all 2nd field */ + lp->name = newstring(cp); + lp->newtype = newstring(LWRENAMETYPE); + } else { /* 3rd field found, parse between 2nd & 3rd fields */ + *tp++ = '\0'; /* change tab to string term. & advance tp */ + if (*tp == '\0') { /* null 3rd field */ + fprintf(stderr, "%s: Syntax error in %s, line %d\n", myname, lwfile, i); + fprintf(stderr, "\t\ttrailing tab character at end of line\n"); + exit(1); + } /* quit if trailing tab at end */ + lp->name = newstring(cp); + lp->newtype = newstring(tp); + } + + /* + * Create the links between this element of list and others. + * + */ + if (ln) + ln->next = lp; + else + lwhead = lp; + ln = lp; + } /* end reading list of printers */ + + if (lwhead == NULL) { + fprintf(stderr, "%s: No entries in %s\n", myname, lwfile); + exit(1); + } + ln->next = NULL; + fclose(fp); + + /* + * Become a daemon, unless single sweep flag was specified + * + */ + if (!sflag) + disassociate(); + + /* + * Set signal so that if HUP is received, calls "reset" to first put the + * printer types back to their original values before exiting. + * + */ + signal(SIGHUP, reset); + + /* init cap */ + abInit(FALSE); /* don't printout -- messes up with */ + nbpInit(); + PAPInit(); /* init PAP printer routines */ + ATPSetResponseTimeout(atpresponsetimeout); /* set to 2 minutes */ + + /* + * Main loop tries to find each printer in input file list. + * If found, renames AppleTalk type to specified or default new type. + * Then sleeps as specified in argument before starting over. + * Goes on forever until program is killed, unless single sweep flag + * specified. + * + */ + do { + lp = lwhead; + /* + * Loop through each printer in the linked list. + * + */ + do { + /* + * Open connection to printer. If found on net with original type, + * reset to new type. + * + */ + if (PAPOpen(&cno, lp->name, atpMaxNum, &status, &ocomp) == noErr) { + do { + abSleep(16, TRUE); + } while (ocomp > 0); + /* + * Need name only (not type or zone) to substitute into rename string. + * Setting the ":" after name to zero and back again does the trick. + * + */ + cp = index(lp->name, ':'); + *cp = '\0'; + sprintf(buf ,renamestr ,lp->passwd ,lp->name ,lp->newtype); + *cp = ':'; + writeit(cno, buf); + PAPClose(cno); + } /* end if printer found on net */ + } while (lp = lp->next); /* end of do-while loop */ + sleep(s_time); + } while (!sflag); /* end of loop */ + if (rflag) + reset(); /* only get here if single sweep flag was set */ + exit(0); +} /* end main */ + +/* + * Reset function used to put things back before exiting. + * Will find the printers on the network that have + * been set to the new AppleTalk type and return them + * to their original types, as specified in LWRENAMEFILE list. + * Then exits the program. + * + */ + +void +reset() +{ + register char *tp, *cp; + register struct lws *lp; + register int i; + int cno, ocomp; + char buf[LWRENAMEBUFSIZ]; + char NBPname[256]; + char origname[256]; + char origtype[256]; + PAPStatusRec status; + + signal(SIGHUP, SIG_IGN); + lp = lwhead; + /* + * Run once through all printers in the linked list. + * + */ + do { + /* + * Create NBP name to search for on network. This has the new type + * that has been previously set by this program. + * + * Pick up name section only from lp->name by trick of finding colon + * and temporarily resetting it to 0. Also save name part alone into + * a variable to substitute into the reset string. + * + */ + tp = index(lp->name, ':'); + *tp = '\0'; + strcpy(origname, lp->name); + strcpy(NBPname, lp->name); + *tp = ':'; + + /* + * Now add new type as the type. + * + */ + strcat(NBPname, ":"); + strcat(NBPname, lp->newtype); + + /* + * Save off the original AppleTalk type (from the linked list of + * printers) to use in the reset string. + * + */ + tp++; /* go past colon */ + cp = index(tp, '@'); + *cp = '\0'; + strcpy(origtype, tp); /* everything past colon up to @ sign */ + *cp = '@'; + + /* + * Finally, get the zone. + * + */ + strcat(NBPname, cp); /* everything from @ sign to end */ + + /* + * Open connection to printer. If found, send reset string to put + * back to original type. + * + */ + if (PAPOpen(&cno, NBPname, atpMaxNum, &status, &ocomp) == noErr) { + do { + abSleep(16, TRUE); + } while (ocomp > 0); + sprintf(buf, resetstr, lp->passwd, origname, origtype); + writeit(cno, buf); + PAPClose(cno); + } + } while (lp = lp->next); /* end of do-while loop */ + exit(0); /* done resetting back to original, exit the program */ +} /* end reset function */ + +disassociate() +{ + int i; + + if (fork()) + _exit(0); /* kill parent */ + for (i=0; i < 3; i++) + close(i); /* kill */ + (void)open("/",0); + (void)dup2(0,1); + (void)dup2(0,2); +#ifndef POSIX +#ifdef TIOCNOTTY + if ((i = open("/dev/tty",2)) > 0) { + (void)ioctl(i, TIOCNOTTY, (caddr_t)0); + (void)close(i); + } +#endif TIOCNOTTY +#else POSIX + (void) setsid(); +#endif POSIX +} + +writeit(cno, str) +int cno; +char *str; +{ + int eof, wcomp, paperr, err, doeof = FALSE; + + wcomp = 0; + if ((paperr=PAPWrite(cno, str, strlen(str), FALSE, &wcomp)) < 0) { + return; + } + /* post initial read from LW */ + err = 1; + /* this is the main read/write loop */ + inithandleread(); /* initialze handleread */ + do { + if ((eof = handleread(cno))) + break; + if (wcomp <= 0) { + if (wcomp != noErr) { + return; + } else { + err = 0; + doeof = TRUE; + if (err || doeof) { + if ((paperr=PAPWrite(cno, NULL, 0, doeof, &wcomp)) < 0) + break; + } else err = 1; + } + } + abSleep(4, TRUE); /* wait a bit */ + } while (err > 0 ); + + if (paperr != noErr) { + wcomp = 0; + } + while (!eof || wcomp > 0) { /* wait for completion */ + abSleep(4,TRUE); + if (!eof) + eof = handleread(cno); + } +} + +private int hr_rcomp = noErr; +private int hr_rlen = 0; +private char hr_rbuf[R_BUFMAX+10]; +private int hr_eof = 0; + +inithandleread() +{ + hr_rcomp = noErr; + hr_rlen = 0; + hr_eof = 0; +} + +/* + * handle the papread + * return: -1 paperr + * 0 ok + * 1 eof + * + */ + +handleread(cno) +{ + int paperr; + + if (hr_rcomp > 0) + return(0); + switch (hr_rcomp) { + case noErr: + break; + default: + return(-1); + } + hr_rbuf[hr_rlen] = '\0'; + if (hr_eof) { + return(1); + } + paperr = PAPRead(cno, hr_rbuf, &hr_rlen, &hr_eof, &hr_rcomp); + switch (paperr) { + case noErr: + break; + default: + return(-1); + } + return(0); +} + +char * +newstring(str) +char *str; +{ + register char *cp; + char *malloc(); + + if ((cp = malloc(strlen(str) + 1)) == NULL) { + fprintf(stderr, "%s: newstring: Out of memory\n", myname); + exit(1); + } + strcpy(cp, str); + return(cp); +} + +char * +newpsstring(str) +char *str; +{ + register char *fp, *tp; + register int len; + char buf[128]; + + tp = buf; + *tp++ = '('; + for (len = 1, fp = str ; *fp ; ) { + if (++len >= (sizeof(buf) - 1)) { + fprintf(stderr, "%s: newpsstring: String too long\n", myname); + exit(1); + } + switch (*fp) { + case '(': + case ')': + case '\\': + if (++len >= (sizeof(buf) - 1)) { + fprintf(stderr, "%s: newpsstring: String too long\n", myname); + exit(1); + } + *tp++ = '\\'; + } + *tp++ = *fp++; + } + *tp++ = ')'; + *tp = 0; + return(newstring(buf)); +} + +is_it_a_number(str) +register char *str; +{ + while (*str) { + if (!isdigit(*str)) + return(0); + str++; + } + return(1); +} + +Usage() +{ + fprintf(stderr, "Usage: %s [-t minutes] [-s] [printer_list_file]\n", myname); + exit(1); +} diff --git a/contrib/lwsrv-relay b/contrib/lwsrv-relay new file mode 100644 index 0000000..dbd943f --- /dev/null +++ b/contrib/lwsrv-relay @@ -0,0 +1,35 @@ +#!/usr/bin/perl + +# lwsrv-relay - relay PostScript jobs received by lwsrv to other hosts +# +# This Perl script is an example of what you can do with the -C option for +# lwsrv. It invokes rcp to copy the received file to a directory on another +# host, renaming it as a cleaned up version of the job name with the process +# id attached to prevent collisions. We use it here to feed jobs to a +# (non-lpr) typesetter. +# +# John J. Chew +# 1991 12 10 + +$dest = 'desire1:/tmp'; # change this to send the file elsewhere + +require 'getopts.pl'; +$opt_s = '-'; +do Getopts('J:P:rs:'); +$dest .= '/' unless $dest =~ m!/$!; +($job = $opt_J) =~ tr/-.A-Za-z0-9//dc; +$in = $opt_s; +$out = "$dest$job'.'$$"; + +print STDERR "lwsrv-relay: invoked as ", join(' ', $0, @ARGV), "\n"; + +sub system { + print STDERR "lwsrv-relay: ", join(' ', @_), "\n"; + print STDERR "lwsrv-relay: returned ", system(@_), "\n"; + } + +# change these two lines to do something else with the job +&system('/usr/ucb/rcp', $in, $out); +&system('/bin/rm', $in); + +exit 0; diff --git a/contrib/makefile b/contrib/makefile new file mode 100644 index 0000000..b22a0ad --- /dev/null +++ b/contrib/makefile @@ -0,0 +1,67 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 13:59:43 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +I=/usr/include +O= + +# Make sure to define needgetopt if your system doesnt have it +GETOPT= + +# for other libraries (like BSD on hpux) +SLIB= + +CAPLIB=-lcap +DESTDIR=/usr/local/cap +RENAMEFLAG=-DLWRENAMEFILE=\"/usr/local/lib/cap/lwrename.list\" + +PROGS=snitch cvt2apple cvt2cap lwrename printqueue + +all: ${PROGS} + +snitch: snitch.o ${O} ${GETOPT} + ${CC} ${LFLAGS} -o snitch snitch.o ${GETOPT} ${O} ${CAPLIB} ${SLIB} + +cvt2apple: cvt2apple.o ${O} + ${CC} ${LFLAGS} -o cvt2apple cvt2apple.o ${O} ${SLIB} + +cvt2cap: cvt2cap.o ${O} + ${CC} ${LFLAGS} -o cvt2cap cvt2cap.o ${O} ${SLIB} + +lwrename: lwrename.o + ${CC} ${LFLAGS} -o lwrename lwrename.o ${O} ${CAPLIB} ${SLIB} + +lwrename.o: lwrename.c + ${CC} ${CFLAGS} ${RENAMEFLAG} -c lwrename.c + +printqueue: printqueue.o + ${CC} ${LFLAGS} -o printqueue printqueue.o ${O} ${CAPLIB} ${SLIB} + +att_getopt.c: + ln -s ../extras/att_getopt.c + +install: ${PROGS} + -strip ${PROGS} + ${INSTALLER} ${PROGS} ${DESTDIR} + +clean: + -rm -f ${PROGS} *.o core make.log err att_getopt.c *~ + -(cd AsyncATalk; make clean) + -(cd AufsTools; make clean) + -(cd MacPS; make clean) + -(cd Messages; make clean) + -(cd Timelord; make clean) + +cleanexe: + -rm -f ${PROGS} + +dist: + @cat todist diff --git a/contrib/printQDA.hqx b/contrib/printQDA.hqx new file mode 100644 index 0000000..5722e28 --- /dev/null +++ b/contrib/printQDA.hqx @@ -0,0 +1,596 @@ +(This file must be converted with BinHex 4.0) + +:#P"4G@9eC5jcC@%!39"36%9B9&)J!*!$2B`!!$+KXR-"!Hph!!!m,i"&CQGQGhG +AL("`Gh!!!'"`CJ#3"3G3"`!!"i!!!)"eGRC`!)#!F'!'8!"`!(!'#!B&!!3)GRB +!ChGhF!#3!hL!!*!&F!!'!*!&"Q!!N!G`!*!&"`!!"`K`J!#3#A!!N!9`"i!!N!B +)!!B*!!%J!`#3"!-#%L#%*%%h!+JR)$26jhkHZeFk-H(mfYqEk6Ep3-chVhh[hD, +QRLr%HRMRQ`aU3rAli-rVliJ4ANj`0,+S[dVpAA6faT%,0k1f0Z%R$$`%PEDm`0m +[jrB3pBCZlrkfE[KCcr@VpTGY3@eP4lAb`YSGX*M@KBK@)[9K,BZBqc*'R)rVJQj +Y6a"9N!"NPfLIL"QcGj1@J%EYArD0"%k@$HpU[`#i3daMj!#3!d)!!*UUTJ!&!+! +!N!G*L3#*9QYRH(ZDUT!!S(H(F!L'CRLA#SQ)LQGjU!#`!!F&G@4RC!KR9'Y&9RD +AQBQ3!!L3!"8!!L-d9@CRCRH)#*!!#3!!#B!+!!#J(b0&999@9QCQC@H@H'KhL(H +*GjHCL!L*LCL*LC!!HJLJqb9eZhIUM$djThB8RYr([Tal1LT`-Ciq-VB[J[SmBhB +2JYdK#[JVrcI+hjCk@F(B[f4r2$4UPJm&Tcfpdrcr,#@ef-r'9fUI"m6Zkbqfa#Z +VdC)3lpXqkcE$aXT+cZfa[K$*k+U[Xe4ebZM$4Tdqr6fqVfk02CLj'KE&eHchHr6 +Sl1V0r[Va9rG$*$lq6'i(IGfD02rI9lF9Ihrq["[hGAYaBX9GIM1@U'6'"UU9f%! ++@ED4XeHAE2G'k(2$ACY[McBJd'&0mBFA4$2kS5[J`RJK1'S"pLqRmI8(*#$R09R +cBEIKK3*$9D$rkIar)),8N6i@V'k!+DH6Nme6`%&feSd,k`Ap0J3cS4pfr`lSdHK +2VPqJ*1c4q&PfVE(&LfB!2hhAbllSkSEChGq)"Fk4`%Nl`Tq4$mZdfKkIi4Phl!h +6,$&3d-AMefJ1fJ[$m`2EkAc,Pq&NX#jMkJjL`16h"jHZ''b-1&Q&Zb&U&*FmX[8 +HE`h0)LUV`XPGAZb`eCBABkV3YG[`XTCi3bENKI$RjqL'iL8F)dKhECfr**T-Jh$ +9cBQbKq3DC"Z&`+8+P&pdb+hafaY`$4%Qr@V&EXXT#qcaMddXY`dA'JA09LU3!1C +DiDJYZX'&RI%brZPY,VES6hJhB-F,,l[Y`%f`ePQ%GA(amC6`3S30HVLk(5&ja%( +($p@6Ei4KiEl`HGdkB)DSD#J6C2r3%i&+4rrha"8'&maXpmbPf,VmVrA'1k[qq-a +A*Eh!2bUdeZfGm8Lm+I+BCL*jk5mBdDC[6(@,'-BG[LrB!JR9[TGAb)NIkK)pmjk +ZlbLL""$8EA"*%4*9BT-C&,Qd3h#-BK60(a,LhHKd$99IdlE1qq(RjiIZ3jN-dM8 +(R@8M#kH!GIa'1pJMQ6ej'),jh("f1($-@@BVq%M45ZjB&&aQaEB'31j86X,E!qE +Nj34PKQX@aL*2hJ-V4l8YGIeLdMjr1(eLq)*&j$dKlYKReBXrBI1BGXLCh#YhbjK +$0@NqjTT'VFQ01c$f[GaG&YqDHU1@#"DAYBqD$k4Aq'd8NifN33eAB0Q#5bIVQRN +RIQ*[1C1"6a8mDEJDJ#DmlKB6i$0Y'!HV12jZ*RkTM8FNAl99045E`lbGmF1fiC@ +UfbBhL$SSK,`M!i&1F#q'l'APpDC#,R!44Q9(G59hHP0q#$F0G!eb(j2A@X+brEG +U2Spf-dGDlI#aY)dhh9QKU-iVS!&'6+YCVP3BTSeFlY[NMB*%Xkl6KI-$,1jq9dX +*@ECITCK+E+392HlCEHFAX$e2L0Y2DZhG(YP!GZIe%-&c))6ViJeQ#$`MfXHJiJJ +K8qj-qk-GAY'eEjS&H`C1q[bLSEf"a6UCfH1fcbG[@IjqV[G@T$rj"ca[+$DlX9G +Iqe8"0KUi1eEIV+H'004L),[ab%9jM2TV*JML5,@j5bfe2Y(bQFZUD"PH5ZY!R6' +fG"iDC-D*acG)j2p1qlPEPbR%ppP"d2$mZ@#UGN!A9qaR'PYV-%#fQFChdRj04R* +FQ(E*+![9E,`"Bla!a(CqbPQXpha`lq!lE$*`E%h8,qKBAih9+aB85k[cY("A(@` +90hD)CTD'p["PQRR2K85$UZmE"XPY#&i$0AF$SKa'hNLAD,kK6&cD!dYA6%[qRD( +XHM*82%EX%!&IjTllSElaj8*bf36qfq(ack1Q'kNlB$Gi&VJ-LX"Qk#U'b@&r'8, +eA3X!(ZhEC$M8-#-X1N)&8`--N5Si4DapBI+Hm9p*N!$[pbN2+#6MF-@jB%5S14q +XTSHH0q!59YAV(j0L&+p0J)9jGfA`[lmEMS[$KZjU[JR08"*"`3*+ci"`b%1H1Y1 +CK#3q'YJcMJ,9@)L-IPM%L1,S9j)+55JfCY1c$E6m%pF2EA$r@8,A,#X+h($Kq%X +"I5"-Tl(cmjCp)BkTrLAlelXEKS!bjd&I(,)JHUlf8R`aQ@`Z3!@$X-(%AJ(RfrT +Yrp1IKeA-1#'RbCNd,[r3A[hi2a"NBD3L$kqLEIIBMV6AYFUBikRV+N@SZDeMCV` +f@"qjej6)IIZe$6I4iV@m*rmN%DeZ9'j94Chqh!*YC4UKNT!!jjm,Z9KJX1d9mD, +ApiCMKa,aGV[e-%kkf@IibJ[#HU@Z8D$lfUDa1SUl1h[1T[B-(DTIT$H1TmX2+&" +H32DLp1PP@LP*dkm+9LY#m3EeTSH94rjqZr,j[-!-!DBli6hHVE%BmqrJ2iKTMH, +YVBp@HYceCaj6l2,Ck&dm+SF8-dr$G[`Ad+S`CJ"SHrBCAdC-9MJA%$"mFGamdKL +9RpA($kIarBYfpX#2Im0bebfr20bFR)+r9er[@h[NCq(-ITp2qFB($44@MHF)SF3 +b%a3pXl050)erjN2-G"[--jX0MjrC6PaLKRPIZDr8V6K"X26e8PG$`L1N!#YCVNh +ldVmL!cT&Dr3UI2$3ZG)Bee5f#*c@EZ,8Uf5V`fY"kaEaa[cf#QB56%BME+'*fEX +E-CT!N93(j'VV8-al-Dr!#pQl5GDk%qXp8raRia("cCc[iTK0*0`1@&i6-pGF'3T +5RbIjG@&a`dMa1&bl8QA,#Q0S!!#!0AC@C@G`H!!!H!"`!!"`J'!!N!8)B!#3!`F +!N!3)KACJ!*!$B(!(Gi"J!)!("AKe#(8!C@9S9P8!K`#3!`G`!*!&F!#3#)!!N"+ +!!*!0"`!!J!K`!*!%#!#3!i8)!!%J0!#3!`3#%J+)N36P!U#D3*lqLVI4[hdepfb +R0GCQdeE+Bm[1@6iKh,Zr&p0p1IlAm!8jreqrj@#-NRedGp9d[CYll(+`0LR[ZMA +%5cfE['*iM(ddElUBqJ+Rf8d@LA,ka+Jq'(rrdaFlYP(MNkUUpc@Nh9A4EG6E%FN +0+ql9CGi9EI[QdIQkfJ*65a-3N4a4ac6fAfe*Hr@lFq[Kp!9c!b64er,jJcNdDGA +@#2KLpX-#*apZH-Ap"Cc$(Q!!!%)!!+S!"3!'!*!*5)N!GeCiGiPhQ*Q3!*USLA! +'GhCiK`PhD'PQD*N+ThJ)"@CNCf@(9e9U98D)KTQ)!!Z`(!!#-c4&99CRCfTi#JQ +3!)LDLJQ3!!UJJ!!*!!#J#L`M4&999QD3!hCRGiL*L(LAKiLCHSTjH)#RH*S)HD# +T#DU!#C!!S!!*S!L3!+N+qkQ[49YdaKkZHbZ9YPAChfqIAbhQ4MSMmUG%ANA`E0P +8Fe&AJj&khmM(llDG(Kd@dI0b,jV,+XpRdpdUDR)cr00HQcj[+fZPE6AhN!#PllY +-G90FCX'ArQ,RQQQj'",QkXhCJbrX"5HINKk[9$%",,M`j1RU`pI4IKa3j,l*ElZ +RT`j-[9rh$00qA*q$!acqcfqh,KbBX(B"RLj2cirpIQ`1G1$VkX'9`Y1RRkm'8K2 +m@4-#@B#5$E)[lJP`,XU[ei-Fhrhrrq23`)pQ$RkZK`M1cUaBHR"cKLmpP@P`,Sh +I*aX#Z,Qk#BRp'K&TrjTm4-#3!*`qq%q)aA+F&'B+U&JKQAeaGA@666cr+bR6$J[ +mE4IlFRBM20--"4fcDeh00k'B!+Q#b6jZV0eUHpi&@%PHMp*4YVQTVP!(P&fL0F[ +5d,%eQS@*p0'L,d-BY[MRMDp$@@RqQ6,0KpP&HQU-dfZ6K%%YUJc(XM6hk`ZVedU +*8k*X'R6Ec9@D2!-U![dV'0'ZLf(!69e8dA-#q$0Mad@dH+'G%YQHV`B(4-R0Zf" +H6Cp,*Q`LY&9&c)ZT`Y&P@haVl@TXHpJM`ebTPZa8H)5BKH!aJEbbbk,GLRXeh"d +9$2CMED(af1'K*L`a0GJ$Bp[[KqX2,2QeaKM&m`K6$ppXGXB8h3fl00%SkB@9eES +Gekb[4'&%2'Q[E+0rZYmV#5*REXe'HU)DfhG-%1lIRC@#Cd`Zb3K@M#Gfa``,+HC +3Ap@9XS,iC2AcFpP9J3p[IRq'HUM4i"%Iham-!"mIfGS!C%E*2iQkJaq$XCfh&9E +HTVTPm1M"2I[IhHQYMhdh%da9(dqPS43hdE'a3Uei#Gl3NHJe*D3ZeK2cAD`h(VM +*"ZGV1,I"dM2!La4qH19Xpb&U))P"2GV[N8pGP'R*'kcEES-8!Rl1X8!8'(3cXXq +8Cq#lA``4,J)-SpeC$bLV*'U0&dAK'Bj@Fi0Cd15%UG8mqMB'2!RSeV'Z''rj0qr +IK[Ea$&8D`5AiIVqX*cd43maCD`%5TK#ccHB(r'%BPV!$6L!5E@QR+EJ5E)pFRab +VB2N[[3r1H8dhfK'Uk+(5LaNGB-2++YJ@AQbpj,PY9jHbM3+#fADc0R#%#$&k!$T +flj,iQ'J9218ZVmp`"V08i3pm3C8(%BjFf'U2L--,j6FqekpSUXZLa@0d8hE,,PS +a5H#b+5k$KdVBP@k&2`4+h4Pm#dI)P@G-44+%r+5Xr-A,Q49'aHQi"r),G#CXl1E +hH%D@%MaE3ZeIKK`5F@Z+Y%f8E+'aFlQ1''V6``dEEEBPEpPhABDrPNMSKGZ!JX4 +TYXVZRj1''m"FRR3a$0Fqfd9rp0)JmZZV9T!!-HhC'[*diifq-iaakaC0Er0Y)@r +P&T@klcJ%rM*(8)$"JVXQNLS9BqU('P@aj"TPG`(2Df-YYYFjjh8''dU5m&#bH0, +pZUYQq2Iai-H$Ii4DHk[p)`0cbY$R5)0rJ9#N#[Q!Y5"X4@1ipJ@8)qQElq#&hK6 +XJ@Pq"Xj`IH)$YNJCG6Db9G89''FD%VLTICL$B'5AL(HBQ9PHMCZRN!#Ga6b-qA$ +3#a-!Y*%&[TZAq+aePfZ*4BS*4cb1D1ha-6R+jYBPUdNB0cABYVfl-`f&[[BrR)& +U)&LiXT9BPm3e&P3USLGajK*4Ni5kdae-2ISf,CPc&d$MCD)#EbpQM*%@[-b)DlL +)Pkp@5)69J1e0-iaY[RfkZeR`,'c1p02`&L0aFY`b%aCCEUMZ2+PcBdBb0H2Eaqr +cS)8@bjb5DCT(Z@*lr!8Yl5A`CB$lk!C,lmA`AG)bJ"Bhhh[`B*REBhI[PkdcpXI +"Pr4HIE%$"MYM"JGCrNkVK,-XN!#Fp9M)D+iUl-0YUBj!*XY(Bd@@'#Z%5$i@D$X +1d`qHX9k#U'Q9)k32k(Cr#3r3ZZ-[RCEi3ZXe5qG&X6b*9BUQ5BSraFS@Kqr,j89 +"Srd!dXA(2mF4[3"6&cBmXC*Z(RR&3erH6F4``1f!!jAh!(,#NP8Y$)6P9`-2QMP +SM$+Ffh9U3N&D$XE9(&QZKSMmD2TQX'PG9FBL[Mkl,2$EX,FP1#EKJA'KQ+N2Vp5 +YK-pVfq2VXP+0Bjhq4h'dYkHa@j,rbYkGHr+!a&f@9k4'Yh&B-#)ABIT+fM4+F1H +-((5Z(2L1R&F3YiZ'$rTk9"k#RIY'Lade6fCQG-2(A2#'a($,JDM!Qf)pZBjI[D$ +0Ui'aVXfpqZ(M(aXYh3PC$6BlBm3c!4G*fbqUbdqSRMKkB8L3!1E'!,mhQ4EKXqf +RhqF4*He"fb"+JYj(PlDA$T!!cY%VKSeiZ8Si([,,&!r1a"J0*eaVlj&iQ&R$@+i +l%FT9J59aQ1K9ZKd[2&"dFf)RPlG4@"&T#+jeBc+RaMlGX[i1lEmQKN3EYhFkVZr +a9BMPLACSF3C`8ZV%Re6)EB*,c3[QPj3GEFFDRYPaI``#"c(AY8(MPA38T#mc#L" +m@1qpc10akHYcX)a9r*&*)AiLXJUp#dP&iLFfk3k60k#[mT,IJ2X)ZJcJPjH89pY +r8"(@U,8baU#*PC@l5kq6K"2&Vb,,@0[peDq&SeSVi9#5EJNNca4-P)f2JTRYM3A +2F5kQ9a0,iR9$iR*Y!)b2Z6hqkdc60K,SrkU`j-M6ph$!HaX!SqH*%*Z%HadQ)T8 +Z5FV9311"VK`K8qN[#1)jB@+B*"@qm*`[,BkXU5pbMrd0Q`rY(G16S-#Ep4GCmbF +jbN1k@B[FZc+@B8G@60e+Y9"YmYh-SVaI@Ccbb)N(Iq@h8565@DH$C9P+HA2X2jY +JZqG-Y'YL1F0!r@D#HUIqc6UU-H1`eXI)dlj4pmJ2rj8jLj6JaBT!Y'Rkj2502jB +[fM5rh4k4K@b[p4C9mVLQcd')Nb`,2rV(dKkB#pGir(MNU+F3N!!r%`eaQ3-55SS +LAe3q+f-(KXP`N!!VJdqjKrY!TFREQRp,N!#$"HUl-#$@$411UF5['B$Urr(5K#H +1LKGI53hrEc*b[ma2"b[mbVC-[DiqGd!PLY-IAkJdJr[G'KNGq+-`r1m45M0X(jh +L+8#h)FCXcl[('E!#6V(0R1H%!ck4Tc4Rqqi!N!8"!*!$!6`!N!-m!*!$4J"$4%9 +'!*!$#Nj@rmK)japi$'i!!`!-E!!!e%KZrqbS#h"aG@9eC5jbFh*M!J#"JM@JaZK +Y!)'#"J'#*b"Z!!iJ8#mS!!4)E`!'UN*86b"I)&!JD!!))&!`+!!'`2`!#%T`!!K +R"P&!E2C`!#e`!!Vre$e`!!lrf%(Z!!JQ'$!B*PJm'$i'!NB!"d+3!#"6d%"$qJ! +fd[%!!%k45LlrkQF8)'lrhU!U5'lrcUS85'lrb+S9U)P)E[rXU*P-hai!N!--!'! +!CJ$f!CV"J&99!*!$+!!"!)'#"A8!J`#*!,m%!Np,!)'#"3F!43"U!5L)#&i`AM& +H-Pic!!!"!*!$!6`!N!-m!*!$4J!UVe3'r!#3!a`!4J!"38a59!#3!a*%594-!*! +$(X'!rrm!JB))`B$rr`#3!a!!+c!)J%4hH(KhH(LUF+Q!J(ZELUKJHS#*HTL)GhZ +!Lk"kN!#*L'ChCSKRHAKAD)GiH(U!N!#BGiL@GiL*GhGkH(D*J,Z3!!TiF*USLl# +3!*HDQS!!J+#,N!"`#VUDLCQCQSZDLiUTQT!!#hL,LBU+!*UJLD#E#jU!X!Q3!+# +!UC!!QjUVF+Z!!)#3!iBJ!!%d8eGRKRH)J*!!!*!!#!#B!!N!!!LA#3N!N!!!N!5 +C"ad649C9Ci"`"QGhL)J(LA!!#BJ)P`!*J!L!J!N!N!#%)J6`1e#(e!*(L`m6k[Q +J5K$a2lHAQ`Med39%+fY426j1G9@&$lP+M5ScCNUKPj2&Sm,-bkL(*cVA3khcBi) +ddIaB8-U%I5kRrIqb"%#3!2")$#bpDE1`,U1SQdH2a1&4h@,PfR$)f)223)J4Y4[ +VU5E(8ElLC(Nj2PZ&(a33)J5&LdSh8k6HaVGe4b+6DEr6NmcSS%K#TdLMFe!MBKh +)C+@Y`5bY9#%hHB'mKAkQKPm6)c+1A+hD@JKB3YI5f*E1fKdNX`lMhi22mmHB@m, +ArYM0h&eH6GeHi"S0QJ646@FeH`J,i&8"IVEr0M83UcJhhJ)lPp-%qip#qYX!Y8K +,YZ5hlJXRJpT23Y+#TM@(d1Ba6TKFeAKmce@&AB38bJ6Tk!*P3Y0[YB4NjP9B))p +65UYFSLh84IU)rc84kUL2I84p$3LVND%%85@IAVN$9pBi[,A"T,K9DAD$'*qV#KL +IRd)BR4[#pr$4(X`,UjHEr54$-Z%1`#-C$1*qIRJ[e`*d!*d3*qN#Hk"0%#G8#GF +#Hq"%Cr#"2L!RrJ%q3#GS#I-"2S!QN!!6YJ6Z!6[%m+)k08"+`#9i%X!*UJ*BJ6@ +!58"0F",-#E%#@`%fJ%Y`*Y3*,!Nd#6iBRYNdp[r8#C3%%(YNBr4j--6rG,5FM1K +'cGprUcmIKI"ccDJ(4hLE83V[,CY40Q@R#6QASFlpZ1J4!M@k&4kfkQA-q0GZX2K +6k1(j0(,ic5MVH%J4!M5LTiqlhYeFae'lTF6KH90bm2bfET!!Jra3)J4Y3qh+$3j +h[IM3)J5'XD8A0l*IaVlR*b-c,bFC9,&AmN#)%DjP92(h#+UhF#TeL(r#"%#0cKl +q)(hFdmSZUR'4cH6fBqb4TPj0e-ZT)9Q2Km6)Ifm6aC*qDXBSrk3)J4Y41`*dNDk +Gak,TEYqVa2V)@)J4!MDLrRB0r'Z[k2)c*hNm6-EQh@,U)%kf1hE#r[EfmMUEr*b +FEJj2)hZCa-Cr092fh+"%#0f)S6,kjM@C@(aZ&KT$E3b"S*d0GXJ9(hQmJ80h[Cd ++c+CfcH-JF0jb!qq3!$l5"%#0U*P#KH4e%c+bXDMIiH0j6Gi03J4!MFkN&-Dr)FU +Alb)miFHf(2ep[q#"%#,Kb0pIlbkM@FE-2dYLYBFKY'ZUEM$QKl28U&BHQDEAm+` +df[m[$bMMTR3KEYGEA9KbGHZ*URddZ*!!VR&a"&hPNKkQ1@R%ENLD33V@-ma'C*% +B(V+kkiIPr$#'VCU9rr-N[VU'R30m%cD3!06h53(N*$Y-q'bh'd'UGNVYRmiV+T0 +fT8U8R8q6b*+8RZ3Vj2FU,F#Fd(B&2[fYIYpV(YcmIi[NNpMqcJq2RfZbl2"NpLV +pV"clM(l2fEM)Tpml1i8V(l0jE@P2[cp(qHEf23P5l*!!KC`fc#(i,1AAm`Q&4Mb +mU&KXBH,d"eLkpSCYi5HaTZTIT'-YU-&#E44h8`FrH%L50$5faqDXq$#dPRB1ICm +"PGB&2jl@[+3lf)Y)GjUN,1AGmc&K'mF6l%K(E9R``dqbaSDbRh,E'KY*9CbH +p#[m@A6rU&+qRf`TXA3TT&T%LQA+X1al02Z5TFUcK@E+A6qHlcFQEDfK1YPfEmTM +jk'H(+(Lm-JAIGVU*3Q6BPaM4#&92+S-[S3VMjcK$!2h"R3jN266fhl8RGKC5Gf0 +f"0q"1+"-i#G%#GJ#IdNlY4A&jmX#I$*lN4iJlbS4UKelS-`3*mq9%$H1%XB%EJ# +I&P9!')Y$ITT+"1YP9)'Y`P2"&N"1MP93'epI,Tr0L`KXkq@D(3Kkj)Zl$JbYE2q +@&[VTq[ZT8Z6XEH&VU,8SLR[,hrk&GrI,KB58NBBZVV2$1$YAL@%UlX['[2MeN!# +eYkc[58XU&CX$h-UX5aY@K@QaDZ8F4Y$`jAMINX`0V8@58V[eSDV%XlEB3XGa,rp +R`Pk-+(FV[)qhRfZeQc,qCYiHTS5YEi8Z'UaDUeKkRSb8T@`"PA+PfG$3qa*5fm' +%+bi)0+Pk(elE@iA2PF$Cf1dXHKJllpP2ZhGaLpq65PGj5R0G5RG8TFC&Gk%c(Uq +a,QD8+LfX%1(IHeK9@Xb&9C&Hf[!-Kr,YBDk'[M2Z1VrcC`m1-qGerHq'E2Z,Lid +HRf-kmq1'eXYD8ipQhpbBXj`Jmjm)2-P4I+,B[P"AZ)&Gj[&ePhbH,AcFfEb3!05 +J05QjS+eE#9)19c$9@&U9TY6NY6pV0i9AC*`BTG$PF'[cHR2Pl1&a,Rbj+AYFVJq +$D-')@RlRE3RlRcqREDHIZGRTj+5A5rMbrirj#EZC[6dH9XkRUD0T8cpcql4RlRp +r+SeRSjh"UNQPp,`Nr`,Yrq[(r0f+ff#HXM`BI5Rc,kH$2m0rHb6J4praNlh3Kk1 +q[,fj"(T!r""(Uh0l0R(6cT*Ui%1bHJem#2j6Gj12QD(pEllHk3KhlQ6FENNeGIA +@r)K(9h,%%E+EFEe$&TJLN!"(L-r#02`,3rY&1R#'RkX1"DHJd-UHf$2A8akS-k$ +3c68!BG)Q'08$21K8-'FJ'(e(6TkU(dqZ$13U60"R,8CjS-21CJcPJcc9'HF$2-@ +%Fi'Fd&(fETqN$1FTMd!C+8Cc!CVP'Fd'@fQar+`q(M4fB!!!J%9RD'GiH(PkDBQ +!LhU-LBGCDBb,H)U)GfL+Lj!!LCalL'GSKhGSDSPSH@GjQ)ZFLjGRH*KfH)PiGhT +iKlU+J)#,H)#JUCUUS*H*N!#JZl#FR*U,QUbTGkb3!*Z-M)#`[(#3!!#)V,ZUQj! +!S,#*QT!!V*Q`V+Q3!*!$LCbFM)UFQT!$HiQ+PK!!!54&9fKhLBUU!+#3!+S!#6` +L499@CQChCiGiH*PhK`L+L)L*KjHSUBN*UTLEQEUJ!*UU#BUD#TN!#TN+#3UJQDU +3!!!+!!!,!!#CX!!*K#)"1krdK#-")H$$QFh-!6+lR8581F3"I#KN`jX2[3r&C`[ +XVKir(i@9(i'A`X[K"2fp5&IVUACLSJ*iB#F3"1D!R5!6UJ*f+ACN@0,3KP!*l&, +3L%T"hM3LNdZc$d`-hi#I$Na!0`!5aJ)[!%pR*N!-+d2$!MDJ*qA*VJ'f`*8!)Q! +*dXQ5!fZXTeEi+X)E5bR3La)3p%BZc$$MYD([`ffYSDlGacUA9h%,M6A""&DR[If +3!,(q5G#A5bdB9G6BE!Q"+a*FG'Cp6HHhUiA'fX1e5bmQ&Kp+%U%R%Y,KS9T+XU2 +aS3jq`MqTrdYJ'Y8@8XZMreKU-5fQr5KDANlrl3K1kF,l4XI%rQcELjh9eJA@iKR +mq1erMR3e&@6F3cr,TCFIdJ-P4cVDqjq[TCHiJ`3X*iM4cZIhFfeUCdH$Y,6DfR3 +hqqrcVGQM2Ur25q+2Y1#RpMk&1bi+6mHajeeaCA9RAAa3N6CD(#`QiK*Z,U%QB4l +FB-*0$hlQ'YKVSU%rr(rcE3f%9#lp6r,f0e3Rcjr6kR9jZmpZ&c-Y5((p'fc9E20 +#$lVrb$P4a2i4E&r##bH)9(-iQVSmcL@@kc0ec!DM!E,h@B"@TB*CB1@,"U8#fAE +h-)9Uh4c1$+Q+jM,k(MiGPQG5K1fN*mkK1TCI4mI$e9Z`-3Y3[G[#KHq6e*YI3[G +TAdXY65phb2Gqd%fMQG6Tq2Y+lmR6YkkKHrkG1KHq[ir#X2,jZ(*6TIcUR[3[LfE +QFhPbEI[lkqMNfpeJAdB)BKG*8h2CV@-lhlcYDk%IJDfqMX+XLCjGjlG(-UbHIhG +6VEcfj4$@9#AYT(PH0Bb)3qKI`a*'9&DNb+@A2UPkR[6je'ea0TTd45FRlp5fPlD +,jQP@4*bI1U@RRGU'dMhSd4peZiDHIE3rhFq(k`8h%j)JKFQkrC!!XYVFrYpcD@P +@mk1r`jmbq[lUVU2$hFqIU1hpMCaa!*)m+4krj+R`4j%*-[fTIY)%Tl[Mea95`MR +DIYjIYAmALpQ9Q'DqCIcm6Vc-%BTHhjY,eSl@kl-*I1SHl3QB*P)d[SrbiAA`[K` +[J`ZYKHpKHlKHjKIV`[eBAqZ&l1&qR#pM#pHMb*(Nq0-Qh-1Gq#mQ8!)qrk&jrhp +5V,QB-H$0YEM5mrR0MC33$d8bK8P9hFDD4&!$TIZcVqG2hrJHT@l@dp1%8I)NHPp +2P9[RK)XVD&D'PUck0Y#`ai`8V3N-DT[dq89D!c661*V3-N3N90A2iE$+3&NZVB! +Y*"D8"&G#3$NQbfd*%9aMf*5LiTcZiR3Vf*PRBED%R66+XVZ%U@&Pb0a@NH6KY$Y ++@!Dbkl0ErMY,Q%RS4Q*ADThAdlU&M49GV66UZ882S@84S%X512eGaL`ViGYp2N5 +*F,1Mb+ljH,ZKkX`qZCpA`!qMhZcj&M[i@Q6BHI8a0EXm@'eKUI2Vm69GeL`m1%h +ZZ9Gdphp5'VbBJHPI6aBDQ&Q93j-HlQB2!hZhp2iIX8[@b5TQckFL0D,URbP),Aq +h@@rN8Zc@q&8XjpU"AZ-&TM"Ef'#hGJ9kV!VrIEjR%P6rk`3d!%k5+J@eS'IMB3D +m#ZH`+f!&Ie6,@1eB(@mbk"$l-dc'&l'X,$V52Hmq)Kj"NU2"`0l5kUXHh2`3LbB +DHA,kFZ,%dI+rcMX1iK(T@fGC-*FZT*PK%@@GQI,@mdB&H&@qH2(KC8Fii&TP((K +,a06Z,Q&GRqT2USBh&Y#d-,[XcE8PP#(hYH#N*-H0#6BCaCG*lRNDEj+eE*LlITk +Ej*YZGf63dI,-%*-0r#aKJ3l`c$86HmaD2S``kXS"TH*FYB1hl&DYT[NI'D0M0EJ +PB8-@'V33$@U%$mA!cDLepHkdfC!!"m#J!d#H*5bqrZYh5MjR0NYCVMB``mqeSid +)9E4&AGFU%,2H3e3m,CTRB'E8Tj-AHcS'NU4pJ"2JTD&GlS#I&F!Pp$*hqDMD',N +NL%&E2P"p-HdU2E4F4Q2()ejVUBQ@#SC#"-5`TD(lrXQ(&R!V4#kKV$*eV#4PMF% +hSZ"[mfTCV3*#-JJNT&R+3@khZjhY,!TlfPS5rL)+ljP@Ze(XBDMSIi4F6$h9$(V +GV1ikNeRFBV0B9C9$'KV-l`#&X-RC,clCX$-(*'GIA&Z'('IF8*SThG2`D8FFE&i +,)m@(,f9p@l8rd#EeKF%I+F`%@53Sm'PcSl@D@VIpZR[ZRZS8-a)Q"Ghq"[Yh5h5 +PJV$%E+'L`,mDqUbM#VCS8TD(EB"+k%1!%fNI40pK`QPJaAZ&A4,)YS#%Tp&(!I4 +3b(!Sqf&&Ld-dZK[meXr1rh[Jhpf581-JNT0+8I,*C8+9$9'FVA3X)GjZ0LM5[q2 +!cCq0#`UDe&9aE3A0&YL*Y3K$*LkH,#cSCQi,50,lQ!i[AIKAG,!-fLB%p&1X3"- +8"1*RrFCBCUK@UlL%rd-mb9)fZkG2Hdlr!TXK3YkQQ%5jX1U-dSjY%5EhI8V[G8l +Zr+QGQI9crq6+L5Frra(Jl+JUT+S9,3qEmXGV4c'dqlQG@qDe+`2`XZ*DY1SFL'k +$$eD0[@qBb$"pRrJ64HhH"[VVGi'jSHrA68FG522d)m(@d"L*II0L4fZa)k@(Rc@ +mH$Vk%M%Z,(2mpJja,M6h'a"#8"Q8`5``Fr!K3$2alr0)'lTSGCqI`-hA6MM%[VM +60bM@5ZkdQ*1Z[d8rZ6jH[qT&+#k0`Zr)iADGdHNBq[aT'*!!bDQZ34)alVVb-VA +d!3e,d)FIAd)T3%FY$%rd#@'TTD(CjbM5,Nlcfl,TdXZif-A*"6b'XhCiJ%F3#-- +lQ@T-N!"C&RPMNf3&@E-XAASHrAc5[443baGFdIG+EDP62[6EA[S6kY$'NCQiYkh +CC66r'D!p'GMDNJ,lT1EJ!4j)%8Bl!`NkC#(elkk[VT*QIrcFU`ULHI'BqDkqPpa +3c+FlE`r6dfK6S4qZG0[5RaHjrYd'8qiQYS-9Q'eekp2lHj*a5qhPjP$-fm*YlF@ +$I!aCkXb2@AXIQep'4`2drQG9(r'(EGG'$E9QbLd*e@6Z*@IjNHaN85IhZ1@f8VV +[eP-rKeU([fXfkkm115+l&eeiZ@"(`aidA*M[XQ+EQ0#TjU'D&E2'0qk8p"Kb&23 +CC3XA#kf0h68+afC84SCGI"#AZH[)U4qe0pTC6[P[*GV4KLDTJC6QfY'G9XbTAQ8 +Y$YFYCd**`(9[KbE$Yjbe5!9A!9h0$'Le"CG4#UN"(!*aIL)fe%`)ZlS3ePef2iG +C`-Q)ffI+0Z'ZBJ4%'@dqU6Fq&1REHBR6%XN1@@+C`0KM+!$X-qEje!&GjaDeMr` +kb)$KJf$,IKq-4$X-rGlce'@L'Ajf'@-VlTCVqrIqGPVA@k(DK$%#5ZY`NcQ%P$P +QcbEAp6Y3XLQhi[@kFhdj%(`3!b4#KdqVk9EVGe1K)ThYPH`X1,qFJMXGd2Lec9Z +a2aB6(*@GJPCV8H1"PXL"Nq0L`+dJ&E*J9*!!+Z'6#l#Q&CIRGN*ABT%&JSJNCc# +U%ZfKEhjJ3r)DKCEN$2dG2DF1%Vp"aZ6qhj8#59XH(cTpA-+P@2VG2*h(6p'(TE5 +mk[f[#c*ZBEaZaKkAAc$KFp*NM0*aIAf@2@keEYFZ)bTFXF0'HZljT@k5V'G`-dh +XP9M4bE'AQ"*P0'Y0)hqE6c0P1VI'KbRk33GY,c%1!*mDhP$@NDP[Np3B0'YfU0l +9eR,J9dr'+hjGrQS+Z)2SSjil'ZC,QPpAJCSYD0(-a)9XF1Y@iFH5r`ia3ilC"QI +qUT!!Lm*f$TmY95eY$*LrPR9[Pj*0Kj*E5m4c59+ET+C'aMKTE1qc8%@1ZhqEF`p +JT*5d2fGiAR56[+'a#&''U'V*F4T9pQPFYV2akhbe,FK*QS#PS@(BB-DYJaU6)aM +Z$(k@$%P$(pk[-XLS%R'laK4096-EBIkhMhAbjPe,&ChXQ1AQ8l[3[1,mHbqSGD9 +l8K@fV"JTP2dp%3+XVN4Id@056b)Id1CZ#h5bSCR3h4P8B)IhR&09e$3cYpdl[+E +2FR&iC@#4fd+1!8pdFrEC0VimUeB6AP@[RrU"31[Nj"[PD+hbVql`2"[X#lhkjkR +[SqChhPY1CdFCaX-"#5cSi06eGjJ)'IV2i,"MKSBa$43dZCBeqKJehE$,D!e,M[M +P)fmrd"qYfL"L*FY#XQ60c''h91%Ed6EGq4[5dhPDm'q2bdZeb582IqJDm$PHXFr +$QfZfG$+Li5'lP2rhI3e%2iSFNXme*q,HfjF@KHidMEk5EAlSa1UGpbiTXRRAGc$ +SAZ0ElH98f%hBehQV'Km*Y3k,24C)fmS`8Q6GM#dkRjYe1A1-b-2rFh5G@plfF63 +))23Y`6ciI&5m52"0BdUPeEkrh02HlcHEjhUdk"2UVUaHmcR%f1Tei@Ma+ld%Ulc +'98DDlXUTS[G9+!4Che@5dCYm"c4cmBla@KelNeP@-1BH!b%0J%49j%ahUX9QL6A +0&&1TG1eE*2!YThK43j2cqHLTlTXf"RjDa4YcllMRSK'ES!q,U+fIr1pR&eZF$Gq +&GA&HfaSfTH*cXDZmhER)TN@c$+[cq%mJlSAZ3T')@9aLZ8rRHeUEAfjk2MT88EH +MYehjNVE'Zjr3aBH)HYlqcP1#RTJKaG8mZ3HljPbp)DQbPM9eHG(1)BU&6[2iX)A +cr4V)NApjkGlN@G'pb*8fp1Ek4N`1iBLa@MIPbT2PcL#ZChYieS1540'rThKQXab +)[1jekZJV)fU0+0lM@4fUCNh'V[1%b8`-PEcfiS+dD1qpDqlfpF5#C93C+lcQA-) +5EF`G2$C2Gk5UVZHXYd`"*Z[KU1FCGT4qpe$Tm`6JimefC(1[RS8mjXH9hR1bU%L +TAHHcE%FBPEm+)(&,8LGec2BIK`lLamr+HGJcT9(IAhNERri0AY4qdebPBYEiPcC +)-#aH1L5J61ch$43&jUPT-m$Um-jrNb'qe$$h2BVD+jj*RXc2CQfpEjb![rjU)l, +*K'ji&0'rR%c2jJ+lA1-2(,CP,VpS-F"[Nk[phiGfcbGI%VGRZr`h!!3hlA+lm1I +p+1eCTB0I@)H2KB"9$-d"@MRH00YGZc1m"R%A8P0P2+38*hQcNPEL[h%TR9Th*0l +dfS6+d-b%AY2Hlj[Q#I)%@(6)%h"jIXlrJYSP9DFX0#%qVRGm*M1'jh+KiHph[IX +52H1XD@e-A9YYlD'k33DYVbJZ5B$1qYS-qa*QijJpfq0$63[("5cK4%$hXllM"#p +F)Va`JZi85MA[(KfNG8jB!%q%$,5-pTN1JfA"kC[LmZF)Rb1@-@fiZBZeZ)Xld*r +S(6i$#NdTU,rTP$N9Ub$$30h#kp#h!rj)VH$dm@N-CRRX!k'XE0`mhPPRH1!eMRH +3!!0CjhfeDaVX@(0MdeA@4hmbr,fV)`X2+imUC(Iq9L[9`Ab1h"5XHYm9EXX"P`# +1aJANAABVGG&5[d9bKF9mMc*`lHm5qkjGpm6C,,lYXlEXN!#5hie#*fA,l2PTipE +XhiB$amCcS-Z`BJBeGk-qV2UYl"&IS68`'+#jRRH!CD90mlF4B-m9@jThCXmhfl1 +h69-lG09Rq#cp!$cC(&"U*i'+K"iSYBhpJTQ3!'9D`PqeL(aZH#&'da1eIq3qaBr +8D64LUf@RY1Mp'VmZ()m(-TRpH)HfKr(M"kdUbZM,a0'-lH'HTkY6c02D9E*(fSq +M9q2T(V!GpA!-5h"Mj%-ArN1Qc8i5*$))q9dbU5JSX@+9Jdka1`hqc%09E#RQ4cN +-DIk0@9U,6%kkq"5(X59(XDldUXfIk0Gk8FjU0(B#0m$U-"QRHi-ep#Zp+H[BK@6 +e6c#"Zbe-Da(qF@9@aPqY9N8rYAr-C%6l(*EY!+XS)$kkPlA(D%)LU5U-LV,[qC9 +XY4DG)Qdl3kEYT(Nm5@Hi$552+Uf'dZDhrSPaUhIIa&VAR[ifKqILprNH$-`GTc0 +[Q8ZUIcaqieTHZ"(b!4kS%IY!MmJ%D)%D!%I[!MSJ4mS%IM!Mq!%HH"(c!4jJ%GN +#2,!MY!4pZEFa,f2Fd%2RUFPJ0!i4N6FJ"Ue6+BKBc$$%BM9''RKJCrY8`'2&)%T +&3q!%895H`f['*8jJphc#VDXKUhBN@c&4I$MjUQVBfDNRa+fI+VI,#dU@,JCqie) +B1R2G3DMY94La*22Ne*(iC*-EeT0[[lkq0[@CdmR9i@U%+d`Y3!fS`V2HHhA4A'q +K,Aa3+jI@JIlC,Pdm`UT(khpd)qIEdXZ%+lSZI,P!L"0KEB',`ShcjB4m2M4jH6i +Z&aq&iXH4Mihe`&i2#M`Sq,`mI,ir#erE,jlpXr2R&DGrKF%%XMMBTTKdfm`Z0`X +VICA$#+,B2BGhYeMam,+3!c+Mb1$`F[+bKMqYLm2'(6(iI(iH&MF2Nm2(UamFK2( +iA(qYNCAM4mE)a12pE#bZ&-88C,MYp9b+-Iq2MajA#if6NBrLKa$D[f[Bf4NH-PR +,b@,MiM`ld@XTe1i@2NCGA&Mi[#iZ4PIAMiq4(iZ3!2l-ad!MMFIC[fi9H&aq1l) +PpY[-jZQ[SAhp0EqQX"GJrAd8`GPl1PmN#YA#mK&ZUIK8`-dkrr'"'X0JX&rik6J +l,TG6-V9TF1jY%([ep,J!N!1!0Q9QCQ"h8)#!GRH)!!"`N!-!N!C`!!!(!!F!!(! +&GSCJ#("`H'#3"3#!!!B'C`!'!!G9F(H)"J!(!(H)F!F!N!T`"J"J!*!0J!#!"`" +hG`!!#!#!!*!&"`!!F!"`#)!!N!J'#3!")!!`!*!$!`)5))LNJR`!U#A!ChqqI"m +lfhJaBmH@I&pXY%mp-0cAE[&JQe-f@I*4Qq8fjMSTaqHcGdH(YbJLY5qX5!LA98I +heITd@X9jQplNaC!!Trh(i%95Yh!EhG2kL1U-rTVlfIerR0Shj1'1d-E-[A'`SMi +TA8L[&3KG5*DpUMlmqANjhmE2Pj@2k#YN'8VGqEJ"QYEZFGm%E8Rrdk5*UfI2$*r +JA%@)Xq1%!*!$3J!!Q3!&!!B!N!P*Q3Tk9RGRH'L)Q*Q3!)D)HJGhCiKh#QKhHRG +SU!#DU3S&K94RC3GR9'"99R@)U3J!#U!J!!)c4%499QCQGhGiQ*!!Q*!!!*J*QUQ +J!!#T!*!&S!Sa)d499@CQ9fGiLAGhQ(KiGhGQH)KiL)F!#CHJU*!!UJQCL3S*!!U +!#)N!N!#+!*!&S2bVXTfEUS`pRq0eG2KRYS`iYI(LB&rPAC9,$iGpVFAjT@Afbf1 +KMh!BG#ZH2PA6&)[Lr+U1UZb-h0ep@RYkp'ITlZ6PdFmhSpAUFMI2e'0C9M(9dk* +[rIrqkHIbPA9$IajFDa[Xj-j$IjV%IPdpA,ekG2Am*T[4qE#$![SjrGTQQQE&IEd +qrS!VkX[l0#fRVl#+9Rd1lPj1hjG1I6d3QQmrkBX8ITI'fbDZbm#-e&e-E,mbKE2 +cAl1bqd$09-F`Cllr0[`K8F$B8SL$!'0[eK$Imc+-*bG3)2G3$Kh'1VGir10V`0G +eIr@$,6cp&&P@b-dfZpXL#DcB(2k)epqYKX%c83D`QmZkN!!GX@2SU+0dF0ZE&LZ +[S[VT8[YP&erqD0Qi+PZ-fD!UVR)Y,b*`@iTG4j4*Ph4D3b"+fm$@,cEq+'r09,i +lDU,i`iB9#c5%YeX-$D(qYaaF%@,Z)&3!r0LKL8B6f4`[$Sim43)5'jXAmiT[IE4 +U$Z`*@pNV3+c!9MH3!'6lbKE'"IZ5+iE3(BTZ@2IABP2'*cFpY&dHf0)1-0[$al3 +@N!"a$CBSIIE,$T!!dL5k[8`bXa*d9ER%UG-JXN&aGKlBC9)G-VlQ%2q$+)dmk@6 +E-(NJ+LcD1bTXfE,#`%$5BG&4"9T6clj'r$akp))FN!$,S)cQ8,Aad4XllpC-kd- +9#@T,AL[PY!i,SL(Y22c8E#4%k@,QZMIZfQ*+$lMIMjd@Q*IBT&+MUc)RlBX@dBX +k0N%9A`2)YY3HLSfFUL-N0SUjaiQpVT!!eQ1S-KikJ,4-Hi@d`)@1`I*#[*!!mU" +)$aSqK6U!1()&5JP&m+2SDJ*S35,'%5k80AB9hdkjcB-SX$(pUShE,r@(SY$lff! +PK5+V`YrMYjGepmV#(6B%*AcP")5G*4qa$,P0jC!!!B)L8(m+F3%dhcYM4i-Th2, +#ap8r2@!dRIi55@0j9GE`PqbM[MmGV564M`#*IqhEG5%hli8cq1%+eJES*6Ycdmk +-9mqA*$K3-`hc3b-Y'0G2@edU+j@VSLI(J5[c9)XT1aYLaSQM1[mi#KI*K4lID`L +LEYG8M61cX+e,fY01YTR$*R6#U5%1QKdqh(Ll*(3Gp-SMY6J"a80IM+h0lpN[Q-G +NY%KG,2['+*SPlKRX@`Z5'!"0j+GaST8QIZR@1QdTp#%@QfLR`8E,bZJl'eM'FkQ +)a0A(*+mCbmEe[C56Da0QjjlUaMQJ6cZ2A1d0PI*6IAj&TN[eaKRj"TC4TL,5N!# +45KqHK)6MaFXTE)d@38*059QBNUr-I4$"K-ZqCD1bk)'1L[CAI&dp,IXGD6cGjfk +AGCL#aC'aCh2Uq,+l[8"11('1PCA4ZR-iYLUcPX%'m65"e@FqAR9ZJlIGiNGdKc, +Jrf#D,RTb8l8#bJ%0McY!kNqJfU[9U+BK"`JUkNb(VA[+Kq$Q'b)8DT@cNL#kXiC +SCM5["`)(534M['lRX`XCZebhE+KKm1`@'ZMEYMB9LKm@hfeq!JIUpm&+X5Na8M4 +RL5QbGRZ2Y1rd66I`66XGX4Z'%!q$&S`)ZqmKMKi8,TEr(("JJ$1R(-9f`rXJ,pC +hi[KpblS52@XdEqS&NF2-$J#)VY19j28PS#m9JX6EKLLVeeSm!QkELq`h3-lpD&S +jDempbaV-LYG"Ri0#@DqQ&GSi`XSmEN@r-@N1E#JETb$!DDliZ,jb&m[L)$0KBRb +iB6XIb"`3p!HF5K,4c$c0ha1"S6+*J2%2mENcBV5CDaQ[SNRr"ik,Bh&iU'm*)q% +`j&"!ZB!K+ZPeD5Z#KE1iJ9F1m6Y(XiFH0cVQQ!Tdf#AcemX&-&j9a`0V@kmGb*6 +(qeMql68BA+&'j-FK0BB+m,&8d')UV'MY'9fP1,-bPqiDQhLf4e%i0Va$)F#LAr` +%U(@IT"d33LXFbSZ6,SU@N!!LeGK5MKXVr5@imqY-[*!!FpC6E4aV(b(,HHkf%cE +*@Gm`f[pXEKZi!LZY'C2*3K'E$PPZXU+aVqIf(!%J6dqMpIdr9)2pRlrUaDV,ji" +YdIG3$ai0lb93R5-b@PSB9VKFl4QHqBI"N[S0SPmT@e-)!riN!ad9Z``0*rM4i)h +b*h[d3`eMH4#`rmRK,8MLBHQ2b+aQF!cfb'3rShV)+Fc,bP)V*#qdFAMf!)aP&"T +k`IUPqmTH*GmAMGhXcL)(!Nrlcj%BS)m%Z@Ad9))P-L0*,KPZ3-j)DU"RE6(*6G2 +M[#jKdEhariK%(hEYK+HbMa("fRA4E$I)-GV1b5CIT6)J+(SLJ+4rY,"CmKPR[MR +S[SK[lq"D(hHfilA9C6")T13N$4+2TXe5J2@E9*lq%5Q1,IZNA(irqK#DKi0%j(M +p4M(LEC`2*FPbM`60HY6FJ3T'2D*X2*8M(XF6PUH#IH0i91Bi@5RIHPJThhTB-b9 +BS#@EC(l9*NLAA%J2aLQL"!52-J8)$jJ99f#eSUeND-@*C*pr6#4lZ'%(lpC"V%A +rf*A1Y"U#eqVXcZ-`,VmkSUjhKj2$RFBBBV+NBaFN0edEIlJR(fcmi*N#U25#HG2 +lbM(`aM&R`mD1D!LmZakQ)eXM#,YMU%j`BV%Nd`,5%kZf,@B8T*f+ReLiNkBZ35S +92e!98DR-cJHk4Y62'l0F2CkK(eh,P&1-5`&EVkM0ekA3LAd'aeC0aNG)`46qm[J +JLQ1EYV-FB)Z0YUajKTAYZ-BQUeZ%C[Y83YZ'$T3,*hRYEGVbhj[$&p`+aqHErVX +&KV6@lbN#GPljK1QrTc$Gh`"ENI"`0#BDiq8V['"XLI3F48B+)a&+`T2md-14q2q +p[iTk&a6B!H5(R'L!cRPSYl,C8PMD4L3IjjeDj,@+[Y5pr`$R$'fbqUY+4m9Le0, +qm@HZlE+jbZ&G3)T8HUp*-[9NHUBqBM,S),GXEQA-@lbNIrcI'lkPZQdk0"lT#j) +!N!4($8dP!!B!#'eKD@j%35jM!3#3!`K849K85d&)6+1Z!DkMVJ'Z!!$(q-,U!!B +!!!&F!!!4R3!!!63!!!Il#("aG@9eC5jM!3!!#6G849K85d&)6+1Z!S@MVJ+&!!! +-,9JY!!B!!!&F!!!BlJ!!!8-!!!Q4#h"aG@9eC5jbFh*M!3!!&!YbFh*M8P0&4++ +DhpkM*p'P!!!l$m&1!*!%!B)!N!B"53#3"""3FA9PG@9%35"3FQpUC@0d!3!!&94 +38Np+5d&)6++DhpqM*p'R!!#h9bVA!!)!!"91!*!'#'S!N!308(*TER4PFL"4G@9 +eC3%!!"fq4%C*6%406eDMVJ0%Ski$4!!!(#i%P3!#!!!L)`#3"K3C!*!%#RGTEQ4 +[Gd4",Q-"!!!aee4&@&4,38K-STVIk+-RdDJ!!"F`0(J!"J!!!9`!!"XE!!!"0!! +!#553!*d!!!%!N!-`C`!!,fF!!!)k!!!Za!"&!`#3$!)C9A4TE'PdD@9c)#J+8&& +eCA9P,R0PBA8#!*!$39"36%9B9&)!N!T"8&"-49K88J#3'+1Z"HJ!N!BbS3!!)!# +ClY@%Q1liB!#3"'C'!*!0)3#3$NfG!!J!N!`2!*!$,X3*3fpYF'&MG'pb!J#3!d& +38%a$8%08)3#3!`+!!*!%G!8!N!d"-G-!!63!SVrSh++rk18!!!&i!*!$8!"38%& +$9!!!#R4SC5"QD@jKE#!!!L!M!!0dD'8,B5"ND@CQCA*PER3!!%K"GA4[4AKdFQ& +MG'pb)(*PFA9TFQ9c)&0jFh4PE5"fCA*cD@pZ)$BZ-#"KEQ3JB5"0B@0TER4[FfJ +J8'aeFb"[FL"ZCAGPFLiY3@iJG@jbC@0[GQ9bB@*XC5"TER4PFQjKE#"PFR*[FL" +SBA-JEf0MGA*bC@3Z*94SCA*P)'Pc)'j[G#"PEQpeCfJJE@9YEh*j)'&fB@PXB@* +XC5iE9'KTFb"`BA0cGfpbC#"TFb"ZEh3JGQ&XD@3Z!*!$rrrqT!!!*%&eG'p&H(4 +bB@0dEh)JU5!a16N`)'*j)%*TE'`J4fp[C'eKEJ!'3f&ZBf9X!!Y&H(4bB@0dD@j +R1L"'D@aPFb"bC@eKD@jTEQFJG'mJBQ8JCAKdFQ&MG'9N1J!I9'KTFb"KFQ0SDAC +P)(GKFb"MFQ9KG'9N)(9cD@jR)!K269""3e428J#3"J`!&!!+$cJ!"!#3!m`!N!- +#rUB!!#+!!&!!!8j@rj3[$$!Z!!M"r!"1d+hZb#K!3LhaYLY-lij1ZKb`5Lh`V'F +Q%#h`VE!X!#&R(%)Ym+a#CcmYmE41ZKpi-"ml32(B5Qhaf'B!!ka+,I#XCK)EE!! +Km+e1ZJ[k5Lh`V'F!!j4#CcmYmE3r2!!",b`!)Nkk(qS`(cY!mGK+EI(BCJ!$G%* +R2bhaZ#mYmES[,Hq1,b`!+LmX!#C1ZL$1-"ml32(B$'hrd2(BCJ!!kLmYlij)EIk +Q5'hqTNKYrUDTLd*R2c`!m8+RUBJ`(dkY!$S!!3!$!ZS#k!!%!i*#CcmYmEJ[,I' +k,bh[MNkk)2S`(cY!mGJ-EIr4mGKQ!!"U2@haZ2qU,@haZ[r%,@h[M[qQ3LlrVN* +Zrl"#CdKZrj4#Cdkk(f3`(cY!mGK+EI(BCJ!#pK!Zrl*)J!J!!!4R)LmYlij)EIk +Q5'hqTNKYrUDTLd*R2c`!md+RUBJ`(f!!!Q"`d6Y!mGK+EI(BCJ!#ZN*R2bhaZ#m +YmES[,Hq1,b`!+LmX!#C1ZKrN-"ml32(B5Qhaf'B!!T)pEI'irkSYEI'krm3YEHq +1rkC#,[qZ3QlrX%*R5'lrP%*R6VSHc$!I1d$af%TYmGKQ!!*H-$ckrX"X!$Bp32q +m,@`!,[rF,@`!-[rJ,@haZ[r%3QG)E[q83QG1ZKkb-"ml32(B5Qhaf'B!!LB),!# +3!ceR%R!"'d$ZVA!"'d$ZE%kk&m*J"%)YlUe`rbY!mGSVE!"'lQ3VE!!qlQK+VHj +NC`!!I%*R2bhaZ#mYmES[,Hq1(c`!!dKYmEj1ZKlD-"ml32(B5Qhaf'B!!FC`!4Y +!mEB),!!"!$eR"Nkk!R*J"%kk!K*+EHjLCaJ-E3!"lQ*R!!&Z$'d!!ZjLC`!"P'! +!!FC#,I'f3QFr,I'q6VSG%$!I1d$af%TYmGKQ!!&d+f`!5ZjN+f`!3ZjS5UhZC'F +!!(a#CcmYmEJ[,I'k,bh[MKmm!!0)EI'q6VSH$M!I1d$af%TYmGKQ!!%iF!%E32' +f##`!!J!pC`C1ZJ(NB!41ZJ'%5QhZBQFB$'d!!HjLC`!!i!aY!!,ZBQF!!3CJ!!% +i3LhaYN*R2bha[Nkk())`(cY!mGK+EI(BCJ!!jL!YmGU`V!!iCJ!!kLeYmEVra%* +R5'lrP%*R6VSG+$!I1d$af%TYmGKQ!!#k,@`!-[rJ,@haZ[r%3QG)E[q83QG1ZKd +J-"ml32(B5Qhaf'B!!*3),!!$!$eR5%*R2bhaZ#mYmES[,Hq16VSHCM!I1d$af%T +YmGKQ!!"XB#C+,I'fCb"#CcmYmEj1ZK[S-"p#CcmYmEJ[,I'k,bh[MNkk(JB`(dk +k'5"6EHq86VSBmR!!B!!!N!"+EI(BC`!!B!aYrpRaf'B85LhZV@F)F'8l32(BB!C +`C$Y!mGK)EI#d6VS8iPL2B$B[,Hq16VS8ePL25J"QLQ!Q,bh[MNKYrUC)EIkQ5'h +qTUQ,3QFr2!$b3UHTL$!I$%!!!@F!rf4+,I'fCb"#CcmYmEj1ZKY+-"p#CcmYmEJ +[,I'k,bh[MNkk(@J`(h!"+&p1ANje3QhZBN*YlR41ZJ53!%TYlQ*Q4LYYmF$aa#Y +YlYEZhQ!Z)#had,#YmG4Q#Nkk"'j+EHjLCL3JEI(38Uhad(!!%"!r!%kk"Fj8MdT +YlQ*Q#NUYlQKQc%kk"bC1G8j@rN")jam)3QhZBN*YlR41ZJ3Z5QhZBQB!!HSVEI( +!mF3VEHl@lYiJEHl@3LJIr5"YlYC#+"rq)'hZeN)S(rmr2!%!,bhZiNKZr`"1ZJ' +q6qm!#NTYlQ*Q!!'U2c`!3#mYlZC)E[l!6VS"SNr[!!T+EHjLCJ!"MMmm!)![,Hl +U5'lq3%kk!BC2l`!+5QhZBQB!!A*q!%*YlR)r2!!36VS$&&525QhZBQB!!9TJ!!% +k0LhZEJJ$!!pR9$!mIrr!3dK!3N")30#YlZ)J3"`3F!!3"NK!3N")30#1)%"`!"! +Sr`"53$m!6VS#c&525QhZBQB!!4*`!"!'2`"1ZJ5m9)p+EHjLCJ!!rP5(B!!!h$! +mIrr!3dK!3N")30#YlZBJ3(!!%"!i!$"%dFj`!"!SrX"53$m!6VS#IP525QhZBQB +!!-3`,HjZiNK)3%*!5%$3VHlU)%"`!"!31J!`4G(1F!!3+2j!2`"1ZJ*-9)p+EHj +LCJ!!NM!&l8Jk!$!YlQjb#Z*SLN!r2!!'6VS#+P525QhZBQB!!(!`45!YlYk3!)J +S3,RYlYCN1NRX)!"J0%UYlQKQ%(!"1d$ZBR$C1d$af'!!!%4`!"!82`"1ZJ2Z9)p ++EHjLCM"5M,RYlYTQ"#KYlYC64%T%E-C@K`b(!!(rm'`)5UhZD'B!rVT+VHjSCJ$ +q3Nkk"5"-ha$i6Pj1G8j@r[j)jam))#had,#YmG4Q$%kk!L"+EHjLCJ!"K#"YmG" +5VI(3F!!3%$`!-!E33,"Z!""Z!!&HGJ"J6#!YmG#`VI(8CJa1ZJ(X5QhZBQB!!9! +`!e*$5-$3VJ!))'had()!%K$S55"!%)%`!e*$5-$3VJ!))'had&+YmG"b!")3!N% +!$b"!%)&64NT'E+jJ$M!$8N0)`0#Z!!JJ3%)3YQi!%'hX0Li!%'!)-%24cK&$r`" +63dT$E2*#,[lr-#i!%&0!0J"J8$"$dFj`!"!Sr`")3%*!5%$3VJ!)-%24cKJSr`& +b!")%5%&K"dUi!##"")N!3%E!3Ba``3p(1-N26cK&Tr`$r!6"$dFi442m!F!% +G32lr8d0+3fbU5LlqrfD@IJ"f!'!!!'3`3p(1'LMr!(!!%!9)3%*!5%$3VJ!))%! +B%(!!%!4+3'FqF!!3"()2NN"`!H0S2!!`"p"'5%"#3%K!$)!!!)!!BLK`!$!(d+i +!$#K!B!)Ba9(1rra`!"!%-Mb!!1"ThN&53lCZ!""YQ'!-F0Nl32(BF!%l31jL60m +3q%jH6R919J!!F!!3,J!*X'hZFQ-!!&iJ,HjZ-LhZFZ1S+d$ZEM!YlR+4,J!*)#h +ad,#YmG4Q#Nkk!&K+EHjLCN`JEI(38Uhad"Y3lR!J,I(3X+hae'B+6VS!1%TYlQ* +Q,#"YmG"5VI(3'e$ZFA!31d$ZFL!YlQi5,J!*ikJV31jZ%#i!#3*!!2q4EHjb6Pj +1G8j@rra)j`-)5UhZC'F!!9C1ZK&`5J"R#R!$1d$ZBQ!!!9!YI!!!%!$rr#!Zrrb +`VHjNB`BYEHjNrra#CcmYmE4)E[rm,bhac%kk&Q)`(cY!mGK+EI(BCd)-EIrCmGK +Q!!%+3Lh`V%*R2bhaY%kk&L)`(cY!mGK+EI(BCJ!!lP)Ym+d3,I#YX#h[S')!!0K +1ZJ+J5Lh`V'F!!0*+V[rmCi)[,[rm6VS6L&L2)#lrr*'YlQ3J,I(-d+lrr#Y!mG3 +VEI(-mG"+,HkYC`!!TLKYmF`Z,[rmB'SJ,IALi)Kb!")8XB%F!8(88S`3KR!!%!B +L,IALXi!F!#!YpGjb'11S)LheiZ#*J)&b!")'5%&K"jB&"lIRQdF%L%,1!+d$ +eiL!YpGlJL()!%JC)38*"5%(PJ8(YpHE4`5)3Xi!V32AH8iG+Kfb3!%SYlQaR*%) +YlQ`JEI(-8)JV52(3)#hae,#YmG"N$($C1d$af(!"1d$ZBNcI%-"1ANje6PB!!#" +YlYi3VJ!*8UhZhL!YlYk`VHlDCJBVEHl@lYj+EHjdCLT`!"!Z!!N-3!#"CK3-V3# +3!`(ZD'F+F!%l31jdB!!")KYZ!!RZGQ!!!2S-E3!"lR4Q!!"bF!!3,J!*$%!!JQB ++F!)l31jdB!!!q"Ym!)(ZGL"YmF33[!#"8Uhaa#!YmF5`VI()CJa1ZJ$F5QhZBQB +!!0"6VHjS5UhZD'F!!-4`!"!Z!!N-3!#"CJ`-V3#3!`(ZD'B!!+a#EHjd'fi!#Hj +fB!!!J%*YlR4+,J!*Cd46,J!*B#iJEI(%%+hZGP+YmF3J,I(%X+hab'B-6VS!H%T +YlQ*Q!!"X8khZD%UYlQKR!!"J%#i!#9-Z!!P+!'E'B!!!8#"YmF33[!#"8Uhaa#! +YmF5`VI()CJT1ZJ!k5QhZBQBZ8khZD%UYlQKR*"Ym!),ZGL"YmF33VHjf8Uhaa#! +YmF5`VI()CJ41ZJ!+8khZD%jH6R919[rm)#haa*!!VI(!,8$rr#mZrr`[,I(!6VS +0LQFr,I'q5'lrr#mYmF"1ZK1J-"ml32(B5Qhaf'F)F!)l31jLB!BVEI(!mF4 +1ANje6PEqPLeYlRMrq%(ZrqK$lHjm)0NJf5$C)0P`!"!Ym+d-3!!"CKilEHqQm+i +VEHqSm,")EI#d5'h[V%kk$H43Mf!!!+Sr2!!8UFK`!"!Ym+e+3'B85'hZM%KYrUC +)EIkQ5'hqTUQ,B#*`!"!Ym+d[!%KZrTj1ZK3+5'hqTNKYlTK)E[kH5'hqTUQ,,bl +rq%KYrUC#Tcmm!!&)E[rS3UG)E[qH2c`"p8+R6VS3aNSZrjjQ#%*YmGKJ!!%q3QF +r,[qN5'h`VNKYm,")E[k@6VS9($!I1d$af%TYmGKQ!!%-5'h`Y%KZrkK1ZJdi8)p +#CcmYm+i[,I#`5'h`Y"mm!!&)EI'd6VS6PM!I1d$af%TYmGKQ!!$@F!JY32kD3QF +r,I'd5'lqQNKYlXj1ZK*5-"ml32(B5Qhaf'B!!+4`!"!YlXi-3!!"C`T`f6Y!mGK +J!!#1-#hZd,"YljjQ*(!!%#h`V8T!C`S3,Hl2X#h`V@B3F!!3,I#Y5N"Q6%UYlY* +Q4R!!%#hZcbm!5'lqRNkk%[3`,Hl3X'h[RQB)3HhZR#!)B!C"lHkJ)!K)E[kH,`" +)EIkQ5'hqTUQ,3QFr2!$@3UHTL$!IB!K`!4Y!m+aJ+N*R2bhaY%kk%B``(f!!rR" +#CcmYmE41ZK&m-"p)EI#d6VS+`&L2B!$q9NjH6R919[pm,`G1ZK$b5'hrr+KZU2i +r22rr3QG1ZK$)U4+T-+R-3UHTHkK33Lh`V%kk#H41ZJGq6VS"B$YYlkEaZ#eYlkM +rJN+R5(P"9&K&2c`$k+JI)&mY52pq5UlrIQFd,blrINkk%1"#Cd(YmEJ[#%(Zri) +[##"ZrhiJ8%k3!$!Ii%JG32pp,blrIUQM5LlrI@F!!2a+,IkVC`41ZJ3N3HhqTLY +)lij#EHq5,bhZlUN96VS-q(!"'d$qUQ!!!,Kq!'!!!+B`"m(m!%l3VHl))%"`!"! +S!#!)!*!$C`!!BMeYmEMrR%*Zrk*#V[qB3QG)E[q'3QG1ZK&i-"p+3'F%6VS*JJa +Z3N6raQF33QFr2!$`3UHTL$!IB!!!GLmZri)r"dkk"#aFMdS!CJ!!C$!(`I`!6Y# +YlXJJ30jS!$CJ+$!(`I`!6Y#YlXJJ3"!S!#'`,IkUCK)VE[q#mESr"dkkm&C8MdS +!CLK54ljYlXaY!2p@8LhqUK!YrUU`,HqJB`$r3%*R3UFr,I'i6VS3CM!I6VS&b#i +I6Pj1G8j@rqT)j`%)5'h[V%KZrqT)E[rXUI8VH!1BlkJ`1!)84%!l31qQ5'h`Y%K +Ylka1ZJTJ8)p#CcmYlkB[,HqS5'h`Y"mm!!&)EI'd6VS3[M!I1d$af%TYmGKQ!!+ +NF!%E32#XF!JY32rm3QFr,I'd5'lrr%KYlXj1ZJpd-"ml32(B5Qhaf'B!!RK`!"! +YlXi-3!!"CJ!#C(!!%#hZc`a!!!&Q!!*@1fhZd1qH5UhZdQBJ3Lh`V%*R2bhaY%k +k$a3`(d)Ym+e1Z[ZU5Lh`V'F!!M!EEHl2lk"`rbY!mGT#CcmYmE3r2!!",bhZdNk +k$ii`(cY!mGK+EI(BCJ!#"(!(,8$rr%*R2bhaY%KZrra)E[rd6VS1fM!I1d$af%T +YmGKQ!!(H5(J!!dKZrrK1ZJLB8)mlE[rilX`q,Hl-$'d&h1l-EJ!"YR!!%#lrqLe +!rra#CcmYmE4)E[rm,bha`%kk$Si`(cY!mGK+EI(BCJ!"NLmZrr`[,I(!6VS)6&# +23Qh[P%+Zrr"#,IkV+'hZb'!!!54`!5e!rra#CcmYmE4)E[rm,`a1ZJj)-"ml32( +B5Qhaf'B!!8a)H!!",`a1ZJJ)8)p`!"!8#!!!"fB%F!"J!R!"'8!!)!)8!(p`!"! +8,8$rr!bZ!*!$(rrmEJ!"$N*R2bhaY%KZrra)E!!"6VS0m$!I1d$af%TYmGKQ!!$ +d,blrr%KX!!&1ZJHZ8)p`!"!X!#!)!*!$CcC`!Le!rra#CcmYmE4)E[rm5'`!0Nk +k$E!`(cY!mGK+EI(BCJ!!Y#mZrra)E!!f6VS(EP#2B&*`,5e!rra#CcmYmE4)E[r +m5'`!)8kk$AS`(cY!mGK+EI(BCJ!!ILmZrra)E!!K6VS(1##`!N!-pC`C`!4Y +!rUY5EHq8)#`!4Y#X!%V4V[r`5H`!6P0(5NGX!2lB)#lrp,#YmGTQ0%)Ym+a#Ccm +YmE41ZJd!-"p)H!%r,blrm%kY!&SV31q@5Uh[PQB'F!%V31q@+fh[PZqDB#K`f6Y +!mGJ-EIrCmGKQ"R"N1d$af%TYmGKR#NKYm,41ZJB'@)p1ZJ+L60m3J%jH6R919[r +X3UFr2!(d3UG)H2rrUA`JAbe)rr`[,[rm2c`!!dKZrrT)E[rf5'lrlUQ03UG)E[r +XUC%-EJ!#rqaQ"%kk!PJ[,[rf5'hZVUQ3!(!!%#hZVJa!!!&P$(!!%#hZVJa!!!K +M)%KYrS0)EIkQ5'hqTNKYrUDTLd*R2c`!dN+RUBJ`(f#U,blrr+Q$6Pj1G8j@ri` +["ciZ!!J`"m(m!%l3VHl))%"`!"!S!#!)!!!$C`C`!'!!!GipEI'irkSYEJ!+rm3 +`"m(m!%l3VHl),8$rTN)Zrkj#E[q`3QG)E[q83QG1ZJbB-"ml32(B5Qhaf'B!!+S +3,[qb5)!)!!!%C`SYE[r%rj!!B!!!bM!(`I`!6Y#YlXJ[!%KYrUC)EIkQ5'hqTUQ +,3QFr2!$a3UHTL$!I6Ud!1J!"!!-",J%X!!S!!R!"B!!"8$!(`I`!6Y#YlXK#Ccm +YmEJ[,J!+,`"1ZJeq-"ml32(B5Qhaf'B!!4!`"m(m!%l3VHl)3QFr,I'i,bi!#Lm +!5'lrN!"1ZJdJ-"ml32(B5Qhaf'B!!14J0JaYrpAaf'B!!0J`"m(m!%l3VHl)3QF +r,I'i,bi!#Lm!5'lrN!"1ZJcS-"ml32(B5Qhaf'B!!+``"m(m!%l3VHl))%!`"p" +S!$Bp32q1B!!!EM!(`I`!6Y#YlXJJ3(!!%#J!)!J!N!0R+#mZrj!!2`G1Z[k%A)p ++!'F'F!&J!!"m-!I"r!"1d+hZb#"!hQJ!0Q!X-!I"r!"1d+hZb#"!%#J!)E!YrUT +Q&LYZrj!!mESr"dkkkUT8MdS!C`4`!@!m-!G54l"ZrijQLR!!B#i`"m(m!%l3VHl +))%!)k!!$!#"`!'!B-!I"r!"1d+hZb#m!6VS$EPL25J"Qe(!",Kp1ANje5Lh`V'F +-3QFr,I'd6VS+!M!I,bhZiNkk#F`[,HlQ6VS*a#mYlZT1ZJQm,bhZeNkk#E3[,I( +!6VS*V#mYmFa1ZJQN,bhZb%kk#C`[,HlZU9B[,HlZU5fTp%je6PErpNMR(`"#TdK +j!!(*#%kk#@`JAbY)lXK#TdKj!!#!!%kk#9SJAbY)mF"#TdKi%!"1ZJP+)&mV52( +-3UG)H#!!6VS*1L"I+dMZeN+R5(N!!)!!6VS*+#"I+dMZiN+R5(N!!)!!6VS*&L" +I+dMZjN+R5(N!!)!!6VS*"#"I+dMZkNUYlXKR0%UYmF"R,NUYmFaR+%UYlYCR)NU +YlZ*R(%UYlZCR&NUYlZTR%%+R6VS+(L!I$)!!!%!!E!T)EIjG6VS#'PL2)'hZeN( +S)!!V51lD)'ha`0(m!!#!!#Y)mFK`8$e!rrK`8$e!rrS`,[ri"N!!@ce!rr``,[r +k"N!"9$e!rrj#TdKYl[*)E[ri5'hqV%*R3QG)H2rr3QG#TkN6)&mV51lZ5UhZlQB +%6VS"SLmYlZkSFd*RU)Fr2!!-U)T)E[ri2c`"&Mmm!#Xr2!&)2c`!2+LR3UF[,Hl +Z5'lrq%KYrY)I2!!"3QG#Cd*R3QG#TkP8)&q`r!!!CJ41ZJ&53QlrpQ!!!-Ti!(B +!HJ!q,[rfI!KJ!!"qF!(!Jh)"`NG)38*"5%'cJ%U!Cb3J!q+)*J!)"!!!C`3)``! +I)!6LL#J!#S6YZ)-J#S2YZ)-JB"BJ!q+)*J!)"!!!C`3)``!I)!6LL#J!F!(!KA) +"`NG)38*"5%'cJ%U!C`iJ"H+)#S$YZ)-J+J"J"L!&iSJU!$!(iN!q!&0'5NCX!2p +q-#lrpNM!jB""lIAQdF!JK$!ZrrC)`1@!3HhjjY(!))-`,[rf5-$PJ%(YmGl4`## +&8QlrpJaZ!3$rpQd!rc"-h`$i6Pj1G8j@rqT#Ccmm!!&)E[r`6VS&9M!I28$rlNT +ZrqjQ%!aZ"J$rp'8)$'i!![rbE!T)EIhQ6VS!4PL23UFr2!"J(c`!!8kk"[JJ(d+ +R2c`!Ramm!!%Y32rU6VS'j#!IX+lrkPE!4!")J"Y!lUa1ANje5'hq,dkk!!CBMdj +e6PB!!#mZ!!K)EIkQ5'hqTNKYrUDTLd*R2c`!dN+RUBB`(kRd6Pj1G8j@r[``EI( +B,`K)E[m!6VS(`LmZ!!K)E[m!5'hqTNKYrUDTLc!YmGK1V3!U!!`!-J"N!$B!C3! +krp-!0[r5!$,re!!Zrm-!-[rH!$Erc`!brp%!,[r@!#Vre3!Qrpd!)Mem!36qrQ! +H2A`""IlqB"BpI!%1r[jJ$Mem!4,qrQ!'2A`"%Ilq3QFr,[lq3UHTL$!I28$qr!a +Z!!(qr&I!4!")J%jH6R919J!!*'i!##3Z!!`L,I(D3qhahQ!3F!!3'V-!j8JJ-3! +!i)QaJ91#DZ`V3I(D6Pj1G8j@rrJVI!%M4@IehLYmLD[0lrAL3HhZVbe)rra`!"! +YlUip32riB'3JE[rm8Ulrr(!!%"!L,IALXi!G32rl)#hehR)BikJL,IALi)Q!JA) +!%LlrqdK"3N&)3H@"3HhjjY(")K#cJ#Y!pH)J,IAHi)Kb!")ZrrY)38*"5%(PJ8( +YpHE4`5)3Xi!V32AH8flrq%TZrrKXNNjH6R919J!!51F"'#KZ!!JQEJ!-F!!3&&* +!2J"J#N(68SY$e&+-%*&64dT(E2"-haL!6Pj1G8j@rr"+,HkXCa4#Ccmmrrp)EHk +i3UG#TkKJ%"pJ%+Qd3QFr22rr5'hZZ+P`%"m`,Hki6Ud!1J#3!`rrbJ%1!"lra!# +Drm$r[J#f!0lrZ2qfrl6rX[q`rklrV2qU3QF[,Hl#5'lrr+NX-"p1V3!k!!%!#2q +5rj!!rii!-J!+riMrK[q%ri*#Tdkk"EJJAb"33qlrm&5))YJLf#mZrr`[,Hl#5'l +rm+NPB!$rA#!Zrrb`VHlZCJ$r8%KYlX+SF8*R,bhZ`LmYlZj)E[riU@``(`a!!!T +Q!2m`F!&J!!"b##d!!1l'C`$r)#!m!*!$rm#YlVS-J!#3!bjQ!2m-F!&J6L!YlVU +`VHlZCJ$qr#mYlZkT)L"YlZj)D!!3U+01ZJ!d,bhZlUNMB!$qhL!YlVTb%1+J5S" +R!2l33QG)EIkQ2bhZ[%kk"$)`(f!!rVa`!%jH6R919[riU*j)E[ri2ccrrcmm!%) +r2!&@2c`!4DLR5'lrq+LK2c`!#Mmm!"1SNdKYrYUSK%kk!+4)E[ri2c`!#Mmm!"d +r2!&,2c`!*DLR5'lrq+LK2c`!!6mm!!DSQcmm!!Xr2!!HU*-r,Hq53QHSNMmm!!S +r2!!iU*0)EIlQU)41ZJ"U2c`!06mm!&5SNcmm!!1SKcmm!!QSLNKYr`LSK$mm!!5 +SKcmm!!QSL$mm!!bSLMmm!%1SJcmm!!QSLNKYrbLSK%*RU)G#CkL)3QHSLLmYlZk +TD8jH6R8r2!"T2c`!%kL6,bh[MUL%6R919[m!2c`!l6mm!$LSNc"Ylj3[#%KZr`" +1ZJ2D5'lr!+L%6Pj1G8j@rrK"lIkQ+dM[MNKZrrJr2!"T2c`!"b"YlZir+!!@2c` +!&kLR5'lrq+LM5'lrq$mm!1dr2!!X2c`"%cmm!$bSTdKZrrLSSdjH6R919J!!,`G +q!#!Z!!L4VHqDB!SJ,Hq@dDh[QP*(5Uh[QQh`-#h[NY"($%!"2Qm+-$`"2T!!EHq +52J!`,Hq5"N!!#cm!2c`!(UL62`G#CkL5hfh[NLiI6Pj1G5!I3IS!%*!!51*))Pm +r!#m*VHTKl'(UBHKKjQ(N5RJ#MQXL-$`!N!#M4L*)-$`!RkG'XmKR%#*I)&m`(bm +*S*!!2d!!"%je)'m!"$!m!!%L5'!'!a-,!J%!-$`!%1*!8d"#@&()rr`b[!!")(J +#VM0mrri!!J`S!2m!#@G#5LJ!#'iJ-hcrr`!#5RJ#MQX`-h`!!3!#5RJ,)QSN-h` +!!J!#B"a#D3!#$#J!!J!)EK"R#$0m!!3!!Q!'-h`!!`!#3QN!"!`i!!)",fi+%$J +",e*!%d!!"dTi!SjV$JJi!!3,)QF'%h`!!3!)$(Jrr`+1BJB6I!!"!!N31!)H3IV +r@c)m!!5`)&I*rra5360"!!T+1!+4DaJ51!(l!J%!$``"!!&Q#L"i!Y`6D!!(!!d +cH!)3!!j+H!2fE84#D3!1-M`!2%*R8FRrr#"2-AJ+@!!BF!LLB'BN-@J!0!!@SJG +Q'L&S!&S!-#&m49**5`!FF!'LB'B'-fJ!&J!1h[`!HL"*)PpF6ckmkS41d5*I)"m +[#D!b6R9`!#m*-F!#)%jeF!"JpNTi!SjU+L"i!6"$q!%8)!L3!*&d$,##C4BL85( +)!43LJ%)4)RJ#UL+))),4U3!-6R@JBdje)PmJ(k%H,SK1q[qf)PmJAk!I6[VrV#* +I)&qJ+8lkrk)LAa)I-"m[#8S"C`5R4Q!#SdB[5!!%6R919[r13HlrcM&Z!!J!'+! +"28!!#NjH)&p8Mdl38F&J!P$"6PErcN(ZrmiKEJ!)!#!aEJ!3!"JLEJ!-)9%!*%* +S!#a#U!!Z5J&Q"+!#B!+J!ce!!")LEJ!-)UJ!+%jH)PrHr!!+6Y&19[r!3Hlr`$& +Z!!J!&L&Z!!S!%U!A28!!$NjH)PpFMdl46PEr`%(Zrm!aEJ!)!"BKEJ!+!"+J%ce +!!!j1AL*IA)p1d8j@rmj"l[r1-@i!$J!B-@i!$!!X)@i!#!!ZS%3p3!!36PiLAe# +26Y%JE`!'3LJ!'L*I%"mJAfB'F!QLB'!%F!QQB$k!6Y%LAa!I)&pQ"R!+SQ"J"(! ++TQ!qJ%l4)Pm3(b"ICJ5L"f!#TJFqJ%l4S'%[3!!%6R8JE`!%)#m!#%*RUHiJAe" +26Y![H!RZ!!41G8j@rm`YEJ!1rpipEJ!@rq*#,[rQ(@i!$2rR3Ulrk#eZ!",rr%( +ZrmbL!#"Z!!J`V[rN6PiJAdr[!"!qJ%l36PErc#eZ!!lrhMeZ!"EriN)ZrqBGEJ! +-rqG#V[rS,@i!%[rm3Hlrc+)+)'i!#$#Zrq41AL"I6qm!%$k!6Y"19[q`,@i!%2r +#2@i!'2r'3LlrbLeZ!"6ri%*Zrma"l[q`SJKV'+)-Da3YEJ!-rp3YEJ!)rp!YEJ! +8rq#L$8jH)&p2l`!52S"1d%j@rm`YEJ!-rpipEJ!8rq)YEJ!3rra"l[r-F!DLB#" +Z!!JJV[rm6PiJAdr[!!iqJ%l36PErc#eZ!!MrhMeZ!"$riN)ZrqBYEJ!-rra"l[r +-SJP1AL"I6qm!#Mk!6Y"19[r-,@i!#2rH2@i!%2rL3LlrjLeZ!!crr%(ZrmbL38j +H)&p2l`!+2S"1d%j@rma#V[rH2@i!&2rL3QlrjN(Zrma`"k*J)'i!%$#Zrq`JEJ! +-)+lrr#"Z!!JJV[rS6PiJAdr[!!iqJ%l3!!!#,J#3!`S!N!0b3RJ+5Th16VS!*%* +R5(N!!2rr5'm!"%KA2c`!!5)krpj1Y4!!)'d!E%k3!+Rd@Bm[2&T&8Np#CkQJ*&G +CMbmm4%&838*RUD!J9b"3)RJ*##45B!ibf'B+-KTJ!N)C8FRrr,[*CZkTSkQM@Bm +[2%4548a#CkQJ)&HJ*5"3iN!N$@!'-KM9Y4!!8FMrq+QM6R8JAc)B0"L`@&I*rrT ++3QIq6[!Jr#"I-KJd',#B9mRrqNT#Crj1m#$k)&mb'$3BX%*Z#T!!3@d'd%""m!! +#-""RrNl`!!!J,`!%,d%!"#)[!!J[A`!%51Fm!#3!*J&)3X6$+!!U!8K&b-A84%K +#N!2!`G##60m!2#)I6R8J,`!%,d%!"#)[!!J[A`!%51Fa!%kk!*a-h`#-)Kp1G5! +[!!3[33!%)Lm!##pI!!4)jc%!6VS!I#!"60m!M#)I6R8J,`!%,d%!"#)[!!J[A`! +%51Fa!%kk!#a-h`#-)Kp1G5![!!3[33!%)Lm!##pI!!4)jc%!6VS!$#!"60m!M#) +I6R9+J'SF5S&U$%5!4)&1ZJ!J4)&1G85!6VS!&N5!4)&1G8U"DJT%J8kk!!C%J%j +e,M`!!2rrXS"M"L)!F!"1GE#(BJb!`8K!-J"#3%K!6R@bKf)D,J"#3%K!J-&)3%K +(2J")4il"-!G)4c)(6R8N!#B"iSMLLE+(B[L!`F#(-J2#`#i$5%I1`%K(dSGP#*+ +#BJ4%J8je8d"Jj%je!*!$D!#3!hJ!!"'H!*!$@!#3!b!!"$mm!!'Tm!#52c`!!DR +`!+Br2!!"UI!!ZMmm!!'Tm!$@2c`!!DR`!3`r2!!"UI!",$mm!!'Tm!&12c`!!DR +`!@ir2!!"UI!#+$mm!!'Tm!j12c`!!UR`!*!$#NJ!!!5m!!!%[!#3"#JR3A9dEd9 +iG(*KBh4[FL"@-5ib-5#T)$%j16!J3QPXE#"(EfpNE@&Z!*!$"d&38%`!N!BF49K +88J#3!`&*3diM!*!&J%C548B!N!-"!))!!!%!(rrrm"!!!"!A`!I3&i!$d"I!"p! +Ai!r3&F!(8"#ImK#3!aJ3%"1B%*!$'"!3(rJ3N!-B%"!6Q"#3!aJ3%"ri%*!$'"! +3%jJ3N!-B%"!Iq"#3!aJ3%"1B%*!$'"!3(rJ3%)rk%"A!"e!Ai!r3&m!(d"H!!p! +A`!I3%!!!%"rrrr!Irrr`(rrrm"rrrr!Irrr`(rrrm"rrrr!Irrr`(rrrm"rrrr! +Irrr`(rrrm"rrrr!Irrr`(rrrm"rrrr!Irrr`(rrrm"rrrr!Irrr`(rrrm"rrrr! +Irrr`(rrrm"rrrr!Irrr`(rrrm"rrrr!Irrr`(rrrm"rrrr!Irrr`(rrrm!#3!a8 +!N!A3!9`!!3#3#!(e!*!%&3"3!&!!Q`%L!!%"!*!(!I3!N!3-!&!!8!$5!9N"%P9 +9!*!$$!"3!&!!``&S!4&993#3!``!8!"3!,3"FJ%1998!N!--!&!!8!$K!@J""99 +9!*!$$!"3!&!!dJ&S!34993#3!``!8!"3!1X"L`$c998!N!--!&!!8!$k!@d!m99 +9!*!$$!"3!&!!Y!&S!2"993#3!``!8!"3!0)"9!$@998!N!--!&!!8!#d!A)!dP9 +9!*!$$!"3!&!!jJ&Y!2*993#3!d`!!`#3"5d!&!""!&S%!Np,!*!&,3"i!%%![J3 +'3f&ZBf9X!*!"B!"N!b*!!!*!'#3!+!"N!8)J*8'&cFhG[FQ3k6J#3!q)!#J# +3"C3"!!#Q!9!%"%a[B@3!N!B%Z`"3"-d%"NKTC'4PEJ#3"Dd"!!#r!9!%"N0KEQ0 +PE!#3"6%!k!"&!9Z!!*!'6J%!!'!"8!3&4@TPBh3Z!*!&C`%!!(N"8!3&4(*TGQ9 +!!*!&-3!-!--!jJ#3"c%!j3$$!2B!N!H'!2`!K`&CJ!#3"`58!'8%p)J!N!B'!!S +!&`&+##P3E'9KFf8JE'pKC#"H-(0PCfePER4H-9ib)'pQ)(4SC5"KFQ0SDACP,J# +3"'S!!J#3"@3!&!"i!&S%#%0[ER4TER9P!*!&C!#[!(J!p33'3f&ZBf9X!*!&#J" +'!&S!riJ`9'KTFb"fEfaeE@8JDA-JCR9XE#i0$G*H-0-JBfpeE'3JEQpd)'*P)(G +bDA4dC@iZ!*!$F!!#!*!&93!8!'N!@J3)3fpZG'PZG@8!N!99!,i!D3%%"!C$B@j +MC@`!N!8+!%B!5`%1L$C")'CTE'8JCA*bEh)J+&ia+5"SBA-JEf0MGA*bC@3JGfK +TE'8JB@0MCA0cD@jR)#$5AM$6,L!!N!0J!!)!N!9'!"3!@J"D"!K$EfjdD@jeC3# +3"8B!b!"D!3i%"N0KEQ0PE!#3"3S!4J!m!4L)*Y*H-0-JDA-JE'pMDf9N)'&ZC#" +MB@jZEh3JBQ8JE@pND@CTC@3Z!*!$K!!#!*!&F`"N!)F!UJ3#6dX!N!8+!%B!2`% +1L"&$B@jZEh3JFQ9KC#$5AM$6,M3!N!9'!!S!DJ%1L%4&DA4SCA)JG'KP)("KFh0 +hEh*N)(0eF("XD@9N)'Pc)'PZBfpbFQ9MG#"[FL"dD'8JBA*MD'PfC5"TFb"NB@e +KCf9N,J#3!eJ!!J#3"@3!D3"i!+m%!Np,!*!&#J"'!$m"$SJ43f&ZEQpd)(*PB@3 +JdPi`dbi!N!C'!%B!@3%1L"G8D'8JBA*MD'PfC5"TFb"NB@eKCf9N,VS!N!2!!!) +!N!9p!(J!N3#q"!*25`#3"3S!4J!m!6')'P0[FR*j,#"MB@iRG#"PH(4bB@0d)0* +H-0-Z!*!&3`!+!(-"-BKh35"ZEfiYC@e`G(NJ4Np-4%95)(GTG'JJG'KKG#"ZB@e +P)'&XFQ9KC(NJCAKTFh4c)#KjEh8JBf&Z*h3JEhCPFRGbDA4P)'%JCQpXC'9b)(G +TG'JJB5"QD@aP)(9ZE'9cFb"dD'8JCQpXC'9b)'Pc)'9YF(4j+5i-!*!$Y!!%!*! +&EJ!8!))!@J3#6Qm!N!@-!"3!S!"D"!0CCA1U!*!&M!$$!+!"#33'3f&ZBf9X!*! +&#J"'!$`"%iJ8dPi`db"KE(*PB@4j)'9iDA0dFbi!N!9%!!S!C!%6L%a%Eb"jEh8 +JGfPcD#"dEb"[GQ9bGh*TG'8JDA3JGfPdD#"dD'8JCQPXC5pQEfaNCA)JCAKdFQ& +MG'9N)'CbEfdJG'KP)'&bBfKTGQ8r!*!$FJ!"!*!&4J"T!&S!V`3#6dX!N!8+!%B +!2!%1L&&8D'Pc)'&bBfKTGQ8JBfpZG'&TER-JB5"QEfaNCA)Z)#"'EfaNCA*c)'0 +KEQj[G#"LC5"PH(4bB@0dC@3JG'mJB5"ZEfiY5%C6)(C[E(9YC5lr!*!$F!!"!*! +&C!"I!(J!T33#6dX!N!8+!%B!@J$kL&"8D'Pc)'Pc)'j[G#"dD'8JFQ9aG@9cG'9 +N)(0PCfePER3JCQPXC5i0$94SDA-JCQPXC5"TFb"cC@GYC@jd)#0H-#"[CL"H-5" +KFQ0SDACP,J#3!b)!!3#3"8B!EJ"D!,3%!Np,!*!&#J"'!$`"')J#AM!!N!0i!!) +!N!9i!"3!M!"D"!K$EfjdD@jeC3#3"AJ!``#-!3N%"N0KEQ0PE!#3"3S!4J"Z!41 +)2Y*H-0-JBfpeE'3JEQpd)'*P)'9iG(*KBh4PC#"`FQp`CA*XH5i0$94SC5"KFQ0 +SDACP)'Pc)'4KE@&RC@3Z!!!"!*!$-'F!!#pR!!!#1J!j%LJ%$!#3!a`#1J!,4%& +833#3!f*D49*2!*!$EN4548`!N!0k3dp%43!#!)C659T&!*!$UN9B9&)!N!1f4P* +&4J#3!m*#6N4-!*!$cNP$6L-!N!2D4%a24`!"!1C"6&*8!!S!rN4*9%`!$!'#!!$ +rrbJ!N!3j%N3!!2rr#!!"I!!j%EJ!!2rr+!!"M!!j%F3!![rr1!!"NJ!j%`!!!Ir +r(!!N&J#3"[rr+!!Q5!#3"2q3"!!!*V3!14-N!!$rr`!!*X)!14'`!),rrb!!*Zi +!14()!)2rrb!!*[N!14)i!)$rrb!!*aN!14'm!IArrb!!+"d!14+`!I6rrb!!+$B +!N!3"%[rr)!!S6`!j%G!"%Irr)!!SA`#3"!%1rrmJ!#K[!*!%!3Arrb!!+(m!N!3 +""2rr)!!SM`#3"I2rrb!!+*m!N!AarrmJ!#L[!*!&m2rr)!!S[`#3"GErrb!!+-m +!N!A5rrmJ!#MI!*!&m[rr)!!Sl`#3"!(drrmJ!#Mr!$N5B!(errmJ!#P2!*!%!4, +rrb!!+M8!N!3"%Irr)!!US`#3"!%1rrmJ!#XA!*!%!3Arrb!!+hX!N!3""2rr)!! +X!`#3"I2rrb!!,&m!N!AarrmJ!#dM!*!&m2rr)!!Yf`#3"GErrb!!,P%!N!A5rrm +J!#l&!*!&m[rr)!!Zk`#3"!F4!: diff --git a/contrib/printqueue.c b/contrib/printqueue.c new file mode 100644 index 0000000..89bf124 --- /dev/null +++ b/contrib/printqueue.c @@ -0,0 +1,691 @@ +/* + * Copyright (c) 1990, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Services, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include +#ifdef NEEDFCNTLDOTH +#include +#endif NEEDFCNTLDOTH +#include +#ifndef _TYPES +# include /* sometimes needed for sys/file.h */ +#endif _TYPES +#include +#ifdef USEDIRENT +#include +#else USEDIRENT +#include +#endif USEDIRENT +#include +#include +#include +#include +#include +#ifdef USESTRINGDOTH +#include +#else USESTRINGDOTH +#include +#endif USESTRINGDOTH +#include +#include + +#include + +#define ASYNC 1 +#define BDSSIZE 512 +#define BITMASK ((1 << NBITSPERFIELD) - 1) +#define HOLDTIME 60 +#define MACID 4 +#define MORESIZE (sizeof(moremsg) - 1) +#define NBDS 8 +#define NBITSPERFIELD (NUSERBITS / (NFIELDS - 1)) +#define NFIELDS 6 +#define NUSERBITS 32 +#define SYNC 0 + +struct Queue { + time_t q_time; /* modification time */ + char q_name[MAXNAMLEN+1]; /* control file name */ +}; + +AddrBlock addr; +ABusRecord abrecord; +BDS bds[NBDS]; +char bdsbuffer[NBDS * BDSSIZE]; /* bds buffer */ +int bdsfull; /* bds buffer full */ +char *bdsptr; /* current position in bds buffer */ +int colwidth[NFIELDS]; /* column widths */ +char current[40]; /* current file being printed */ +char empty[] = ""; /* empty string */ +EntityName entity; /* Entity name of register naming */ +int holdtime = HOLDTIME; /* minimum time between looking at queue */ +long lasttime; /* last time queue looked at */ +char line[BUFSIZ]; /* line buffer */ +char *lock = "lock"; /* lock file name */ +char moremsg[] = " (More ...)\r"; +char *myname; /* name of program */ +char *printer; /* name of LW spooler */ +int rank; /* order to be printed (-1=none, 0=active) */ +struct stat statbuf; +char *status = "status"; /* status file name */ +char temp[512]; /* temp buffer for bds */ +char type[] = "Printer Queue"; /* AppleTalk type */ +char zone[] = "*"; /* AppleTalk zone */ + +char *pgetstr(); +char *malloc(); + +main(argc, argv) +int argc; +char **argv; +{ + register ABusRecord *abr = &abrecord; + register OSErr err; + register int i, j, k; + int sock; + long l; + void cleanup(); + + if(myname = rindex(*argv, '/')) + myname++; + else + myname = *argv; + for(argc--, argv++ ; argc > 0 && **argv == '-' ; argc--, argv++) { + switch((*argv)[1]) { + case 'l': /* alternate lock file */ + if((*argv)[2]) + lock = &(*argv)[2]; + else if(argc < 2) + Usage(); /* never returns */ + else { + argc--; + lock = *++argv; + } + break; + case 's': /* alternate status file */ + if((*argv)[2]) + status = &(*argv)[2]; + else if(argc < 2) + Usage(); /* never returns */ + else { + argc--; + status = *++argv; + } + break; + case 't': /* new hold time */ + if((*argv)[2]) + holdtime = atoi(&(*argv)[2]); + else if(argc < 2) + Usage(); /* never returns */ + else { + argc--; + holdtime = atoi(*++argv); + } + if(holdtime < 0) + Usage(); + break; + default: + Usage(); /* never returns */ + } + } + if(argc < 2) + Usage(); /* never returns */ + printer = *argv++; + if (chdir(*argv) < 0) + fatal("cannot chdir to spooling directory"); + disassociate(); + for(i = 0 ; i < NBDS ; i++) + bds[i].buffPtr = &bdsbuffer[i * BDSSIZE]; + signal(SIGHUP, cleanup); + signal(SIGQUIT, cleanup); + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + abInit(TRUE); + nbpInit(); + sock = 0; + if((err = ATPOpenSocket(&addr, &sock)) != noErr) + fatal("Error opening ATP socket (%d)\n", err); + if((err = nbp_register(printer, type, zone, sock)) != noErr) + fatal("Error registering name (%d)\n", err); + for( ; ; ) { + abr->proto.atp.atpSocket = sock; + abr->proto.atp.atpReqCount = BDSSIZE; + abr->proto.atp.atpDataPtr = line; + if((err = ATPGetRequest(abr, SYNC)) != noErr) + fatal("Error setting up ATPGetRequest (%d)\n", err); + if(abr->abResult != 1) { + time(&l); + if(l - lasttime >= holdtime) { + lasttime = l; + bdsptr = bdsbuffer; + bdsfull = FALSE; + tobds("\"%s\" Printer Queue %s", + printer, ctime(&lasttime)); + displayq(); + *bdsptr = 0; + k = bdsptr - bdsbuffer; + abr->proto.atp.atpNumBufs = + abr->proto.atp.atpBDSSize = j = + (k + (BDSSIZE - 1)) / BDSSIZE; + for(i = 0 ; i < j ; i++) { + bds[i].buffSize = k > BDSSIZE ? + BDSSIZE : k; + k -= BDSSIZE; + } + l = 0; + for(i = (NFIELDS - 2) ; i >= 0 ; i--) + l = (l << NBITSPERFIELD) | + (colwidth[i] > BITMASK ? BITMASK : + colwidth[i]); + /* + * We shouldn't have to do this, but we do! + */ + bds[0].userBytes = htonl(l); + } + abr->proto.atp.fatpEOM = TRUE; + abr->proto.atp.atpRspBDSPtr = bds; + if((err = ATPSndRsp(abr, SYNC)) != noErr) + fprintf(stderr, + "%s: Error sending response (%d)\n", + myname, err); + } + } +} + +disassociate() +{ + int i; + + if (fork()) + _exit(0); /* kill parent */ + for (i=0; i < 3; i++) close(i); /* kill */ + (void)open("/",0); + (void)dup2(0,1); + (void)dup2(0,2); +#ifndef POSIX +#ifdef TIOCNOTTY + if ((i = open("/dev/tty",2)) > 0) { + (void)ioctl(i, TIOCNOTTY, (caddr_t)0); + (void)close(i); + } +#endif TIOCNOTTY +#else POSIX + (void) setsid(); +#endif POSIX +} + +void +cleanup() +{ + NBPRemove(&entity); + exit(1); +} + +Usage() +{ + fprintf(stderr, + "Usage: %s [-l lockfile] [-s statusfile] [-t holdtime] name spooldir\n", + myname); + exit(1); +} + +/* + * Put unformated text into BDS buffers. + */ +writebds(buf, size) +char *buf; +{ + bcopy(buf, bdsptr, size); + bdsptr += size; +} + +/* + * Put formatted text into BDS buffers. + */ +tobds(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) +char *fmt; +{ + register int len, bdslen; + + if(bdsfull) + return; + sprintf(temp, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9); + if((bdslen = bdsptr - bdsbuffer) + (len = strlen(temp)) > + NBDS * BDSSIZE) { + bdsfull = TRUE; + while(NBDS * BDSSIZE - bdslen < MORESIZE) { + if(*--bdsptr != '\r') + bdsptr++; + while(*--bdsptr != '\r') + {} + bdsptr++; + bdslen = bdsptr - bdsbuffer; + } + strcpy(bdsptr, moremsg); + bdsptr += MORESIZE; + return; + } + strcpy(bdsptr, temp); + bdsptr += len; +} + +/* + * Display the current state of the queue. Format = 1 if long format. + */ +displayq() +{ + register struct Queue *q; + register int i, nitems, fd; + struct Queue **Queue; + FILE *fp; + + rank = -1; + for(i = 0 ; i < (NFIELDS - 1) ; i++) + colwidth[i] = 0; + if ((nitems = Getq(&Queue)) < 0) + fatal("cannot examine spooling area\n"); + if (stat(lock, &statbuf) >= 0) { + if (statbuf.st_mode & 0100) { + tobds("Warning: \"%s\" is down: ", printer); + fd = open(status, O_RDONLY); + if (fd >= 0) { + OSLockFileforRead(fd); + while ((i = read(fd, line, BUFSIZ)) > 0) + writebds(line, i); + OSUnlockFile(fd); + (void) close(fd); + } else + writebds("\n", 1); + } + if (statbuf.st_mode & 010) + tobds("Warning: \"%s\" queue is turned off\n", printer); + } + + if (nitems) { + fp = fopen(lock, "r"); + if (fp == NULL) + warn(); + else { + register char *cp; + + /* get daemon pid */ + cp = current; + while ((*cp = getc(fp)) != EOF && *cp != '\n') + cp++; + *cp = '\0'; + i = atoi(current); + if (i <= 0 || kill(i, 0) < 0) + warn(); + else { + /* read current file name */ + cp = current; + while ((*cp = getc(fp)) != EOF && *cp != '\n') + cp++; + *cp = '\0'; + /* + * Print the status file. + */ + fd = open(status, O_RDONLY); + if (fd >= 0) { + OSLockFileforRead(fd); + while ((i = read(fd, line, BUFSIZ)) > 0) + writebds(line, i); + OSUnlockFile(fd); + (void) close(fd); + } else + writebds("\n", 1); + } + (void) fclose(fp); + } + /* + * Now, examine the control files and print out the jobs to + * be done for each user. + */ + for (i = 0; i < nitems; i++) { + q = Queue[i]; + inform(q); + free(q); + } + free(Queue); + } else if (nitems == 0) + tobds("no entries\n"); +} + +/* + * Print a warning message if there is no daemon present. + */ +warn() +{ + tobds("Warning: print spooler not running\n"); + current[0] = '\0'; +} + +inform(q) +register struct Queue *q; +{ + register int copies, pc; + register char *cp; + register long size; + int mac; + FILE *cfp; + char *alias; + char *class; + char cstr[128]; /* 'H' */ + char fstr[128]; /* [a-z] */ + char jstr[128]; /* 'J' */ + char nstr[128]; /* 'N' */ + char ustr[128]; /* 'P' */ + char *job; + char *user; + + /* + * There's a chance the control file has gone away + * in the meantime; if this is the case just keep going + */ + if ((cfp = fopen(q->q_name, "r")) == NULL) + return; + + alias = class = user = job = empty; + *cstr = *fstr = *jstr = *nstr = *ustr = '\0'; + pc = -1; + size = -1L; + mac = FALSE; + if (rank < 0) + rank = 0; + if (strcmp(q->q_name, current) != 0) + rank++; + copies = 0; + while (getline(cfp)) { + switch (line[0]) { + case 'H': + strcpy(cstr, line + 1); + continue; + case 'J': + strcpy(jstr, line + 1); + continue; + case 'P': + strcpy(ustr, line + 1); + continue; + default: + if(line[0] < 'a' || line[0] > 'z') + continue; + if(copies == 0) + strcpy(fstr, line + 1); + copies++; + continue; + case 'N': + if(*fstr && stat(fstr, &statbuf) >= 0) + size = copies * statbuf.st_size; + strcpy(nstr, line + 1); + continue; + } + } + fclose(cfp); + if(strcmp(ustr, "root") == 0 && strncmp(jstr, "MacUser: ", 9) == 0) { + mac = TRUE; + cp = jstr + 9; + for( ; ; ) { + if((cp = index(cp, ' ')) == NULL) { + job = "Untitled"; + pc = pagecount(jstr + 9); + break; + } + if(strncmp(cp, " Job: ", 6) == 0) { + *cp = 0; + job = cp + 6; + pc = pagecount(job); + break; + } + cp++; + } + strcpy(ustr, jstr + 9); + if((cp = rindex(ustr, '!')) && strlen(cp) == (MACID + 1)) { + *cp++ = 0; + class = cp; + if(cp = rindex(ustr, ' ')) { + *cp++ = 0; + alias = ustr; + user = cp; + } else + user = ustr; + } else { + user = ustr; + class = "Macintosh"; + } + } else { + if(*jstr) + job = jstr; + else if(strcmp(nstr, " ") == 0) + job = "(standard input)"; + else + job = nstr; + user = ustr; + class = cstr; + if((copies = strlen(class)) >= 13 && + strcmp(cp = &class[copies - 13], ".berkeley.edu") == 0) + *cp = 0; + } + cp = bdsptr; + prank(rank); + if(pc >= 0) + tobds("%s\t%s\t%s\t%s\t%d Page%s\n", alias, user, job, class, + pc, pc == 1 ? "" : "s"); + else if(size >= 0L) + tobds("%s\t%s\t%s\t%s\t%ld byte%s\n", alias, user, job, class, + size, size == 1 ? "" : "s"); + else + tobds("%s\t%s\t%s\t%s\t?? bytes\n", alias, user, job, class); + *bdsptr = 0; + measurecols(cp); +} + +measurecols(cp) +register char *cp; +{ + register char *tp; + register int i, j; + + for(i = 0 ; i < (NFIELDS - 1) ; i++) { + if((tp = index(cp, '\t')) == NULL) { + if((j = strlen(cp)) > colwidth[i]) + colwidth[i] = j; + break; + } + if((j = tp - cp) > colwidth[i]) + colwidth[i] = j; + cp = tp + 1; + } +} + +pagecount(str) +register char *str; +{ + for( ; ; ) { + if((str = index(str, ' ')) == NULL) + return(-1); + if(strncmp(str, " Pages: ", 8) == 0) { + *str = 0; + str += 8; + return(atoi(str)); + } + str++; + } +} + +/* + * Print the job's rank in the queue, + * update col for screen management + */ +prank(n) +{ + char line[100]; + static char *r[] = { + "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" + }; + + if (n == 0) { + tobds("active\t"); + return; + } + if ((n/10) == 1) + tobds("%dth\t", n); + else + tobds("%d%s\t", n, r[n%10]); +} + +/* + * fatal error + */ +fatal(fmt, arg1, arg2, arg3) +char *fmt; +{ + fprintf(stderr, "%s: ", myname); + fprintf(stderr, fmt, arg1, arg2, arg3); + exit(1); +} + +/* + * register the specified entity + * +*/ +nbp_register(sobj, stype, szone, skt) +char *sobj, *stype, *szone; +int skt; +{ + nbpProto nbpr; /* nbp proto */ + NBPTEntry nbpt[1]; /* table of entity names */ + int err; + + strcpy((char *)entity.objStr.s, sobj); + strcpy((char *)entity.typeStr.s, stype); + strcpy((char *)entity.zoneStr.s, szone); + + + nbpr.nbpAddress.skt = skt; + nbpr.nbpRetransmitInfo.retransInterval = 4; + nbpr.nbpRetransmitInfo.retransCount = 3; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + nbpr.nbpDataField = 1; /* max entries */ + nbpr.nbpEntityPtr = &entity; + + err = NBPRegister(&nbpr,FALSE); /* try synchronous */ + return(err); +} + +getline(cfp) + FILE *cfp; +{ + register int linel = 0; + register char *lp = line; + register c; + + while ((c = getc(cfp)) != '\n') { + if (c == EOF) + return(0); + if (c == '\t') { + do { + *lp++ = ' '; + linel++; + } while ((linel & 07) != 0); + continue; + } + *lp++ = c; + linel++; + } + *lp++ = '\0'; + return(linel); +} + +/* + * Scan the current directory and make a list of daemon files sorted by + * creation time. + * Return the number of entries and a pointer to the list. + */ +Getq(namelist) + struct Queue *(*namelist[]); +{ +#ifdef USEDIRENT + register struct dirent *d; +#else USEDIRENT + register struct direct *d; +#endif USEDIRENT + register struct Queue *q, **Queue; + register int nitems; + int arraysz, compar(); + DIR *dirp; + + if ((dirp = opendir(".")) == NULL) + return(-1); + if (fstat(dirp->dd_fd, &statbuf) < 0) + goto errdone; + + /* + * Estimate the array size by taking the size of the directory file + * and dividing it by a multiple of the minimum size entry. + */ + arraysz = (statbuf.st_size / 24); + Queue = (struct Queue **)malloc(arraysz * sizeof(struct Queue *)); + if (Queue == NULL) + goto errdone; + + nitems = 0; + while ((d = readdir(dirp)) != NULL) { +#ifdef __hpux + if (d->d_name[0] != 'c' || d->d_name[1] != 'A') +#else /* __hpux */ + if (d->d_name[0] != 'c' || d->d_name[1] != 'f') +#endif /* __hpux */ + continue; /* daemon control files only */ + if (stat(d->d_name, &statbuf) < 0) + continue; /* Doesn't exist */ + q = (struct Queue *)malloc(sizeof(time_t)+strlen(d->d_name)+1); + if (q == NULL) + goto errdone; + q->q_time = statbuf.st_mtime; + strcpy(q->q_name, d->d_name); + /* + * Check to make sure the array has space left and + * realloc the maximum size. + */ + if (++nitems > arraysz) { + Queue = (struct Queue **)realloc((char *)Queue, + (statbuf.st_size/12) * sizeof(struct Queue *)); + if (Queue == NULL) + goto errdone; + } + Queue[nitems-1] = q; + } + closedir(dirp); + if (nitems) + qsort(Queue, nitems, sizeof(struct Queue *), compar); + *namelist = Queue; + return(nitems); + +errdone: + closedir(dirp); + return(-1); +} + +/* + * Compare modification times. + */ +compar(p1, p2) + register struct Queue **p1, **p2; +{ + if ((*p1)->q_time < (*p2)->q_time) + return(-1); + if ((*p1)->q_time > (*p2)->q_time) + return(1); + return(0); +} diff --git a/contrib/snitch.c b/contrib/snitch.c new file mode 100644 index 0000000..315caff --- /dev/null +++ b/contrib/snitch.c @@ -0,0 +1,503 @@ +/* + * snitch.c: a mimic of the "responder" INIT for Inter-Poll from Apple. + * Advertises itself as "machinename.UNIX/CAP@*" via NBP, + * and responds to ATP system info requests. + * + * To kill this cleanly, send the process a QUIT or TERM signal. + * It will nbp_delete itself (as all nbp services should). + * + * To really make this work with Inter-Poll, you want to add the string + * "UNIX/CAP" (or your snitchtype) to the STR# that specifies the kinds of + * expected machine types. The first item in that STR# is a number that + * indicates the number of following valid entries - add one to it and then + * add "UNIX/CAP" at the end. The string list is STR# "NIP Devices". + * + * This code takes pieces from atistest.c and efsd.c from the CAP distribution. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * From atistest.c: + * + * Copyright (c) 1986,1987 by The Trustees of Columbia University in the + * City of New York. + * + * + * snitch by Rob Chandhok, Computer Science Department, + * Carnegie Mellon University + * + * Edit History: + * + * March 16,1988 chandhok Created snitch. + * March 17, 1988 cckim clean up a bit + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef USESTRINGDOTH /* based on sysvcompat.h in appletalk.h */ +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#define MAXNBPSTRING 32 + +/* MAXSNITCHSTRING is the max string length for a packed string in + * with interpoll (127) minus some slop + */ + +#define MAXSNITCHSTRING 127 +#define USER_MAXSNITCHSTRING 100 +private char snitchname[MAXNBPSTRING + 1] = ""; +private char snitchtype[MAXNBPSTRING + 1] = "UNIX/CAP"; +private int snitchdebug = 0; + +private int spawn = 0; /* disassociate process? (def. no) */ + +private char finderstring[USER_MAXSNITCHSTRING + 1] = ""; +private char lwstring[USER_MAXSNITCHSTRING + 1] = "lwsrv"; + +/* saved values for snitch handler */ +private char real_finderstring[MAXSNITCHSTRING + 1]; +private char real_lwstring[MAXSNITCHSTRING + 1]; +private char real_system[MAXSNITCHSTRING + 1]; + +private char fullhostname[256] = ""; +private int gmypid = -1; + +#ifndef kipnetnumber +/* high and low part of a "kip" network number - assume number in network */ +/* format */ +#define nkipnetnumber(x) ntohs((x))>>8 +#define nkipsubnetnumber(x) ntohs((x))&0xff + +/* same but assumes in host form */ +#define kipnetnumber(x) (x)>>8 +#define kipsubnetnumber(x) (x)&0xff +#endif + +/* forward */ +void snitch_handler(); + +void +cleanup() +{ + int err; + + if (snitchdebug) { + fprintf(stderr,"snitch: Un-Registering \"%s:%s@*\"\n", + snitchname,snitchtype); + } + err = nbp_delete(snitchname, snitchtype, "*"); + if (err != noErr) + aerror("nbp delete",err); + else + fprintf(stderr,"snitch: Bye\n"); + + exit(1); +} + +usage(name) +char *name; +{ + char *DBLevelOpts(); + + fprintf(stderr,"usage: %s [-d cap-flags] [-n name] [-t type]\n",name); + fprintf(stderr,"\t[-f finderstring] [-l laserwriterstring] [-S]\n"); + fprintf(stderr,"\t-d for CAP debugging flags:\n"); + fprintf(stderr,"\t l = lap, d = ddp, a = atp, n = nbp, p = pap,"); + fprintf(stderr,"i = ini, s = asp\n"); + fprintf(stderr,"\t-Dn for %s debugging level n (writes to stderr)\n",name); + fprintf(stderr,"\t-S to disassociate snitch process\n"); + fprintf(stderr,"\nExample: %s -n 'MyCapMachine'\n", + name); + + exit(1); +} + +doargs(argc,argv) +int argc; +char **argv; +{ + int c; + extern char *optarg; + extern int optind; + + while ((c = getopt(argc,argv,"n:t:f:d:D:l:S")) != EOF) { + switch (c) { + case 'n': + strncpy(snitchname, optarg, MAXNBPSTRING); + break; + case 't': + strncpy(snitchtype, optarg, MAXNBPSTRING); + break; + case 'f': + strncpy(finderstring, optarg, USER_MAXSNITCHSTRING); + break; + case 'l': + strncpy(lwstring, optarg, USER_MAXSNITCHSTRING); + break; + case 'd': + dbugarg(optarg); /* '-d' is debug */ + break; + case 'S': + spawn++; + break; + case 'D': + snitchdebug = atoi(optarg); + if (!snitchdebug) snitchdebug++; /* make sure -D at least sets to 1*/ + break; + default: + usage(argv[0]); + break; + } + } +} + +main(argc,argv) +int argc; +char **argv; +{ + char *cp; + AddrBlock useaddr; + int skt, err; + ABusRecord req_abr; + atpProto *ap; + char req_buf[atpMaxData]; /* max data to receive on request */ + + doargs(argc,argv); + if (snitchdebug) { + fprintf(stderr,"snitch: debugging level %d\n",snitchdebug); + } + if (!snitchdebug && spawn) { + /* disassociate */ + if (fork()) + _exit(0); /* kill parent */ + { + int i; + + for (i=0; i < 20; i++) close(i); /* kill */ + (void)open("/",0); +#ifndef NODUP2 + (void)dup2(0,1); + (void)dup2(0,2); +#else + (void)dup(0); /* slot 1 */ + (void)dup(0); /* slot 2 */ +#endif +#ifdef TIOCNOTTY + if ((i = open("/dev/tty",2)) > 0) { + (void)ioctl(i, TIOCNOTTY, (caddr_t)0); + (void)close(i); + } +#endif TIOCNOTTY +#ifdef POSIX + (void) setsid(); +#endif POSIX + } + } + + abInit(snitchdebug); + nbpInit(); + + gmypid = getpid(); + + if (snitchdebug) { + fprintf(stderr,"snitch: my pid is %d\n",gmypid); + } + + /* Find our host name */ + gethostname(fullhostname, sizeof(fullhostname)); + if (! *snitchname) { + strcpy(snitchname,fullhostname); + /* strip off any domain name */ + if ((cp = index(snitchname, '.')) != NULL) *cp = 0; + } + if (! *finderstring) { + struct in_addr thishost; + + if (getipaddr(fullhostname, &thishost) < 0) + sprintf(finderstring,"%s @ ",fullhostname); + else + sprintf(finderstring,"%s @ %s",fullhostname,inet_ntoa(thishost)); + } + /* setup snitch vars */ + setup_snitch(); + + useaddr.net = useaddr.node = useaddr.skt = 0; /* accept from anywhere */ + skt = 0; /* dynamically allocate skt please */ + if ((err = ATPOpenSocket(&useaddr, &skt)) < 0) { + perror("ATP Open Socket"); + aerror("ATP Open Socket",err); + exit(1); + } + + if (snitchdebug) { + fprintf(stderr,"Registering \"%s:%s@*\" on socket %d\n", + snitchname,snitchtype,skt); + } + + err = nbp_register(snitchname, snitchtype, "*", skt); + if (err != noErr) + aerror("nbp register",err); + else { + if (snitchdebug) { + fprintf(stderr,"snitch ready\n"); + } + } + + signal(SIGQUIT, cleanup); + signal(SIGTERM, cleanup); + + ap = &req_abr.proto.atp; + do { + ap->atpSocket = skt; + ap->atpReqCount = atpMaxData; + ap->atpDataPtr = req_buf; + err = ATPGetRequest(&req_abr, FALSE); + if (err != noErr) + fprintf(stderr,"ATPGetRequest error: %d\n",err); + else + snitch_handler(&req_abr); + } while (1); + +} + +/* + * register the specified entity + * +*/ +nbp_register(sobj, stype, szone, skt) +char *sobj, *stype, *szone; +int skt; +{ + EntityName en; + nbpProto nbpr; /* nbp proto */ + NBPTEntry nbpt[1]; /* table of entity names */ + int err; + + strcpy((char *)en.objStr.s, sobj); + strcpy((char *)en.typeStr.s, stype); + strcpy((char *)en.zoneStr.s, szone); + + + nbpr.nbpAddress.skt = skt; + nbpr.nbpRetransmitInfo.retransInterval = 4; + nbpr.nbpRetransmitInfo.retransCount = 3; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + nbpr.nbpDataField = 1; /* max entries */ + nbpr.nbpEntityPtr = &en; + + err = NBPRegister(&nbpr,FALSE); /* try synchronous */ + return(err); +} + +/* + * delete the specified entity + * +*/ +nbp_delete(sobj, stype, szone) +char *sobj, *stype, *szone; +{ + EntityName en; + int err; + + strcpy((char *)en.objStr.s, sobj); + strcpy((char *)en.typeStr.s, stype); + strcpy((char *)en.zoneStr.s, szone); + + err = NBPRemove(&en); + return(err); +} + +aerror(msg, err) +char *msg; +int err; +{ + fprintf(stderr,"%s error because: ",msg); + switch (err) { + case tooManySkts: + fprintf(stderr,"too many sockets open already\n"); + break; + case noDataArea: + fprintf(stderr,"internal data area corruption - no room to \ +create socket\n"); + break; + case nbpDuplicate: + fprintf(stderr,"name already registered\n"); + break; + case nbpNoConfirm: + fprintf(stderr,"couldn't register name - is atis running?\n"); + break; + case nbpBuffOvr: + fprintf(stderr,"couldn't register name - too many names already \ +registered\n"); + break; + default: + fprintf(stderr,"error: %d\n",err); + break; + } +} + +/* append to appendto as pascal string, update appendto pointer */ +int packstring(appendto,cstring) +u_char **appendto; +char *cstring; +{ + int len = strlen(cstring); + register u_char *app = *appendto; + + if (len > MAXSNITCHSTRING) len = MAXSNITCHSTRING; + + *app++ = (u_char)len; + bcopy(cstring, app, len); + *appendto += (len+1); + return(len+1); +} + +/* + * setup snitch strings + * +*/ +setup_snitch() +{ + AddrBlock nisaddr; /* nis network and node number */ + AddrBlock thisaddr; /* our network and node number */ + import struct in_addr bridge_addr; /* the kbox we use */ + + GetNisAddr(&nisaddr); + GetMyAddr(&thisaddr); + + if (thisaddr.net != nisaddr.net || thisaddr.node != nisaddr.node) + sprintf(real_system, "System: CAP: bridge %s atis: net %3d.%02d node %d", + inet_ntoa(bridge_addr), nkipnetnumber(nisaddr.net), + nkipsubnetnumber(nisaddr.net), nisaddr.node); + else + sprintf(real_system, "System: CAP: bridge %s", inet_ntoa(bridge_addr)); + sprintf(real_finderstring,"Finder: %s",finderstring); + sprintf(real_lwstring,"LaserWriter: %s",lwstring); +} + +struct snitch_userbytes { + byte su_code; /* snitch code */ +#define SNITCH_REQUEST 0 +#define SNITCH_REPLY 1 + byte su_xxxx; /* unknown */ + byte su_version; /* snitch version */ + byte su_subversion; /* snitch subversion */ +}; + +struct snitch_buf { + word sb_atalk_version; /* 0: atalk version: note: it may only */ + /* be the second byte */ + byte sb_dummy[8]; /* 2: unknown? */ +#define SB_STRING_OFFSET 10 + byte sb_strings[1]; /* 10: start of string area */ +}; + +void snitch_handler(req_abr) +ABusRecord *req_abr; +{ + int cnt; + atpUserDataType userData; + char buffer[atpMaxData]; /* room for translated message */ + struct atpProto *ap = &req_abr->proto.atp; + struct snitch_userbytes *su; + struct snitch_buf *sb; + byte *p; + + /* Check out the user data field */ + su = (struct snitch_userbytes *)&ap->atpUserData; + if (snitchdebug > 1) { + u_char *p = (u_char *)&ap->atpUserData; + int i; + + for ( i = 0;i < 4; i++) { + fprintf(stderr,"snitch: user data as bytes[%d]=%x\n",i,p[i]); + } + } + + if ((su->su_code != SNITCH_REQUEST)) { + if (snitchdebug) { + fprintf(stderr,"snitch: bad request code = %x\n",su->su_code); + } + return; + } + + + su = (struct snitch_userbytes *)&userData; + su->su_code = SNITCH_REPLY; + su->su_xxxx = 0; /* ??? */ + su->su_version = 0; + su->su_subversion = 0xCA; /* subversion (as close as I can get to CAP)*/ + + /* now fill the user bytes with info. */ + + /* atalk driver version is buffer[1] (maybe a short from 0 to 1)*/ + sb = (struct snitch_buf *)buffer; + sb->sb_atalk_version = htons(2); /* random? */ + + /* packed strings */ + p = sb->sb_strings; + cnt = SB_STRING_OFFSET; + cnt += packstring(&p,real_system); /* system name */ + cnt += packstring(&p,real_finderstring); /* finder name */ + cnt += packstring(&p,real_lwstring); /* laserwriter driver */ + + do_reply(req_abr, userData, buffer, cnt); +} + +/* + * Take need information from the ATP request and turn it into + * an ATP response of the given data. + * + * +*/ +do_reply(req_abr, userdata,data,datalength) +ABusRecord *req_abr; +atpUserDataType userdata; +char *data; +int datalength; +{ + ABusRecord res_abr; + atpProto *ap; + BDS aBDS[1]; + + ap = &res_abr.proto.atp; + ap->atpAddress = req_abr->proto.atp.atpAddress; + ap->atpTransID = req_abr->proto.atp.atpTransID; + ap->atpSocket = req_abr->proto.atp.atpSocket; + ap->atpNumBufs = setup_bds(aBDS,sizeof(aBDS),atpMaxData, + data,datalength,userdata); + ap->atpRspBDSPtr = aBDS; + ap->atpBDSSize = ap->atpNumBufs; /* usually equal in an ATP response */ + ap->fatpEOM = 1; /* is always EOM */ + + return(ATPSndRsp(&res_abr, FALSE)); +} + + +/* + * Get the ip address based on the hostname + * +*/ +getipaddr(hostname, sin) +char *hostname; +struct in_addr *sin; +{ + struct hostent *host; + + if ((host = gethostbyname(hostname)) == NULL) + return(-1); + bcopy(host->h_addr, (caddr_t)sin, host->h_length); + return(0); +} diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..e5773f2 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,11 @@ +install.doc: install.ms + nroff -ms install.ms > install.doc + +dist: + @cat todist + +clean: + rm -f install.doc + +spotless: + rm -f install.doc *.orig diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..0ada25f --- /dev/null +++ b/doc/README @@ -0,0 +1,13 @@ +The following documentation is primarily for the installer. + install.ms - notes on how to install CAP libraries + install.doc - formatted copy of install.ms + print.cookbook - simple steps for CAP printing/installation + +The following documentation is primarily for the programmer. + atp.notes - notes on our version of ATP + asp.notes - notes on our version of ASP + cap.notes - notes on programming CAP + nbp.ext - notes on our NBP extensions for Unix + pap.notes - notes on our version of PAP + abmisc.doc - notes on the routines in abmisc + sched.notes - notes on protocol scheduler diff --git a/doc/abmisc.doc b/doc/abmisc.doc new file mode 100644 index 0000000..faa6136 --- /dev/null +++ b/doc/abmisc.doc @@ -0,0 +1,120 @@ +- void cpyc2pstr(char *ps,*cs) +Copy a C style string to an Apple Pascal style string. Restrictions: +sizeof(*ps) must be >= 1+sizeof(*cs). + + +- void cpyp2cstr(char *cs,*ps) +Copy a Apple Pascal style string to C string + + +- pstrcpy(byte *d,s) +like strcpy, but for pascal strings + + +- pstrncpy(byte *s,*d; int n) +like strncpy, but for pascal strings + +- int pstrlen(byte *s) +like strlen, but for pascal strings + +- pstrcmp(bytes1,s2) +like strcmp, but for pascal strings + +- int cmptime (struct timeval *t1,*t2) +cmptime compares two time values and returns an integer greater than, +equal to, or less than zero according to whether the time represented +in t1 is greater than, equal to, or less than t2. + + +- abstoreltime(struct timeval *at,*rt) +convert absolute time in at to relative time by subtracting the +current time as returned by gettimeofday. + + +struct TimerEntry { + struct TimerEntryt_next; pointer to next/ + struct timeval t_time; timeout internal form/ + ProcPtr t_fun; function to call/ + caddr_t t_arg; argument for function/ +}; + +- void apptoabstime(struct timval *tv,int t) +Construct actual time of timeout given unit tick 1/4 of a second. + +- void Timeout(ProcPtr fun,caddr_t arg,int t) +Call "fun" with "arg" after elapsed time "t" has expired. "t" is in +internal tick units of 1/4 seconds. This unit conforms to LAP and PAP +timeout units. + +- void remTimeout(ProcPtr fun, caddr_t arg); +Given the function and function arg of a pending timeout remove that +timeout from the q. Question: should we remove all instances? Should +timeout enforce a single (fun, arg) pair? + +- void doTimeout() +doTimeout expires entries on the timeout queue. The timeout function +is called with the function argument, the timeout entry is unlinked +from the q and memory is returned. + +doTimeout can be called at any time, if the q is empty, or no timers +have expired then no action is taken. + +- boolean minTimeout(struct timeval *mt) +minTimeout returns the minimum timeout of all entries on the timeout +q. The timer records are ordered earliest first so this routine only +needs to check on the first one. + +- dbugarg(char *chars) +Process the -d argument from the command line, setting debug options +in global dbug structure. a - db_atp; l - db_lap; d - db_ddp; n - +db_nbp; i - db_ini; p - db_pap; + + + +- int setup_bds(BDS bds[]; int numbds, segsize; charbuf; int bufsiz; + atpUserDataType userdata) +Establish a bds (of size most numbds) with buffer buf of size bufsiz +set all bds userdata fields to userdata return count of bds's. More +buffer than bds is okay. + + +- int sizeof_bds(BDS bds[]; int numbds) +return size of BDS dataSize fields + + +** routines to deal with "Indexed" strings + +- void IniIndStr(byte *istr) +Initialize an Indexed string for calls to AddIndStr + + +- void AddIndStr(char *src,byte *istr) + +Add a c string to a Indexed string. The c string always is added to +the end of the indexed string. + + +- void GetIndStr(char *dest,byte *istr,int index) +Copy from an indexed string into a c string. Use index to select +the string entry within the indexed string type. + +- int IndStrCnt(byte *istr) + Return the count of entries in an indexed string. + +- int IndStrLen(byte *istr) +Return the length of the indexed string istr including the count byte. + +- PrtIndStr(byte *istr) +For debugging, dump the content indexed string. + +- int strcmpci(char *s, *t) +Case insensitive version of strcmp. + +- int strncmpci(char *s, *t,int n) +Case insensitive version of strcmp. + +- char *strdup(char *p) +duplicates the pointed to by p. returns pointer to copy or null if no +space. returns pointer to emtpy string if p is null. + + diff --git a/doc/asp.notes b/doc/asp.notes new file mode 100644 index 0000000..fdc6e2f --- /dev/null +++ b/doc/asp.notes @@ -0,0 +1,139 @@ +CAP note: last revision April 1, 1987 + +UNIX Appletalk Session Protocol (ASP) Implementation notes + +Last revision: February 23, 1988 +Previous revision: April 1, 1987 + +Design document: Inside Appletalk, June 1986. + +There are two major things to note about this version of ASP. + +First, it uses a non-standard interface. Second, some of the error +codes have slightly different meanings. For now, you'll have to go +the source to get to the interfaces (sorry). + +This is a preliminary note that defines some of the specifics of the +CAP version of ASP. Mostly, they are error codes which are added or +used slighltly differently or out of the specified context. Maybe in +the future, there will be a more detailed document that will go into +more depth. + +SPGetStat + aspFault - unexpected internal inconsistency - usually bad atp + completion. + noATPResource - can't get ATP resources + SizeErr - request too big (shouldn't happen) + +SPOpenSession + aspFault - unexpected internal inconsistency - usually bad atp + completion. + noATPResource - can't get ATP resources + SizeErr - request too big (shouldn't happen) + +SPCloseSession + aspFault - unexpected internal inconsistency - usually bad atp + completion. + noATPResource - can't get ATP resources + SizeErr - request too big (shouldn't happen) + sktClosed - socket already closed (in some way) + + +SPWrite + aspFault - unexpected internal inconsistency - usually bad atp + completion. + noATPResource - can't get ATP resources + SizeErr - request too big + NoAck - no acknowlegment in the specfied timeout + +SPCommand + aspFault - unexpected internal inconsistency - usually bad atp + completion. + noATPResource - can't get ATP resources + SizeErr - request too big + NoAck - no acknowlegment in the specfied timeout + +SPWrtContinue + aspFault - unexpected internal inconsistency - usually bad atp + completion. + noATPResource - can't get ATP resources + SizeErr - request too big + NoAck - no acknowlegment in the specfied timeout + +SPAttention + aspFault - unexpected internal inconsistency - usually bad atp + completion. + noATPResource - can't get ATP resources + SizeErr - request too big + +SPGetStat + aspFault - unexpected internal inconsistency - usually bad atp + completion. + noATPResource - can't get ATP resources + SizeErr - request too big (Shouldn't happen) + + +SPWrtReply, SPCmdReply + noATPResource - can't get ATP resources + NoAck - No acknowledgement to reply - decided to do this instead + of closing session (really means no rel received, but should + close session based on tickle then...) + +SPGetRequest + noATPResource - can't get ATP resources + SessClosed - session closed underneath us + BadReqRcvd - unexpected request received - please reissue getRequest + +SPGetSession + NoMoreSessions - either out of sessions or atp sockets + SktClosed - SLS closed!!!!! + +SPInit + noATPResource - can't get ATP resources + + +EXTENSIONS + +* aspInit(n) tells asp to allow up to n sessions (total, not per sls) + +The SPFork and SPShutdown calls are necessary because the way ASP is +defined, the client sends tickles to the Server Listener Socket (SLS). +If one needs to fork off clients to handle ASP connections, then it is +necessary for the base process to watch for incoming tickles. The +child process cannot do this since the SLS must not be read from by +the child (or else the base process will not see the input). + +* SPFork(srn, hc_tickle, open_tickle) tells asp to fork and leave things +in the following state: + In the base process: + If srn points to a server connection, then the connection is left + "half-closed". It listens for incoming tickle requests. + Certain outgoing calls are still allowed (SPClose, + SPAttention). In all other respects, the connection is deemed + closed (and in fact, the Sever Session Socket created is + closed -- SPAttention and SPClose sends out on the SLS when + half closed). If the srn points to a client connection, the + connection is torn down. + In the child process: + All half closed connections are discarded (without sending + closes etc). The SLS is discarded. + +In addition, hc_tickle tells the base process to continue tickling the +remote (useful if child process may block). (Default is considered +off since it the parent really shouldn't need to do this, but then +again this whole exercise shouldn't be necessary). hc stands for the +half-closed copy of the connection. + +open_tickle tells the child process whether the child should tickle +the remote. If the base process does the tickling, it probably isn't +necessary for the child process to do it... open, of course, stands +for the "open" copy of the connection. + +* SPShutdown(srn) finishes closing down a half closed socket. + +* SPGetNetworkInfo(srn, AddrBlock *addr) + returns the remote WSS (server) or SSS (client) address. You must + supply a block that is filled in. + + + diff --git a/doc/atp.notes b/doc/atp.notes new file mode 100644 index 0000000..ff2c222 --- /dev/null +++ b/doc/atp.notes @@ -0,0 +1,57 @@ +CAP note: last revision April 1, 1987 + +UNIX Appletalk Transport Protocol (ATP) Implementation notes + +Last revision: April 1, 1987 + +Design document: Inside Appletalk, June 1986. + +The CAP ATP is fairly complete and follows the protocol definition +fairly closely. The programmer's interface is as close to that of +Inside MacIntosh as could be expected. The differences are: + - there is no facility for an ATPAddResponse + - ATPSndRequest will accept a socket number in atpsocket + instead of generating some internal socket unless zero is + passed. (note: this facility is suggested by in Inside + Appletalk). Furthmore, any fork using ATPSndRequest will + open a new socket if the passed atpsocket is zero. + - the flags atpXO and atpEOM in the AppleBus record are + renamed to fatpXO and fatpEOM. + +In addition, a special interface for protocol implementors has been +added. All implemented routines (except the cancel routines) have a +"call back" version. The arguments are the same as the standard +version of the call except no async flag is passed (it is assumed to +be asynchronous) and this argument is replaced by the address of the +callback routine and a unsigned long integer. When the routine is +called back, it is passed the address of the applebus record and the +unsigned integer. + + +The following documents return codes which are not as documented by +Apple in Inside MacIntosh. + +ATPSndReq + atplenerr => reqcount too big or bad rspbdsptr + toomanyskts => can't open socket for request + badAtpSkt => final net write failed - probably bad write + reqFailed => timeout + tooManyReqs => should never happen +In the BDS, we pass buffSize to encode length of the various BDS +buffers. If a datasize is larger than buffSize for any element of the +BDS, then this simply means the remote sent more data than the buffer +size. Varying protocols will handle this event differently. + +ATPOpenSocket + Always returns tooManySkts if DDPOpenSocket fails... + +ATPCloseSocket + Always ignors DDPCloseSocket errors + +ATPGetRequest + noDataArea - means can't startup request + +ATPSndRsp + badBuffNum - really means that we couldn't find the specified + request to respond to.. + diff --git a/doc/cap.auth.doc b/doc/cap.auth.doc new file mode 100644 index 0000000..53fbc58 --- /dev/null +++ b/doc/cap.auth.doc @@ -0,0 +1,28 @@ +Authentication Methods + +The file /etc/cap.auth specifies a network list (permit or deny) +on a per server per type (lwsrv, aufs) basis. Rules are additive +in that later rules can modify previous rules. The comments below +are for explanation only and should not be included in the file. +Examples: + +lwsrv.*.innet 32.152 33.9 # For lwsrv of any name, only accept + # connections from the listed networks. + +lwsrv.PrivateLW.outnet 33.9 # Independent of other rules the + # "PrivateLW" does not accept + # connections from 33.9. + +*.*.innet 32.152 33.9 # Any server with any name allows + # only the listed networks. + +*.*.innet * # Any server allows any network + # (in reality deletes access list) + +lwsrv.*.network ImOK # client mac must have NBP registered + # X.Y.Z:ImOK where X.Y is net, Z node + +To use authentication, you must define "AUTHENTICATE" in the features +list at Configuration time. It is suggested that you use only one of +AUTHENTICATE and LWSRV_AUFS_SECURITY. The latter requires an lwsrv spooler +user to be concurrently connected to an aufs server on the same machine. diff --git a/doc/cap.notes b/doc/cap.notes new file mode 100644 index 0000000..671cf4f --- /dev/null +++ b/doc/cap.notes @@ -0,0 +1,106 @@ +CAP note: last revision March, 1988 + +General CAP notes. +------------------ + +The major thing that should be recognized about CAP is that the +protocol is carried by each process. Unlike TCP/IP, none of the +protocol is carried in the kernel. In the design of CAP, we stayed +away from having asynchronous events. This makes programming the +protocol code and the user code much easier. However, because of +this, the programmer must allow the protocol to run. This is +generally done by issuing "abSleep" (scheduler) calls at regular +intervals. If you fail to allow protocol to run, then you'll see all +sorts of aberrant behavior. In general, a good rule of thumb is that +you shouldn't allow more than about a second to pass before allowing +protocol to run. [Thus, things like blocking reads or writes will +generally mess things up royally]. + +See doc/sched.notes for a full description. + +LAP/UDP encapsulation +--------------------- + +When using UDP encapsulation (often in CAP referred to as KIP or KIP +encapsulation), the only LAP protocol that may be used is DDP. Doing +otherwise would require modifications to the encapsulation method. +Basically, the encapsulation method assumes that UDP sockets map to +DDP sockets in some what and thus all UDP packets are a DDP packet of +some type. + +For this reason, a LAP module is not included for UDP encapsulation. +Note: lib/cap/ablap.c is inclued only for reference. + +NBP server +---------- + +A number of extensions were undertaken for the current UNIX nbp +server. These are documented in doc/nbp.ext. + +ABMISC +------ + +A number of miscellaneous, but useful routines are listed in +abmisc.doc. + +ATP/ASP/PAP +----------- + +See atp.notes, asp.notes, and pap.notes for a list of some of the +major differences of the CAP implementation of these protocols from +the "Inside AppleTalk/MacIntosh" documentation. Note: the interfaces +are now out of sync with the "Inside" documentation. + + +ZIP +--- + +The ATP GetZoneList call has been implemented. The routines are: + +* OSErr atpgetzonelist(int start_index, byte buffer, + atpUserDataType zipuserbytes) + +atpgetzonelist sends a get zone list command to the current bridge. +start_index is the first zone index to start enumeration at (very +first is always 1). This is used to get all the zones when they are +larger than a single atp response -- zip only allows a single pkt +response. + +buffer should be of size atpMaxData and contains the returned packet +if there is no error. + +zipuserbytes will be set to the atp user data bytes a packet is +returned from the bridge successfully. + +* OSErr GetZoneList(char *zones[], int nzones, int *realcount) + +GetZoneList repeatedly calls atpgetzonelist until all zones have been +retrieved from the bridge. It returns the count of zones in realcnt. +It will return up to nzones pointers to zone names in the array zones. +The memory for the zone name is allocated by GetZoneList and should be +deallocated by going down the list or by use of FreeZoneList. + +The zone list and realcount returned should not be considered valid if +noErr is not returned. + +* FreeZoneList(char *chararray[], int cnt) + +Goes through the passed array of character pointers up to cnt and +does a free on the pointers. A free is issued only if the pointer is +not null. In addition, the pointers are cleared. + +* char *GetMyZone() + +returns the zone the program is running in. It does not ask the +bridge for the current zone because it may not correspond. + +Overwriting the returned value is strongly discouraged. + +VERSION INFO +------------ + +CAP version information is now part of the library. The call +what_cap_version returns a pointer to a structure of type +"cap_version". All values are pointers to ascii strings. See +appletalk.h for the contents. + diff --git a/doc/glossary b/doc/glossary new file mode 100644 index 0000000..5eacdb3 --- /dev/null +++ b/doc/glossary @@ -0,0 +1,75 @@ +CAP GLOSSARY + +CAP Columbia AppleTalk Package + A freeware suite of programs that partially implement the AppleTalk + protocol stack on a variety of UNIX machines. Includes an AFP 2.0 + AppleShare Server, LaserWriter Spooler and lpr/lpd "input" filter. + +ETHERTALK + AppleTalk protocols transmitted in specific type Ethernet packets. + May be Phase 1 (Ethernet packets type 0x809B) or Phase 2 (IEEE 802.3 + Ethernet packets with 0xAAAA03 802.2 header and 0x080007809B SNAP + headers). CAP in "Native EtherTalk" mode uses AppleTalk in Ethernet. + +LOCALTALK + AppleTalk protocols transmitted over twisted pair cable at + 230.4Kbits/second. + +IPTALK IP encapsulated AppleTalk + AppleTalk protocols transmitted within IP packets. Usually used with + CAP on UNIX hosts that are unable to speak EtherTalk. Requires an + external AppleTalk gateway to translate IPTalk to/from EtherTalk. + +UAB UNIX AppleTalk Bridge + Phase 1 EtherTalk Router. Used to connect two EtherTalk networks + together via Ethernet interfaces on a UNIX host. Also supports + CAP processes via local UDP/IP loopback using modified IPTalk. + Bundled with CAP. + +UAR UNIX AppleTalk Router + Phase 1/Phase 2 EtherTalk Router for UNIX boxes. Also supports CAP + and simple AppleTalk tunneling over IP networks. Not bundled with + CAP. Available in two versions: freeware with basic features and + shareware with extra features (supports ARNS clients, NBP registry). + +AUFS AppleTalk UNIX File Server + UNIX based AppleShare server. Supports Apple Filing Protocol v 2.0. + Nominated UNIX directories can be mounted as a AppleShare Volumes + for storage of Mac files. + +LWSRV LaserWriter Server/Spooler + UNIX based LaserWriter Spooler. Macintosh print jobs are spooled to + the UNIX printer queue for subsequent printing via PAPIF or serially + connected printer. + +PAPIF PAP Input Filter + BSD lpd filter for sending print-jobs to AppleTalk connected + printers. Can be used in a script for non-BSD printing systems. + +AARPD AARP Daemon + UNIX process for handling AppleTalk Address Resolution Protocol (AARP) + lookups. Stores ethernet hardware address/AppleTalk address mappings + which are available to other CAP processes via RPC. Also handles + updating of /etc/etalk.local. Used only with "Native EtherTalk". + +ATIS AppleTalk Information Server + UNIX process for handling AppleTalk Name Binding Protocol (NBP) + lookups and registrations, AppleTalk Echo Protocol (AEP) traffic + and Routing Table Maintenance Protocol (RTMP) data. Sends RTMP + router updates to AARPD via RPC. Other CAP processes register + Chooser visible names with ATIS. + +GETZONES + List AppleTalk zones for the local Internet. + +ATLOOK AppleTalk Lookup + Lists AppleTalk NBP entities visible on the network. Also ATPINGER + for sending Echo Protocol packets and ATLOOKLWS for LaserWriters. + +SNITCH UNIX InterPoll client + Equivalent to Macintosh "Responder" program. Provides limited + information about the CAP host to Mac InterPoll lookups. + +AppleTalk Reference + "Inside AppleTalk, Second Edition", Sidhu, Andrews, Oppenheimer. + Addison-Wesley, ISBN 0-201-55021-0 diff --git a/doc/install.ms b/doc/install.ms new file mode 100644 index 0000000..5e3b6ef --- /dev/null +++ b/doc/install.ms @@ -0,0 +1,567 @@ +.\" formatted output is in install.doc +.TL +CAP installation procedures +.AB +This document gives a step by step approach to installation of the +Columbia AppleTalk Package for UNIX. +.sp +.AE +.SH +SEE ALSO +.LP +Notes and cookbooks in cap60/doc and UNIX manual entries in cap60/man. +.SH +Notes on CAP with EtherTalk +.LP +.nh +This file describes how to install CAP in its "traditional" default +configuration IPTalk - which is based on Appletalk protocols encapsulated in +UDP/IP packets - and +configurations that support communications via EtherTalk Phase 1 or Phase +2 - Native EtherTalk, Kernel AppleTalk or the UNIX AppleTalk +Bridge, UAB (both Phase 1 only, obsolete) or via the UNIX AppleTalk Router, +UAR. +.sp +Native EtherTalk mode is available on a limited range +of hosts - those that support the Stanford ENET Packet Filter +model - currently SUN workstations, NetBSD, FreeBSD and BSDi, NeXT and +DEC OSF/1 or ULTRIX hosts only. +.sp +UAR will run on SGI IRIX, Sony NEWS, DEC OSF/1 or ULTRIX, SUN SunOS +and Solaris, IBM AIX and HP-UX workstations. IPTalk mode is supported +on all of the above and more and is easily ported to other platforms. +.sp +.IP +Note: UAR is not bundled with CAP, see the CAP FAQ for more information, +the most recent copy of the FAQ +can always be obtained via FTP from munnari.OZ.AU in mac/CAP.faq. +.sp +.LP +The setup for Native EtherTalk, Kernel AppleTalk, UAB/UAR is performed +from within the +Configure script (but only if Configure determines that the host +is suitable) by modifying the CAP libraries as necessary. +Refer to the README files in the support/uab or +support/ethertalk directories for more information. +.sp +The choice of whether to use IPTalk, +Native EtherTalk/Kernel AppleTalk or UAB/UAR depends on your network +setup. If you have a majority of Macintoshes on LocalTalk connected to +an IPTalk gateway (see "Prerequisites" below) then use CAP in IPTalk mode. +If your Macintoshes are mostly connected via EtherNet and you +have other EtherTalk +gateways (Ciscos for example) then use CAP with Native EtherTalk or Kernel +AppleTalk. If you have no other gateways, or need to bridge two +EtherTalk networks, or want to run CAP with EtherTalk on hosts that don't +have Native EtherTalk support then use UAR. On a SUN running SunOS you may +choose to use +the native NIT interface or install kernel modifications to run the 'ENET' +driver. +.SH +Prerequistes +.LP +To use CAP with IPTalk you must have a hardware bridge +that has the ability to gateway IPTalk packets to and from EtherTalk and/or +LocalTalk. Suitable candidates are the Webster MultiPort GateWay, +Cisco Router, Shiva FastPath and Cayman GatorBox. The code originally +written to handle +the IPTalk gateway function on the FastPath is often known as KIP (Kinetics +IP). +.sp +The file /etc/atalk.local (see atalk.local.5 in +the cap60/man directory) contains routing information for the static IPTalk +network and node numbers. It is not necessary to create this file +if you are using Native EtherTalk or UAR drivers and its presence may cause +problems if you are using Native EtherTalk without a gateway box. +.sp +Before you start here, you should have read the "README" and "NOTES" +files in the cap60 directory. The README file gives a very basic overview +of CAP necessary to understand the following. +.sp +The installation NOTES file tries to point out places where you might +want to redefine things a little, explains configurations, and points +out machine dependencies. +.sp +A simple cookbook approach to CAP installation and printing is available in +cap60/doc/print.cookbook. +.SH +Review +.LP +or the the "before you do anything else" step. If you had a previous +distribution of CAP (eg: 5.00), you should consider +whether it is important enough to dump it to tape and put it away for +safe keeping or not. There are often considerable changes between +distributions. You should make sure you keep around modifications, +patches, etc. even if you think they should be installed in the new +distribution just in case they are not. You don't have to do this, +but if you are paranoid about problems occurring, you will (and +probably would without this prompting). +.SH +UDP Ports +.LP +Versions of IPTalk/UDP gateway code prior to April, 1988 had +"well known" AppleTalk DDP sockets mapped to priviledged +UDP/IP ports starting at +768 (so the NBP socket 2 was UDP port 770, ECHO socket 4 was 772 etc). +.sp +In April, 1988, the NIC assigned a range +of UDP ports starting at 200 for the defined DDP services and assigned +the names "at-rtmp", "at-nbp", "at-echo", "at-zis" (not "at-zip" as +documented in some versions of KIP). In addition, +ports were allocated where there were holes or unused sockets between +at-rtmp and at-zis. CAP Release 6.0 +and above dynamically decides which mappings +to use by doing "getservbyname()" calls based upon those names and using +the UDP port number returned. getservbyname() normally searches the file +/etc/services but the information may also be cached by a NIS (aka Yellow +Pages) server. +If the requested service name does not exist, then the old +mappings (based on 768) are used. +.sp +Entries to be put in /etc/services to utilise the NIC range are +.sp +.nf +at-rtmp 201/udp +at-nbp 202/udp +at-echo 204/udp +at-zis 206/udp +.fi +.sp +[Important Note: In some circumstances, ports in the 768 +range can be "stolen" by the portmapper. The symptom of this is that atis +complains that the NBP or ECHO ports are not available. There are two +solutions, start atis before portmap or change to the NIC assigned ports]. +.sp +It should be clear from the above that you must make sure +that any mappings defined in /etc/services do not conflict +with the mappings used by the IPTalk gateway you are using. +Refer to the gateway documentation for details on how to alter the +port range. +.SH +Preparation +.LP +or the "make sure +things are okay first" step. +Some of the steps require +that you have access to directories that are often only accessible by +a systems person. We'll try to identify them below. In addition, +some of the servers can only be run from the "root" id. We'll try to +identify these too. +.SH +[1] Get CAP Files. +.LP +CAP is distributed as a compressed tar file (see below for a list of +anonymous FTP sites). Since you are reading this information it could +be assumed that you have everything unpacked already. For completeness, +however, the commands to unpack the distribution are +.sp +.nf + % compress \-d cap60.pl100.tar.Z + % tar xf cap60.pl100.tar +.fi +.sp +This creates a 'cap60' directory containing the +patch level 100 CAP distribution. +CAP updates and bug fixes are +normally distributed as 'patch' files. These are simply context diffs +of the original and the new files (see the UNIX command diff(1)). +To apply the patches +with a minimum of effort, it is recommended that you use the 'patch' +program written by Larry Wall. This can be obtained from sites that +archive postings from the newsgroup comp.sources.unix in the directory +volume7/patch2. It is important to ensure that any patches for +the 'patch' program sources are applied, some CAP 6.0 patches can fail if the +unmodified patch code is used. +.sp +CAP 6.0 patches are sent to the newsgroup comp.protocols.appletalk, +and, for sites with FTP access, patches for CAP 6.0 are +archived on munnari.OZ.AU in the directory mac/cap.patches, on rutgers.EDU +in src/cap60.patches and gatekeeper.DEC.COM in pub/net/cap/cap.patches. +The current patch level is recorded in the cap60/README file. +New patches are applied from the top level directory with the command +.sp +.nf + % patch \-p < cap60.patches/cap60.patchNNN +.fi +.sp +where NNN is the patch number. The \-p option tells patch to obtain +information about which file to alter from the patch header. If you attempt +to apply a patch more than once, patch will enquire about "a reversed or +previously applied patch", answering yes to this will remove the patch, +leaving the original file, this is not good ... +.sp +In the following, it is assumed that you are in the top level +directory (e.g. so an ls(1) shows up samples, contrib, etc, lib, +applications, etc.) +.SH +[2] Configuration +.sp +.nf + % ./Configure +.fi +.sp +.LP +You should run Configure to establish the baseline setup for your +system. The Configure script has inbuilt support for various hardware +platforms, making suitable changes for the vagaries of each. +Configure also +checks the byte ordering on the host machine and if necessary ensures +that the code is built correctly for "little-endian" machines. +Answering Configure questions with the default answers is +normally enough to build an IPTalk version +of CAP with a basic feature set, it is also possible to include +an extra set of features for the various CAP components, see the +files CAP60.README and m4.features for more details. +.sp +One Configure option allows the CAP files to be built and used within +a single directory hierarchy. This is useful for testing or evaluation +(see below). +.SH +[3] Make sure things will end up where you want them. +.LP +Figure out where you want everything. The assumptions are: +.sp +.nf + cap libraries - /usr/local/lib - as a unix archive file + cap programs - /usr/local/cap - as programs + cap servers - /usr/local/cap - as programs + cap config files - /usr/local/lib/cap or /etc +.fi +.sp +If you want things elsewhere, edit the file m4.setup and then run gen.makes. +.sp +By the way, /etc may be a bad place to put things. At Columbia, +everything is generally put in /usr/local/lib/cap instead of /etc. +(Warning: if you want cap.printers in /etc, it is not enough to +redefine the "etcdir". You must also uncomment the definition that +allows an alternate location for cap.printers. This also applies to +the file atalk.local. You have to change the definition in the makefile +in lib/cap). +.sp +This is also a good point to think about reconfiguring papif, lwsrv, +etc. to do what you want. If you don't know yet, then don't worry, +you can recompile after you verify basic functionality. +.SH +[4] Re-Generate system makefiles +.sp +.nf + % ./gen.makes +.fi +.sp +.LP +If you have edited the files m4.setup or m4.features, run +gen.makes to (re)generate your baseline makefiles. +There are "Makefile"s included, +but they are included for systems without the m4 preprocessor +and shouldn't be used unless absolutely necessary. Some machines +will generate a 'make' warning message about the presence of both +makefile and Makefile. You can ignore these warnings or, after the +first gen.makes, +run 'make spotless' which removes both makefile versions. At this point it +is necessary to run gen.makes again to continue with the CAP build. +.SH +[5] Install header files. +.sp +.nf + % make include +.fi +.sp +.LP +The simplest method is to type "make include". +This will create /usr/include/netat and copy the contents of cap60/netat +to /usr/include/netat. +.B Alternatively, +you could symbolically link +/usr/include/netat to cap60/netat with the command +.sp +.nf + % ln \-s cap60/netat /usr/include/netat +.fi +.sp +.SH +[6] Testing +.LP +You can test things out without installing programs in system directories by +answering 'y' to the Configure question relating to installing CAP in a +single directory tree. +.sp +This will put: +.nf + libraries in cap60/lib + programs and servers in cap60/bin + etc stuff in cap60/etc +.fi +.sp +In this case, it is not necessary to install the header files. +.LP +.SH +[7] Build Libraries. +.sp +.nf + % make libsmade +.fi +.sp +.LP +Type: "make libsmade" from the top level cap directory. +This should build the cap libraries. +If this step doesn't work, then: +.sp +.nf + (a) you didn't get the distribution correctly + (b) you didn't install the header files correctly + (c) you didn't Configure and generate the makefiles correctly + (d) or worst of all the libraries don't work on your machine +.fi +.sp +If the problem is (d), you can refer to "PORTING" for some help. +.SH +[8] Installing libraries and building sample programs. +.sp +.nf + % make programs +.fi +.sp +.LP +After building the libraries, you use "make programs" to install the +libraries into a readily accessible place (usually /usr/local/lib) and +compile the samples, applications and contributed programs. +.SH +[9] Installation +.sp +.nf + % make install +.fi +.sp +.LP +You can install the various programs into their final destinations by +typing "make install", but you might want to test them first. +.sp +Change directory to cap60/etc. Look at start-cap-servers and figure out if this +is what you want - modify it if necessary. If you don't know +what you want it to be, don't worry - you can do it later, but make +sure you don't remove the line with atis in it. +.sp +You should then copy start-cap-servers to /usr/local/lib/cap (or other +desired location). +.sp +At this point, primary installation is done. +.SH +[10] Verification. +.LP +.sp +If you are using IPTalk, +you should have already tested the gateway software before +proceeding further and +you must have /etc/atalk.local +installed (see the file man/atalk.local.5 for details of +the file contents). Skip from here to step A. below. +.sp +If you are +using Native EtherTalk, then you should read the file +cap60/support/ethertalk/README and then run aarpd with +suitable interface and zone name arguments. Follow this by +atis which determines the AppleTalk network numbers from the network (see +step C below for atis verification procedure). +.sp +If you are using Kernel AppleTalk, ensure that the necessary +code is installed in the kernel (comes installed with Linux), +read cap60/support/capd/README, +run capd with suitable interface and zone name arguments. Follow +this with atis, as above. +.sp +If you are using UAB, read the file cap60/support/uab/README +and the files to which it refers. Ensure that you edit and +install the 'bridge_desc' file. +.sp +If you are using UAR, see the README file supplied with the distribution. +The CAP FAQ also contains information about where to get UAR and its use +with CAP. +.sp +At this point, the /etc/etalk.local file should look +similar to the following: +.sp +.nf + # + # EtherTalk dynamic configuration data + # + # Last update: Sat Dec 29 14:46:45 1990 + # + # Generated by Native EtherTalk (Phase 1) + # + thisNet 99.116 + thisNode 69 + thisZone "unimelb-CompSci" + bridgeNet 99.116 + bridgeNode 69 + bridgeIP 127.0.0.1 + nisNet 99.116 + nisNode 69 + asyncNet 170.170 + asyncZone "unimelb-Async" +.fi +.sp +If things look OK to this point, proceed with the rest of +the verification steps. +.sp +.LP +A. Change directory to cap60/samples and run +.I getzones. +You should see something similar to the following: +.sp +.nf + % getzones + unimelb-CivEng + unimelb-AgEng + unimelb-Ed1888 + unimelb-History + unimelb-FineArts + unimelb-Research + unimelb-Classic + unimelb-Language + unimelb-Chemistry + unimelb-Registrar + ... +.fi +.sp +If getzones prints a warning message or fails with a -1096 error then the +problem is usually that there is no AppleTalk router on the local network +and therefore no zones available. In this situation you should be using "*" +as the zone name argument for aarpd. +.sp +The next program to try is +.I atlook. +If everything is okay, you +should see some appletalk entities. If you installed the IPTalk gateway code +(aka KIP) correctly before, then you should minimially see the IPGATEWAY entry. +[Should you not see the IPGATEWAY entry, then the assumption is that +the UDP code isn't functional or you are not using IPTalk]. For example: +.nf + % atlook + abInit: [ddp: 55.32, 130] starting + 01 - 128.59.35.40:IPGATEWAY@* [Net: 58.01 Node:220 Skt: 72] + ... + $ +.fi +Another really simple program to try is +.I "atlooklws" +which will look for +and query LaserWriters. +.LP +If +.I atlook +doesn't work, then: +.IP +(a) you may not have installed the IPTalk gateway correctly + +(b) you may not have installed atalk.local in the place +.I atlook +expects it, although it should complain if the /etc/atalk.local file is not +there or is incorrectly formatted + +(c) if +.I atlook +coredumps, then something is really wrong. you are +probably on a machine that CAP doesn't work on. +.LP +B. If you have a LaserWriter and you see it in +.I atlook, +then another +level of testing is to run the sample program +.I tlw +(just type "tlw "). +.LP +C. To test the server functionality, as "root" run +.I "atis". +To see if +atis is running properly, run "atistest" from the samples directory. +.sp +.nf + % atistest + CAP distribution 6.00 using UDP encapsulation, January 1991 + Copyright (c) 1986,1987,1988 by The Trustees of Columbia + University in the City of New York + + abInit: [ddp: 93.38, 1] starting + debugging NBP + Registering "atis test:testing@*" + NBP SndNBP: sending + NBP nbp_timeout: 4 tick timeout on -134218532, 3 remain + NBP SndNBP: sending + NBP nbp_timeout: 4 tick timeout on -134218532, 2 remain + NBP SndNBP: sending + NBP nbp_timeout: 4 tick timeout on -134218532, 1 remain + NBP SndNBP: sending + NBP nbp_timeout: 4 tick timeout on -134218532, 0 remain + NBP SndNBP: sending + NBP status done: found -134218532 + Okay +.fi +.sp +If it signals proper operation with an "Okay" message, then you can +confirm things again (odds are everything is okay) by: +.IP +(a) running +.I atlook, +there should be an entry like +.sp +.nf +1 - atis test:testing@* [Net: 93.38 Node: 1 Skt:143] +.fi +.sp +(b) typing "atis dump" (as root) and looking at /usr/tmp/nis.db +you should see (apart from some comments at the top of the file) +.sp +.nf +net=93.38, node=1, skt=143, enum=7, !9!7!1! ? atis test:testing@* +.fi +.sp +(the nis.db entry is the information that atis returns when an NBP name +lookup is performed, such as by atlook). +.LP +To get rid of the "atis test" entry, simply edit /usr/tmp/nis.db and +delete the line, then type "atis reload" (as root). Alternatively, +simply kill the running atis process. A useful maintenance tool is +to alias 'nised' to 'atis dump; vi /usr/tmp/nis.db; atis reload'. + +The most common problem in getting atis to run is failing to setup IPTalk +on the gateway, the atalk.local file +(or, if used, atalkatab) properly. The UNIX/CAP host and the gateway may +also not agree on the network broadcast address, this will affect NBP lookups. + +.LP +D. After verification, you will want things to start up +automatically, edit (or get a superuser to edit) /etc/rc.local to run the +following lines (or an equivalent): +.sp +.nf +if [ \-f /usr/local/lib/cap/start-cap-servers ]; then + /usr/local/lib/cap/start-cap-servers & echo \-n " CAP " > /dev/console +fi +.fi +.sp +.SH +[11] Cleaning Up. +.sp +.nf + % make clean +.fi +.sp +.LP +After final installation, you can do a 'make clean'. This removes all +the compiled binaries and object files thus saving on disc space. +.SH +CHANGES FROM RUTGERS CAP +.LP +There are some important changes to note if you are already using Native +EtherTalk from the original Rutgers distribution. Shared memory is no longer +used, the file /etc/cap.ether.shared is thus not required. The modifications +needed to the file /etc/atalk.local to select a zone and an ethernet interface +are not needed as CAP now uses the file /etc/etalk.local for both Native +EtherTalk and UAB. See the man files atalk.local.5 and etalk.local.5 for more +details. For Native EtherTalk, you may use etalk.local to seed the values for +"interface" and "thisZone" but the preferred method is to supply this +information as arguments to aarpd, as in +.sp +.nf + aarpd ie0 myZone +.fi diff --git a/doc/iptalk.cookbook b/doc/iptalk.cookbook new file mode 100644 index 0000000..6cced87 --- /dev/null +++ b/doc/iptalk.cookbook @@ -0,0 +1,76 @@ +Herewith 10 easy steps to installing CAP with IPTalk: + +1. Make sure that you have the latest CAP code. If in doubt, FTP the CAP +FAQ file mac/cap.patches/CAP.faq from munnari.OZ.AU. It contains a list of +the sites where CAP is available, please choose the closest site. + +2. Run the CAP Configure program, answer all of the questions with the +defaults (by hitting RETURN). Run gen.makes to create the makefiles. + +3. Find out the IP address of the CAP host, say 132.45.67.89. This is a 32 +bit number represented as four 8-bit quantities written as decimal numbers. +It could also be represented as a hexadecimal number, 0x842d4359. The CAP +host node number is the bottom eight bits of the IP address, written as a +decimal number, in this case 89. + +4. Find out the IP address of the IPTalk compatible gateway, such as a +FastPath, GatorBox or MultiPort Gateway, say 132.45.67.90. The "bridge" +node number is the bottom eight bits of the IP address, in this case 90. + +5. Check that the top 24-bits of the two IP addresses are identical, in +this case 132.45.67. For simplicity I'll call this the IP subnet number. +If they do not match you have to investigate the atalkad administration +package, or use CAP with Native EtherTalk or one of the AppleTalk routers. + +6. Find out the IPTalk network number being used by the gateway. This is +a 16-bit number represented as two 8-bit quantities separated by periods +or a single decimal number. For example 93.57 is 93*256 + 57, or 23865. +Each IPTalk network number is uniquely associated with one IP subnet +number, each IPTalk installation must have a unique network number where +the IP subnets differ. + +7. (optional) Check all the other network numbers in use on your network, +make sure that the IPTalk network number is not being used for LocalTalk, +EtherTalk (Phase 1 or Phase 2) or on any other IPTalk network where the +IP subnet numbers differ. + +8. Find out the zone name associated with the IPTalk network number. This +may be the same as other zone names on the network but must be identical +to the zone name programmed into the IPTalk gateway. eg: unimelb-CompSci + +9. Create a file called /etc/atalk.local using the template provided in +cap60/etc/atalk.local and the UNIX manual entry in cap60/man/atalk.local.5 +As a minimum, the file would look like the following, using the numbers +from the examples used above, comment lines start with a '#' + +# mynet mynode myzone +93.57 89 unimelb-CompSci +# bridgenet bridgenode bridgeIP +93.57 90 132.45.67.90 + +10. Find out what UDP ports are being used on the IP network. These are +also called the "NIC Assigned" ports. These ports map to AppleTalk socket +numbers and are used to deliver packets to the correct UNIX processes. By +default, CAP will use the ports starting at 768 so that the RTMP socket +number 1 maps to UDP port 769 and the ECHO socket 4 maps to 772. The +official port range starts at 200, so RTMP becomes 201 and ECHO becomes +204. To ensure that CAP uses the official ports, add the following entries +to the file /etc/services or the NIS database + +at-rtmp 201/udp +at-nbp 202/udp +at-echo 204/udp +at-zis 206/udp + +The port numbers should already be defined in the gateway configuration. + +Continue testing from the [10] Verification step in cap60/doc/install.ms + +Note: if you are using CAP with Native EtherTalk then ignore all but step 1. +The Native EtherTalk code is able to learn the network configuration. If +you have an /etc/atalk.local file, you should remove it. If there are no +other routers on the network, start aarpd with "*" as the zone name. The +UDP ports are also used in Native EtherTalk, as markers for sockets in use. +If a CAP process has trouble starting the ZIS listener or ECHO or NBP +sockets are unavailable, consider installing the official UDP port entries. + diff --git a/doc/nbp.ext b/doc/nbp.ext new file mode 100644 index 0000000..f86d1da --- /dev/null +++ b/doc/nbp.ext @@ -0,0 +1,232 @@ + +NBP in the UNIX environment. + +Charlie C. Kim +User Services Group +Academic Information Services Division +Libraries and Center for Computing Activities (Scholarly Information Center) +Columbia University + +Draft: July 12, 1986 + +INTRODUCTION + +The NBP protocol specification (June 1986 version) assumes that the +NBP database within a particular machine is accessible by any process +in that machine. In the Unix environment, this is possible, but +probably not the best idea (would be easy in System V with shared +memory, would be easy with shared files in BSD 4.2). We extend and +modify NBP for the Unix in the following ways. + +Our goal is quite simple. We wish to provide a name data base for a +particular unix node which is reliable and secure. To achieve this +goal, the information in the database should be correct and timely. +In addition, we should be able to prevent "malicious" or "ignorant" +users from inserting invalid or deleting correct information. + +Since there is a need for a privileged process acting as a name +information socket (NIS) listener in any event, we leverage our +position by extending the functionality of this listener. We modify +its simple purpose of answering NBP lookup requests to that of acting +as the NBP database for a particular unix node (or set of nodes). + +SPECIFICATION + +We now outline the actions the client and server must take, in the +unix environment of course, to register and deregister a name. We +also outline an optional method for maintain the validity of the +database. + +Note that the Unix NBP server differs from the standard NBP +specification in that it WILL respond to NBP Lookups from itself. +Under a shared table os environment like the MacIntosh it is +reasonable to impose this restriction. Under the independent process +environment of Unix, we do not belive the restriction to be reasonable. +In addition, we are assuming the overhead involved on the Ethernet-Unix +end to be smaller (relatively speaking that the overhead on the +AppleTalk-MacIntosh end). + +First, we shall note that the table the NBP server keeps contains a +list of the various "local" NVEs and the associated client process +NBP addresses. + +Name registration +-------------- + +We first extend the range of NBP control types to include NBPRegister +and NBPStatusReply. The standard NBP packet format is used for +NBPRegister. The name to be registered goes into the NBP tuple. Note +that exactly one entity can be registered with a single NBP register +message and the contents of the NBP packet tuple count field must be +zero (note: actually the tuple count field is used for something else, +but we'll ignore that and say that it should be zero for now). +NBPStatusReply uses the same packet, but overloads the usage of the +tuple count field - it is now used to indicate a status. + +The transaction for a register is as follows. [Note: The client is +responsible for checking whether the name is already in use within the +specified zone and should do this before sending the NBPRegister +message to the internal server.] Build a NBP Register message with +the correct entity name encoded in the NBP tuple area. The address +placed there should be that of the local process's NBP manager. The +message is then sent out on the socket which the name should be +registered. This requirement assures us that the service is available +and the agency requesting the service is responsible if the service +applies to a WKS. [Note: it does confuse things a little though]. + +The NBP server, upon receiving a registration message, will first +check the name encoded in the incoming NVE is not already in use +within its table. If the name is already in use, we know that a +"valid" NVE exists (entries in table are assumed valid). We know +check the "valid" NVE's address against the address of the incoming +NVE. If the addresses match, a valid name registration update event +has occurred. [Note: we cannot do otherwise - the process sending the +message "owns" that socket - if there was a previous owner then +tough]. + +At this point, up to three conditions may be in effect. We have a +valid name registration event, a valid name registration update event, +or the NVE may already be in use. For the third condition, we simply +respond to the NBP client with a NBP Status Reply with a code +indicating the name was already in use. + +For a valid name registration event, we must insert the new item into +our table. Since we can't use the trick of the MacIntosh's NBP to use +memory allocated to the client process, we must be prepared to deal +with table/memory overflow. If this happens, a NBP server must send a +NBP Status Reply message with the code "table overflow". Otherwise, +all is okay and we send a NBP Status Reply to the client NBP process +with condition all okay. + +For a valid name registration update event, we need only update the +address of the client NBP and send a NBP Status Reply to the client +NBP process with the condition all okay. + +The client is responsible for sending NBP registration messages to the +server until some retry count is exceed or a NBP Status Reply is +received. Our semantics for the NBP registration attempt ensures that +the client will always receive the correct reply from the server no +matter how many times the server receive the NBP registration message. + +Note: we maintain the NBP id and the server MUST respond with the +same NBP id as the registration request. + + +Name deletion +------------- + +The client sends a NBP deletion message to the server with the +specified NVE encoded the NBP packet tuple area. The server upon +receiving the message will attempt to look up the specified NVE. +[Note: it will use the address that it received the packet from as the +client address!]. If the NVE does not exist, the NBP server will send +back a NBP Status Reply message with the condition "no error". If the +NVE exists in the server's tables, the server will validate the +deletion request by checking the address of the NBP client against the +NBP client address associated with the NVE in the table. If the +addresses do not match, the server will respond with a "permission +denied" NBP Status Reply message. Otherwise, we have reached the +final case and the NBP server will remove the NVE from its tables and +send a NBP Status reply message with condition "no error". + +Note: we do not specify the NBP deletion request be sent from the +socket the NVE exists upon to allow the client to close down the +socket before it sends the name deletion request. If a client has +opened the socket and wishes to delete the name and has received a +"permission denied", the client should send a NBP register message +(which is probably all it ever wanted to do unless it's some sort of +maintenance program) to override the previous owner. + +Note: we maintain the NBP id and the server MUST respond with the +same NBP id as the deletion request. + +Note: deleting a name not in table returns no error so multiple delete +requests won't return an error (since we need to be able to rexmit) + + + +Name table maintenance (optional) +---------------------- + +We need a method of ensuring that some or all of the names held by the +NBP server process are valid. How? We can require that the client +process "tickle" the server process at regular intervals. Also, less +satisfying is to require the NBP server process to tickle the client +process of each NVE at regular intervals and have the client process +respond. + + +We take the following approach. The client may note that name will be +"tickled" by encoding a flag in the NBP tuple count field on a name +registration (note: we may require that all NVEs using dynamic sockets +do this - enforcement would come from server: either it would timeout +NVEs which didn't respond or it would reject the registration +message). The server will then age NVEs based upon NBP "tickle" +messages from client. When the NVE is in danger of timing out - the +server will send a NBP "tickle" to the NBP address of the client +associated with the NVE. The client can validate the server "NVE" by +checking the socket - since it is a privileges socket we know that +the message is valid. The client must respond it desires to keep the +NVE active by sending a tickle packet. + +Note that the client may send a list of tuple in NBP tickle packet. + + +Finding the NBP server +---------------------- + +We have assumed throughout the above discourse that the NBP server +address is known. We leave this unspecified - allowing the NBP server +to reside on a different node than the client. The current Unix +implementation assumes that the NBP server is running on the same node +as the NBP client. + + +POSSIBLE ENHANCEMENTS + +NBP Lookup server +----------------- +To reduce the number of message flying across the bridge/gateway to +the Appletalk network, we could have the NBP server do lookups and +keep track of NVEs it finds. The clients would then be required to do +a NBPConfirm to ensure the NVE (since we must assume early binding in +this situation). + +We would in this case look at the NBP server (for the limited purpose +of this discussion) as the NBP lookup server. The server would do look +ups on its own infrequently (app. 5-10 minutes would be frequently). +The server, upon receiving a lookup request from one of its lookup +clients that it could not satisfy or involved wildcards, would check +to see when it last did a lookup - if less than a small interval (say +10-30 seconds) would issue another NBP lookup request to the zone. + +Note: when we're dealing with wildcards, it's probably best for the +client to issue its own request instead of going through the lookup +server. + +Priviledged is priviledged +-------------------------- + +Allowed a name registration request from a WKS override a name used by +someone else. Should allow for notification too; possibly the message +used in maintenace to inform of a name going away should be extended +to allow a message which say that a name has gone away. + + +NBP Delete all message +---------------------- + +An NBP delete all message from a client would specify that all names +"owned" by the client be removed - useful for a closing down state. +In addition, a message to remove all names based on (network +address,socket) pairs would be useful. These two functions would be +encoded by using the NBP tuple count field as a flag field. + + +Combining with internet server +------------------------------ + +Would be nice to integrate this with the internet name server. In +particular, it would nice to be able to register TCP/IP hosts. + + diff --git a/doc/pap.notes b/doc/pap.notes new file mode 100644 index 0000000..e72606c --- /dev/null +++ b/doc/pap.notes @@ -0,0 +1,60 @@ +CAP note: last revision July 26, 1986 + +UNIX Printer Access Protocol (PAP) Programmer and implementation notes + +Protocol specification: Inside Appletalk, June 1986. + +The document briefly describes implementation specific aspect of PAP. + +PAPOpen, PAPRead, PAPWrite, PAPClose, PAPStatus, GetNextJob, +PAPRegName, PAPRemName work as documented. SLInit and HeresStatus +differ in that they record the pointer to the buffer which contains +the status message, thus allowing the user to modify the status +message. SLClose differs in that it does not deregister names +submitted via SLInit or PAPRegName. PAPUnload is unimplemented at +present. Note: SLClose as implemented probably should be renamed to +SLShutdown (to correspond to PAPShutdown as below) and SLClose should +be implemented to work as documented by Apple. + +One additional call has been added. PAPHalfClose closes a pap socket +for further writes. PAPShutdown closes a pap socket without notifying +the remote. These were added to allow PAP connections to be used in a +multi-fork environment. + +Note on forks. + +After a PAP connection has been opened via PAPOpen or GetNextJob, it +is possible to have one fork reading and another writing. It is not +possible to have more than one fork reading or writing. This is an +implementation restriction caused by the fact that the PAP and ATP +protocols are carried by each fork instead of some system process. +Any fork except the one which is to do the writing (via PAPWrite) must +issue a PAPHalfClose call after a connection has been opened. The +write fork listens for remote close and tickle requests (and thus, +also carries the timeout which prevents half-open connections from +sticking around). All processes forked off after the connection has +been established run the tickle functions. Note: you should fork +after the open and before any reads or writes or you may well get +unexpected results. + +Doing forks for the server environment is a little more involved. +Assuming you want the server to stick around, you must do a SLClose on +the server refnum in the child after the fork call. The server must +do a PAPShutdown on the opened PAP socket. [This assumes that any +getnextjob has completed]. + + +General implementation notes: + +Each PAP connection has a private ATP responding socket used to +receive data from the remote. Each fork has one ATP requesting +socket. The responding socket is only used by PAPWrite. + +Note: two pap connections and one server connection is allowed at +present. To change this, modify the parameters in abpap.h. + + +ABPP.C routines. These are synchronous, unix-style calls which +provide access to PAPOpen, PAPRead and PAPWrite. See the module or +any of the sample programs for examples of how to call them. + diff --git a/doc/print.cookbook b/doc/print.cookbook new file mode 100644 index 0000000..630bef3 --- /dev/null +++ b/doc/print.cookbook @@ -0,0 +1,294 @@ +Printing from a Sun to a LaserWriter with CAP +============================================= + + +These notes are a beginner's "cookbook" approach to a basic CAP printer +installation - just getting a Sun SS2 to print to an Apple LaserWriter +via EtherTalk. They consolidate information scattered through the CAP +Installation Procedures, the man pages, the CAP FAQ, and postings in +comp.protocols.appletalk. + + +Network Setup. +============== + +The objective which prompted these notes was to get a new Sun SS2 to print +to an existing Apple LaserWriter IIG via an isolated Ethernet subnet. There +were no other A/Talk devices on the Ethernet; in particular, there was no +A/Talk router, and therefore no zones. However the LaserWriter was already +connected to an established LocalTalk network, with LocalTalk zones +established by a Webster Multiport Gateway. + + LaserWriter Lots of Webster + Sun SS2 LW IIG Macs Multigate + | | | | | | + -------Enet------ ----LocalTalk----------- ---Another Enet-- + +The LaserWriter IIG is claimed to service both LocalTalk and EtherTalk +interfaces concurrently, but several people have reported difficulties, +especially with firmware prior to Revision 2. However, the LocalTalk side +of the printer turns out to be largely irrelevant to the CAP setup, other +than for initially setting the printer's AppleTalk name. + +The good news is that CAP _will_ work in this configuration, using the +"Native Ethertalk" mode through the Sun Network Interface Tap (NIT). There +is no need to have an AppleTalk router on the Ethernet segment. Network +numbers will be assigned by CAP; the zone must (ie, can only) be the +"default" zone "*" (because there is no router); and the printer name +will be the same as its LocalTalk name. + +If there had been Macs already on the Ethernet, CAP would simply fit in with +the existing network numbers. Ditto if there had been an AppleTalk router, +provided that the printer's zone names on the EtherTalk and LocalTalk sides +were different. (Otherwise, the printer name would match two network +addresses, and it would shut down the LocalTalk port). + + +Pre-requisites. +=============== + +Get the latest CAP FAQ from munnari.OZ.AU (mac/CAP.faq). The version that +comes with the CAP distribution is NOT the latest. + +Get and install the "patch" utility (after getting and installing the gzip +utility!). + +Check that the NIT (network interface tap) has been configured into your +kernel. If etherfind runs, it is probably OK, but see the FAQ for details. + +Check that the LaserWriter firmware is Version 2 or later, and arrange for a +ROM upgrade if necessary. (The version number is in the fine print at the +bottom right of the startup test page). + + +Getting Organised. +================== + +Download the CAP file patched to level 100 (cap60.pl100.tar.Z), and all +the subsequent patch files (patches.XXX-YYY.tar.Z). + +Unpack CAP and all the patch files in /usr/local/cap. (Patches end up in +this directory, CAP files in ./cap60). + +Change to the top-level CAP directory (cap60). + +Check the README file to find out the current patch level. Apply all the +required patches, adapting the 3-line command in the FAQ (painless). + +Check file ownerships, and change to root.daemon or something sensible +(" chown -R root.daemon . ", as user root from the cap60 directory). + +Print and read the Installation Procedure in cap60/doc/install.ms +(" nroff -ms doc/install.ms | more ") + +Read the relevant UNIX manual entries in cap60/man. +(" nroff -man man/CAP.8 | more ") + +Add the assigned AppleTalk UDP port numbers to /etc/services, as per the +instructions in the Installation Procedure. + + +Configure and Build CAP. +======================= + +From the cap60 directory: + +Run ./Configure, and take all defaults except: + + Use Native EtherTalk? Yes + Use Phase 2? Yes + Restrict CAP to one directory? Yes + +Run ./gen.makes to build the makefiles from the m4 scripts. + +With CAP resticted to one directory, there is no need to "make include". + +Run "make libsmade" to build CAP libraries. + +Run "make programs" to compile the applications. + +Run "make install" to move things into ./bin + +The files ./etc/atalk.local and ./etc/etalk.local contain addressing "seed" +information ex Melbourne Uni, and should be deleted. etalk.local will be +re-created from scratch by "aarpd" and "atis" in the steps following. + + +Initial Testing. +================ + +Start aarpd with the default zone name ("*" or \*): bin/aarpd le0 "*" + +Aarpd may take 15 seconds or so before returning, while it establishes an +initial node number and creates a new version of etalk.local. + +Start atis: bin/atis + +Wait while atis listens for routing packets. There won't be any, unless +there is a router on the Ethernet. After about 15 seconds, atis updates the +file ./etc/etalk.local with either the existing or its own network +numbering. With no router, it should look something like: + + # + # EtherTalk dynamic configuration data + # + # Last update: Tue Jan 18 15:34:30 1994 + # + # Generated by Native EtherTalk (Phase 2) + # + interface "le0" + netRangeStart 0.00 + netRangeEnd 255.254 + thisNet 255.00 + thisNode 168 + thisZone "*" + bridgeNet 0.00 + bridgeNode 0 + bridgeIP 127.0.0.1 + nisNet 255.00 + nisNode 168 + asyncNet 0.00 + asyncZone "" + +If there is an AppleTalk router on the Ethernet, run bin/getzones and check +that the existing zones are visible to the Sun. If there is no AppleTalk +router, there can be no zones either, and getzones will fail. + +Run bin/atlook to show the devices visible on the network. This should show +(at least) the LaserWriter. See the man pages for the many options to +atlook - no arguments will show all devices in the default zone. + + auric# bin/atlook + abInit: [ddp: 255.00, 168] starting + Looking for =:=@* ... + 1 - Administration Room A1.62 :LaserWriter@* [Net:255.246 + Node:246 Skt:128] + +Note the extra space character at the end of the printer name (before the +colon). This is due to a bug in some versions of the naming program, but it +_is_ part of the printer name and must be included in the setups following. + +Test the Printer Access Protocol (PAP) by sending a PostScript file direct +to the printer with the lwpr utility: + + bin/lwpr -p PrinterName:LaserWriter@* PSfilename + +where the PrinterName is exactly as output by atlook above. + +If this works, it means that the CAP name and address daemons and the +printer access protocol are all OK. Next step is to set up lpd spooling. + + +Set up Printer Spooling. +======================== + +There are several ways to do this, as per the papif man pages. This one is +the simplest and most understandable. + +Create a printcap entry, spool directory, and so on, as per normal. (Note +that CAP _does_ write to the accounting and log files, so you will need to +keep an eye on them). + + # LaserWriter IIG (Admin) via CAP/Ethertalk + lp2|lw2|Administration Room A1.62:\ + :lp=/dev/lw2:\ + :sd=/var/spool/lw2:\ + :sf:\ + :mx#0:\ + :lf=/var/adm/lw-errs:\ + :af=/var/adm/lw-acct:\ + :if=/usr/local/cap/cap60/bin/lw2if:\ + :of=/usr/local/cap/cap60/bin/papof: + +Some systems are said to perform locking on the device file for local +printers (the "lp" entry), so just create an empty normal file for each +CAP printer: + + auric# touch /dev/lw2 + auric# chmod 660 /dev/lw2 + auric# ls -lg /dev/lw2 + -rw-rw---- 1 root daemon 0 Jan 19 12:19 /dev/lw2 + +The input filter "lw2if" is a small shell script which simply passes the +Unix short printer name (eg "lw2") and any other lpd arguments to the +"real" filter (CAP papif). There are more elaborate examples in the +papif man pages. Create one of these for each CAP printer: + + #!/bin/sh + # lw2if - CAP Input filter for lw2 + /usr/local/cap/cap60/bin/papif -P lw2 $* + +The Unix short printer name ("lw2" above) then has to be mapped to a +fully-qualified AppleTalk entity name (name:type@zone) via a list +in ./etc/cap.printers: + + # Unix to AppleTalk Printer Name Mappings + # + lw2=Administration Room A1.62 :LaserWriter@* + +Note again the spurious space character at the end of the printer name. + + +Ownerships and Permissions. +=========================== + +Now, the final key point, from the cap60/support/ethertalk/README file - +you need to set matching ownerships and permissions on the network +interface /dev/nit and all of the executables, or none of this will work +(not even for root, if you got there via a regular su). The most secure +method is: + +Create a group "nit" in /etc/groups. + +Change /dev/nit to be group "nit", with group read/write permission: + + crw-rw---- 1 root nit 37, 40 Jul 30 1992 /dev/nit + +Change papif, papof, and all your input filters ("lw2if" above) to be group +"nit" and setgid "nit": + + -rwxr-sr-x 1 root nit 203 Jan 14 16:05 lw2if + -rwxr-sr-x 1 root nit 106496 Jan 14 15:15 papif + -rwxr-sr-x 1 root nit 16384 Jan 14 15:15 papof + + +Final Stages. +============= + +Now test the printer by sending a PostScript file via lpr. Watch the job +progress with "lpc stat lw2", check the printout, and check the entries in +the printer accounting and log files. + +When all is well, re-compile if desired using the "normal" directories, make +clean, and add a suitable startup script to /etc/rc.local: + + if [ -f /usr/local/cap/cap60/start-cap-servers ]; then + /usr/local/cap/cap60/start-cap-servers & echo -n ' CAP ' > /dev/co + nsole + fi + +where start-cap-servers is something like: + + #!/bin/sh + # start-cap-servers + # Called from /etc/rc.local + # + # Start aarpd for Native Ethertalk + /usr/local/cap/cap60/bin/aarpd le0 "*" + sleep 5 + + # Start atis for RTMP and NBP + /usr/local/cap/cap60/bin/atis + sleep 15 + + + + + + + +John Wolff 24 January 1994 +Electronics & Networks Engineer Phone: +61 3 542 2281 +CSIRO Ian Wark Laboratories Fax: +61 3 543 6613 +Private Bag 10, Rosebank MDC Private: +61 3 754 2426 +Clayton, VIC, 3169, Australia Email: J.Wolff@forprod.csiro.au diff --git a/doc/sched.notes b/doc/sched.notes new file mode 100644 index 0000000..f0fb718 --- /dev/null +++ b/doc/sched.notes @@ -0,0 +1,210 @@ +First revision: March 29, 1988 + +Protocol Scheduler + +The CAP protocol libraries are designed so that protocols run based on +two types of events. The first type is incoming packets/input ready +and the second is timeouts. These are "scheduled" or handled by +giving up the CPU for a specified amount of time during which these +events are awaited and processed. Thus, these events run +non-preemptively. Because of this, there's a lot less worry about +critical sections, etc. (Warning: this means running protocol from +signal handlers is a bad idea since the signal handler may have been +invoked inside the protocol scheduler). + +Both input ready and timeout routines should follow these rules: + o don't block for any significant amounts of time (>.25 + seconds is a good rule of thumb) + o don't call abSleep or doTimeout + +SCHEDULING + +o int abSleep(giveup time, until_first) + giveup time - time in tick (1/4 second units) + until_first - TRUE/FALSE + +abSleep runs protocol events. If until_first is set to TRUE, then +abSleep will return after the first (set) of protocol events occur. +In this case the giveup time specifies the maximum time abSleep would +keep control. Warning: more than one protocol event may occur even if +until_first is set. + +The basic algorithm is: + while giveup time is still valid + sleep until input ready or Timeout + if input ready then + get all input + else if Timeout then + run all timeouts + if event occurred and until_first set, then return + end + +There should probably be a version of abSleep that accepts +absolute/relative times via struct timeval to allow better control, +but there isn't a need yet... + + +INTERNAL CLOCK: + +An internal clock is used to redefine relative times as absolute +(system TOD based) times to minimize the effects of scheduling, etc. +(e.g. we can't trust relative times in a timeshared environment). + +o getschedulerclock(struct timeval **tv) + +getschedulerclock returns the current scheduler reference clock +setting. (Pass pointer to timeval pointer and it will return pointer +to the reference clock.) Even though you can update the reference +clock through this pointer, you shouldn't unless you know what you are +doing. + +o updateschedulerclock() + +updates the scheduler reference clock settting (safely). + + + +EVENT: INPUT + +Input protocol event handling below the user level is generally done +on the basis of incoming packets. At the user level, it is possible +to specify non-protocol file descriptors for input protocol event +handling. This is useful when you don't want to block on input +because protocol must be scheduled--it's silly to make the user level +program have to another set of selects. + +o int init_fdlistening(); + +Initialize the file descriptor listening code. This is generally done +in abInit. Calling init_fdlistening twice is okay -- the second call +is ignored. Returns: number of file descriptors that it can handle. + +The rest of the calls return -1 for error and 0 for success. + +o int fdlistener(int fd, int (*listener)(), caddr_t addrarg, int intarg) + +Install the listener "listener" that is to be called with the +arguments "addrarg" and "intarg" when input is ready on the file +descriptor "fd". Note: fdlistenread is used to call the listener. If +"listener" is NULL, input ready waits are still done on the file +descriptor, but the no listener will be called. The addrarg and +intarg distinction is made in case a pointer is not necessarily an int +on a particular machine. The reason for having both is that it is +useful to be able to pass an array/buffer with a size to "read" +routines. The read listener should not do more than a single read +call unless it is know that sufficient data is there to satisfy it or +else things will block. Extreme care should be taken when using stdio +since the read system call is not under user control. + +o int fdunlisten(int fd) + +Remove any listeners on file descriptor "fd". Returns error if no +listener was installed. + +o int fdlistenread(int fd) + +Calls the listener for file descriptor "fd" with the argument saved by +fdlistener. Included at the user level to allow a forced "read" call. + +o int fdlistensuspend(int fd) + +Suspend input ready check on file descriptor "fd". Returns error if +not installed by fdlistener. Note: this can be called from inside a +"fd" listener and it will take effect immediately. + +o int fdlistenresume(int fd) + +Resume input ready check on file descriptor "fd". Returns error if +not installed by fdlistener. Note: this can be called from inside a +"fd" listener and it will take effect immediately. + + +fdlistensuspend and fdlistenresume are useful to when the listener +doesn't actually read the data, but instead just marks that input is +ready. For example: + waitforinput(fd, waitvar, dummy) + int fd; + int *waitvar; + int dummy + { + (void)fdlistensuspend(fd); + *waitvar = 1; + } + /* calling sequence */ + fdlistener(fileno(stdin), waitforinput, &waitvar, 0); + do { + /* okay to do even if not suspended */ + fdlistenresume(fileno(stdin)); + waitvar = 0; + do { + abSleep(4, TRUE); /* run protocol events */ + } until (waitvar); + read input on stdin + } while (not end of file on stdin) + + +EVENT: TIMEOUTS + +Timeouts are used to schedule a procedure to "run" at a later point. +Timeouts are always stored internally in absolute time format (see the +notes on the internal clock above). Due to the nature of the +environment, timeouts can only occur near the times specified. Things +are designed so that timeouts should never occur before the time +specified. + +o void Timeout(void (*fun)(), caddr_t arg, int timeout) +o void AppleTimeout(void (*fun)(), caddr_t arg, int timeout, boolean doupdate) +o void relTimeout(void (*fun)(), caddr_t arg, struct timeval *tv, + boolean doupdate)) +o void absTimeout(void (*fun)(), caddr_t arg, struct timeval *tv) + +Timeout schedules "fun" to be called with argument "arg" at "timeout" +ticks in the future. The call will never occur before "timeout" +ticks. It will only occur near timeout ticks if abSleep is called +around that time. Thus, if timeouts are critical, then it is +important to call "abSleep" often. + +AppleTimeout is actually the basis for "Timeout". It takes as an +additional argument "doupdate" that, if FALSE, notifies the timeout +code that the internal clock should be considered "accurate" and need +not be updated. One may make this assumption if AppleTimeout or +relTimeout (see below) was called just before with doupdate TRUE with +little or no code intervening. Note: Timeout just calls AppleTimeout +with doupdate TRUE. + +relTimeout replaces the Apple tick time with "struct timeval" set to +the interval (relative time). This allows us to schedule to down to +microseconds (be warned, the resolution of your interval timer is +unlikely to be that accurate! It is even more unlikely you can get +your scheduler to guarantee that you will be scheduled at microsecond +accuracy. Don't count on better than about 1/10 second or so). See +AppleTimeout above for information on the doupdate flag. + +absTimeout is like relTimeout except it takes an absolute time and +thus removes the need for the "doupdate" flag. + +Note: two or more timeouts scheduled for the same time will occur in +an indeterminate order and within the same "abSleep" call. + +o int remTimeout(void (*fun)(), caddr_t arg) + +remTimeout removes a timeout to call function "fun" with argument +"arg". remTimeout will only remove the first instance of the pair + that it finds. remTimeout returns true if it removed the + pair, false otherwise. So to guarantee that you have +removed all instances, you can (a) make sure you only have one +outstanding at a time or (b) use the following code fragment: + while (remTimeout(fun,arg)) + /* NULL STATEMENT */; + +o boolean minTimeout(struct timeval *timeval) + +minTimeout will return in "timeval" the time that the "next" timeout +is scheduled to occur. Note: it may be in the past if the scheduler +has not run. It returns FALSE if there are no timeouts enqueued. + +o int doTimeout() + +doTimeout is included to allow forcing of timeouts without running the +"input" events. If doTimeout is used, it should probably be used in +conjunction with "minTimout" to minimize the work. diff --git a/doc/uab.desc.ms b/doc/uab.desc.ms new file mode 100644 index 0000000..212b033 --- /dev/null +++ b/doc/uab.desc.ms @@ -0,0 +1,174 @@ +.\" nroff -ms +.TL +Unix AppleTalk Bridge +.AB +This document describes a +Unix based AppleTalk Bridge +.I (UAB) +designed to work on a variety of +host unix systems. UAB also provides for mechanisms to deliver +packets internally. +.AE +.SH +INTRODUCTION +.LP +The Unix AppleTalk +Bridge (UAB) program allows certain unix systems to act as AppleTalk +Bridges. UAB consists of a number of layers that can have multiple +implementations. UAB can be functionally divided into two parts. The +first is the actual AppleTalk Bridge implementation and the second are +the routines that define particular "Link Access Protocols" (aka +"hardware" delivery methods e.g. EtherTalk). UAB also supports an +internal demultiplexing that allows +packets delivered to the UAB node to be delivered to other processes +within that system. +.PP +Currently, UAB runs on Ultrix 1.2 (and beyond) and SunOS 4.0 and +supports EtherTalk. Unfortunately, with the current definition of +KIP's UDP encapsulation and delivery rules, it is not feasible to +implement KIP. +The only internal packet +delivery mechanism defined is a modified version of KIP's UDP +encapsulation (modified-KIP) that uses a different UDP port range over +the internal +loopback; thus CAP programs must be relinked with a different low +level delivery mechanism to work with UAB. Note that all packets for +these programs are sent and received through the UAB process. +Since UAB does not understand KIP, +it is necessary to have an AppleTalk Bridge that +understands both KIP encapsulation and EtherTalk before KIP based +"systems" (e.g. programs compiled with CAP, bridges that only speak +KIP on the ethernet interface--revisions of KIP before 2/88, etc) can +work with UAB modified-KIP based programs. +.SH +Definitions +.LP +.IP +An +.I interface +defines the +underlying delivery protocol. The only delivery protocols supported at +the present time are EtherTalk (Phase 1) and Asynchronous AppleTalk. +.IP +A +.I port +abstracts an interface into a workable DDP entity. DDP level +functions deal with ports rather than interfaces. A port carries +information such as interface input/output mechanisms, ddp network +numbers, etc. +.IP +The +.I port manager +is a set of routines that handle ports. Only the port manager +directly manipulates a port. Both the lap layer and the ddp layer +call the port manager. +.IP +A +.I node +is a DDP/RTMP concept that defines nodes in a way that should contain +all the various LAP definitions. In particular, a node is defined as the +tuple <# of bits, bits> where the number of bits can be between 1 and +255. This is more general than the original LocalTalk LAP definition +which defines a node as 8 bits. +.SH +AppleTalk Bridge +.LP +UAB builds upon the concepts of +.I interface, +.I port, +and +.I node +to separate itself from the underlying delivery mechanism. +As an AppleTalk bridge, it provides full RTMP and ZIP services as +defined in Inside AppleTalk. In addition, it provides the NBP Bridge +Lookup services. +.PP +As all AppleTalk bridges, it is also a node on the various AppleTalk +networks to which it is directly connected. Packets directed to its node +number (e.g. that aren't supposed to be forwarded) and which are not +directly related to bridge management (RTMP, ZIP, and NBP BrLk) are +handled in two ways. The first provides a simple "port" wide +services: when the socket is "opened" it is opened for all known and +future ports. The only one currently defined is DDP ECHO. In the +future, it may be necessary or advisable to add other NBP services +such as outgoing lookup, internal name management, etc; however, that +has not yet been done. For "unopened" sockets the packets are +sent to a "demultiplexer". (NBP is considered "partially" opened for +our purposes-the handler only picks out the bridge lookup packets). +.PP +For historical reasons, we don't consider UAB to be directly connected to +the asynchronous appletalk network (thus no node number). This is because +we are really acting as a 'half-bridge' in conjunction with the async +client process. This will probably change in the future. +.PP +The demultiplexor/multiplexor is supposed to solve the problems of +sending to other +processes on the system (if the system is processes based like unix). +There are a number of requirements associated with the demultiplexor under +Unix. First, the demultiplexor delivery mechanism does not have to be +reliable since it is delivering ddp packets: since DDP is considered +unreliable, there must be higher level policies that ensure delivery. +Second, the demultiplexing end must be able to send DDP packets to the +correct processes +in a way that the processes can decode what the DDP socket number was. +For example, with UDP, it is simple enough to define a port range and +send the packet to a particular port: if a program is listening on it, +it will receive it and know exactly which socket (based upon a +mapping) it was meant for. With UDP, the process knows that +a stronger condition holds +because the processes knows apriori what the DDP socket number is and +can do different reads based upon this (e.g. customized io vectors). +Third, the multiplexing end must be able to know the DDP socket that the +process is sending to. With UDP, the best way is to have the +multiplexing end listen to a single socket: the recv call can return +the source port number (which then can be translated in to the DDP +socket). Fourth, both sides must be relatively sure of the +"trustworthiness" of the packets: e.g. one must not be able to have +"untrustworthy" agents intercept or inject packets undetectably. +Fifth, it is necessary that the mechanism work within reasonable +implementation boundaries. For instances, a mechanism that required +the full DDP range of 254 sockets to be opened (e.g have that many +file descriptors/open files) would not fit within +those requirements upon most if not all of today's unix systems. +.PP +The only mechanism defined so far that allows these requirements to be +fulfilled in a reasonable fashion is the modified KIP scheme, but even +there, the security requirement has been loosened. The primary reason +that it works well is that one can define a single point of contact on +the demux/mux process that goes to many points (on many processes) +within the constraints mentioned above. Basically, +it's real easy to use UDP because it allows one to use the +kernel to do the fan-in and fan-out +functionality. +.SH +Link Access Protocols/Interfaces +.LP +.I UAB +uses DDP ports and interfaces to abstract the bridge functionality +from the delivery mechanisms. The level of separation is at the ddp +layer. As defined before, an interface is a basic description of a +particular delivery protocol such as EtherTalk (ELAP, implemented) or +LocalTalk (ALAP, not implemented at present). +When initialized, an interface sends information to the ddp port +manager that defines its basic operating characteristics. +.PP +The only delivery protocols defined at present are the EtherTalk Link +Access Protocol (aka EtherTalk) and Asynchronous AppleTalk. Other delivery +protocols may be +defined for other systems with particular hardware (e.g. Mac II +running A/UX with a localtalk card) in the future or by other parties. +The SunOS and Ultrix ELAPs are implemented on top of a specialized +facility available on both (in different forms) that allow "opening" +an Ethernet protocol: all packets addressed to that host with a +particular protocol type are delivered to the UAB process. No or very +little processing is done by the kernel. To complete these ELAP, AARP +is also implemented. The only protocol interface library +implemented under SunOS is based upon the streams version of the +Network Interface Tap first made available in SunOS 4.0. The protocol +interface library for Ultrix is based on the Data Link Inteface +facility (c.f. DECNET documentation). +.PP +The ELAP implementation is abstracted from the actual "ethernet +protocol" facilities by the use of a set of "protocol interface +routines" (poor choice of names, but was made a long time ago when the +routines were meant for a far different purpose). diff --git a/doc/uar.cookbook b/doc/uar.cookbook new file mode 100644 index 0000000..d5cdbf0 --- /dev/null +++ b/doc/uar.cookbook @@ -0,0 +1,86 @@ +Herewith 10 easy steps to installing CAP with UAR: + +1. Get the UAR package (mac/uar.tar.Z) via anonymous FTP from munnari.OZ.AU +and place it in a local directory 'uar'. 'cd uar', un-compress and un-tar +the package with 'compress -d uar.tar.Z' and 'tar -xvf uar.tar' + +2. Check the README to ensure that your host is supported, currently UAR +supports Phase 1 and Phase 2 EtherTalk networks connected to SUN, DEC ULTRIX, +SGI IRIX, Sony NEWS 4.2, HP-UX and IBM RS6000 AIX workstations, and Phase 1 +only on Sony NEWS pre-4.2 workstations. + +3. Edit the 'Makefile' to add the necessary info to CFLAGS= and LIBS= +The requirements are listed per machine type (only NEWS, AIX & Solaris 2.N). + +4. Compile the program by typing 'make'. If compilation procedes without +error, type 'make install'. This step copies the binary to /usr/local/cap/uar + +5. Decide on whether or not you are going to run UAR as a "seed router". +That is, if UAR is to be configured with details of your local AppleTalk +network or is to determine information empirically from the network. For +UAR to function as a "seed router", you must create a uar.conf file that +contains network number(s) and zone name(s) for each of the participating +ethernet interfaces on your UAR host (see the sample uar.conf provided). +The information in uar.conf *must* be identical to the configuration in +any other AppleTalk routers on the local networks, this is not optional! +Normally your campus/institution network manager is the best source of +such information. + +6. If you are absolutely certain that there are no other local AppleTalk +routers then you may choose numbers for the "network", "networklo" and +"networkhi" entries. A "node" entry is optional. Network numbers are +16-bit quantities and can range from 1 to 65534 (the values 65280 to +65534 are reserved as the "startup range" on Phase 2 networks, don't +assign a network number in this range). These 16-bit numbers can also +be represented as two decimal numbers separated by a dot. In this +notation, 56284 is represented as 219.220 (ie: 219 x 256 + 220). The +node number is an 8-bit number, the valid range is 1 - 254 on Phase 1 +AppleTalk networks and 1 - 253 on Phase 2 AppleTalk networks (assume +Phase 2 for recent Macintoshes unless you know otherwise). Node numbers +specified for UAR should be towards the high end of the range, ie: 253. +You must also choose a zone name or list of zone names for your network, +in the latter case specify one of them as the "default" zone name. The +interface names are the device names for your ethernet interfaces and +can be listed using the command 'netstat -i'. Under AIX, use "ent0", +"ent1" rather than the listed "en0", "en1". + +7. Ensure that your CAP distribution is at least at patch level 144. +Run the Configure script and answer 'y' or 'yes' to the question +"Do you wish to use UAR (Unix AppleTalk Router) (default no) ?" and 'y' +or 'n' as necessary to "Do you want Phase 2 compatibility (no) ? " +Run 'gen.makes', 'make include' and 'make programs'. See the CAP +documentation for more details. + +8. To use UAR with CAP you must specify an interface for CAP to be +"attached" to. With a uar.conf file, this is achieved with a "cap on" +entry, all other interfaces should have "cap off". If no uar.conf file +exists then run UAR with a -C option. This attaches CAP to the first +interface name listed in the UAR arguments. + +9. Run UAR. For example, on a single interface machine where UAR is used +only to support CAP (and not in it's usual function as an AppleTalk Router) +you would use + + uar -C le0 + +or, on a multiple interface machine + + uar -C ie1 ie0 + +where CAP is attached to "ie1", or with a uar.conf file + + uar et0 enp0 + +where "et0" and "enp0" are listed in the uar.conf file with "interface" +entries containing network and node numbers, zones and one "cap on" entry. + +10. Check for "/etc/etalk.local" created by UAR. Normally this takes up +to 15 seconds to appear (so in a start-cap-servers file insert a "sleep 20" +after starting UAR before starting the rest of the CAP programs). With CAP +compiled for use with UAR it should now be possible to run test programs such +as 'cap60/samples/getzones' and 'cap60/samples/atlook' and see meaningful +results. If not, consult your local network administrator or send email to +uar@munnari.OZ.AU. Please note that UAR vers 1.0 is FreeWare. A ShareWare +or site-license fee is payable for UAR version 1.1. + + diff --git a/etc/Makefile.m4 b/etc/Makefile.m4 new file mode 100644 index 0000000..9fe86a3 --- /dev/null +++ b/etc/Makefile.m4 @@ -0,0 +1,47 @@ +CFLAGS=cflags() nbpflags() specialcflags() +SDESTDIR=capsrvrdestdir() +UDESTDIR=capdestdir() +ETCDIR=etcdest() +CAPLIB=libcap() +I=includedir() +# for other libraries (like BSD on hpux) +SLIB=libspecial() + +ifdef([useatis],[],[# ])ATISPROGS=atis +PROGS=${ATISPROGS} + +# aufs.c definitions: USEVPRINTF - use vprintf in logging +ifdef([usevprintf],[],[#])ATISDEFS=-DUSEVPRINTF + +# Make sure to define needgetopt if your system doesnt have it or +# just set GETOPT=att_getopt.o (or to a getopt of your own liking) +GETOPT=ifdef([needgetopt],[att_getopt.o]) + +all: ${PROGS} + +atis: atis.o nisaux.o ${GETOPT} + ${CC} ${LFLAGS} -o atis atis.o nisaux.o ${GETOPT} ${CAPLIB} ${SLIB} + +atis.o: $I/netat/abnbp.h + ${CC} ${CFLAGS} ${ATISDEFS} -DETCDIR=\"${ETCDIR}\" -c atis.c + +nisaux.o: $I/netat/abnbp.h + +att_getopt.c: + ln -s ../extras/att_getopt.c + +install: ${PROGS} + -strip ${PROGS} + -mkdir ${SDESTDIR} ${UDESTDIR} + ifdef([sysvinstall],[install -f ${SDESTDIR} ${PROGS}], + [${INSTALLER} ${PROGS} ${SDESTDIR}]) + +clean: + -rm -f atis *.o core att_getopt.c *~ + +spotless: + -rm -f atis *.o *.orig core att_getopt.c *~ Makefile makefile + +dist: + @cat todist + diff --git a/etc/README b/etc/README new file mode 100644 index 0000000..404fe9d --- /dev/null +++ b/etc/README @@ -0,0 +1,8 @@ + atis.c - AppleTalk Information Server. It must run as a root process. + nisaux.c - Part of atis.c. Manages the names table. + start-cap-servers - shell script to start CAP servers (call from rc.local) + atalk.local - a sample network configuration file for CAP IPTalk. + etalk.local - a sample network configuration file for CAP UAB/EtherTalk. + +The nisaux routines could stand a good rewriting. + diff --git a/etc/S99appletalk b/etc/S99appletalk new file mode 100644 index 0000000..9ff3f62 --- /dev/null +++ b/etc/S99appletalk @@ -0,0 +1,59 @@ +#!/bin/sh +#(@)/etc/rc3.d/S99appletalk +# startup script for CAP60 servers + +CAPHOME=/usr/local/cap; export CAPHOME +CAPLIBS=/usr/local/lib/cap; export CAPLIBS + +killproc () +{ pid=`/bin/ps -e | grep $1 | awk '{ print $1 }'` + [ "pid" != "" ] && kill $pid +} + +case "$1" in +'start') + echo "Starting Columbia AppleTalk package ..." + +# edit for required zone name + ${CAPHOME}/aarpd le0 Fysik + + ${CAPHOME}/atis > /dev/null 2>&1; sleep 5 + + #${CAPHOME}/snitch -S -f "SPARCserver MP670" + +# mkdir -m 0700 /tmp/at.auth + +# AppleShare for Unix +# ${CAPHOME}/aufs \ +# -U 8 \ +# -l /tmp/CAPShare.log \ +# -n "`uname -n`" \ +# -X /tmp/at.auth \ +# -V /etc/CAP60/afpvols + +# LaserWriter print spooler +# ${CAPHOME}/lwsrv \ +# -N \ +# -a ${CAPLIBS}/ProcSets \ +# -f ${CAPLIBS}/LWFonts \ +# -X /tmp/at.auth + +# check integrity + FILES="/etc/rc0.d/K02appletalk \ + /etc/rc1.d/K02appletalk \ + /etc/rc2.d/K02appletalk \ + /etc/init.d/CAP60" + for FILE in $FILES; do + if [ ! -f $FILE ]; then + ln /etc/rc3.d/S99appletalk $FILE + fi + done + echo "... done." + ;; +'stop') +# killproc lwsr +# killproc aufs + ${CAPHOME}/atis exit > /dev/null 2>&1 + killproc aarpd + ;; +esac diff --git a/etc/atalk.local b/etc/atalk.local new file mode 100644 index 0000000..9863e0b --- /dev/null +++ b/etc/atalk.local @@ -0,0 +1,21 @@ +# +# This is a sample atalk.local file. Comment lines start with #. +# mynet, mynode, myzone refer to network values for the CAP host. +# bridgenet, bridgenode, bridgeIP refer to the hardware gateway. +# NB: bridgenode has to be the lower 8 bits of the IP address. +# nisnet, nisnode point to the host running atis. Normally these +# are the same as mynet and mynode. This line is semi optional. +# asyncnet and asynczone apply to Asynchronous AppleTalk on the +# CAP host. This line is also optional but, if included, *must* be +# the fourth active line (ie: nisnet and nisnode must be present). +# Zone names containing spaces must be quoted with " or ' quotes. +# Zone names containing " or ' can use \" or \' as escapes. +# +# mynet mynode myzone +93.31 18 unimelb-CompSci +# bridgenet bridgenode bridgeIP +93.31 123 128.250.135.123 +# nisnet nisnode +93.31 18 +# asyncnet asynczone +170.170 "unimelb Async" diff --git a/etc/atis.c b/etc/atis.c new file mode 100644 index 0000000..e09ae3a --- /dev/null +++ b/etc/atis.c @@ -0,0 +1,1251 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/06/18 10:51:30 $"; +static char rcsident[] = "$Header: /mac/src/cap60/etc/RCS/atis.c,v 2.11 1996/06/18 10:51:30 djh Rel djh $"; +static char revision[] = "$Revision: 2.11 $"; + +/* + * atis.c - a simple appletalk information server + * + * This provides a simple name information and echo server. + * The NBP it assumes is a slightly extended form (cf. nbp.ext). + * + * Also acts as the RTMP listener for use with Native EtherTalk and + * Kernel EtherTalk for which we also maintain a simple routing table. + * + * Needs some cleaning. A quit signal causes it to dump it's database. + * a HUP signal tells it to reload it. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * July 10, 1986 CCKim Created + * August 2, 1986 CCKim Add dump and load functionality + * December 17, 1986 CCKim Revise to rev1086 of UDP code + * April 28, 1991 djh Add Phase 2 support + * + */ + +#include +#include +#include +#ifndef _TYPES +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef USETIMES +# include +#endif +#ifdef USEVPRINTF +# include +#endif + +#ifdef linux +#define SIGEMT SIGUNUSED +#endif /* linux */ + +/* signals in use */ +#define DUMPSIG sigmask(SIGQUIT) +#define EXITSIG sigmask(SIGTERM) +#define DBUGSIG sigmask(SIGIOT) +#define NDBGSIG sigmask(SIGEMT) +#define LOADSIG sigmask(SIGHUP) + +#ifdef NOSIGMASK +static int allsigs[] = { SIGIOT, SIGHUP, SIGTERM, SIGEMT, SIGQUIT, 0 }; +#else NOSIGMASK +#define ALLSIGS (sigmask(SIGIOT)|sigmask(SIGHUP)|sigmask(SIGTERM)|\ + sigmask(SIGEMT)|sigmask(SIGQUIT)) +#endif NOSIGMASK + +/* logging flags */ +#define L_UERR 0x20 /* want unix error message */ +#define L_EXIT 0x10 /* exit after logging */ +#define L_LVL 0xf /* debug levels */ +#define L_LVLMAX 15 /* maximum level */ + +/* good place to stick the copyright so it shows up in object files */ +private char Columbia_Copyright[] = "Copyright (c) 1986,1987,1988 by The Trustees of Columbia University in the City of New York"; + +private int tempdebugfile = 0; +private int nbpskt = nbpNIS; +private int echoskt = echoSkt; +private int rtmpskt = rtmpSkt; + +char *pidfile; + +extern short lap_proto; /* identifies the "LAP" level */ + +extern u_char bridge_node; +extern u_short this_net, nis_net, bridge_net; + +#ifdef PHASE2 +extern u_short net_range_start, net_range_end; +#endif PHASE2 + +#ifndef ETCDIR +# define ETCDIR "/etc" +#endif + +#ifndef NISDUMPFILE +# define NISDUMPFILE "/usr/tmp/nis.db" +#endif + +#ifndef ATISRUNFILE +# define ATISRUNFILE "/usr/tmp/atis.run" +#endif + +private char *nisdumpfile = NISDUMPFILE; +private char *atisrunfile = ATISRUNFILE; +private char *progname; +import DBUG dbug; + + +/* each tuple is entity name + addrblock + enumerator */ +#define NBPTUPSIZE (sizeof(EntityName)+sizeof(AddrBlock)+1) +/* for nbp, must offset for control word and nbp id */ +#define NUMREPLYMAX ((ddpMaxData-2)/NBPTUPSIZE) +NBPTEntry reply[NUMREPLYMAX]; + +extern int get_debug_level(); +extern int set_debug_level(); + +void nbp_extensions(); +void nbp_reload(); +void nbp_dump(); +void nbp_listener(); +void echo_listener(); +void rtmp_listener(); +void atis_end(); +void atis_debuginc(); +void atis_undebug(); +private int nbpcpy(), c2pkt_ename(), pkt2c_ename(); + +void +nbp_reload() +{ + FILE *fd; + int cnt; + int mask; + +#ifdef NOSIGMASK + sighold_all(); +#else NOSIGMASK + mask = sigblock(ALLSIGS); /* block so we don't get interrupted */ +#endif NOSIGMASK + (void)logit(1, "reloading from %s",nisdumpfile); + nbptab_init(); /* reset tables */ + cnt = 0; + if ((fd = fopen(nisdumpfile,"r")) != NULL) { + cnt = nbptab_load(fd); + (void)fclose(fd); + } else logit(L_UERR|1, "dump file open failed"); + logit(1, "loaded %d entries",cnt); +#ifdef NOSIGMASK + sigrelse_all(); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK + (void)signal(SIGHUP, nbp_reload); +} + +void +nbp_dump() +{ + FILE *fd; + int cnt; + int mask; + +#ifdef NOSIGMASK + sighold_all(); +#else NOSIGMASK + mask = sigblock(ALLSIGS); /* block so we don't get interrupted */ +#endif NOSIGMASK + logit(1, "Dumping to %s",nisdumpfile); + (void)signal(SIGQUIT, SIG_IGN); + cnt = 0; + if ((fd = fopen(nisdumpfile,"w+")) != NULL) { + (void)chmod(nisdumpfile, 0774); + cnt = nbptab_dump(fd); + (void)fclose(fd); + } else logit(L_UERR|1, "dump file (write) open failed"); + logit(1, "Dumped %d entries",cnt); +#ifdef NOSIGMASK + sigrelse_all(); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK + (void)signal(SIGQUIT, nbp_dump); +} + +void +atis_end() +{ +#ifdef linux + if (lap_proto == LAP_KERNEL) { + void rtmp_release(); + rtmp_release(); + } +#endif /* linux */ + logit(0, "exiting"); + (void)unlink(pidfile); + exit(1); +} + +void +atis_undebug() +{ +#ifdef NOSIGMASK + sighold(DBUGSIG); + sighold(NDBGSIG); +#else NOSIGMASK + int mask = sigblock(DBUGSIG|NDBGSIG); +#endif NOSIGMASK + set_debug_level(0); + logit(0, "DEBUGGING OFF"); + if (tempdebugfile) + nologitfile(); + signal(SIGEMT, atis_undebug); +#ifdef NOSIGMASK + sigrelse(DBUGSIG); + sigrelse(NDBGSIG); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK +} + +void +atis_debuginc() +{ + int dlevel; + +#ifdef NOSIGMASK + sighold(DBUGSIG); + sighold(NDBGSIG); +#else NOSIGMASK + int mask = sigblock(DBUGSIG|NDBGSIG); +#endif NOSIGMASK + if (!islogitfile()) { + tempdebugfile++; + logitfileis(atisrunfile, "w+"); + } + if ((dlevel = get_debug_level()) < L_LVLMAX) + set_debug_level(++dlevel); + logit(0, "DEBUG LEVEL %d", dlevel); + signal(SIGIOT, atis_debuginc); +#ifdef NOSIGMASK + sigrelse(DBUGSIG); + sigrelse(NDBGSIG); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK +} + +usage() +{ + fprintf(stderr,"usage: atis -l logfile -Dlevel -d -E -N -R [reload]\n"); + fprintf(stderr," [dump] [exit] [debug] [nodebug]\n"); + exit(1); +} + +setuppidfilename() +{ + int el; + char *p; + + el = strlen(ETCDIR); + pidfile = (char *)malloc(el+sizeof("atis.pid")+5); + if (pidfile == NULL) { + fprintf(stderr,"Can't allocate memory for pid file"); + exit(999); + } + strcpy(pidfile, ETCDIR); + if (el > 0) { + p = pidfile+el - 1; /* point to last char */ + while (el-- && *p == '/') /* strip trailing slashes */ + *p-- = '\0'; + p++; /* make sure at end of string */ + } + strcpy(p, "/atis.pid"); +#ifdef DEBUG + printf("pid file name = %s\n", pidfile); +#endif +} + +setatispid() +{ + FILE *fd; + + if ((fd = fopen(pidfile, "w")) != NULL) { + fprintf(fd, "%d\n",getpid()); + (void)fclose(fd); + } +} + +getatispid() +{ + FILE *fp; + int pid; + + if ((fp = fopen(pidfile, "r")) == NULL) { + logit(L_UERR|0, "No pid file - maybe the daemon wasn't running?"); + return(-1); + } + if (fscanf(fp, "%d\n", &pid) != 1) { + logit(0, "pid file was bad"); + return(-1); + } + return(pid); +} + +#define NSIGACT 5 + +struct sigtab { + char *s_name; + int s_signal; + char *s_action; +} sigtab[NSIGACT] = { + "reload", SIGHUP, "reload nis database", + "dump", SIGQUIT, "dump nis database", + "debug", SIGIOT, "increment debug level", + "nodebug", SIGEMT, "no debug level", + "exit", SIGTERM, "stop running atis" +}; + +handlesigact(s, pid) +char *s; +{ + int i; + struct sigtab *st; + + pid = getatispid(); + for (st = sigtab, i = 0; i < NSIGACT; i++, st++) { + if (strcmp(s, st->s_name) != 0) + continue; + if (kill(pid, st->s_signal) < 0) + logit(0, "Couldn't send %s signal to daemon[%d] - is it running?", + st->s_action, pid); + else + logit(0, "Sent %s signal to daemon[%d]",st->s_action,pid); + return(0); + } + return(-1); +} + +doargs(argc, argv) +int argc; +char **argv; +{ + int pid; + int c, dlevel; + extern char *optarg; + extern int optind; + extern boolean dochecksum; + + while ((c = getopt(argc, argv, "kENRD:d:l:")) != EOF) { + switch (c) { + case 'k': + dochecksum = 0; + break; + case 'N': + nbpskt = 0; + logit(0, "no nis server will be established"); + break; + case 'E': + echoskt = 0; + logit(0, "no echo listener will be established"); + break; + case 'R': + rtmpskt = 0; + logit(0, "no rtmp listener will be established"); + break; + case 'D': + dlevel = atoi(optarg); + if (dlevel > L_LVLMAX) + dlevel = L_LVLMAX; + set_debug_level(dlevel); + break; + case 'd': + dbugarg(optarg); + set_debug_level(1); + break; + case 'l': + logitfileis(optarg, "w"); + break; + } + } + + if (optind == argc) + return; + if ((pid = getatispid()) < 0) { + logit(L_EXIT|0, "Couldn't get pid of daemon - is it running?"); + } + for (; optind < argc; optind++) + if (handlesigact(argv[optind], pid) < 0) + usage(); + exit(0); +} + +disassociate() +{ + int i; + /* disassociate */ + if (fork()) + _exit(0); /* kill parent */ + for (i=0; i < 3; i++) close(i); /* kill */ + (void)open("/",0); +#ifdef NODUP2 + (void)dup(0); /* slot 1 */ + (void)dup(0); /* slot 2 */ +#else NODUP2 + (void)dup2(0,1); + (void)dup2(0,2); +#endif NODUP2 +#ifndef POSIX +#ifdef TIOCNOTTY + if ((i = open("/dev/tty",2)) > 0) { + (void)ioctl(i, TIOCNOTTY, (caddr_t)0); + (void)close(i); + } +#endif TIOCNOTTY +#ifdef xenix5 + /* + * USG process groups: + * The fork guarantees that the child is not a process group leader. + * Then setpgrp() can work, whick loses the controllong tty. + * Note that we must be careful not to be the first to open any tty, + * or it will become our controlling tty. C'est la vie. + */ + setpgrp(); +#endif xenix5 +#else POSIX + (void) setsid(); +#endif POSIX +} + +main(argc, argv) +int argc; +char **argv; +{ + int nbperr = noErr; + int echoerr = noErr; + int rtmperr = noErr; + + set_debug_level(0); + setuppidfilename(); + + progname = argv[0]; + doargs(argc, argv); + + abInit(TRUE); /* init driver */ + nbpInit(); /* init NBP */ + nbptab_init(); + (void)signal(SIGHUP, nbp_reload); + (void)signal(SIGQUIT, nbp_dump); + (void)signal(SIGTERM, atis_end); + (void)signal(SIGIOT, atis_debuginc); + (void)signal(SIGEMT, atis_undebug); + + logit(0, "Reply num max for lkup reply is %d (based on %d)", + NUMREPLYMAX, NBPTUPSIZE); + + if (nbpskt) { + if ((nbperr = DDPOpenSocket(&nbpskt, nbp_listener)) != noErr) + logit(L_UERR|0, "NIS: NIS socket not available: error %d", nbperr); + } + + if (echoskt) { + if ((echoerr = DDPOpenSocket(&echoskt, echo_listener)) != noErr) + logit(L_UERR|0, "ECHO: ECHO socket not available: error %d", echoerr); + } + + if ((lap_proto == LAP_ETALK || lap_proto == LAP_KERNEL) && rtmpskt) { + if ((rtmperr = DDPOpenSocket(&rtmpskt, rtmp_listener)) != noErr) + logit(L_UERR|0, "RTMP: RTMP socket not available: error %d", rtmperr); +#ifdef linux + if (lap_proto == LAP_KERNEL) { + extern void rtmp_timer(); + Timeout(rtmp_timer, 0, 40); + } +#endif /* linux */ + } + + if (echoerr != noErr && nbperr != noErr && rtmperr != noErr) + logit(0|L_EXIT,"Couldn't establish nis, rtmp or echo socket, nothing to do"); + + if (!dbug.db_flgs && (get_debug_level() == 0)) + disassociate(); + + /* store pid where people can see it */ + setatispid(); + logit(0,"pid = %d",getpid()); + + /* sleep for a day (but wake up on events) */ + + for (;;) + abSleep(sectotick(60*60*24), TRUE); + +} + + +/* + * This is the NBP NIS listener + * +*/ +void +nbp_listener(skt, type, nbp, len, addr) +u_char skt; +u_char type; +NBP *nbp; +int len; +AddrBlock *addr; +{ + int cnt; + NBPTEntry looks; + + if (len < nbpMinSize || type != ddpNBP) { + logit(3, "Packet too small or bad packet type"); + return; + } + /* technically, we should check to see if it is ourselves and not */ + /* respond if so, but it is convient to do so and is part of the */ + /* nbp extensions */ + switch (nbp->control) { + default: + case nbpLkUpReply: + logit(3, "Dropping nbp of type %d",nbp->control); + return; /* drop the packet */ + case nbpTickle: + case nbpRegister: + case nbpDelete: + case nbpBrRq: + case nbpLkUp: + break; + } + /* at this point we know we have a lookup */ + logit(2, "Got nbp %d lkup from net %d.%d, node %d, skt %d", + nbp->tcnt, ntohs(addr->net)>>8, ntohs(addr->net)&0xff, + addr->node, addr->skt); + /* should only get one entity in incoming packet */ + switch (nbp->control) { + case nbpBrRq: /* treat as lkup */ + case nbpLkUp: + cnt = nbpcpy(&looks, 0, 1, nbp, (int)nbp->tcnt, FALSE); + answer(&looks, &looks.addr, nbp->id); + break; + case nbpTickle: + /* tickle may be implemented in the future */ + /* tickle may take list of entities in future */ + break; + case nbpRegister: + case nbpDelete: + cnt = nbpcpy(&looks, 0, 1, nbp, 1, FALSE); + nbp_extensions(&looks, addr, nbp, len); + break; + } +} + + +/* + * reply to a NBP LkUp request + * +*/ +answer(en, addr, id) +NBPTEntry *en; +AddrBlock *addr; +u_char id; +{ + NBP nbp; + ABusRecord ddp; + ddpProto *ddpr; + int cnt, nsize, tsize; + NBPTuple *tp; + int i, start = 0; + int mask; + +#ifdef ISO_TRANSLATE + EntityName isoEn; + void cMac2ISO(); + + bcopy(en->ent.objStr.s, isoEn.objStr.s, sizeof(Str32)); + bcopy(en->ent.typeStr.s, isoEn.typeStr.s, sizeof(Str32)); + bcopy(en->ent.zoneStr.s, isoEn.zoneStr.s, sizeof(Str32)); + cMac2ISO(isoEn.objStr.s); + cMac2ISO(isoEn.typeStr.s); + cMac2ISO(isoEn.zoneStr.s); + logit(2, "Looking for entities to answer a lookup with %s:%s@%s", + isoEn.objStr.s, isoEn.typeStr.s, isoEn.zoneStr.s); +#else ISO_TRANSLATE + logit(2, "Looking for entities to answer a lookup with %s:%s@%s", + en->ent.objStr.s, en->ent.typeStr.s, en->ent.zoneStr.s); +#endif ISO_TRANSLATE +#ifdef NOSIGMASK + sighold(DUMPSIG); + sighold(LOADSIG); +#else NOSIGMASK + mask = sigblock(DUMPSIG|LOADSIG); /* prevent inconsistency */ +#endif NOSIGMASK + while ((cnt = nbpt_find(&en->ent, &start, reply, NUMREPLYMAX)) > 0) { + if (cnt > 255) { + logit(3, "Dropping count to 255 from %d",cnt); + cnt = 255; + } + logit(2, "Answering lookup with %d entities",cnt); + nbp.tcnt = cnt; + nbp.control = nbpLkUpReply; + nbp.id = id; + for (i = 0, tsize = 0, tp = nbp.tuple; i < cnt; i++) { + tp->enume = (byte)reply[i].enume; + bcopy((caddr_t)&reply[i].addr, (caddr_t)&tp->addr, sizeof(AddrBlock)); + nsize = c2pkt_ename(&reply[i].ent, tp->name); + nsize += sizeof(AddrBlock) + sizeof(tp->enume); + tsize += nsize; + tp = (NBPTuple *)(((char *)tp) + nsize); + } + ddpr = &ddp.proto.ddp; /* handle on DDP protocol args */ + ddpr->ddpAddress = *addr; + ddpr->ddpSocket = nbpskt; + ddpr->ddpType = ddpNBP; + ddpr->ddpDataPtr = (u_char *) &nbp; + ddpr->ddpReqCount = 2+tsize; /* control + id + rest */ + DDPWrite(&ddp,FALSE); /* write it out... */ + } +#ifdef NOSIGMASK + sigrelse(DUMPSIG); + sigrelse(LOADSIG); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK +} + + +/* + * Handle the extended NBP functions + * +*/ +void +nbp_extensions(nbptent, addr, nbp, len) +NBPTEntry *nbptent; +AddrBlock *addr; +NBP *nbp; +int len; +{ + ABusRecord abr; + int mask; + int rc; + char objStr[33], typeStr[33], zoneStr[33]; + + switch (nbp->control) { + case nbpRegister: + logit(2, "Register from net %3d.%02d node %d skt %d ", + ntohs(addr->net)>>8, ntohs(addr->net)&0xff,addr->node, addr->skt); + if (get_debug_level() > 1) { + objStr[32] = typeStr[32] = zoneStr[32] = '\0'; /* tie off? */ + strcpy(objStr, (char *)nbptent->ent.objStr.s); + strcpy(typeStr, (char *)nbptent->ent.typeStr.s); + strcpy(zoneStr, (char *)nbptent->ent.zoneStr.s); + logit(2, "\tfor %s:%s@%s, net %3d.%02d node %d skt %d ", + objStr, typeStr, zoneStr, + ntohs(nbptent->addr.net)>>8, + ntohs(nbptent->addr.net)&0xff, nbptent->addr.node, + nbptent->addr.skt); + } + + +#ifdef NOSIGMASK + sighold(DUMPSIG); + sighold(LOADSIG); +#else NOSIGMASK + mask = sigblock(DUMPSIG|LOADSIG); /* prevent inconsistency */ +#endif NOSIGMASK + rc = nbptab_insert(addr, &nbptent->addr, &nbptent->ent); +#ifdef NOSIGMASK + sigrelse(DUMPSIG); + sigrelse(LOADSIG); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK + logit(2, "Register return code %d",rc); + /* return address */ + bcopy((caddr_t)&nbptent->addr, (caddr_t)&abr.proto.ddp.ddpAddress, + sizeof(AddrBlock)); + break; + case nbpDelete: + if (get_debug_level() > 1) { + objStr[32] = typeStr[32] = zoneStr[32] = '\0'; /* tie off? */ + strcpy(objStr, (char *)nbptent->ent.objStr.s); + strcpy(typeStr, (char *)nbptent->ent.typeStr.s); + strcpy(zoneStr, (char *)nbptent->ent.zoneStr.s); + logit(2, "Delete %32s:%32s@%32s, net %3d.%02d node %d skt %d", + objStr, typeStr, zoneStr, ntohs(addr->net)>>8, + ntohs(addr->net)&0xff,addr->node, addr->skt); + } +#ifdef NOSIGMASK + sighold(DUMPSIG); + sighold(LOADSIG); +#else NOSIGMASK + mask = sigblock(DUMPSIG|LOADSIG); /* prevent inconsistency */ +#endif NOSIGMASK + rc = nbptab_delete(addr, &nbptent->ent) ; +#ifdef NOSIGMASK + sigrelse(DUMPSIG); + sigrelse(LOADSIG); +#else NOSIGMASK + sigsetmask(mask); +#endif NOSIGMASK + logit(2, "Delete returns code %d",rc); + abr.proto.ddp.ddpAddress = *addr; /* return address */ + break; + case nbpTickle: + logit(2, "Got a tickle"); + return; + default: + logit(2, "Unknown NBP type %d",nbp->control); + return; + } + nbp->control = nbpStatusReply; + nbp->tcnt = rc; + abr.proto.ddp.ddpSocket = nbpskt; + abr.proto.ddp.ddpType = ddpNBP; + abr.proto.ddp.ddpDataPtr = (u_char *)nbp; + abr.proto.ddp.ddpReqCount = len; + DDPWrite(&abr, FALSE); +} + +/* + * This is the Echo Protocol listener + * +*/ +void +echo_listener(skt, type, pkt, len, addr) +u_char skt; +u_char type; +char *pkt; +int len; +AddrBlock *addr; +{ + u_char ec; /* echo command */ + ABusRecord abr; + + if (type != ddpECHO) { + logit(1, "Got non-echo pkt in echolistener"); + return; /* drop packet */ + } + ec = (u_char)*pkt; + if (ec != echoRequest) { + printf("got %u when expecting echorequest\n",ec); + return; /* drop packet */ + } + *pkt = echoReply; + abr.proto.ddp.ddpAddress = *addr; + abr.proto.ddp.ddpSocket = echoskt; + abr.proto.ddp.ddpType = ddpECHO; + abr.proto.ddp.ddpDataPtr = (u_char *)pkt; + abr.proto.ddp.ddpReqCount = len; + DDPWrite(&abr, FALSE); +} + +/* + * This is the RTMP listener + * + */ + +void +rtmp_listener(skt, type, pkt, len, addr) +u_char skt; +u_char type; +u_char *pkt; +int len; +AddrBlock *addr; +{ + u_char rc; /* rtmp command */ + time_t now; + u_short net; + ABusRecord abr; + static int goodness = 0; + static AddrBlock last_addr; + static time_t last_time = 0; + static AddrBlock current_addr; + static time_t current_time = 0; + static int current_goodness = -1; +#ifdef PHASE2 + u_short new_net_range_start, new_net_range_end, increment; +#endif PHASE2 + + if (type != ddpRTMP) { + logit(1, "Got non-rtmp pkt in rtmplistener"); + return; /* drop packet */ + } + + net = htons((pkt[0] << 8) | pkt[1]); + + logit(5, "Got RTMP pkt net %d from %d.%d", + ntohs(net), ntohs(addr->net), addr->node); + + if (bridge_net == 0 && net != 0) { + addr->net = net; + SetBridgeAddress(addr); + logit(1, "Gleaned network number %d from bridge %d",ntohs(net),addr->node); + } + +#ifdef PHASE2 + if (ntohs(net)>=ntohs(net_range_start) && ntohs(net)<=ntohs(net_range_end)) { +#else PHASE2 + if (net == addr->net) { +#endif PHASE2 +/* + * Compute the goodness of this router. We prefer routers that do + * split horizon, and among them, those that have the most routes. + * The goodness is thus the number of routes, and 0 for non-split horizon. + * At Rutgers the effect is to prefer cisco (which does split horizon) + * over Kinetics, and to pick the cisco that is the most "central". + * This should tend to produce the best routes. It will probably + * work reasonably at most other places as well. We expire an old + * router after 15 sec. This allows us to miss one update, but no + * more. Apple wants us to use the most recent router we heard from, + * but that allows no selectivity at all, and also tends to lead to + * inconsistent results. Unfortunately Apple specifies that when + * several routes have the same metric, routers choose the most recently + * heard. This leads to rapidly changing entries. The result can be + * small changes in goodness. If two routers are very close, we could + * end up going between them. If this is a problem for you, redefine + * MARGIN to be 10% or more of the current_goodness. + * + * Be aware that very good routers can send a full RTMP packet followed + * by a small "overflow" RTMP packet, be careful not to overreact. + * + */ + + /* + * go to beginning of routing triples + * + */ +#ifdef PHASE2 + pkt += 4; + len -= 4; + if (pkt[0] == 0 && pkt[1] == 0 && pkt[2] == 0x82) { /* non-extended net */ + new_net_range_start = net; /* net byte order */ + new_net_range_end = net; /* net byte order */ + } else if (pkt[2] & 0x80 && pkt[5] == 0x82) { /* extended network */ + new_net_range_start = htons((pkt[0] << 8) | pkt[1]); + new_net_range_end = htons((pkt[3] << 8) | pkt[4]); + pkt += 3; + len -= 3; + } else { + logit(2, "RTMP: unknown format packet, dropped!"); + return; + } + pkt += 3; + len -= 3; +#ifdef notdef + /* ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ */ + /* we should be doing this, in case the router was down when */ + /* we started up. However, doing so will proably confuse our */ + /* clients too much. We should also do it to ensure that the */ + /* correct zone multicast address gets enabled on the intrfc */ + /* ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ */ + if (net_range_start == 0x00 && net_range_end == htons(0xfffe)) { + this_net = nis_net = net_range_start = new_net_range_start; + net_range_end = new_net_range_end; + SetNetRange(net_range_start, net_range_end); + } +#endif notdef +#else PHASE2 + pkt += 7; + len -= 7; +#endif PHASE2 + +#ifdef linux + /* + * build RTMP table for kernel + * + */ + if (lap_proto == LAP_KERNEL) { + void rtmp_data(); + rtmp_data(addr->node, addr->net, pkt, len); + } +#endif /* linux */ + + now = time(NULL); + + /* + * reset goodness if more than 2 seconds + * old or this is from a different router. + * + */ + if (now > last_time+2 + || last_addr.net != addr->net + || last_addr.node != addr->node) + goodness = 0; + + /* + * loop over routing triples, counting them. + * If we find the router network, this router + * isn't doing split horizon. + * + */ + while (len > 0) { + net = htons((pkt[0] << 8) | pkt[1]); /* pkt data is not aligned */ + if (net == addr->net) { /* not doing split horizon */ + logit (10, "No split horizon in router %d.%d", + ntohs(addr->net), addr->node); + goodness = 0; + } + goodness++; +#ifdef PHASE2 + if (pkt[2] & 0x80 && pkt[5] == 0x82) + increment = 3; + else + increment = 0; + len -= increment; + pkt += increment; +#endif PHASE2 + len -= 3; + pkt += 3; + } + + logit (8, "Router %d.%d has goodness %d", + ntohs(addr->net), addr->node, goodness); + + last_time = now; + last_addr.net = addr->net; + last_addr.node = addr->node; + +#define MARGIN (current_goodness / 20) /* currently 5% */ + + /* + * this router knows more than 5% more + * routes than the current one, adopt it + * (may be subsequent packets from current) + * + */ + if (goodness > (current_goodness+MARGIN)) { + if (addr->node != current_addr.node + || addr->net != current_addr.net) { + SetBridgeAddress(addr); + logit (1, "New default router %d.%d, goodness %d -> %d", + ntohs(addr->net), addr->node, current_goodness, goodness); + current_addr.node = addr->node; + current_addr.net = addr->net; + } + current_goodness = goodness; + current_time = now; + return; + } + + /* + * same router, update heard-from time + * + */ + if (addr->node == current_addr.node + && addr->net == current_addr.net) { + logit (1, "Current default router %d.%d, goodness %d", + ntohs(addr->net), addr->node, current_goodness); + current_time = now; + return; + } + + /* + * different router, and we haven't heard + * from our current router for more than + * 15 seconds, adopt it + * + */ + if ((addr->node != current_addr.node + || addr->net != current_addr.net) + && (now - current_time) > 15) { + SetBridgeAddress(addr); + logit (1, "New default router %d.%d (old expired)", + ntohs(addr->net), addr->node); + current_addr.node = addr->node; + current_addr.net = addr->net; + current_goodness = goodness; + current_time = now; + return; + } + } +} + +/* + * The following should be integrated into ABNBP.C someday + * (Looks like that someday will never come :-). +*/ + +/* + * private int nbpcpy(en, enc, start, nbp, nbptcnt, unique) + * + * nbpcpy copies the entities in nbp to the NBP Table entry array + * pointed to by en. Maximum number of entities is enc. Start specifies + * where in the array to start. Unique is used as a flag: if true + * then make sure the incoming items are unique (e.g. don't duplicate + * items in the table pointed to by en). + * + * at end number of entries inserted is returned + * +*/ +private int +nbpcpy(entab, enstart, entabmax, nbp, nbptcnt, unique) +NBPTEntry *entab; +int enstart; +int entabmax; +NBP *nbp; +int nbptcnt; +int unique; +{ + NBPTuple *ep; + int i, tcount, len; + NBPTEntry curr; + + /* Add NBP tuples to user's data structure */ + /* make sure curr's ent is empty */ + bzero((caddr_t)&curr.ent, sizeof(curr.ent)); + for (i=enstart, tcount=nbptcnt, ep = nbp->tuple; + tcount != 0 && i < entabmax; + tcount--) { + bcopy((caddr_t)&ep->addr,(caddr_t)&curr.addr, sizeof(AddrBlock)); + bcopy((caddr_t)&ep->enume,(caddr_t)&curr.enume, sizeof(curr.enume)); + len = pkt2c_ename(ep->name,&curr.ent); + ep = (NBPTuple *) ((char *) ep+(len+sizeof(AddrBlock)+1)); + i += nbpinsertentry(entab, i, &curr, unique); + } + return(i-enstart); +} + + +/* insert entry into specified NBP Table. Check previous entries for */ +/* conflict - if conflict - then update entry if update flag is on */ +/* ow. insert at specified point */ +/* returns 1 if inserted item, zero o.w. - no errors possible */ +nbpinsertentry(entab, point, nbptentry, update) +NBPTEntry entab[]; +int point; +NBPTEntry *nbptentry; +boolean update; +{ + NBPTEntry *cetp = entab; + int i; + + if (update) { /* update==false ==> always insert */ + for (i=0; i < (point-1); i++) { + if (bcmp((caddr_t)&cetp->addr, (caddr_t)&nbptentry->addr, + sizeof(cetp->addr)) == 0) + if (cetp->enume == nbptentry->enume) { + bcopy((caddr_t)&nbptentry->ent,(caddr_t)&cetp->ent, + sizeof(cetp->ent)); + return(0); /* done */ + } + } + } + bcopy((caddr_t)nbptentry, (caddr_t)cetp, sizeof(NBPTEntry)); + return(1); +} + + + +/* + * Private int c2pkt_ename(EntityName *cn, u_char *pn) + * + * Copy entity name from c form into contiguous Apple Pascal + * form (packet form). + * + * return: length of pascal form entity name + * + */ + +private int +c2pkt_ename(cn,pn) +u_char *pn; +EntityName *cn; +{ + int i, cnt; + byte *s; + byte *pc; + + cnt = 0; + for (s = cn->objStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) { + *pn = *s; + if (*s == '\0') + break; + } + if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */ + i = ENTITYSIZE; /* too large: turncated to 32 chars */ + *pc = i; + cnt += (i+1); + for (s = cn->typeStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) { + *pn = *s; + if (*s == '\0') + break; + } + if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */ + i = ENTITYSIZE; /* too large: turncated to 32 chars */ + *pc = i; + cnt += (i+1); + for (s = cn->zoneStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) { + *pn = *s; + if (*s == '\0') + break; + } + if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */ + i = ENTITYSIZE; /* too large: turncated to 32 chars */ + *pc = i; + cnt += (i+1); + return(cnt); /* return number of bytes used */ +} + +/* + * Private int pkt2c_enames(u_char *pn, EntityName *cn); + * + * Copy entity names from packet form (abutting Apple Pascal + * strings) to c form into structure of type EntityName. + * + * return: the length of the packed string. + * + */ + +private int +pkt2c_ename(pn,cn) +u_char *pn; +EntityName *cn; +{ + int ol,tl,zl; + + ol = *pn; /* length of object */ + tl = *(pn+ol+1); /* length of type */ + zl = *(pn+ol+tl+2); /* length of zone */ + if (ol > ENTITYSIZE || tl > ENTITYSIZE || zl > ENTITYSIZE) { + logit(3,"pkt2c_entity_names: invalid length!"); + return(0); + } + cpyp2cstr(cn->objStr.s,pn); /* copy them... */ + cpyp2cstr(cn->typeStr.s,pn+ol+1); + cpyp2cstr(cn->zoneStr.s,pn+ol+tl+2); + return(ol+tl+zl+3); /* return length */ +} + +#ifdef notdef + +/* + * print message - use vprintf whenever possible (solves the problem + * of using the varargs macros -- you must interpret the format). + * This is something all machine should, but don't have :-) + */ + +private FILE *lfp = stderr; + + +#ifndef USEVPRINTF +/* Bletch - gotta do it because pyramids don't work the other way */ +/* (using _doprnt and &args) and don't have vprintf */ +/* of course, there will be something that is just one arg larger :-) */ +/*VARARGS1*/ +logit(level, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +int level; +char *fmt; +#else +logit(va_alist) +va_dcl +#endif +{ + long time(), tloc; + char *timestr; +#ifdef USEVPRINTF + register char *fmt; + va_list args; + int level; +#endif + int saveerr; + extern int errno; + extern int sys_nerr; + extern char *sys_errlist[]; + + if (lfp == NULL) /* no logging? */ + return; + + saveerr = errno; +#ifdef USEVPRINTF + va_start(args); + level = va_arg(args, int); + fmt = va_arg(args, char *); +#endif + + if (dlevel < (level & L_LVL)) + return; + (void)time(&tloc); + timestr = (char *)ctime(&tloc); + timestr[24] = '\0'; /* hokey */ + fprintf(lfp,"atis: %s ",timestr); + +#ifdef USEVPRINTF + vfprintf(lfp, fmt, args); + va_end(args); +#else + fprintf(lfp, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); +#endif + if (level & L_UERR) { + if (saveerr < sys_nerr) + fprintf(lfp, ": %s", sys_errlist[saveerr]); + else + fprintf(lfp, ": error %d\n", saveerr); + } + putc('\n', lfp); + fflush(lfp); + if (level & L_EXIT) + exit(1); +} + +islogitfile() +{ + if (lfp == stderr) + return(FALSE); + return(lfp != NULL); +} + +logitfileis(filename, mode) +char *filename; +char *mode; +{ + FILE *fp; + + if ((fp = fopen(filename, mode)) != NULL) { + logit(0, "log file name %s", filename); + } else { + logit(0|L_UERR, "couldn't open logfile %s", filename); + } + lfp = fp; /* reset */ +} + +nologitfile() +{ + if (lfp && lfp != stderr) + fclose(lfp); +} +#endif notdef + +#ifdef NOSIGMASK + +sighold_all() +{ + int i; + + for (i = 0; allsigs[i]; ++i) + sighold(allsigs[i]); +} + +sigrelse_all() +{ + int i; + + for (i = 0; allsigs[i]; ++i) + sigrelse(allsigs[i]); +} + +#endif NOSIGMASK diff --git a/etc/aufsIPFilter b/etc/aufsIPFilter new file mode 100644 index 0000000..cc582ee --- /dev/null +++ b/etc/aufsIPFilter @@ -0,0 +1,26 @@ +# +# aufs/AppleShareIP Address Access Filter List +# +# NB: The filter file format is compatible with that used by the ARNS +# Remote Access package (http://www.cs.mu.OZ.AU/appletalk/atalk.html) +# +# The filter list consists of a single character mode, an IP mask and +# optional IP address. If the latter is included, the mask is applied +# to the incoming IP address and tested against the provided address. +# Otherwise the incoming IP address must be unchanged by the mask. +# +# Modes: +# +# * IP_MASK [ IP_ADDR ] permit access +# + IP_MASK [ IP_ADDR ] permit access +# - IP_MASK [ IP_ADDR ] deny access +# +# +# any mac on a specific subnet ++ 255.255.255.0 192.43.207.0 +# connections from ariel +* 128.250.255.255 128.250.20.3 +# anybody on campus ++ 128.243.255.255 +# nobody else +- 255.255.255.255 diff --git a/etc/etalk.local b/etc/etalk.local new file mode 100644 index 0000000..997e68f --- /dev/null +++ b/etc/etalk.local @@ -0,0 +1,20 @@ +# +# EtherTalk dynamic configuration data +# This file is only provided as a sample of what to expect. +# UAB and Native EtherTalk rewrite this file (for use by CAP) when +# they have discovered sufficient information about connected networks. +# +# Last update: Sat Dec 29 14:46:45 1990 +# +# Generated by UAB +# +thisNet 99.116 +thisNode 69 +thisZone "unimelb-CompSci" +bridgeNet 99.116 +bridgeNode 69 +bridgeIP 127.0.0.1 +nisNet 99.116 +nisNode 69 +asyncNet 170.170 +asyncZone "unimelb-Async" diff --git a/etc/kill-cap-servers b/etc/kill-cap-servers new file mode 100644 index 0000000..a946ffc --- /dev/null +++ b/etc/kill-cap-servers @@ -0,0 +1,18 @@ +#! /bin/sh +TEMP=/tmp/kcs$$ +trap "rm -f $TEMP" 1 2 3 15 +ps agx | egrep ':[0-9][0-9]( | [^ ]*/)(atalkrd|atis|aufs|lwsrv|lwrename|printqueue)( |$)' > $TEMP +cat $TEMP +echo -n 'Kill [ny]? ' +read reply junk +if [ X${reply-n} = Xy ] ; then + echo Killing... + AUFS=`egrep ':[0-9][0-9]( | [^ ]*/)aufs( |$)' $TEMP | awk '{print $1}'` + if [ x"$AUFS" != x ]; then + kill -HUP $AUFS + fi + kill `egrep -v ':[0-9][0-9]( | [^ ]*/)aufs( |$)' $TEMP | awk '{print $1}'` +else + echo Cancelled... +fi +rm -f $TEMP diff --git a/etc/list-cap-servers b/etc/list-cap-servers new file mode 100644 index 0000000..f260e85 --- /dev/null +++ b/etc/list-cap-servers @@ -0,0 +1,2 @@ +#! /bin/sh +ps agx | egrep ':[0-9][0-9]( | [^ ]*/)(atalkrd|atis|aufs|lwsrv|lwrename|printqueue)( |$)' diff --git a/etc/makefile b/etc/makefile new file mode 100644 index 0000000..ad4fe07 --- /dev/null +++ b/etc/makefile @@ -0,0 +1,51 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 13:59:59 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +DESTDIR=/usr/local/cap +ETCDIR=/etc +CAPLIB=-lcap +I=/usr/include +# for other libraries (like BSD on hpux) +SLIB= + +PROGS=atis + +# aufs.c definitions: USEVPRINTF - use vprintf in logging +ATISDEFS=-DUSEVPRINTF + +# Make sure to define needgetopt if your system doesnt have it or +# just set GETOPT=att_getopt.o (or to a getopt of your own liking) +GETOPT= + +all: ${PROGS} + +atis: atis.o nisaux.o ${GETOPT} + ${CC} ${LFLAGS} -o atis atis.o nisaux.o ${GETOPT} ${CAPLIB} ${SLIB} + +atis.o: $I/netat/abnbp.h + ${CC} ${CFLAGS} ${ATISDEFS} -DETCDIR=\"${ETCDIR}\" -c atis.c + +nisaux.o: $I/netat/abnbp.h + +att_getopt.c: + ln -s ../extras/att_getopt.c + +install: ${PROGS} + -strip ${PROGS} + ${INSTALLER} ${PROGS} ${DESTDIR} + +clean: + -rm -f atis *.o core att_getopt.c *~ + +dist: + @cat todist + diff --git a/etc/nisaux.c b/etc/nisaux.c new file mode 100644 index 0000000..c624ab7 --- /dev/null +++ b/etc/nisaux.c @@ -0,0 +1,338 @@ +/* + * $Author: djh $ $Date: 1996/05/18 14:27:15 $ + * $Header: /mac/src/cap60/etc/RCS/nisaux.c,v 2.7 1996/05/18 14:27:15 djh Rel djh $ + * $Revision: 2.7 $ + * + */ + +/* + * nisaux.c - name table management routines + * + * Follow are a set of simple minded name table management routines for + * the unix name server. + * + * Needs some cleaning. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Edit History: + * + * July 10, 1986 CCKim Created + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * Network Table - internal use + * + * We do NOT assume that the only entries in this table will be for this + * node. + * + */ + +typedef struct { + AddrBlock addr; + AddrBlock nc_addr; /* address of nbp in client */ + u_char enume; + EntityName ent; +} SNBPTEntry; + +#define NUMNVE 96 /* careful - no more than 255 */ + +SNBPTEntry nbptable[NUMNVE]; +private int numnve = 0; +private int newenum = 0; + +extern u_char *GetMyZone(); + +nbptab_init() +{ + numnve = 0; +} + +nbptab_insert(addr, nbpaddr, en) +AddrBlock *addr; +AddrBlock *nbpaddr; +EntityName *en; +{ + int i; + SNBPTEntry *nve; + + if (numnve == NUMNVE) { + logit(4, "register: nbptab overflow with %s:%s@%s", + en->objStr.s, en->typeStr.s, en->zoneStr.s); + return(nbpSR_overflow); + } + + if (en->objStr.s[0] == '=' || en->typeStr.s[0] == '=') { + logit(4, "register: bad entity: wildcards in object or type %s:%s@%s", + en->objStr.s, en->typeStr.s, en->zoneStr.s); + return(nbpSR_access); + } + /* only allow name registration of local zone */ + if (en->zoneStr.s[0] == '=' && en->zoneStr.s[1] == '\0') { + logit(4, "register: bad zone name '=' in %s:%s@%s", + en->objStr.s, en->typeStr.s, en->zoneStr.s); + return(nbpSR_access); + } + if (!(en->zoneStr.s[0] == '*' && en->zoneStr.s[1] == '\0')) + if (strcmpci((char *)en->zoneStr.s, GetMyZone()) != 0) { + logit(4, "register: our zone is %s, bad register attempted in zone %s", + GetMyZone(), en->zoneStr.s); + return(nbpSR_access); + } + +#ifdef PHASE2 + strcpy((char *)en->zoneStr.s, GetMyZone()); /* keep our name in table */ +#else PHASE2 + strcpy((char *)en->zoneStr.s, "*"); /* replace zone name with * in records */ +#endif PHASE2 + + /* scan the list - see if we should be in the list at all. */ + for (newenum = 0, i = 0; i < numnve; i++) { + nve = &nbptable[i]; + if (strncmp((char *)en->objStr.s, (char *)nve->ent.objStr.s, 32) == 0 && + strncmp((char *)en->typeStr.s, (char *)nve->ent.typeStr.s, 32) == 0 && + strncmp((char *)en->zoneStr.s, (char *)nve->ent.zoneStr.s, 32) == 0) + if (bcmp((caddr_t)addr, (caddr_t)&nve->addr, sizeof(AddrBlock)) == 0) { + logit(4, "register: %s:%s@%s already registered: updating", + en->objStr.s, en->typeStr.s, en->zoneStr.s); + /* update */ + bcopy((caddr_t)nbpaddr, (caddr_t)&nve->nc_addr, sizeof(AddrBlock)); + return(0); /* in list, but okay */ + } + else { + logit(4, "register: %s:%s@%s already registered, different address", + en->objStr.s, en->typeStr.s, en->zoneStr.s); + return(nbpSR_access); /* name already in list, diff addr */ + } + } + nve = &nbptable[numnve]; + nve->enume = (u_char)numnve; /* assumes less than 255 */ + bcopy((caddr_t)addr, (caddr_t)&nve->addr, sizeof(AddrBlock)); + /* update */ + bcopy((caddr_t)nbpaddr, (caddr_t)&nve->nc_addr, sizeof(AddrBlock)); + bcopy((caddr_t)en, (caddr_t)&nve->ent, sizeof(EntityName)); + numnve++; + logit(4, "register: NVE count is %d", numnve); + return(nbpSR_noErr); +} + +nbptab_delete(addr, nve) +AddrBlock *addr; +EntityName *nve; +{ + int i; + +#ifdef PHASE2 + if (nve->zoneStr.s[0] == '*' && nve->zoneStr.s[1] == '\0') + strcpy((char *)nve->zoneStr.s, GetMyZone()); /* we kept our zone in table */ +#endif PHASE2 + for (i=0; i < numnve; i++) { + if (bcmp((caddr_t)nve, (caddr_t)&nbptable[i].ent, sizeof(EntityName)) == 0) + break; /* found it */ + } + if (i==numnve) { + logit(4, "deregister: name %s:%s@%s not found", + nve->objStr.s, nve->typeStr.s, nve->zoneStr.s); + return(nbpSR_nsn); /* err: nbpNotFound */ + } + if (bcmp((caddr_t)addr,(caddr_t)&nbptable[i].nc_addr,sizeof(AddrBlock))!=0) { + logit(4, "deregister: %s:%s@%s access error -- address mismatch", + nve->objStr.s, nve->typeStr.s, nve->zoneStr.s); + return(nbpSR_access); + } + logit(4, "deregister: %s:%s@%s removed", + nve->objStr.s, nve->typeStr.s, nve->zoneStr.s); + if (i != (numnve - 1) && i != (NUMNVE - 1)) { + bcopy((caddr_t)&nbptable[i+1], (caddr_t)&nbptable[i], + sizeof(NBPTEntry)*((numnve-1) - i)); + } + numnve--; + logit(4, "deregister: NVE count is %d", numnve); + return(nbpSR_noErr); +} + +nbpt_find(sent, start, reptab, reptabcnt) +EntityName *sent; +int *start; +NBPTEntry *reptab; +int reptabcnt; +{ + SNBPTEntry *p = nbptable; + EntityName *pent; + int nbpMatch(); + int i, cnt; + + if ((*start) < numnve) { + logit(4, "look: starting at nbptab entry %d", *start); + p = nbptable+(*start); + } else { + logit(4, "look: start index %d beyond range of table", *start); + return(0); /* nothing then */ + } + + /* If not in local zone, then forget it */ + if (!((sent->zoneStr.s[0] == '*' && sent->zoneStr.s[1] == '\0') || + (sent->zoneStr.s[0] == '\0') || /* zero length string is "*" */ + strcmpci((char *)sent->zoneStr.s, GetMyZone()) == 0)) { + logit(4, "look: name %s:%s@%s not in local zone, ignoring", + sent->objStr.s, sent->typeStr.s, sent->zoneStr.s); + return(0); + } + + for (i=(*start),cnt=0; i < numnve && reptabcnt > 0; i++, p++) { + pent = &p->ent; + logit(5, "look: checking %s:%s@%s", + pent->objStr.s, pent->typeStr.s, pent->zoneStr.s); +#ifdef notdef + if (!(sent->objStr.s[0] == '=' && sent->objStr.s[1] == '\0') && + strcmpci((char *)sent->objStr.s,(char *)pent->objStr.s) != 0) { + logit(5, "look: object mismatch"); + continue; + } + if (!(sent->typeStr.s[0] == '=' && sent->typeStr.s[1] == '\0') && + strcmpci((char *)sent->typeStr.s,(char *)pent->typeStr.s) != 0) { + logit(5, "look: type mismatch"); + continue; + } +#endif notdef + if (!nbpMatch(sent->objStr.s, pent->objStr.s)) { + logit(5, "look: object mismatch"); + continue; + } + if (!nbpMatch(sent->typeStr.s, pent->typeStr.s)) { + logit(5, "look: type mismatch"); + continue; + } + logit(5, "look: match at addr, match count is %d", cnt); + reptab->addr = p->addr; /* copy in entry */ + reptab->ent = p->ent; + reptab->enume = p->enume; + cnt++; + reptab++; /* move to next answer */ + reptabcnt--; + } + *start = i; + return(cnt); +} + +private boolean +getentry(fd, nve) +FILE *fd; +SNBPTEntry *nve; +{ + char str[256]; + int osl, tsl, zsl; + int kipnet, kipsubnet; + int node, skt; + int enume; + char *cp; + + do { + if (fgets(str, 256, fd) == NULL) + return(TRUE); + if (str[0] == '#' || str[0] == '\n') + continue; + if ((cp = (char *)index(str, '\n')) != NULL) + *cp = '\0'; /* delete the newline char */ + sscanf(str, "net=%d.%d, node=%d, skt=%d, enum=%d, !%d!%d!%d! ? ", + &kipnet, &kipsubnet, &node, &skt, &enume, + &osl, &tsl, &zsl); + nve->addr.node = node; + nve->addr.skt = skt; + nve->addr.net = htons(tokipnet(kipnet, kipsubnet)); + if (osl < 0 || tsl < 0 || zsl < 0) { + logit(6, "nis load: entry has negative lengths!"); + continue; + } + + if ((cp = (char *)index(str, '?')) == NULL) { + logit(6, "nis load: Can't find ? delimiter, bad file"); + continue; + } + cp += 2; /* move past '?' and space */ + if (strlen(cp) < (osl+tsl+zsl+2)) { + logit(6, "nis load: name length exceed available data"); + continue; + } + bzero((caddr_t)&nve->ent, sizeof(nve->ent)); + strncpy((char *)nve->ent.objStr.s, cp, min(32, osl)); /* get object */ + cp += (osl+1); /* move past object and : */ + strncpy((char *)nve->ent.typeStr.s, cp, min(32, tsl)); /* get object */ + cp += (tsl+1); /* move past object and @ */ +#ifdef PHASE2 + strncpy((char *)nve->ent.zoneStr.s, cp, sizeof(nve->ent.zoneStr.s)); +#else PHASE2 + if (zsl > 1 || *cp != '*') { + if (strcmpci(cp,GetMyZone()) != 0) { + logit(6, "%s is not the current zone",cp); + continue; /* skip entry */ + } + logit(6, "nis load: only '*' allowed for zone"); + } + nve->ent.zoneStr.s[0] = '*'; + nve->ent.zoneStr.s[1] = '\0'; +#endif PHASE2 + nve->enume = enume; + logit(5, "Load: %s:%s@%s [%d] - [net %d.%d, node %d, skt %d]", + nve->ent.objStr.s, nve->ent.typeStr.s, nve->ent.zoneStr.s, + enume, kipnet, kipsubnet, node, skt); + return(FALSE); + } while(1); + return(TRUE); /* should never get here... */ +} + +int +nbptab_load(fd) +FILE *fd; +{ + int i; + SNBPTEntry *nve; + + for (i=0; i < NUMNVE; i++) { + nve = &nbptable[i]; + if (getentry(fd, nve)) + break; + } + numnve = i; + return(numnve); +} + +int +nbptab_dump(fd) +FILE *fd; +{ + long clock; + int i; + SNBPTEntry *nve; + int net; + + clock = time(0); + fprintf(fd, "# Dump of NIS name table at %s", asctime(localtime(&clock))); + fprintf(fd, "# Zone is fixed as %s (This is '*' - my zone)\n",GetMyZone()); + fprintf(fd,"# Format: ddp net, node, skt, and nbp enumerator followed by\n"); + fprintf(fd,"# obj, type, zone length enclosed in !'s and then the name\n"); + fprintf(fd, "# %d entries (%d max)\n\n",numnve,NUMNVE); + for (i=0; i < numnve; i++) { + nve = &nbptable[i]; + net = ntohs(nve->addr.net); + fprintf(fd, "net=%d.%d, node=%d, skt=%d, enum=%d, !%d!%d!%d! ? %s:%s@%s\n", + net>>8, net&0xff, nve->addr.node, nve->addr.skt, nve->enume, + strlen((char *)nve->ent.objStr.s), + strlen((char *)nve->ent.typeStr.s), + strlen((char *)nve->ent.zoneStr.s), + nve->ent.objStr.s, nve->ent.typeStr.s, nve->ent.zoneStr.s); + } + return(numnve); +} + diff --git a/etc/start-cap-servers b/etc/start-cap-servers new file mode 100644 index 0000000..e4a988f --- /dev/null +++ b/etc/start-cap-servers @@ -0,0 +1,45 @@ +#!/bin/sh + +### sample start servers file (copy to /usr/local/lib) +### start from '/etc/rc.local' + +LOGd=/usr/tmp +LOGf=/dev/null +CAP=/usr/local/cap +LIB=/usr/local/lib/cap + +LWARGS="-a ${LIB}/procsets -f ${LIB}/LW+Fonts" + +# +# Start UAR first if used +# +# ${CAP}/uar -C "interface" +# sleep 10 + +# +# Otherwise start aarpd first for Native EtherTalk +# +# ${CAP}/aarpd "interface" "zone" + +# +# Or capd for Kernel AppleTalk under Linux +# +# ${CAP}/capd "interface" "zone" + +# +# allow atis to startup before other CAP programs +# +${CAP}/atis + +# +# Make this 15 seconds if using aarpd +# +sleep 5 + +${CAP}/snitch -S -f "SUN 4 SunOS 4.0 UNIX" -l lwsrv +${CAP}/lwsrv -n "Technical Services Spool" -p lw.tsa ${LWARGS} + +# if CAP compiled with FIXED_DIRIDS then start AFP ID server first +# ${CAP}/afpidsrvr -l ${LOGd}/afpidsrvr.log + +${CAP}/aufs -U 20 -V ${LIB}/afpvols -l ${LOGf} -n `hostname` diff --git a/extras/Makefile.m4 b/extras/Makefile.m4 new file mode 100644 index 0000000..aeeed8b --- /dev/null +++ b/extras/Makefile.m4 @@ -0,0 +1,33 @@ +CFLAGS=cflags() specialcflags() +DESTDIR=capdestdir() +CAPLIB=libcap() +I=includedir() +LFLAGS= +O= + +PROGS=iwif + +all: ${PROGS} + +iwif: iwif.o $(O) + ${CC} ${LFLAGS} -o iwif iwif.o + +des.o: des.c + ${CC} ${CFLAGS} -c des.c + +install: ${PROGS} + -strip ${PROGS} + ifdef([sysvinstall],[install -f $(DESTDIR) ${PROGS}], + [${INSTALLER} ${PROGS} ${DESTDIR}]) + +clean: + -rm -f ${PROGS} *.o core make.log err *~ + +spotless: + -rm -f ${PROGS} *.o *.orig core make.log err *~ Makefile makefile + +cleanexe: + -rm -f ${PROGS} + +dist: + @cat todist diff --git a/extras/README b/extras/README new file mode 100644 index 0000000..2922033 --- /dev/null +++ b/extras/README @@ -0,0 +1,11 @@ + des.c - Public Domain routines for DES encryption + + iwif.c - input filter for BSD LPD for Imagewriter hooked up via + a serial line + + printcap.iw - sample printcap entry for ImageWriter hooked up via + a serial line + + att_getopt.c - an getopt from mod.sources for lwsrv and aufs (PUBLIC + DOMAIN SOFTWARE) + diff --git a/extras/afpfile b/extras/afpfile new file mode 100644 index 0000000..576c9a5 --- /dev/null +++ b/extras/afpfile @@ -0,0 +1,31 @@ +# This is a sample CAP AUFS afpfile +# +# It controls the type of translation performed on the file content and the +# mapping between UNIX filename extension and the Macintosh Creator and Type. +# +# The available forms of translation are +# Ascii, map LF to CR and vice versa +# Text, map Mac quotes to UNIX quotes (not vice-versa, implies Ascii) +# Iso, map to/from ISO 8859 characters (requires ISO_TRANSLATE) +# Raw, no translation +# +# EXTN XLate CREAT TYPE Comment +# +.c Ascii 'MPS ' 'TEXT' "This is a UNIX C source file." +.h Ascii 'MPS ' 'TEXT' "This is a UNIX C source header file." +.p Ascii 'KAHL' 'TEXT' "This is a UNIX pascal source file." +.o Raw 'unix' 'OBJ ' "This is a UNIX object file." +.a Raw 'unix' 'AR ' "This is a UNIX archive file." +.Z Raw 'LZIV' 'ZIVU' "This is a UNIX compressed file." +.ps Raw 'ASPS' 'TEXT' "This is a UNIX PostScript file." +.sit Raw 'SIT!' 'SIT!' "This is a UNIX StuffIt file." +.gif Raw 'JRVL' 'GIFf' "This is a UNIX GIF file." +.tar Raw 'TAR ' 'TARF' "This is a UNIX Tar file." +.tex Raw '*TEX' 'TEXT' "This is a UNIX TeX source file." +.fram Raw 'Fram' 'FASL' "This is a UNIX FrameMaker file." +.xlt Ascii 'XCEL' 'TEXT' "This is a UNIX Excel Text file." +.hqx Ascii 'BnHq' 'TEXT' "This is a UNIX BinHex file." +.wrd Ascii 'MSWD' 'TEXT' "This is a UNIX Text file." +.wrt Ascii 'MACA' 'TEXT' "This is a UNIX Text file." +.txt Ascii 'ttxt' 'TEXT' "This is a UNIX Text file." +* Ascii 'ttxt' 'TEXT' "This is a UNIX created file." diff --git a/extras/att_getopt.c b/extras/att_getopt.c new file mode 100644 index 0000000..e1d70b7 --- /dev/null +++ b/extras/att_getopt.c @@ -0,0 +1,120 @@ +/* + * Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site seismo.CSS.GOV + * Posting-Version: version B 2.10.2 9/3/84; site panda.UUCP + * Path: seismo!harvard!talcott!panda!sources-request + * From: sources-request@panda.UUCP + * Newsgroups: mod.sources + * Subject: public domain AT&T getopt(3) + * Message-ID: <1159@panda.UUCP> + * Date: 3 Dec 85 15:22:25 GMT + * Sender: jpn@panda.UUCP + * Organization: IEEE/P1003 Portable Operating System Environment Committee + * Lines: 138 + * Approved: jpn@panda.UUCP + * + * Mod.sources: Volume 3, Issue 58 + * Submitted by: seismo!ut-sally!jsq (John Quarterman, Moderator mod.std.unix) + * + * [ + * There are two articles here, forwarded from mod.std.unix. Also, the + * getopt source code is NOT in shar format - you will have to hand + * edit this file. - John P. Nelson, moderator, mod.sources + * ] + * + * ************************ + * + * Newsgroups: mod.std.unix + * Subject: public domain AT&T getopt source + * Date: 3 Nov 85 19:34:15 GMT + * + * Here's something you've all been waiting for: the AT&T public domain + * source for getopt(3). It is the code which was given out at the 1985 + * UNIFORUM conference in Dallas. I obtained it by electronic mail + * directly from AT&T. The people there assure me that it is indeed + * in the public domain. + * + * There is no manual page. That is because the one they gave out at + * UNIFORUM was slightly different from the current System V Release 2 + * manual page. The difference apparently involved a note about the + * famous rules 5 and 6, recommending using white space between an option + * and its first argument, and not grouping options that have arguments. + * Getopt itself is currently lenient about both of these things White + * space is allowed, but not mandatory, and the last option in a group can + * have an argument. That particular version of the man page evidently + * has no official existence, and my source at AT&T did not send a copy. + * The current SVR2 man page reflects the actual behavor of this getopt. + * However, I am not about to post a copy of anything licensed by AT&T. + * + * I will submit this source to Berkeley as a bug fix. + * + * I, personally, make no claims or guarantees of any kind about the + * following source. I did compile it to get some confidence that + * it arrived whole, but beyond that you're on your own. +*/ + +/*LINTLIBRARY*/ +#define NULL 0 +#define EOF (-1) +#define ERR(s, c) if(opterr){\ + extern int strlen(), write();\ + char errbuf[2];\ + errbuf[0] = c; errbuf[1] = '\n';\ + (void) write(2, argv[0], (unsigned)strlen(argv[0]));\ + (void) write(2, s, (unsigned)strlen(s));\ + (void) write(2, errbuf, 2);} + +extern int strcmp(); +/* bsd based system will require index */ +#define strchr index +extern char *strchr(); + +int opterr = 1; +int optind = 1; +int optopt; +char *optarg; + +int +getopt(argc, argv, opts) +int argc; +char **argv, *opts; +{ + static int sp = 1; + register int c; + register char *cp; + + if(sp == 1) + if(optind >= argc || + argv[optind][0] != '-' || argv[optind][1] == '\0') + return(EOF); + else if(strcmp(argv[optind], "--") == NULL) { + optind++; + return(EOF); + } + optopt = c = argv[optind][sp]; + if(c == ':' || (cp=strchr(opts, c)) == NULL) { + ERR(": illegal option -- ", c); + if(argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return('?'); + } + if(*++cp == ':') { + if(argv[optind][sp+1] != '\0') + optarg = &argv[optind++][sp+1]; + else if(++optind >= argc) { + ERR(": option requires an argument -- ", c); + sp = 1; + return('?'); + } else + optarg = argv[optind++]; + sp = 1; + } else { + if(argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + return(c); +} diff --git a/extras/des.c b/extras/des.c new file mode 100644 index 0000000..56d87c6 --- /dev/null +++ b/extras/des.c @@ -0,0 +1,524 @@ +#ifdef __alpha +#define long int +#endif /* __alpha */ + +/* + * Sofware DES functions + * written 12 Dec 1986 by Phil Karn, KA9Q; large sections adapted from + * the 1977 public-domain program by Jim Gillogly + */ + +#define NULL 0 + +#ifdef BYTESWAPPED +unsigned long byteswap(); +#endif BYTESWAPPED + +/* Tables defined in the Data Encryption Standard documents */ + +/* initial permutation IP */ +static char ip[] = { + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7 +}; + +/* final permutation IP^-1 */ +static char fp[] = { + 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25 +}; + +/* expansion operation matrix + * This is for reference only; it is unused in the code + * as the f() function performs it implicitly for speed + */ +#ifdef notdef +static char ei[] = { + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1 +}; +#endif + +/* permuted choice table (key) */ +static char pc1[] = { + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 +}; + +/* number left rotations of pc1 */ +static char totrot[] = { + 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 +}; + +/* permuted choice key (table) */ +static char pc2[] = { + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 +}; + +/* The (in)famous S-boxes */ +static char si[8][64] = { + /* S1 */ + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13, + + /* S2 */ + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9, + + /* S3 */ + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12, + + /* S4 */ + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14, + + /* S5 */ + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3, + + /* S6 */ + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13, + + /* S7 */ + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12, + + /* S8 */ + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 +}; + +/* 32-bit permutation function P used on the output of the S-boxes */ +static char p32i[] = { + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25 +}; +/* End of DES-defined tables */ + +/* Lookup tables initialized once only at startup by desinit() */ +static long (*sp)[64]; /* Combined S and P boxes */ + +static char (*iperm)[16][8]; /* Initial and final permutations */ +static char (*fperm)[16][8]; + +/* 8 6-bit subkeys for each of 16 rounds, initialized by dessetkey() */ +static unsigned char (*kn)[8]; + +/* bit 0 is left-most in byte */ +static int bytebit[] = { + 0200,0100,040,020,010,04,02,01 +}; + +static int nibblebit[] = { + 010,04,02,01 +}; +static int desmode; +static permute(), round(), perminit(), spinit(); +static long f(); + +/* Allocate space and initialize DES lookup arrays + * mode == 0: standard Data Encryption Algorithm + * mode == 1: DEA without initial and final permutations for speed + * mode == 2: DEA without permutations and with 128-byte key (completely + * independent subkeys for each round) + */ +desinit(mode) +int mode; +{ + char *malloc(); + + if(sp != NULL){ + /* Already initialized */ + return 0; + } + desmode = mode; + + if((sp = (long (*)[64])malloc(sizeof(long) * 8 * 64)) == NULL){ + return -1; + } + spinit(); + kn = (unsigned char (*)[8])malloc(sizeof(char) * 8 * 16); + if(kn == NULL){ + free((char *)sp); + return -1; + } + if(mode == 1 || mode == 2) /* No permutations */ + return 0; + + iperm = (char (*)[16][8])malloc(sizeof(char) * 16 * 16 * 8); + if(iperm == NULL){ + free((char *)sp); + free((char *)kn); + return -1; + } + perminit(iperm,ip); + + fperm = (char (*)[16][8])malloc(sizeof(char) * 16 * 16 * 8); + if(fperm == NULL){ + free((char *)sp); + free((char *)kn); + free((char *)iperm); + return -1; + } + perminit(fperm,fp); + + return 0; +} +/* Free up storage used by DES */ +desdone() +{ + if(sp == NULL) + return; /* Already done */ + + free((char *)sp); + free((char *)kn); + if(iperm != NULL) + free((char *)iperm); + if(fperm != NULL) + free((char *)fperm); + + sp = NULL; + iperm = NULL; + fperm = NULL; + kn = NULL; +} +/* Set key (initialize key schedule array) */ +dessetkey(key) +char *key; /* 64 bits (will use only 56) */ +{ + char pc1m[56]; /* place to modify pc1 into */ + char pcr[56]; /* place to rotate pc1 into */ + register int i,j,l; + int m; + + /* In mode 2, the 128 bytes of subkey are set directly from the + * user's key, allowing him to use completely independent + * subkeys for each round. Note that the user MUST specify a + * full 128 bytes. + * + * I would like to think that this technique gives the NSA a real + * headache, but I'm not THAT naive. + */ + if(desmode == 2){ + for(i=0;i<16;i++) + for(j=0;j<8;j++) + kn[i][j] = *key++; + return; + } + /* Clear key schedule */ + for (i=0; i<16; i++) + for (j=0; j<8; j++) + kn[i][j]=0; + + for (j=0; j<56; j++) { /* convert pc1 to bits of key */ + l=pc1[j]-1; /* integer bit location */ + m = l & 07; /* find bit */ + pc1m[j]=(key[l>>3] & /* find which key byte l is in */ + bytebit[m]) /* and which bit of that byte */ + ? 1 : 0; /* and store 1-bit result */ + } + for (i=0; i<16; i++) { /* key chunk for each iteration */ + for (j=0; j<56; j++) /* rotate pc1 the right amount */ + pcr[j] = pc1m[(l=j+totrot[i])<(j<28? 28 : 56) ? l: l-28]; + /* rotate left and right halves independently */ + for (j=0; j<48; j++){ /* select bits individually */ + /* check bit that goes to kn[j] */ + if (pcr[pc2[j]-1]){ + /* mask it in if it's there */ + l= j % 6; + kn[i][j/6] |= bytebit[l] >> 2; + } + } + } +} +/* In-place encryption of 64-bit block */ +endes(block) +char *block; +{ + register int i; + unsigned long work[2]; /* Working data storage */ + long tmp; + + permute(block,iperm,(char *)work); /* Initial Permutation */ +#ifdef BYTESWAPPED + work[0] = byteswap(work[0]); + work[1] = byteswap(work[1]); +#endif BYTESWAPPED + + /* Do the 16 rounds */ + for (i=0; i<16; i++) + round(i,work); + + /* Left/right half swap */ + tmp = work[0]; + work[0] = work[1]; + work[1] = tmp; + +#ifdef BYTESWAPPED + work[0] = byteswap(work[0]); + work[1] = byteswap(work[1]); +#endif BYTESWAPPED + permute((char *)work,fperm,block); /* Inverse initial permutation */ +} +/* In-place decryption of 64-bit block */ +dedes(block) +char *block; +{ + register int i; + unsigned long work[2]; /* Working data storage */ + long tmp; + + permute(block,iperm,(char *)work); /* Initial permutation */ + +#ifdef BYTESWAPPED + work[0] = byteswap(work[0]); + work[1] = byteswap(work[1]); +#endif BYTESWAPPED + + /* Left/right half swap */ + tmp = work[0]; + work[0] = work[1]; + work[1] = tmp; + + /* Do the 16 rounds in reverse order */ + for (i=15; i >= 0; i--) + round(i,work); + +#ifdef BYTESWAPPED + work[0] = byteswap(work[0]); + work[1] = byteswap(work[1]); +#endif BYTESWAPPED + + permute((char *)work,fperm,block); /* Inverse initial permutation */ +} + +/* Permute inblock with perm */ +static +permute(inblock,perm,outblock) +char *inblock, *outblock; /* result into outblock,64 bits */ +char perm[16][16][8]; /* 2K bytes defining perm. */ +{ + register int i,j; + register char *ib, *ob; /* ptr to input or output block */ + register char *p, *q; + + if(perm == NULL){ + /* No permutation, just copy */ + for(i=8; i!=0; i--) + *outblock++ = *inblock++; + return; + } + /* Clear output block */ + for (i=8, ob = outblock; i != 0; i--) + *ob++ = 0; + + ib = inblock; + for (j = 0; j < 16; j += 2, ib++) { /* for each input nibble */ + ob = outblock; + p = perm[j][(*ib >> 4) & 017]; + q = perm[j + 1][*ib & 017]; + for (i = 8; i != 0; i--){ /* and each output byte */ + *ob++ |= *p++ | *q++; /* OR the masks together*/ + } + } +} + +/* Do one DES cipher round */ +static +round(num,block) +int num; /* i.e. the num-th one */ +unsigned long *block; +{ + /* The rounds are numbered from 0 to 15. On even rounds + * the right half is fed to f() and the result exclusive-ORs + * the left half; on odd rounds the reverse is done. + */ + if(num & 1){ + block[1] ^= f(block[0],kn[num]); + } else { + block[0] ^= f(block[1],kn[num]); + } +} +/* The nonlinear function f(r,k), the heart of DES */ +static +long +f(r,subkey) +unsigned long r; /* 32 bits */ +unsigned char subkey[8]; /* 48-bit key for this round */ +{ + register unsigned long rval,rt; +#ifdef TRACE + unsigned char *cp; + int i; + + printf("f(%08lx, %02x %02x %02x %02x %02x %02x %02x %02x) = ", + r, + subkey[0], subkey[1], subkey[2], + subkey[3], subkey[4], subkey[5], + subkey[6], subkey[7]); +#endif TRACE + /* Run E(R) ^ K through the combined S & P boxes + * This code takes advantage of a convenient regularity in + * E, namely that each group of 6 bits in E(R) feeding + * a single S-box is a contiguous segment of R. + */ + rt = (r >> 1) | ((r & 1) ? 0x80000000 : 0); + rval = 0; + rval |= sp[0][((rt >> 26) ^ *subkey++) & 0x3f]; + rval |= sp[1][((rt >> 22) ^ *subkey++) & 0x3f]; + rval |= sp[2][((rt >> 18) ^ *subkey++) & 0x3f]; + rval |= sp[3][((rt >> 14) ^ *subkey++) & 0x3f]; + rval |= sp[4][((rt >> 10) ^ *subkey++) & 0x3f]; + rval |= sp[5][((rt >> 6) ^ *subkey++) & 0x3f]; + rval |= sp[6][((rt >> 2) ^ *subkey++) & 0x3f]; + rt = (r << 1) | ((r & 0x80000000) ? 1 : 0); + rval |= sp[7][(rt ^ *subkey) & 0x3f]; +#ifdef TRACE + printf(" %08lx\n",rval); +#endif TRACE + return rval; +} +/* initialize a perm array */ +static +perminit(perm,p) +char perm[16][16][8]; /* 64-bit, either init or final */ +char p[64]; +{ + register int l, j, k; + int i,m; + + /* Clear the permutation array */ + for (i=0; i<16; i++) + for (j=0; j<16; j++) + for (k=0; k<8; k++) + perm[i][j][k]=0; + + for (i=0; i<16; i++) /* each input nibble position */ + for (j = 0; j < 16; j++)/* each possible input nibble */ + for (k = 0; k < 64; k++)/* each output bit position */ + { l = p[k] - 1; /* where does this bit come from*/ + if ((l >> 2) != i) /* does it come from input posn?*/ + continue; /* if not, bit k is 0 */ + if (!(j & nibblebit[l & 3])) + continue; /* any such bit in input? */ + m = k & 07; /* which bit is this in the byte*/ + perm[i][j][k>>3] |= bytebit[m]; + } +} + +/* Initialize the lookup table for the combined S and P boxes */ +static int +spinit() +{ + char pbox[32]; + int p,i,s,j,rowcol; + long val; + + /* Compute pbox, the inverse of p32i. + * This is easier to work with + */ + for(p=0;p<32;p++){ + for(i=0;i<32;i++){ + if(p32i[i]-1 == p){ + pbox[p] = i; + break; + } + } + } + for(s = 0; s < 8; s++){ /* For each S-box */ + for(i=0; i<64; i++){ /* For each possible input */ + val = 0; + /* The row number is formed from the first and last + * bits; the column number is from the middle 4 + */ + rowcol = (i & 32) | ((i & 1) ? 16 : 0) | ((i >> 1) & 0xf); + for(j=0;j<4;j++){ /* For each output bit */ + if(si[s][rowcol] & (8 >> j)){ + val |= 1L << (31 - pbox[4*s + j]); + } + } + sp[s][i] = val; + +#ifdef DEBUG + printf("sp[%d][%2d] = %08lx\n",s,i,sp[s][i]); +#endif DEBUG + } + } +} +#ifdef BYTESWAPPED +/* Byte swap a long */ +static +unsigned long +byteswap(x) +unsigned long x; +{ + register char *cp,tmp; + + cp = (char *)&x; + tmp = cp[3]; + cp[3] = cp[0]; + cp[0] = tmp; + + tmp = cp[2]; + cp[2] = cp[1]; + cp[1] = tmp; + + return x; +} +#endif BYTESWAPPED diff --git a/extras/dummy.des.c b/extras/dummy.des.c new file mode 100644 index 0000000..165cad7 --- /dev/null +++ b/extras/dummy.des.c @@ -0,0 +1,59 @@ +/* There are dummy routines that describe what the des subroutines */ +/* should do */ +/* If you get the "real" routines, the names should match - in other words */ +/* you will have to rename "setkey" to "dessetkey" */ + +#define bit8 char + +/* + * desinit - should intialize things for DES encryption. Return code of + * 0 means all okay. Return code of -1 should indicate that des encryption + * is not available. + * + * The desinit routine, if it allows mutiple modes of operations, should + * take (mode == 0) to mean "standard DES". + * + * Multiple calls to desinit should not result in multiple initializations + * (e.g. desinit is in effect until desdone is issued) + * + */ +desinit(mode) +int mode; +{ +#ifdef sun + return (0); +#else + return(-1); /* nothing here. */ +#endif +} + +/* + * setkey will set the encryption/decryption key to the specified + * 64 bit block (organized as an array of 8 8-bit values) + * + * ***WARNING*** THIS IS CALLED setkey IN THE ROUTINES AS DISTRIBUTED. +*/ +dessetkey(key) +bit8 key[8]; +{ +} + +/* + * endes should encrypt the specifed block (8 element array of + * 8-bit values). The result is returned in-place. + * +*/ +endes(block) +bit8 block[8]; +{ +} + + +/* + * desdone should undo what desinit does (e.g. free memory, etc) + * +*/ +desdone() +{ +} + diff --git a/extras/iwif.c b/extras/iwif.c new file mode 100644 index 0000000..310de87 --- /dev/null +++ b/extras/iwif.c @@ -0,0 +1,101 @@ +static char rcsid[] = "$Author: djh $ $Date: 91/02/15 22:30:48 $"; +static char rcsident[] = "$Header: iwif.c,v 2.1 91/02/15 22:30:48 djh Rel $"; +static char revision[] = "$Revision: 2.1 $"; +/* + * + * iwif.c -- ImageWriter Input Spooler - for BSD 4.2 LPD + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Jan. 1986 CCKim Created - ln03 input filer. + * Feb. 1987 CCKim Modified for ImageWriter + * + */ + +char copyright[] = "Copyright (c) 1986, 1987 by The Trustees of Columbia University in the City of New York"; + +#include +#include + +char printer[32]; /* printer name */ +char user[32]; /* user name */ +char host[32]; /* printing host name */ + +/* this guy sets up the printer -- don't do anything fancy now */ +reset_imagewriter() +{ + putchar('\033'); /* reset printer */ + putchar('c'); +} + +/* quit gets called when we've been killed by lprm via SIGINT */ +quit() +{ + putchar('\033'); /* reset printer */ + putchar('c'); + exit(0); /* bye bye */ +} + +main(argc, argv) +int argc; +char *argv[]; +{ + int i,c,c2; + + signal(SIGINT,quit); /* this is what lprm sends us */ + + printer[0]=0; + + for (i=0; i= 0) { + sendbanner(ptr, bannerfd); /* don't care if we can't send it */ + close(bannerfd); + } +#endif + + reset_imagewriter(); /* get things set up */ + + do { + if ((c = getchar()) == EOF) + break; +#ifdef IMAGEWRITER + /* These codes are ImageWriter II only */ + if (c == '\033') { + if ((c2 = getchar()) == EOF) { + putchar(c); + break; + } + switch (c2) { + case 'K': + getchar(); /* eat it */ + break; + case 'H': + getchar(); getchar(); getchar(); getchar(); /* eat it */ + break; + default: + putchar(c); + putchar(c2); + break; + } + continue; + } +#endif + putchar(c); + } while (1); + + /* How do we figure out how many pages we printed? */ + fflush(stdout); /* flush print buffer */ + quit(); /* and go away */ +} diff --git a/extras/lib.cap.macusers b/extras/lib.cap.macusers new file mode 100644 index 0000000..950074a --- /dev/null +++ b/extras/lib.cap.macusers @@ -0,0 +1,14 @@ +# File to convert those fancy macintosh names into normal unix names +# for the laserwriter server (lwsrv compiled with RUN_AS_USER/USER_REQURED). + +# lines that start with # are comments lines +# no other comments allowed +# all characters are significant +# spaces are mapped onto underscores, as well as all unprintables +# everything is mapped to lowercase and all letters with diacriticals +# are mapped to normal letters before lookup. + +# We map macuser onto something nonexistent to discourage using that name. +nonexisting:macuser +# more names below... +# unixname:macname diff --git a/extras/lib.cap.refused b/extras/lib.cap.refused new file mode 100644 index 0000000..7ce628e --- /dev/null +++ b/extras/lib.cap.refused @@ -0,0 +1,25 @@ + +To use the laserwriters, you must have a login name on one of the Unix +machines. You must set the name of your Macintosh to your Unix login name. + +If you are running System version 6.0.7 or earlier in your Macintosh, +select "Chooser" from the Apple menu and change the name to your Unix +login name. + +If you are using System version 7.0 or later, start "Sharing Setup" from +the "Control Panels" folder and fill in your login name under "Owner Name". + +If you really like to have a special name, you can ask the systems group +to have your special name registered. + + +OzTeX users: +In order to print properly you must add one line to the DVItoPS.ps and +TEXTtoPS.ps files. Be sure that they start with these two lines: + %! + %%For: username +The first line should already be there. Replace username by the name +you have in the Chooser, subject to the same restrictions as above. + + +The SystemsGroup. diff --git a/extras/lnof.c b/extras/lnof.c new file mode 100644 index 0000000..e4b01d9 --- /dev/null +++ b/extras/lnof.c @@ -0,0 +1,39 @@ +/* + * Basic output filter for the 4.2 spooling system + * + * Write out the banner (the input) into .banner for the input filter. + * + * Note: Do a sigstop on self when we see ^Y^A which denotes end of job. + * exiting is the WRONG thing to do at this point. + * + * Copyright (c) 1985 by The Trustees of Columbia University in the City + * of New York + * + * Author: Charlie C. Kim +*/ + +#include +#include + +FILE *bannerfile; + +main() +{ + char c; + while (1) { + if ((bannerfile = fopen(".banner", "w")) < 0) { + perror("Can't open .banner"); + exit(8); + } + while ((c=getchar()) != '\031' && c != EOF) + putc(c,bannerfile); + if (c==EOF) break; /* should never happen(?) */ + if ((c=getchar()) == '\01') { +#ifdef DEBUG + fprintf(stderr,"Waiting for next job..."); +#endif DEBUG + fclose(bannerfile); /* close off file here - end of job */ + kill(getpid(), SIGSTOP); + } + } +} diff --git a/extras/makefile b/extras/makefile new file mode 100644 index 0000000..0b4b475 --- /dev/null +++ b/extras/makefile @@ -0,0 +1,40 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 13:59:56 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +DESTDIR=/usr/local/cap +CAPLIB=-lcap +I=/usr/include +LFLAGS= +O= + +PROGS=iwif + +all: ${PROGS} + +iwif: iwif.o $(O) + ${CC} ${LFLAGS} -o iwif iwif.o + +des.o: des.c + ${CC} ${CFLAGS} -c des.c + +install: ${PROGS} + -strip ${PROGS} + ${INSTALLER} ${PROGS} ${DESTDIR} + +clean: + -rm -f ${PROGS} *.o core make.log err *~ + +cleanexe: + -rm -f ${PROGS} + +dist: + @cat todist diff --git a/extras/printcap.iw b/extras/printcap.iw new file mode 100644 index 0000000..8048716 --- /dev/null +++ b/extras/printcap.iw @@ -0,0 +1,7 @@ +iwii|ImageWriter II:\ + :lp=/dev/ImageWriter:sd=/usr/spool/lpd/ImageWriter:\ + :lf=/usr/adm/ImageWriter-log:af=/usr/adm/ImageWriter.acct:\ + :br#9600:rw:fc#0000374:fs#0006003:xc#0:xs#0040040:mx#0:\ + :if=/usr/local/lib/lwif:\ + :of=/usr/local/lib/lnof: + diff --git a/gen.makes b/gen.makes new file mode 100755 index 0000000..398e591 --- /dev/null +++ b/gen.makes @@ -0,0 +1,38 @@ +#!/bin/sh +# Simple script to generate makefiles from the templates +# -f specifies a different setup file +# -o specifies a different output file +appdirs="applications applications/aufs applications/papif applications/lwsrv" +supportdirs="support/uab support/ethertalk support/capd" +libdirs="lib lib/afp lib/afpc lib/cap" +dirs="netat contrib samples extras etc ${libdirs} ${appdirs} ${supportdirs}" +m4setup=`pwd`/m4.setup +m4features=`pwd`/m4.features +output=makefile +while test $# != 0 +do case "$1" in + -f) + shift + m4setup=`pwd`/$1 + echo "Using $1 to configure makefiles" + ;; + -o) + shift + output=$1 + ;; + *) + break + ;; + esac + shift +done +if [ -n "$*" ]; then + dirs=$* +fi +for i in ${dirs} +do + echo Creating ${output} for $i + (cd $i; /bin/rm -f ${output}; \ + cat ${m4features} ${m4setup} Makefile.m4 | m4 | \ + sed -e "1,/##########MARKER##########/d" > ${output}) +done diff --git a/lib/Makefile.m4 b/lib/Makefile.m4 new file mode 100644 index 0000000..7012065 --- /dev/null +++ b/lib/Makefile.m4 @@ -0,0 +1,37 @@ + + +all: + (cd cap; make) + (cd afp; make) + (cd afpc; make) + ifelse(os,[xenix5],[(cd xenix; make)],[]) + +install: ifelse(os,[xenix5],[xenix5],[normal]) + +normal: + (cd cap; make install) + (cd afp; make install) + (cd afpc; make install) + +xenix5: + (cd xenix; make install) + +clean: + -(cd cap; make clean) + -(cd afp; make clean) + -(cd afpc; make clean) + ifelse(os,[xenix5],[-(cd xenix; make clean)],[]) + +spotless: + -rm -f *.orig Makefile makefile + -(cd cap; make spotless) + -(cd afp; make spotless) + -(cd afpc; make spotless) + ifelse(os,[xenix5],[-(cd xenix; make clean)],[]) + +dist: + @cat todist + @(cd cap; make dist) + @(cd afp; make dist) + @(cd afpc; make dist) + diff --git a/lib/afp/Makefile.m4 b/lib/afp/Makefile.m4 new file mode 100644 index 0000000..73d0305 --- /dev/null +++ b/lib/afp/Makefile.m4 @@ -0,0 +1,59 @@ +CFLAGS=cflags() bigcflags() specialcflags() +DESTDIR=libdestdir() +OSDEFS=afposdefs() +LIBAFP=afplib() +I=includedir() +DES=desloc() + +LIBAFPSRCS=afperr.c afpcmd.c afppacks.c afposlock.c afppass.c \ + afpidaufs.c afpidclnt.c afpidgnrl.c +LIBAFPOBJS=afperr.o afpcmd.o afppacks.o afposlock.o des.o afppass.o \ + afpidaufs.o afpidclnt.o afpidgnrl.o + +$(LIBAFP): $(LIBAFPOBJS) + ifdef([uselordertsort],[ar cr $(LIBAFP) `lorder $(LIBAFPOBJS)| tsort`], + [ar rv $(LIBAFP) $(LIBAFPOBJS)]) + +des.o: ${DES}/des.c + (cd ${DES}; make des.o) + cp ${DES}/des.o . + +clean: + -rm -f ${LIBAFPOBJS} ${LIBAFP} core *~ + +spotless: + -rm -f ${LIBAFPOBJS} ${LIBAFP} core *~ *.orig Makefile makefile + +install: $(LIBAFP) + ifdef([sysvinstall],[install -f $(DESTDIR) $(LIBAFP)], + [${INSTALLER} $(LIBAFP) $(DESTDIR)]) +ifdef([uselordertsort],[],[ ranlib $(DESTDIR)/$(LIBAFP)]) + +dist: + @cat todist + +lint: $(LIBAFPSRCS) + lint $(LIBAFPSRCS) + +afposlock.o: afposlock.c + ${CC} ${OSDEFS} ${CFLAGS} -c afposlock.c + +# Dependencies +afpcmd.o: afpcmd.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/afp.h $I/netat/afpcmd.h +afperr.o: afperr.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h $I/netat/afp.h +afppacks.o: afppacks.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h $I/netat/afp.h \ + $I/netat/afpcmd.h +afposlock.o: afposlock.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/afp.h $I/netat/afpcmd.h + +afpidaufs.o: afpidaufs.c afpidaufs.h afpidnames.h +afpidclnt.o: afpidclnt.c afpidaufs.h afpidnames.h +afpidgnrl.o: afpidgnrl.c afpidaufs.h afpidnames.h +afppass.o: afppass.c $I/netat/afppass.h diff --git a/lib/afp/README b/lib/afp/README new file mode 100644 index 0000000..e6add64 --- /dev/null +++ b/lib/afp/README @@ -0,0 +1,5 @@ + afpcmd.c - packing and unpacking routines for afp pkts + afperr.c - error messages, etc. for afp + afppacks.c - definition for various afp pkts + afposlock.c - locking routines + diff --git a/lib/afp/afpcmd.c b/lib/afp/afpcmd.c new file mode 100644 index 0000000..72a9319 --- /dev/null +++ b/lib/afp/afpcmd.c @@ -0,0 +1,656 @@ +/* + * $Author: djh $ $Date: 1995/05/30 08:51:20 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afpcmd.c,v 2.5 1995/05/30 08:51:20 djh Rel djh $ + * $Revision: 2.5 $ + * + */ + +/* + * afpcmd.c - Packing and unpacking commands + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * WARNING: if your machine is not a vax, sun, ibm pc/rt or pyramid you + * (or Configure) must define BYTESWAPPED if your machine is byteswapped + * + * Edit History: + * + * Thu Oct 30 Schilit Created + * + */ + +#include +#include +#include /* for ntohs, etc. */ +#include +#include +#include + +#ifndef BYTESWAPPED +# ifdef ultrix +# ifdef mips +# define BYTESWAPPED +# endif mips +# endif ultrix +#endif BYTESWAPPED + +#ifndef BYTESWAPPED +# ifdef vax +# define BYTESWAPPED +# endif vax +#endif BYTESWAPPED + +#ifndef BYTESWAPPED +# ifdef ns16000 +# define BYTESWAPPED +# endif ns16000 +#endif BYTESWAPPED + +#ifndef BYTESWAPPED +# ifdef ns32000 +# define BYTESWAPPED +# endif ns32000 +#endif BYTESWAPPED + +/* add this in case sequent doesn't define nsxxxxx */ +#ifndef BYTESWAPPED +# ifdef sequent +# define BYTESWAPPED +# endif sequent +#endif BYTESWAPPED + +#ifndef BYTESWAPPED +# ifdef MIPSEL +# define BYTESWAPPED +# endif MIPSEL +#endif BYTESWAPPED + +#ifndef BYTESWAPPED +# ifdef i386 +# define BYTESWAPPED +# endif i386 +#endif BYTESWAPPED + +/* machines that aren't byteswapped include: ibm032, pyr, sun, hpux, etc */ + +#ifndef NULL +#define NULL 0 +#endif NULL + +export void InitPackTime(); + +char *PackNames[] = { + "Word", /* P_WORD */ + "Byte", /* P_BYTE */ + "Double Word", /* P_DWRD */ + "Bytes", /* P_BYTS */ + "Pascal String", /* P_PSTR */ + "BitMap", /* P_BMAP */ + "Offset String", /* P_OSTR */ + "Path String", /* P_PATH */ + "Offset to Pointer", /* P_OPTR */ + "Offset Path", /* P_OPTH */ + "Even boundary", /* P_EVEN */ + "Zero", /* P_ZERO */ + "Time" /* P_TIME */ + }; + +/* + * Convert between Internal Unix time and External AFP time. + * + * All date and time quantities used by AFP are Greenwich Mean Time (GMT) + * values. They are 32 bit signed integers corresponding to the number of + * seconds measured from 12:00AM January 1, 2000 (the start of the next + * century corresponds to datetime=0). + * + * BSD time base is 12:00AM January 1, 1970 + * AFP time base is 12:00AM January 1, 2000 + * + * Actually, it seems that the Mac sends MacTime - could it be + * that our copy of AppleShare does the wrong thing? Version 1.00 was + * bad. Fixed in 1.10 and (hopefully) beyond. + * + * Another problem exists with DST which is not handled here. + * I mean, things will be within 2 hours isn't that good enough :-) + * + * WARNING: if this code breaks, it will be on or near Jan 1, 2000 + * or Jan 18, 2038. Of course, it's doubtful that this code + * will last that long :-) + * + */ + +private int mactime_not_inited = 1; +private long mymactime; +private long maczerotime; + +/* difference btwn unix and mac in seconds */ +/* mac starts at jan 1, 1904 for appleshare 1.0 */ +#define AS1DOT0_MACZEROTIME 0 /* zero time - see comments above */ +#define AS1DOT0_MACTIME (66*365+17)*24*60*60L + +/* difference btween midnight jan 1, 1970 and midnight jan 1, 2000 */ +/* for all others */ +#define MACTIME (((30*365+7)*(-24)*3600L)) +#define MACZEROTIME 0x80000000 /* zero time - from AppleShare 1.1 on */ + +/* Does time zone adjustment */ + +/* + * call with flag set for appleshare 10 style + * + */ + +export void +InitPackTime(flag) +int flag; +{ + struct timeval tp; + struct timezone tzp; + + mactime_not_inited = 0; + if (flag) { + mymactime = AS1DOT0_MACTIME; + maczerotime = AS1DOT0_MACZEROTIME; + } else { + mymactime = MACTIME; + maczerotime = MACZEROTIME; + } +#ifdef SOLARIS + tzset(); + mymactime -= ((long)timezone); +#else /* SOLARIS */ + gettimeofday(&tp, &tzp); + mymactime -= ((long)tzp.tz_minuteswest*60); +#endif /* SOLARIS */ + return; +} + +int +ntohPackX(pt, net, nlen, host) +PackEntry *pt; +byte *net; +byte *host; +int nlen; +{ + int ntohPackXbitmap(); + + return(ntohPackXbitmap(pt, net, nlen, host, (word)0)); +} + +int +ntohPackXbitmap(pt, net, nlen, host, bitmap) +PackEntry *pt; +byte *net; +byte *host; +int nlen; +word bitmap; +{ + int i; + int si = 0; + word bmap = bitmap; + void upack(); + + /* need to find bmap */ + + for (i=0; pt[i].pe_typ != P_END; i++) + if (si < nlen) + upack(&pt[i],net,&si,host,&bmap); /* unpack each item */ + + return(si); +} + +typedef struct Var_Object { + int vo_typ; /* type of variable object */ + byte *vo_offset; /* store offset word (INT) here */ + byte *vo_object; /* pointer to the object to store */ +} VarObject; + +typedef struct { + int vol_first; /* index at start of var objects */ + int vol_count; /* count of var objects */ + VarObject vol_vos[10]; /* array of variable objects */ +} VarObjList; + +/* + * Convert Packet from Host to Net, returns size of net packet. + * + */ + +int +htonPackX(pe, host, net) +PackEntry *pe; +byte *host; +byte *net; +{ + return(htonPackXbitmap(pe, host, net, 0)); +} + +int +htonPackXbitmap(pe, host, net, bitmap) +PackEntry *pe; +byte *host; +byte *net; +word bitmap; +{ + VarObjList vol; + int i; + word bmap = bitmap; + int nidx = 0; + void pack(); + + vol.vol_first = 0; /* position 0 for first var object */ + vol.vol_count = 0; /* no var objects */ + + for (i=0; pe[i].pe_typ != P_END; i++) + pack(&pe[i],host,net,&nidx,&bmap,&vol); + + /* scan variable length object list and pack them now */ + + for (i = 0; i < vol.vol_count; i++) { + byte *d; + VarObject *vo; + OPTRType *optr,**optrr; + word wrd; + + vo = &vol.vol_vos[i]; /* handle on a var object */ + /* set offset from start (in remember spot) */ + wrd = htons(nidx-vol.vol_first); + bcopy(&wrd, vo->vo_offset, sizeof(word)); + d = &net[nidx]; /* destination */ + /* assume object is PSTR */ + switch (vo->vo_typ) { + case P_OSTR: + cpyc2pstr(d,vo->vo_object); /* copy the pascal string */ + nidx += (*d)+1; + break; + case P_OPTR: /* is a variable length object */ + optrr = (OPTRType **) (vo->vo_object); + optr = *optrr; + bcopy(optr->optr_loc,d,optr->optr_len); /* copy the bytes */ + nidx += optr->optr_len; /* increment length */ + break; + } + } + return(nidx); /* return length */ +} + + +/* + * Pack host to net. + * + */ + +void +pack(pe, src, dest, di, bmap, vol) +PackEntry *pe; +byte *src, *dest; +int *di; +VarObjList *vol; +word *bmap; +{ + byte *s, *d, *t; + int siz; + word wrd; + dword dwrd; + sdword sdwrd; + + if (pe->pe_bit != 0) { /* check if bitmap item */ + if ((*bmap & pe->pe_bit) == 0) /* see if a requested bitmap item */ + return; /* no, forget it */ + } + + d = &dest[*di]; /* destination is net */ + s = &src[pe->pe_off]; /* source is host */ + siz = pe->pe_siz; + + switch (pe->pe_typ) { /* according to the data type */ + case P_BMAP: /* bitmap (2 bytes) */ + t = (byte *)bmap; + t[0] = s[0]; + t[1] = s[1]; + vol->vol_first = *di; /* offset */ + break; /* BITMAP does NOT get included */ + case P_WORD: /* word (2 bytes) */ +#ifdef BYTESWAPPED + d[0] = s[1]; + d[1] = s[0]; +#else BYTESWAPPED + d[0] = s[0]; + d[1] = s[1]; +#endif BYTESWAPPED + *di += 2; + break; + case P_DWRD: /* double word (4 bytes) */ +#ifdef BYTESWAPPED + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; +#else BYTESWAPPED + d[0] = s[0]; + d[1] = s[1]; + d[2] = s[2]; + d[3] = s[3]; +#endif BYTESWAPPED + *di += 4; + break; + case P_BYTE: /* single byte */ + d[0] = s[0]; + *di += 1; + break; + case P_BYTS: /* multiple bytes */ + bcopy((char *)s, (char *)d, siz); + *di += siz; + break; + case P_TIME: /* internal to external time conv. */ + t = (byte *)&sdwrd; + t[0] = s[0]; + t[1] = s[1]; + t[2] = s[2]; + t[3] = s[3]; + if (mactime_not_inited) + InitPackTime(0); /* assume version 1.0 obsolete */ + if ((time_t)sdwrd == 0) + sdwrd = maczerotime; + else + sdwrd += mymactime; +#ifdef BYTESWAPPED + d[0] = t[3]; + d[1] = t[2]; + d[2] = t[1]; + d[3] = t[0]; +#else BYTESWAPPED + d[0] = t[0]; + d[1] = t[1]; + d[2] = t[2]; + d[3] = t[3]; +#endif BYTESWAPPED + *di += 4; /* advance */ + break; + case P_PSTR: /* c to pascal string */ + cpyc2pstr(d,s); + *di += (*d)+1; + break; + case P_OPTR: + case P_OSTR: /* offset string */ + { + VarObject *vo; + + vo = &vol->vol_vos[vol->vol_count++]; /* handle on object entry */ + vo->vo_typ = pe->pe_typ; /* save type */ + vo->vo_object = s; /* the object to pack */ + vo->vo_offset = d; /* location to store offset */ + *di += 2; /* increment by offset size */ + } + break; + case P_PATH: + bcopy(s,d,(int) (*s)+1); /* pascal string with imbedded nulls */ + *di += (*s)+1; + break; + case P_EVEN: + if ((*di % 2) != 0) { + *d = 0; /* zero the current byte */ + (*di)++; + } + break; + case P_ZERO: + bzero(d, siz); + *di += siz; + break; + } + return; +} + + +/* + * Unpack net to host. Source is net, dest is host. + * + */ + +void +upack(pe, src, si, dest, bmap) +PackEntry *pe; +int *si; +byte *src, *dest; +word *bmap; +{ + word sos; + byte *d, *s, *t; + word wrd; + dword dwrd; + time_t timet; + int siz; + + if (pe->pe_bit != 0) /* check bitmap active */ + if ((*bmap & pe->pe_bit) == 0) /* then if not a bitmap item */ + return; /* forget it */ + + d = &dest[pe->pe_off]; /* remember host destination */ + s = &src[*si]; /* net source */ + siz = pe->pe_siz; + + switch (pe->pe_typ) { /* according to the data type */ + case P_BMAP: /* bitmap (2 bytes) */ + t = (byte *)bmap; + t[0] = d[0]; + t[1] = d[1]; + break; + case P_WORD: /* word (2 bytes) */ +#ifdef BYTESWAPPED + d[0] = s[1]; + d[1] = s[0]; +#else BYTESWAPPED + d[0] = s[0]; + d[1] = s[1]; +#endif BYTESWAPPED + *si += 2; + break; + case P_DWRD: /* double word (4 bytes) */ +#ifdef BYTESWAPPED + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; +#else BYTESWAPPED + d[0] = s[0]; + d[1] = s[1]; + d[2] = s[2]; + d[3] = s[3]; +#endif BYTESWAPPED + *si += 4; + break; + case P_BYTE: /* byte (1 byte) */ + d[0] = s[0]; + *si += 1; + break; + case P_BYTS: /* byte string */ + bcopy(s,d,siz); /* copy bytes */ + *si += siz; + break; + case P_TIME: /* external to internal time conv. */ + t = (byte *)&timet; +#ifdef BYTESWAPPED + t[0] = s[3]; + t[1] = s[2]; + t[2] = s[1]; + t[3] = s[0]; +#else BYTESWAPPED + t[0] = s[0]; + t[1] = s[1]; + t[2] = s[2]; + t[3] = s[3]; +#endif BYTESWAPPED + if (mactime_not_inited) + InitPackTime(0); /* assume version 1.0 obsolete */ + if ((sdword)timet == maczerotime) + timet = 0; + else + timet = (time_t)((sdword)timet - mymactime); + d[0] = t[0]; + d[1] = t[1]; + d[2] = t[2]; + d[3] = t[3]; + *si += 4; /* advance */ + break; + case P_PSTR: /* c to pascal string */ + cpyp2cstr(d, s); + *si += (*s)+1; + break; + case P_OSTR: /* offset string */ + t = (byte *)&sos; +#ifdef BYTESWAPPED + t[0] = s[1]; + t[1] = s[0]; +#else BYTESWAPPED + t[0] = s[0]; + t[1] = s[1]; +#endif BYTESWAPPED + *si += 2; + cpyp2cstr(d, &src[sos]); /* copy string */ + break; + case P_OPTH: + t = (byte *)&sos; +#ifdef BYTESWAPPED + t[0] = s[1]; + t[1] = s[0]; +#else BYTESWAPPED + t[0] = s[0]; + t[1] = s[1]; +#endif BYTESWAPPED + *si += 2; + bcopy(&src[sos], d, (int)src[sos]+1); /* copy string */ + break; + case P_PATH: + bcopy(s, d, (int)(*s)+1); /* pascal string with imbedded nulls */ + *si += (*s)+1; + break; + case P_OPTR: + t = (byte *)&sos; +#ifdef BYTESWAPPED + t[0] = s[1]; + t[1] = s[0]; +#else BYTESWAPPED + t[0] = s[0]; + t[1] = s[1]; +#endif BYTESWAPPED + *si += 2; + *((char **)d) = (char *)&src[sos]; + break; + case P_EVEN: + if ((*si % 2) != 0) /* even boundary? */ + (*si)++; /* no, move ahead */ + break; + case P_ZERO: /* supposed to be zero? */ + *si += siz; /* then, just skip */ + *d = 0; /* zero dest */ + break; + } + return; +} + +/* + * void PackDWord(dword s, byte *d) + * + * Pack a double word into destination. + * + */ + +void +PackDWord(s, d) +dword s; +byte *d; +{ + byte *t = (byte *)&s; +#ifdef BYTESWAPPED + d[0] = t[3]; + d[1] = t[2]; + d[2] = t[1]; + d[3] = t[0]; +#else BYTESWAPPED + d[0] = t[0]; + d[1] = t[1]; + d[2] = t[2]; + d[3] = t[3]; +#endif BYTESWAPPED + return; +} + +/* + * void PackWord(dword s, byte *d) + * + * Pack a word into destination. + * + */ + +void +PackWord(s, d) +word s; +byte *d; +{ + byte *t = (byte *)&s; +#ifdef BYTESWAPPED + d[0] = t[1]; + d[1] = t[0]; +#else BYTESWAPPED + d[0] = t[0]; + d[1] = t[1]; +#endif BYTESWAPPED + return; +} + +/* + * + * Unpack double word (4 bytes) from src into dest. + * The src pointer is updated. + * + */ + +void +UnpackDWord(src, dest) +byte **src; +dword *dest; +{ + byte *s = *src; + byte *d = (byte *)dest; +#ifdef BYTESWAPPED + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; +#else BYTESWAPPED + d[0] = s[0]; + d[1] = s[1]; + d[2] = s[2]; + d[3] = s[3]; +#endif BYTESWAPPED + *src += 4; /* update source pointer */ + return; +} + +/* + * + * Unpack word (2 bytes) from src into dest. + * The src pointer is updated. + * + */ + +void +UnpackWord(src, dest) +byte **src; +word *dest; +{ + byte *s = *src; + byte *d = (byte *)dest; +#ifdef BYTESWAPPED + d[0] = s[1]; + d[1] = s[0]; +#else BYTESWAPPED + d[0] = s[0]; + d[1] = s[1]; +#endif BYTESWAPPED + *src += 2; /* update source pointer */ + return; +} diff --git a/lib/afp/afperr.c b/lib/afp/afperr.c new file mode 100644 index 0000000..7cd1ef4 --- /dev/null +++ b/lib/afp/afperr.c @@ -0,0 +1,109 @@ +/* + * $Author: djh $ $Date: 1995/06/19 01:31:07 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afperr.c,v 2.2 1995/06/19 01:31:07 djh Rel djh $ + * $Revision: 2.2 $ + * + */ + +/* + * afperr.c - Error messages + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Wed Apr 1 Schilit Created, based on Charlies routine + * + */ + +#include +#include +#include + +#define NUMERR 45 + +private char *afperrs[NUMERR] = { + "Access Denied", /* 5000 */ + "Authorization not yet complete", /* 5001 */ + "Unknown User Authentication Method (UAM)", /* 5002 */ + "Unknown AFP version", /* 5003 */ + "Bitmap error", /* 5004 */ + "Move of a directory to an inferior is illegal", /* 5005 */ + "Deny conflict", /* 5006 */ + "Directory is not empty", /* 5007 */ + "Disk is full", /* 5008 */ + "End of File", /* 5009 */ + "File is busy", /* 5010 */ + "Volume is a flat volume", /* 5011 */ + "Item not found", /* 5012 */ + "Lock error", /* 5013 */ + "Miscellaneous error, seriously", /* 5014 */ + "No more locks available", /* 5015 */ + "Server not responding", /* 5016 */ + "Object Exists", /* 5017 */ + "Object not found", /* 5018 */ + "Parameter error", /* 5019 */ + "Range not locked", /* 5020 */ + "Range overlap", /* 5021 */ + "Session Closed", /* 5022 */ + "Authorization failure", /* 5023 */ + "Call not supported", /* 5024 */ + "Object type error", /* 5025 */ + "Too many files open", /* 5026 */ + "Server going down", /* 5027 */ + "Can't rename", /* 5028 */ + "Directory not found", /* 5029 */ + "Icon Type error", /* 5030 */ + "Volume locked", /* 5031 */ + "Object locked", /* 5032 */ + "unknown error -5033", /* 5033 */ + "File ID not found", /* 5034 */ + "File already has a file ID", /* 5035 */ + "unknown error -5036", /* 5036 */ + "Catalogue changed", /* 5037 */ + "Exchanging same object", /* 5038 */ + "Nonexistent file ID", /* 5039 */ + "Same password", /* 5040 */ + "Password too short", /* 5041 */ + "Password expired", /* 5042 */ + "Folder inside shared folder", /* 5043 */ + "Folder inside Trash folder" /* 5044 */ +}; + +char *afperr(err) +int err; +{ + static char errbuf[50]; + int e; + + if (err == 0) + return("noErr"); + + e = (-err) - 5000; /* get index */ + if (e < 0 || e >= NUMERR) { + (void) sprintf(errbuf,"unknown error %d",err); + return(errbuf); + } + return(afperrs[e]); +} + +aerror(msg, err) +char *msg; +int err; +{ + int e ; + + if (err == noErr) { + fprintf(stderr, "%s: no error\n",msg); + return; + } + e = (-err) - 5000; /* get index */ + if (e < 0 || e >= NUMERR) { + fprintf(stderr, "%s: unknown error %d\n",msg, err); + return; + } + fprintf(stderr,"%s: %s\n", msg, afperrs[e]); +} diff --git a/lib/afp/afpidaufs.c b/lib/afp/afpidaufs.c new file mode 100644 index 0000000..558d7df --- /dev/null +++ b/lib/afp/afpidaufs.c @@ -0,0 +1,637 @@ +/* + * $Author: djh $ $Date: 1996/06/19 10:14:39 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afpidaufs.c,v 2.5 1996/06/19 10:14:39 djh Rel djh $ + * $Revision: 2.5 $ + * + */ + +/* + * AUFS fixed directory IDs + * + * These routines provide the external <-> path mechanism for + * aufs itself. As well as the direct approach there is a backup + * mechanism using a direct table. This is not very efficient, + * and should not be used normally. It is intended to avoid + * crashing aufs if the server falls over for some reason. + * + * John Forrest + * + */ + +#ifdef FIXED_DIRIDS + +#include +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#include "afpidaufs.h" +#include "../../applications/aufs/afps.h" + +typedef struct { + sdword ext; + char *path; +} Xtable; + +#define TimeOut (15) /* time to wait for answer from server */ + +static Xtable *extras; +static int num_extras; +static int db_open; +static int server_probs; + +static +void check_db() +{ + if (db_open) { + int temp = poll_db(FALSE); + + if (temp < 0) { + db_open = 0; + if (DBDIR) + logit(0, "Can't poll database %d", temp); + } + } +} + +void +aufsExtInit() +{ + int temp; + + init_aufsExt(); + + if ((temp = open_dbase(TRUE)) >= 0) { + db_open = 1; + } else { + db_open = 0; + logit(0, "Can't open database %d",temp); + } + server_probs = 0; + extras = NULL; +#ifdef USE_GDBM + close_dbase(); +#endif /* USE_GDBM */ +} + +static int +existing_entry(id) +sdword id; +{ + datum contents; + + contents = get_datum(id); + return(contents.dptr != NULL); +} + +static sdword +add_to_extras(path) +char *path; +{ + for ( ; ; ) { + int found = 0; + sdword try = valid_id(); + + if (db_open && existing_entry(try)) /* first check in database */ + continue; + + if (extras != NULL) { /* and check for dup in Xtras */ + Xtable *ptr; + int found = 0; + + for (ptr = extras; ptr->path != NULL; ptr++) { + if (ptr->ext == try) { + found = 1; + break; + } + } + if (found) + continue; + } + + /* have unique id! */ + + if (extras == NULL) { + num_extras = 2; + extras = (Xtable *)calloc(num_extras, sizeof(*extras)); + } else { + num_extras += 1; + extras = (Xtable *)realloc(extras, num_extras*sizeof(*extras)); + extras[num_extras-1] = extras[num_extras-2]; + } + extras[num_extras-2].ext = try; + extras[num_extras-2].path = (char *)malloc(strlen(path)+1); + strcpy(extras[num_extras-2].path, path); + return(try); + } + /* NOTREACHED */ +} + +sdword +aufsExtFindId (path) +char *path; +{ + int i, res; + + if (db_open) + check_db(); + + if (db_open) { + sdword id; + + /* have we got an existing entry ? */ + if (lookup_path(path, &id, NULL, 0) > 0) + return(id); + } + + /* + * No, return an error + */ + return (-1); +} + +void +aufsExtExchange(apath, bpath) +char *apath, *bpath; +{ + sdword aID, bID; + char t_path[MAXPATHLEN]; + char *t_ptr; + char *tname = ".txxx"; + + /* + * Get the ID's + */ + aID = aufsExtFindId(apath); + bID = aufsExtFindId(bpath); + if (aID >= 0 && bID >= 0) { + /* + * Both have fileID's -- need to do three renames + */ + strcpy(t_path, apath); + t_ptr = (char *)strrchr(t_path, '/'); + if (t_ptr) { + t_ptr++; + strcpy(t_ptr, tname); + } + aufsExtRename(apath, t_path); + aufsExtRename(bpath, apath); + aufsExtRename(t_path, bpath); + } else if (aID >= 0) { + /* + * Only aID has a filID -- rename to bpath + */ + aufsExtRename(apath, bpath); + } else if (bID >= 0) { + /* + * Only bID has a filID -- rename to apath + */ + aufsExtRename(bpath, apath); + } + return; +} + +sdword +aufsExtEDir(path) +char *path; +{ + int i, res; + + if (db_open) + check_db(); + + if (db_open) { + sdword id; + + /* have we got an existing entry ? */ + if (lookup_path(path, &id, NULL, 0) > 0) + return(id); + + /* ask the server to create a new record - even if having problems */ + if ((res = send_new(path)) >= 0 && server_probs < 5) { + for (i = 0; i < TimeOut*4; i++) { + int poll; + + abSleep(1, FALSE); /* wait 0.25s! */ + poll = poll_db( i%(4*4) == 0 ); /* force poll each 4 s */ + if (poll < 0) { /* lost dbase */ + logit(0, "Lost database %d errno=%d",poll,errno); + db_open = 0; + break; + } else { + if (poll == 0) /* db has changed */ + if (lookup_path(path, &id, NULL, 0) > 0) + return(id); + } + } + } else { + logit(0, "Send_new failed with %d errno=%d", res, errno); + server_probs++; + } + } + + if (DBDIR) + logit(0, "Lost connection to aufsExt server?"); + + return(add_to_extras(path)); +} + +sdword +aufsExtEDirId(parent, rest) +sdword parent; +char *rest; +{ + int i, res; + + if (db_open) + check_db(); + + if (db_open) { + sdword id; + + /* have we got an existing entry ? */ + if (lookup_path_id(parent, rest, &id, NULL, 0) > 0) { + if (DBDIR) + logit(0, "aufsExtEDirId for %s is %d", rest, id); + return(id); + } + + /* ask the server to create a new record - even if having problems */ + if ((res = send_new_id(parent, rest)) >= 0 && server_probs < 5) { + for (i = 0; i < TimeOut*4; i++) { + int poll; + + abSleep(1, FALSE); /* wait 0.25s! */ + poll = poll_db( i%(4*4) == 0 ); /* force poll each 4 s */ + if (poll < 0) { /* lost dbase */ + logit(0, "Lost database %d errno=%d",poll,errno); + db_open = 0; + break; + } else { + if (poll == 0) { /* db has changed */ + if (lookup_path_id(parent, rest, &id, NULL, 0) > 0) { + if (DBDIR) + logit(0, "aufsExtEDirId created new id for %s:%d", rest, id); + return(id); + } + } + } + } + } else { + logit(0, "Send_new failed with %d errno=%d", res, errno); + server_probs++; + } + } + + if (DBDIR) + logit(0, "Lost connection to aufsExt server?"); + + { /* should never really get here, but just in case ... */ + char *str = equiv_path(parent); + char *temp = (char *)malloc(strlen(str) + strlen(rest) + 2); + sdword id; + + strcpy(temp, str); + if (strlen(str) > 1) + strcat(temp, "/"); + strcat(temp, rest); + id = add_to_extras(temp); + free(temp); + return(id); + } +} + +char * +aufsExtPath(edir) +sdword edir; +{ + char *ans = NULL; + + if (db_open) + check_db(); + + if (db_open) + ans = equiv_path(edir); + + if (ans == NULL && extras != NULL) { /* lookup in extras */ + Xtable *ptr; + + for (ptr = extras; ptr->path != NULL; ptr++) + if (ptr->ext == edir) { + ans = ptr->path; + break; + } + } + return(ans); +} + +void +aufsExtDel(path) +char *path; +{ + int found = 0; + + if (db_open) + check_db(); + + if (db_open) { + sdword id; + if (lookup_path(path, &id, NULL, 0) > 1) { + found = 1; + send_delete_id(id); + } + } + + if (! found && extras != NULL) { /* lookup in extras */ + Xtable *ptr; + + for (ptr = extras; ptr->path != NULL; ptr++) { + if (ptr->ext == -1) + continue; + if (strcmp(path, ptr->path) == 0) { + free(ptr->path); + ptr->ext = -1; + break; + } + } + } +} + +void +aufsExtDelId(edir) +sdword edir; +{ + int found = 0; + + if (db_open) + check_db(); + + if (db_open) { + if (existing_entry(edir)) { + found = 1; + send_delete_id(edir); + } + } + + if (! found && extras != NULL) { /* lookup in extras */ + Xtable *ptr; + + for (ptr = extras; ptr->path != NULL; ptr++) { + if (ptr->ext == -1) + continue; + if (ptr->ext == edir) { + free(ptr->path); + ptr->ext = -1; + break; + } + } + } +} + +sdword +aufsExtEFileId(parent, rest) +sdword parent; +char *rest; +{ + int i, res; + + if (db_open) + check_db(); + + if (db_open) { + sdword id; + + /* have we got an existing entry ? */ + if (lookup_path_id(parent, rest, &id, NULL, 0) > 0) + return(id); + + /* ask the server to create a new record - even if having problems */ + if ((res = send_new_fid(parent, rest)) >= 0 && server_probs < 5) { + for (i = 0; i < TimeOut*4; i++) { + int poll; + + abSleep(1, FALSE); /* wait 0.25s! */ + poll = poll_db( i%(4*4) == 0 ); /* force poll each 4 s */ + if (poll < 0) { /* lost dbase */ + logit(0, "Lost database %d errno=%d",poll,errno); + db_open = 0; + break; + } else { + if (poll == 0) { /* db has changed */ + if (lookup_path_id(parent, rest, &id, NULL, 0) > 0) + return(id); + } + } + } + } else { + logit(0, "Send_new failed with %d errno=%d", res, errno); + server_probs++; + } + } + + if (DBDIR) + logit(0, "Lost connection to aufsExt server?"); + + return(-1); +} + +static void +move_in_extras(from, to) +char *from, *to; +{ + if (extras) { /* go through and change any */ + Xtable *ptr; + + for (ptr = extras; ptr->path != NULL; ptr++) { + if (ptr->ext == -1) + continue; + if (strcmp(from, ptr->path) == 0) { + free(ptr->path); + ptr->path = string_copy(to); + break; + } + } + } +} + +static void +rename_in_extras(path, new_name) +char *path, *new_name; +{ + char temp [MAXPATHLEN]; + char *ptr; + + strcpy(temp, path); + if ((ptr = (char *)rindex(temp, '/')) == NULL) + return; /* not a real path */ + strcpy(ptr+1, new_name); + move_in_extras(path, temp); +} + +static void +rename_id_in_extras(id, new_name) +sdword id; +char *new_name; +{ + char temp [MAXPATHLEN]; + char *p; + + if (extras) { /* go through and change any */ + Xtable *ptr; + + for (ptr = extras; ptr->path != NULL; ptr++) { + if (ptr->ext == -1) + continue; + if (ptr->ext == id) { + strcpy(temp, ptr->path); + if ((p = (char *)rindex(temp, '/')) == NULL) + return; /* not a real path */ + strcpy(p+1, new_name); + free(ptr->path); + ptr->path = string_copy(temp); + break; + } + } + } +} + + +void +aufsExtMove(from, to) +char *from, *to; +{ + send_move(from, to); /* send anyway */ + + if (extras) + move_in_extras(from, to); +} + +void +aufsExtMoveIds(orig, newParent, newName) +sdword orig, newParent; +char *newName; +{ + datum data; + sdword parent; + char *name; + + if (db_open) + check_db(); + + if (db_open) { + data = get_datum(orig); + if (data.dptr != NULL) { + if (extract_entry(data, &name, &parent, NULL, NULL) < 0) + return; + send_move_id(parent, name, newParent, newName); + return; + } + } + + /* try to move the extras - note move this only and not children */ + + { + char *newParent_name = aufsExtPath(newParent); + char *temp = (char *)malloc(strlen(newParent_name)+strlen(newName)+2); + + strcpy(temp, newParent_name); + if (strlen(newParent_name) > 1) + strcat(temp, "/"); + strcat(temp, newName); + + move_in_extras(aufsExtPath(orig), temp); + + free(temp); + } +} + +void +aufsExtMoveId(origPath, newParent, newName) +sdword newParent; +char *newName, *origPath; +{ + datum data; + sdword parent; + char *name; + + if (db_open) + check_db(); + + if (db_open) { + if (lookup_path(origPath, NULL, &data, 0) > 0) { + if (extract_entry(data, &name, &parent, NULL, NULL) < 0) + return; + send_move_id(parent, name, newParent, newName); + return; + } + } + + /* otherwise not sure what to do, try falling back on aufsExtMove! */ + + { + char *newParent_name = aufsExtPath(newParent); + char *temp = (char *)malloc(strlen(newParent_name)+strlen(newName)+2); + + strcpy(temp, newParent_name); + if (strlen(newParent_name) > 1) + strcat(temp, "/"); + strcat(temp, newName); + + aufsExtMove(origPath, temp); + + free(temp); + } +} + +void +aufsExtRename(path, newName) +char *path, *newName; +{ + sdword id; + + if (db_open) + check_db(); + + if (db_open) { + if (lookup_path(path, &id, NULL, 0) > 0) { + send_rename_id(id, newName); + return; + } + } + + /* try to move the extras - note rename this only and not children */ + + rename_in_extras(path, newName); +} + +void +aufsExtRenameId(edir, newName) +sdword edir; char *newName; +{ + datum data; + int found = 0; + char *name; + + if (db_open) + check_db(); + + if (db_open) { + data = get_datum(edir); + if (data.dptr != NULL) { + send_rename_id(edir, newName); + return; + } + } + + /* try to move the extras - note rename this only and not children */ + + rename_id_in_extras(edir, newName); +} +#else FIXED_DIRIDS +int afpdid_dummy_1; /* keep loader happy */ +#endif FIXED_DIRIDS diff --git a/lib/afp/afpidaufs.h b/lib/afp/afpidaufs.h new file mode 100644 index 0000000..ef46c87 --- /dev/null +++ b/lib/afp/afpidaufs.h @@ -0,0 +1,182 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:46:58 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afpidaufs.h,v 2.2 1996/06/18 10:46:58 djh Rel djh $ + * $Revision: 2.2 $ + * + */ + +/* + * AUFS fixed directory IDs + * + * joint declarations for libafp.a + * + * John Forrest + * + */ + +#ifndef _afpidaufs_h +#define _afpidaufs_h + +#include +#include +#ifdef USE_GDBM +#include +#define DBM_INSERT GDBM_INSERT +#define DBM_REPLACE GDBM_REPLACE +#else /* USE_GDBM */ +#include +#endif /* USE_GDBM */ + +#include "afpidnames.h" + +#ifndef LAP_ETALK /* not included before */ +#include +#endif LAP_ETALK + +/* query_socket: create socket for use for queries */ +int query_socket( /* void */ ); + +/* connect_query: connect created socket to query entry */ +int connect_query( /* int sock */ ); + +/* query_addr: setup pointer to query socket, and give socket length */ +void query_addr (/* sockaddr **addr, int *len */); + +/* is_directory: return true if a directory of this name exists */ +int is_directory ( /* char *name */ ); + +/* number_datum: returns properly setup datum for an external num */ +datum num_datum ( /* sdword num */ ); + +/* num_from_datum: returns numerical value in number datum */ +sdword num_from_datum ( /* datum dat */ ); + +/* new_entry: create virgin directory entry - assume exists */ +datum new_entry ( /* char *name, sdword parent */ ); + +/* modify: modify the given entry as appropriate - given new */ +datum modify_name ( /* datum entry, char *new_name */ ); +datum modify_parent ( /* datum entry, sdword parent */ ); +datum modify_children ( /* datum entry, int num_children, + sdword *children */ ); + +/* create_entry: create entry datum */ +datum create_entry ( /* char *name, sdword parent, + int num_children, sdword *children */ ); + +/* extract_entry: get data from entry. Returns neg on error. + NB. if NULL is given as actual param, will not return that + particular field. */ +int extract_entry ( /* datum entry, char **name, sdword *parent, + int *num_children, sdword **children */ ); + +/* copy_children: take copy of children (eg as returned by + extract_children). Needed because areas from db are static. */ +sdword *copy_children (/* sdword *children, int num_children */); + +/* lookup_path: if "create" then create elements as needs (from server only). + Return positive on found, neg on error, 0 if not found. Set id and data if found. */ +int lookup_path (/* char *path, sdword *id, data data, int create */); + +/* lookup_path_id: similar, but from already known parent */ +int lookup_path_id + (/* sdword parent, char *remaining, sdword *id, data data, int create */); + +/* get_datum: get datum for an id. result.dptr == NULL if not found */ +datum get_datum ( /* sdword id */ ); + +/* store_datum: write value for id to d/b */ +int store_datum ( /* sdword id, datum data, int flags */ ); + +/* do_delete_entry: delete entry data, but not the record in its parent. */ +void do_delete_entry ( /* sdword id */); + +/* add_child: add a child to its parents record */ +int add_child (/* sdword id, sdword child */); + +/* delete_child: delete a child from its parents record */ +int delete_child (/* sdword id, sdword child */); + +/* equiv_path. Given id, return equiv filename, NULL if not found */ +char *equiv_path (/* sdword id */); + +/* create_init: create initial datum contents */ +datum create_init ( /* char *version, sdword rootEid */ ); + +/* extract_init: extract initial datum contents */ +int extract_init ( /* datum initial, char **version, sdword *rootEid */ ); + +/* valid_id: returns random external id in valid range */ +sdword valid_id( /* void */ ); + +/* init_aufsExt: initialise library */ +void init_aufsExt ( /* void */ ); + +/* open_dbase: open database for reading, and check version if first */ +int open_dbase ( /* int first */ ); + +/* close_dbase: close database */ +void close_dbase ( /* void */ ); + +/* flush_database: force write - used in server only */ +void flush_database( /* void */ ); + +/* poll_db: if db has been changed since open, close and reopen. + returns 0 if has changed, pos if no change, neg on error. + if force, reopen anyway. + */ +int poll_db ( /* int force */ ); + +/* amAufsExt: 1 in server, 0 in clients */ +int amAufsExt ( /* void */); + +/* string_copy: general string copy routine. Should not + really be here! */ +char *string_copy ( /* char * */ ); + +/* send_X: send commands to the server. Neg result on error. */ +int send_new ( /* char *path */); +int send_new_id ( /* sdword parent, char *remaining */); +int send_delete ( /* char *path */); +int send_delete_id ( /* sdword id */ ); +int send_move ( /* char *from, char *to */); +int send_move_id ( /* sdword from_parent, char *old_name, + sdword to_parent, char*new_name */); +int send_rename ( /* char *old_path, char *new_name */); +int send_rename_id ( /* sdword id, char *new_name */ ); +int send_clean ( /* void */ ); + +#define is_init_datum(dat) (dat.dsize == 2 && dat.dptr[0]=='I' && dat.dptr[1] == '\0') +#define is_num_datum(dat) (dat.dptr[0]=='N') + +extern char * aufsSockname; +extern char * aufsDbName; +extern char * aufsDbVersion; +#ifdef USE_GDBM +extern GDBM_FILE db; +#else /* USE_GDBM */ +extern DBM *db; +#endif /* USE_GDBM */ +extern sdword rootEid; +extern char * aufsRootName; + +/* The following are intended to be called from aufs */ + +/* initialise - call before other routines */ +void aufsExtInit ( /* void */ ); + +sdword aufsExtEDir ( /* char *path */ ); +sdword aufsExtEDirId ( /* sdword parent, char *name */ ); +char *aufsExtPath ( /* sdword edir */ ); +void aufsExtDel ( /* char *path */ ); +void aufsExtDelId ( /* sdword edir */ ); +void aufsExtMove ( /* char *from, *to */ ); +void aufsExtMoveId ( /* char *origPath, sdword newParent, + char *newName */ ); +void aufsExtMoveIds ( /* sdword orig, sdword newParent, + char *newName */ ); +void aufsExtRename ( /* char *origPath, char *newName */ ); +void aufsExtRenameId ( /* sdword edir, char *newName */ ); +sdword aufsExtLookForId ( /* sdword parent, char *name */ ); /* -1 if not found */ + +#endif /* _afpidaufs_h */ diff --git a/lib/afp/afpidclnt.c b/lib/afp/afpidclnt.c new file mode 100644 index 0000000..6a5938e --- /dev/null +++ b/lib/afp/afpidclnt.c @@ -0,0 +1,307 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:46:58 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afpidclnt.c,v 2.5 1996/06/18 10:46:58 djh Rel djh $ + * $Revision: 2.5 $ + * + */ + +/* + * AUFS fixed directory IDs + * + * library functions used by client programs only + * + * John Forrest + * + */ + +#ifdef FIXED_DIRIDS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_GDBM +#include +#else /* USE_GDBM */ +#include +#endif /* USE_GDBM */ +#include +#include + +#include "afpidaufs.h" + +static time_t openTime; +static char *pagFile = NULL; + +int +connect_query(sock) +int sock; +{ + struct sockaddr *addr; + int addrlen; + + query_addr(&addr, &addrlen); + return(connect(sock, addr, addrlen)); +} + +time_t +getMtime(file) +char *file; +{ + struct stat buf; + + if (stat(file, &buf) < 0) + return(0); + + return(buf.st_mtime); +} + +void +setPagFile() +{ + pagFile = (char *)malloc(strlen(aufsDbName)+4+1); + strcpy(pagFile, aufsDbName); +#ifndef USE_GDBM + strcat(pagFile, ".pag"); +#endif /* USE_GDBM */ +} + + +int +open_dbase(first) +int first; +{ + datum initial_key, initial; + char *version; + + if (db != NULL) + return(1); + + if (pagFile == NULL) + setPagFile(); + + openTime = getMtime(pagFile); /* do before for safety! */ + +#ifdef USE_GDBM + { int tries = 0; + while ((db = gdbm_open(aufsDbName, 2048, GDBM_READER, 0644, 0L)) == NULL) { + if (++tries > 60) + return(-1); + usleep(250000); + } + } +#else /* USE_GDBM */ + if ((db = dbm_open(aufsDbName, O_RDONLY, 0644)) == NULL) + return(-1); +#endif /* USE_GDBM */ + + if (first) { + initial_key.dptr = "I"; + initial_key.dsize = strlen(initial_key.dptr); +#ifdef USE_GDBM + initial = gdbm_fetch(db, initial_key); +#else /* USE_GDBM */ + initial = dbm_fetch(db, initial_key); +#endif /* USE_GDBM */ + if (initial.dptr == NULL) { + close_dbase(); + return(-1); + } + if (extract_init(initial, &version, &rootEid) < 0) { + close_dbase(); + return(-1); + } + if (strcmp(version, aufsDbVersion) != 0) { + close_dbase(); + return(-2); + } +#ifdef USE_GDBM + if (initial.dptr != NULL) + free(initial.dptr); +#endif /* USE_GDBM */ + } + return(1); +} + +void +close_dbase() +{ + if (db == NULL) + return; +#ifdef USE_GDBM + gdbm_close(db); +#else /* USE_GDBM */ + dbm_close(db); +#endif /* USE_GDBM */ + db = NULL; + return; +} + +int +poll_db(force) +int force; +{ + if (pagFile == NULL) + setPagFile(); + + if (force || getMtime(pagFile) > openTime) { + int res; + + close_dbase(); + res = open_dbase(FALSE); + return((res >= 0) ? 0 : res); + } + + return(1); +} + +static int +send_command(command) +char *command; +{ + int sock; + int n; + + if ((sock = query_socket()) < 0) + return(-2); + + if (connect_query(sock) < 0) { + close(sock); + return(-3); + } + + n = send(sock, command, strlen(command), 0); + close(sock); + + return(n); +} + +int +send_new(path) +char *path; +{ + char command[MAXPATHLEN+3]; + + sprintf(command, "A%s\277", path); + return(send_command(command)); +} + +int +send_new_id(parent, remaining) +sdword parent; +char *remaining; +{ + char command[MAXPATHLEN+32]; + + sprintf(command, "a%d\277%s\277", parent, remaining); + return(send_command(command)); +} + +int +send_new_fid(parent, remaining) +sdword parent; +char *remaining; +{ + char command[MAXPATHLEN+32]; + + sprintf(command, "f%d\277%s\277", parent, remaining); + return(send_command(command)); +} + +int +send_delete(path) +char *path; +{ + char command[MAXPATHLEN+3]; + + sprintf(command, "D%s\277", path); + return(send_command(command)); +} + +int +send_delete_id(id) +sdword id; +{ + char command[32]; + + sprintf(command, "d%d\277", id); + return(send_command(command)); +} + +int +send_move(from, to) +char *from, *to; +{ + char command[2*MAXPATHLEN+4]; + + sprintf(command, "M%s\277%s\277", from, to); + return(send_command(command)); +} + +int +send_move_id(from_parent, old_name, to_parent, new_name) +sdword from_parent, to_parent; +char *old_name, *new_name; +{ + char command[2*MAXPATHLEN+32]; /* should be sufficient */ + + sprintf(command, "m%d\277%s\277%d\277%s\277", from_parent, + old_name, to_parent, new_name); + return(send_command(command)); +} + +int +send_rename(orig_path, new_name) +char *orig_path, *new_name; +{ + char command[2*MAXPATHLEN+4]; + + sprintf(command, "R%s\277%s\277", orig_path, new_name); + return(send_command(command)); +} + +int +send_rename_id(id, new_name) +sdword id; +char *new_name; +{ + char command[MAXPATHLEN+32]; + + sprintf(command, "r%d\277%s\277", id, new_name); + return(send_command(command)); +} + +int +send_clean() +{ + char command[32]; + + sprintf(command, "C"); + return(send_command(command)); +} + +int +amAufsExt() +{ + return(0); +} + +/* + * never called, but needed for linking + * + */ + +void +flush_database() +{ +} +#else FIXED_DIRIDS +int afpdid_dummy_2; /* keep loader happy */ +#endif FIXED_DIRIDS diff --git a/lib/afp/afpidgnrl.c b/lib/afp/afpidgnrl.c new file mode 100644 index 0000000..4376ba4 --- /dev/null +++ b/lib/afp/afpidgnrl.c @@ -0,0 +1,817 @@ +/* + * $Author: djh $ $Date: 1996/06/19 10:27:21 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afpidgnrl.c,v 2.5 1996/06/19 10:27:21 djh Rel djh $ + * $Revision: 2.5 $ + * + */ + +/* + * AUFS fixed directory IDs + * + * general library functions + * + * John Forrest + * + */ + +#ifdef FIXED_DIRIDS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_GDBM +#include +#else /* USE_GDBM */ +#include +#endif /* USE_GDBM */ +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH +#include + +#include "afpidaufs.h" + +/* #define DEBUG 0 */ + +/* globals */ +char *aufsSockname = SOCKNAME; +char *aufsDbName = DBNAME; +char *aufsDbVersion = DBVERSION; +char *aufsRootName = ROOTNAME; +sdword rootEid; +#ifdef USE_GDBM +GDBM_FILE db = NULL; +#else /* USE_GDBM */ +DBM *db = NULL; +#endif /* USE_GDBM */ + +char * +string_copy(str) +char *str; +{ + char *new = (char *)malloc(strlen(str)+1); + + strcpy(new, str); + return(new); +} + +sdword * +copy_children(children, num_children) +sdword *children; +int num_children; +{ + sdword *temp = (sdword*)calloc(num_children, sizeof(sdword)); + + bcopy(children, temp, num_children*sizeof(sdword)); + return(temp); +} + +int +query_socket() +{ + return(socket(AF_UNIX, SOCK_STREAM, 0)); +} + +void +query_addr(addr, len) +struct sockaddr **addr; int *len; +{ + static struct sockaddr_un un_addr; + + un_addr.sun_family = AF_UNIX; + sprintf((char *)un_addr.sun_path, "%s", aufsSockname); + *addr = (struct sockaddr *)&un_addr; + *len = strlen(un_addr.sun_path) + 1 + sizeof(un_addr.sun_family); +} + +int +is_directory(path) +char *path; +{ + struct stat statbuf; + + if (stat(path, &statbuf) < 0) + return(0); + + return(S_ISDIR(statbuf.st_mode)); +} + +int +is_file(path) +char *path; +{ + struct stat statbuf; + + if (stat(path, &statbuf) < 0) + return(0); + + return(S_ISREG(statbuf.st_mode)); +} + +datum +num_datum(num) +/* note only does one at a time! */ +sdword num; +{ + datum temp; + static unsigned char data[1+sizeof(sdword)]; + + data[0] = 'N'; + bcopy((char *)&num, (char *)&data[1], sizeof(sdword)); + temp.dsize = 1+sizeof(sdword); + temp.dptr = (char *)&data[0]; + + return(temp); +} + +sdword +num_from_datum(dat) +datum dat; +{ + sdword temp; + + if (dat.dptr == NULL || dat.dptr[0] != 'N') + return(-1); + + bcopy(&dat.dptr[1], (char *)&temp, sizeof(temp)); + + return(temp); +} + +/* + * format is {} + * all but are sdwords. name is null terminated string + * + */ + +static sdword sdtemp[MAXPATHLEN/sizeof(sdword)]; /* should be sufficient ! */ +static char *temp = (char *)sdtemp; + +datum +new_entry(name, parent) +char *name; +sdword parent; +{ + datum new; + + sdtemp[0] = parent; + sdtemp[1] = 0; /* no children */ + strcpy((char *)&sdtemp[2], name); + new.dptr = temp; + new.dsize = 2*sizeof(sdword) + strlen(name)+1; + + return(new); +} + +datum +create_entry(name, parent, num_children, children) +char *name; +sdword parent; +int num_children; +sdword *children; +{ + datum new; + + sdtemp[0] = parent; + sdtemp[1] = num_children; /* no. children */ + bcopy((char *)children,(char *)&sdtemp[2],num_children*sizeof(sdword)); + strcpy((char *)&sdtemp[2+num_children], name); + new.dptr = temp; + new.dsize = (2+num_children)*sizeof(sdword) + strlen(name)+1; + + return(new); +} + +int +extract_entry(entry, name, parent, num_children, children) +datum entry; +char **name; +sdword *parent; +int *num_children; +sdword **children; +{ + if (entry.dptr == NULL) + return(-1); + + if (entry.dptr == temp) { /* we know we are word aligned! */ + if (parent != NULL) + *parent = sdtemp[0]; + if (num_children != NULL) + *num_children = sdtemp[1]; + if (children != NULL) + *children = &sdtemp[2]; + if (name != NULL) + *name = (char *)&sdtemp[2 + *num_children]; + } else { + int num; + sdword *area = (sdword *)entry.dptr; + + bcopy((char *)&area[1], (char *)&num, sizeof(sdword)); + if (parent != NULL) + bcopy((char *)&area[0], (char *)parent, sizeof(sdword)); + if (num_children != NULL) + *num_children = num; + if (children != NULL) { /* need to ensure this is word aligned! */ + bcopy((char *)&area[2], temp, num*sizeof(sdword)); + *children = sdtemp; + } + if (name != NULL) + *name = (char *)&area[2 + num]; + } + + return(0); +} + +datum +modify_name(entry, new_name) +datum entry; +char *new_name; +{ + int num_children; + + assert(entry.dptr != NULL); + + if (entry.dptr != temp) { /* don't overwrite db */ + bcopy(entry.dptr, temp, entry.dsize); + entry.dptr = temp; + } + num_children = sdtemp[1]; + strcpy((char *)&sdtemp[2+num_children], new_name); + entry.dsize = (2+num_children)*sizeof(sdword)+strlen(new_name)+1; + + return(entry); +} + +datum +modify_parent(entry, parent) +datum entry; +sdword parent; +{ + assert(entry.dptr != NULL); + + if (entry.dptr != temp) { /* don't overwrite db */ + bcopy(entry.dptr, temp, entry.dsize); + entry.dptr = temp; + } + sdtemp[0] = parent; + /* entry.dsize remains unchanged */ + + return(entry); +} + +datum +modify_children(entry, num_children, children) +datum entry; +int num_children; +sdword *children; +{ + sdword *from; + sdword *temp_area = NULL; + datum new; + char *name; + int old_num; + + assert(entry.dptr != NULL); + + if (entry.dptr == temp) { + /* we are going to use temp to create new area! */ + temp_area = (sdword *)malloc(entry.dsize); + bcopy(entry.dptr, temp_area, entry.dsize); + from = temp_area; + } else + from = (sdword *)entry.dptr; + + bcopy((char *)&from[0],(char *)&sdtemp[0],sizeof(sdword)); /* parent */ + bcopy((char *)&from[1], (char *)&old_num, sizeof(sdword)); + sdtemp[1] = num_children; + bcopy(children, &sdtemp[2], num_children*sizeof(sdword)); + name = (char *)&from[2+old_num]; + strcpy((char *)&sdtemp[2+num_children], name); + + new.dsize = (num_children+2)*sizeof(sdword)+strlen(name)+1; + new.dptr = temp; + + if (temp_area != NULL) + free(temp_area); + + return(new); +} + +/* + * note we always place the db version as first field, even though + * it is a bit of a pain to extract the other data - since it is + * not alligned properly + * + */ + +datum +create_init(version, rootEid) +char *version; +sdword rootEid; +{ + datum new; + int len = strlen(version); + + strcpy(temp, version); + bcopy((char *)&rootEid, &temp[len+1], sizeof(sdword)); + new.dptr = temp; + new.dsize = len+1+sizeof(sdword); + + return(new); +} + +int +extract_init(initial, version, rootEid) +datum initial; +char **version; +sdword *rootEid; +{ + int len; + + if (initial.dptr == NULL) + return(-1); + + if (version != NULL) + *version = initial.dptr; + if (rootEid != NULL) { + len = strlen(initial.dptr); + bcopy(&initial.dptr[len+1], (char *)rootEid, sizeof(sdword)); + } + return(1); +} + +#ifdef USE_GDBM +static char ftchtmp[MAXPATHLEN+32]; +#endif /* USE_GDBM */ + +datum +get_datum(id) +sdword id; +{ + datum key,entry; + + key = num_datum(id); +#ifdef USE_GDBM + if (db != NULL) + gdbm_close(db); + entry.dptr = NULL; + { int tries = 0; + while ((db = gdbm_open(aufsDbName, 2048, GDBM_READER, 0644, 0L)) == NULL) { + if (++tries > 60) + return(entry); + usleep(250000); + } + } + entry = gdbm_fetch(db, key); + if (entry.dptr != NULL && entry.dsize <= sizeof(ftchtmp)) { + /* simulate ndbm static data areas */ + bcopy(entry.dptr, ftchtmp, entry.dsize); + free(entry.dptr); + entry.dptr = ftchtmp; + } + gdbm_close(db); + db = NULL; +#else /* USE_GDBM */ + entry = dbm_fetch(db, key); +#endif /* USE_GDBM */ +#ifdef DEBUG + fprintf(stderr, "Read for id %d datum (%d,%x)\n", + id, entry.dsize, entry.dptr); +#endif DEBUG + return(entry); +} + +int +store_datum(id, entry, flags) +sdword id; +datum entry; +int flags; +{ + int ret; + datum key; + + key = num_datum(id); +#ifdef USE_GDBM + if (db != NULL) + gdbm_close(db); + { int tries = 0; + while ((db = gdbm_open(aufsDbName, 2048, GDBM_WRITER, 0644, 0L)) == NULL) { + if (++tries > 60) + return(-1); + usleep(250000); + } + } + ret = gdbm_store(db, key, entry, flags); + gdbm_close(db); + db = NULL; +#else /* USE_GDBM */ + ret = dbm_store(db, key, entry, flags); +#endif /* USE_GDBM */ +#ifdef DEBUG + fprintf(stderr, "Write for id %d datum (%d,%x) gave %d\n", + id, entry.dsize, entry.dptr, ret); +#endif DEBUG + flush_database(); + return(ret); +} + +void +delete_datum(id) +sdword id; +{ + datum key; + + key = num_datum(id); +#ifdef USE_GDBM + if (db != NULL) + gdbm_close(db); + { int tries = 0; + while ((db = gdbm_open(aufsDbName, 2048, GDBM_WRITER, 0644, 0L)) == NULL) { + if (++tries > 60) + return; + usleep(250000); + } + } + gdbm_delete(db, key); + gdbm_close(db); + db = NULL; +#else /* USE_GDBM */ + dbm_delete(db, key); +#endif /* USE_GDBM */ +} + +static +int find_equiv(id, buffer) +sdword id; +char *buffer; +{ + if (id == rootEid) { + strcpy(buffer, ""); /* equiv path copes with special cases */ + return(1); + } else { + datum contents; + char *name; + sdword parent; + int ans; + + contents = get_datum(id); + if (contents.dptr == NULL) { + fprintf(stderr, "find_equiv: get_datum returns NULL\n"); + return(-1); + } + + if (extract_entry(contents, &name, &parent, NULL, NULL) < 0) { +#ifdef DEBUG + fprintf(stderr, "find_equiv: extract_entry returns -1\n"); +#endif DEBUG + return(-1); + } + + name = string_copy(name); /* avoid probs with recursion */ + + ans = find_equiv(parent, buffer); /* recurse */ + + if (ans >= 0) { + strcat(buffer, "/"); + strcat(buffer, name); + } +#ifdef DEBUG + else + fprintf(stderr, "find_equiv: find_equiv returns -1\n"); +#endif DEBUG + + free(name); + + return(ans); + } +} + + +char * +equiv_path(id) +sdword id; +{ + static char buffer[MAXPATHLEN]; /* used for returned string */ + int result; + + if (id == rootEid) + return(aufsRootName); + + buffer[0] = '\0'; /* start with null string in buffer */ + + result = find_equiv(id, buffer); + +#ifdef DEBUG + fprintf(stderr, "Looking up %d gave %s\n", id, /* debug*/ + (result < 0) ? NULL : buffer); +#endif DEBUG + return((result < 0) ? NULL : buffer); +} + +#define null_string(str) (str == NULL || str[0] == '\0') + +static char * +next_ele(path, name) +char *path, *name; +{ + char *ptr; + + if (null_string(path)) { /* given "/" to start with ? */ + strcpy(name, ""); + return(NULL); + } + + ptr = index(path, '/'); + if (ptr == NULL) { + strcpy(name, path); + return(NULL); + } + + name[0] = '\0'; /* use strncat to overcome occational bug */ + strncat(name, path, ptr-path); + + return(ptr+1); /* rest */ +} + +/* + * add child entry to record + * + */ + +int +add_child(id, child) +sdword id; +sdword child; +{ + datum entry; + sdword *children, *new_children; + int num_children; + int ret; + + if (child <= 0 || id <= 0) + return(0); + +#ifdef DEBUG + fprintf(stderr, "adding child %d to %d\n", child, id); +#endif DEBUG + + entry = get_datum(id); + if (entry.dptr == NULL) /* must assume it exists */ + return(-1); + if (extract_entry(entry,NULL,NULL,&num_children,&children) < 0) + return(-1); + new_children = (sdword *)calloc(num_children+1, sizeof(sdword)); + bcopy(children, new_children, num_children*sizeof(sdword)); + num_children += 1; + new_children[num_children-1] = child; + entry = modify_children(entry, num_children, new_children); + if (ret = store_datum(id, entry, DBM_REPLACE) < 0) { +#ifdef DEBUG + fprintf(stderr, "add_child: store datum failed for id %d (%d)\n", + id, ret); +#endif DEBUG + } + free(new_children); + return(1); +} + +/* + * delete child entry to record + * + */ + +int +delete_child(id, child) +sdword id; +sdword child; +{ + datum entry; + sdword *children, *new_children; + int num_children; + int i,new_num; + + if (child <= 0 || id <= 0) + return(0); + + entry = get_datum(id); + if (entry.dptr == NULL) /* must assume it exists */ + return(-1); + if (extract_entry(entry,NULL,NULL,&num_children,&children) < 0) + return(-1); + /* too big won't hurt */ + new_children = copy_children(children, num_children); + bcopy(children, new_children, num_children*sizeof(sdword)); + for (i=0,new_num=0; i= 0) + break; + } + + child = new_entries(id, remaining); /* recurse */ + + (void)add_child(id, child); /* not sure what to do with error */ + + return(id); +} + +int +lookup_path_id(parent, path, id, dat, create) +sdword parent; +char *path; /* relative to parent */ +sdword *id; +datum *dat; +int create; /* server only */ +{ + sdword current = parent; + char name[128]; + char *remaining; + sdword *children = NULL, child; + int num_children; + datum data, child_data; + int indx; + char *child_name; + + data = get_datum(parent); + if (data.dptr == NULL) + return(-2); /* help!! Lost something !! */ + + remaining = next_ele(path, name); + + if (null_string(name)) { /* were after parent */ + if (dat) + *dat = data; + if (id) + *id = parent; + return(1); + } + + redo: + for ( ; ; ) { +#ifdef DEBUG + fprintf(stderr, "Looking up %d/%s\n", current, name); /* debug */ +#endif DEBUG + if (extract_entry(data, NULL, NULL, &num_children, &children) < 0) + return(-1); /* error */ + /* take copy in dbm overwrites original area */ + children = copy_children(children, num_children); + indx = 0; + while (indx < num_children) { + child = children[indx++]; + child_data = get_datum(child); + if (child_data.dptr == NULL) + continue; /* error - missing child */ + if (extract_entry(child_data, &child_name, NULL, NULL, NULL) < 0) + continue; /* error, no name? */ + if (strcmp(name, child_name) == 0) { /* found it ! */ + if (null_string(remaining)) { + if (id) + *id = child; + if (dat) + *dat = child_data; + free(children); + return(1); + } else { + free(children); + path = remaining; + remaining = next_ele(remaining, name); + current = child; + data = child_data; + goto redo; + } + } + } + /* if we get this far, something is missing */ + free(children); + + /* something missing */ + if (!create || !amAufsExt()) + return(0); + + /* assume we are in the server */ + + child = new_entries(current, path); + if (add_child(current, child) < 0) + return(-1); + flush_database(); + + /* having created object, loop back to it. Perhaps not the most + efficient way, but seems reliable */ + data = get_datum(current); + if (data.dptr == NULL) /* should never happen */ + return(-2); + } +} + +int +lookup_path(path, id, dat, create) +char *path; +sdword *id; +datum *dat; +int create; /* server only */ +{ + char *remaining; + + + if (null_string(path)) + remaining = NULL; + else + remaining = path+1; /* skip '/' */ + + return(lookup_path_id(rootEid, remaining, id, dat, create)); +} + +void +do_delete_entry(id) +sdword id; +{ + datum data; + sdword *children; + int num_children; + int i; + + data = get_datum(id); + if (data.dptr == NULL) + return; + if (extract_entry(data, NULL, NULL, &num_children, &children) >= 0) { + children = copy_children(children, num_children); + for (i=0; i < num_children ; i++) + do_delete_entry(children[i]); + free(children); + } + delete_datum(id); +} + +#define MINID 200 /* The first so many have special meanings. */ +#define MAXID ((1<<30)-1) /* 2^30-1, guessed that this is in range. */ + +sdword +valid_id() +{ +#ifdef USERAND + sdword id = rand()%(MAXID-MINID)+MINID; +#else /* USERAND */ + sdword id = random()%(MAXID-MINID)+MINID; +#endif /* USERAND */ + + return(id); +} + +#undef MINID +#undef MAXID + +void +init_aufsExt() +{ +#ifdef USERAND + srand(time(NULL)); +#else /* USERAND */ + srandom((int)time(NULL)); +#endif /* USERAND */ +} +#else FIXED_DIRIDS +int afpdid_dummy_3; /* keep loader happy */ +#endif FIXED_DIRIDS diff --git a/lib/afp/afpidnames.h b/lib/afp/afpidnames.h new file mode 100644 index 0000000..5825f44 --- /dev/null +++ b/lib/afp/afpidnames.h @@ -0,0 +1,25 @@ +/* + * $Author: djh $ $Date: 1996/04/27 12:31:28 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afpidnames.h,v 2.1 1996/04/27 12:31:28 djh Rel djh $ + * $Revision: 2.1 $ + * + */ + +/* + * AUFS fixed directory IDs + * + * database details + * + * John Forrest + * + */ + +#ifndef _afpidnames_h +#define _afpidnames_h + +#define DBVERSION "1.1" +#define DBNAME "/usr/local/lib/cap/afpIDdb" +#define SOCKNAME "/usr/local/lib/cap/afpIDsock" +#define ROOTNAME "/" + +#endif _afpidnames_h diff --git a/lib/afp/afposlock.c b/lib/afp/afposlock.c new file mode 100644 index 0000000..46d8a29 --- /dev/null +++ b/lib/afp/afposlock.c @@ -0,0 +1,621 @@ +/* + * $Author: djh $ $Date: 1996/03/07 09:52:04 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afposlock.c,v 2.12 1996/03/07 09:52:04 djh Rel djh $ + * $Revision: 2.12 $ + * + */ + +/* + * afposlock.c - Appletalk Filing Protocol OS Interface for Byte Range Lock + * and other file lock routines + * + * Four cases: (a) have flock, (b) have lockf, (c) have sysV locking + * (d) have both lockf and flock + * For A: can't do byte range lock + * For B: can't lock out writers on read-only files + * For C: just right + * For D: just right + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * July 1987 CCKim Created. + * + */ + +#ifdef NeXT +#define NOLOCKF +#endif NeXT + +/* + * This is a fall-back in case Configure messes up. + * flock() lives in /lib/libbsd.a under IBM AIX and Configure won't + * normally find it. Usually we could live with that except that the + * result of not using flock() with AIX is a disaster, second and + * subsequent AppleShare sessions hang until the first one finishes. + */ + +#ifdef AIX +# ifdef NOFLOCK +# undef NOFLOCK +# endif NOFLOCK +#endif AIX + +#ifdef SOLARIS +#define USEFCNTLLOCK +#include +#include +#endif SOLARIS + +#ifndef NOLOCKF +/* on convex, lockf requires fcntl.h */ +# if defined(LOCKFUSESFCNTL) || defined(USEFCNTLLOCK) +# include +# else /* LOCKFUSESFCNTL || USEFCNTLLOCK */ +# ifdef apollo + /* include F_LOCK etc. in unistd.h defns. */ +# define _INCLUDE_SYS5_SOURCE +# endif apollo +# include +# ifdef apollo +# include +# include +# endif apollo + /* + * unistd defines these unnecessarily + * (and problematically) + * + */ +# ifdef ultrix +# undef R_OK +# undef W_OK +# undef X_OK +# undef F_OK +# endif ultrix +# ifdef pyr +# undef R_OK +# undef W_OK +# undef X_OK +# undef F_OK +# endif pyr +# endif /* LOCKFUSESFCNTL || USEFCNTLLOCK */ +#endif NOLOCKF + +#ifdef gould +#include +#endif gould + +#include +#include +#include +#include +#include +#include + +#ifdef NOFLOCK +# ifndef NOLOCKF +# ifndef LOCKF_ONLY +# define LOCKF_ONLY +# endif LOCKF_ONLY +# endif NOLOCKF +#endif NOFLOCK + +#ifdef xenix5 +# include +# define XENIX_LOCKING +#endif xenix5 + +/* + * The following routines define Byte Range Locking + * + * The following system calls (that only exist on some machines) will + * do the right thing: + * lockf - system V routine + * + * For systems without lockf, etc., someone has to hack together + * some system daemon or kernel driver. + * +*/ + +#ifdef NOLOCKF +/* These are here for systems that can't do byte range lock */ + +OSByteRangeLock(fd, offset, length, unlockflag, startendflag, fpos) +int fd; +sdword offset, length; +int unlockflag, startendflag; +sdword *fpos; +{ +#ifdef DEBUG + printf("OS Byte Range lock: unimplemented on this OS\n"); +#endif DEBUG +#ifdef notdef + return(aeMiscErr); +#endif notdef + return(aeParamErr); /* specially for System 7.0 */ +} + +OSErr +OSTestLock(fd, length) +int fd; +sdword length; +{ + return(noErr); +} +#else NOLOCKF +/* + * Institute a byte range lock on a file + * + * offset can be negative if startendflag = 1 and then denotes + * offset relative from end of fork + * length must be positive + * unlockflag is set if want unlock + * + * Unlike AFP Spec: multiple ranges may overlap and unlock thusly + * may return NoMoreLocks as an error. + * + */ + +OSErr +OSByteRangeLock(fd, offset, length, flags, fpos) +int fd; +sdword offset, length; +byte flags; +sdword *fpos; +{ + int pos, err; + int startendflag = (BRL_START & flags); + int unlockflag = (BRL_UNLOCK & flags); + extern int errno; + +#ifdef DEBUG + printf("OSBRL: %slocking offset %ld, length %x (%ld), from %s\n", + unlockflag ? "un" : "", offset, length, length, + startendflag ? "end" : "start"); +#endif DEBUG + + if (length < 0 /* can only be -1 */ + || length == 0x7fffffff) /* or magic flag used by MicroSoft */ + length = 0; /* length zero means entire file! */ + +#ifdef USEFCNTLLOCK + { struct flock flck; + flck.l_type = unlockflag ? F_UNLCK : F_WRLCK; + flck.l_whence = startendflag ? L_XTND : L_SET; +#ifdef DENYREADWRITE + flck.l_start = offset+4; +#else DENYREADWRITE + flck.l_start = offset; +#endif DENYREADWRITE + flck.l_len = length; + + if (fcntl(fd, F_SETLK, &flck) < 0) { + switch (errno) { + case EAGAIN: + case EBADF: + case EDEADLK: + case EINVAL: + case ENOLCK: + return(aeNoMoreLocks); + break; + default: + return(aeParamErr); + break; + } + } + } +#else USEFCNTLLOCK + if ((pos = lseek(fd, (off_t)offset, (startendflag ? L_XTND : L_SET))) < 0) { + *fpos = -1; /* unknown */ + return(aeParamErr); + } + *fpos = pos; + +#ifdef DENYREADWRITE + lseek(fd, 4, L_INCR); +#endif DENYREADWRITE + + err = lockf(fd, unlockflag ? F_ULOCK : F_TLOCK, length); + +#ifdef DENYREADWRITE + lseek(fd, -4, L_INCR); +#endif DENYREADWRITE + + if (err < 0) { +#ifdef DEBUG + printf("CSByteRangeLock"); +#endif DEBUG + switch (errno) { +#ifdef EREMOTE + /* not sure if this is the best thing to do */ + case EREMOTE: + return(noErr); +#endif EREMOTE +#ifdef notdef + return(aeRangeNotLocked); + return(aeRangeOverlap); +#endif notdef + case EACCES: + case EAGAIN: +#ifdef EINTR + case EINTR: +#endif EINTR +#ifdef notdef + return(aeLockErr); +#endif notdef +#ifdef EBADF + case EBADF: /* SunOS */ +#endif EBADF + /* really permission denied (or, unlikely: bad file) */ +#ifndef gould +#ifdef ENOLCK /* sunos */ + case ENOLCK: +#endif ENOLCK +#endif gould +#ifdef EDEADLK + case EDEADLK: +#endif EDEADLK +#ifdef notdef + return(aeNoMoreLocks); +#endif notdef + default: + return(aeParamErr); /* specially for System 7.0 */ + } + } +#endif USEFCNTLLOCK + return(noErr); +} + +/* + * returns noErr if the range starting at the current file position + * for length "length" is not locked. + * + */ + +OSErr +OSTestLock(fd, length) +int fd; +sdword length; +{ + int n; + extern int errno; + +#ifdef XENIX_LOCKING + if ((n = locking(fd, LK_NBRLCK, length)) >= 0) + n = locking(fd, LK_UNLCK, length); +#else XENIX_LOCKING +#if defined(DENYREADWRITE) || defined(USEFCNTLLOCK) + { struct flock flck; + + n = 0; + flck.l_type = F_RDLCK; + flck.l_whence = L_INCR; /* SEEK_CUR */ +#ifdef DENYREADWRITE + flck.l_start = 4; +#else DENYREADWRITE + flck.l_start = 0; +#endif DENYREADWRITE + flck.l_len = length; + if (fcntl(fd, F_GETLK, &flck) != -1) { + if (flck.l_type == F_WRLCK) { + errno = EAGAIN; + n = -1; + } + } + } +#else /* DENYREADWRITE || USEFCNTLLOCK */ + n = lockf(fd, F_TEST, length); +#endif /* DENYREADWRITE || USEFCNTLLOCK */ +#endif XENIX_LOCKING + + if (n < 0) { +#ifdef DEBUG +#ifdef EBADF + if (errno == EBADF) { + printf("OSTestLock: Bad File Descriptor %d\n", fd); + return(aeLockErr); + } +#endif EBADF +#ifdef EAGAIN + if (errno == EAGAIN) { + printf("OSTestLock: File already locked %d\n", fd); + return(aeLockErr); + } +#endif EAGAIN +#endif DEBUG +#ifdef EREMOTE + if (errno == EREMOTE) + return(noErr); +#endif EREMOTE +#ifdef DEBUG + printf("File is locked\n"); +#endif DEBUG + return(aeLockErr); + } + return(noErr); +} +#endif NOLOCKF + +/* + * The following calls are used to coordinate read/writes for various + * files used by aufs. + * + * The basic primatives are: + * Lock File for Read - lock a file for reading (others may do the + * same). Do it if no write locks are in effect. + * Lock File for Write - lock a file for writing (other may not at the + * same time). Do it if no read or write locks are in effect. + * Unlock file + * + * Note, lffr is essentially a shared lock while lffw is an exclusive + * lock. + * + * Since most unix systems only issue advisory locks, the deal is that + * you call the routines before you do any reading or writing - this + * should be sufficient. + * Note: the lock calls are assumed to block - routines must be "good" + * about unlocking or things will break in a big way. + * + * Implementations: + * flock - implements shared and exclusive locks: perfect + * lockf - implements exclusive locks only (and only on writable fds): + * okay, but reads can get "out of date" or "bad" data + * + * + */ + +boolean +OSLockFileforRead(fd) +int fd; +{ +#ifdef USEFCNTLLOCK + { struct flock flck; + + flck.l_type = F_RDLCK; + flck.l_whence = L_SET; + flck.l_start = 0; + flck.l_len = 0; + + if (fcntl(fd, F_SETLKW, &flck) < 0) { + switch (errno) { + case EBADF: /* pass read-only files */ + return(TRUE); + break; + default: + return(FALSE); + break; + } + } + } +#else USEFCNTLLOCK +#ifdef hpux + off_t saveoffs; +#endif hpux +#ifdef xenix5 + lseek(fd, 0L, L_SET); +#endif xenix5 +#ifdef XENIX_LOCKING + if (locking(fd, LK_RLCK, 0L) < 0) { +#ifdef DEBUG + printf("OSLockFileforRead"); +#endif DEBUG + return(FALSE); /* problem!!! */ + } +#else XENIX_LOCKING +# ifndef NOFLOCK + if (flock(fd, LOCK_SH) < 0) + return(FALSE); /* problem!!! */ +# else +# ifdef LOCKF_ONLY +# ifdef hpux + saveoffs = lseek(fd, 0L, SEEK_CUR); + lseek(fd, 0L, SEEK_SET); +# endif hpux + if (lockf(fd, F_LOCK, 0) < 0) { +# ifdef hpux + lseek(fd, saveoffs, SEEK_SET); +# endif hpux +# ifdef EREMOTE + if (errno == EREMOTE) + return(TRUE); +# endif + if (errno == EBADF) /* file is open read-only */ + return(TRUE); /* can't do lock, so let it go on */ + return(FALSE); + } +# ifdef hpux + lseek(fd, saveoffs, SEEK_SET); +# endif hpux +# endif +# endif +#endif XENIX_LOCKING +#endif USEFCNTLLOCK + return(TRUE); +} + +boolean +OSLockFileforWrite(fd) +int fd; +{ +#ifdef USEFCNTLLOCK + { struct flock flck; + + flck.l_type = F_WRLCK; + flck.l_whence = L_SET; + flck.l_start = 0; + flck.l_len = 0; + + if (fcntl(fd, F_SETLKW, &flck) < 0) { + switch (errno) { + case EAGAIN: + case EBADF: + default: + return(FALSE); + break; + } + } + } +#else USEFCNTLLOCK +#ifdef hpux + off_t saveoffs; +#endif hpux +#ifdef xenix5 + lseek(fd, 0L, L_SET); +#endif xenix5 +#ifdef XENIX_LOCKING + if (locking(fd, LK_LOCK, 0L) < 0 + || locking(fd, LK_UNLCK, 0L) < 0) { +#ifdef DEBUG + printf("OSLockFileforWrite"); +#endif DEBUG + return(FALSE); /* problem!!! */ + } +#else XENIX_LOCKING +# ifndef NOFLOCK + if (flock(fd, LOCK_EX) < 0) + return(FALSE); /* problem!!! */ +# else NOFLOCK +# ifdef LOCKF_ONLY +# ifdef hpux + saveoffs = lseek(fd, 0L, SEEK_CUR); + lseek(fd, 0L, SEEK_SET); +# endif hpux + if (lockf(fd, F_LOCK, 0) < 0) { +# ifdef hpux + lseek(fd, saveoffs, SEEK_SET); +# endif hpux +# ifdef EREMOTE + if (errno == EREMOTE) + return(TRUE); +# endif /* EREMOTE */ + return(FALSE); + } +# ifdef hpux + lseek(fd, saveoffs, SEEK_SET); +# endif hpux +# endif /* LOCKF ONLY */ +# endif NOFLOCK +#endif XENIX_LOCKING +#endif USEFCNTLLOCK + return(TRUE); +} + +/* + * This implements an exclusive lock on the file. It differs from + * OSLockFileforRead in that it doesn't block + * + */ + +#ifdef notdef /* not used */ +boolean +OSMaybeLockFile(fd) +{ +#ifdef xenix5 + lseek(fd, 0L, L_SET); +#endif xenix5 +#ifdef XENIX_LOCKING + if (locking(fd, LK_NBLCK, 0L) < 0 + || locking(fd, LK_NBRLCK, 0L) < 0) { +#ifdef DEBUG + printf("OSMaybeLockFile"); +#endif DEBUG + return(FALSE); /* problem!!! */ + } +#else XENIX_LOCKING +# ifndef NOFLOCK + if (flock(fd, LOCK_EX|LOCK_NB) < 0) + return(FALSE); /* problem!!! */ +# else +# ifdef LOCKF_ONLY + if (lockf(fd, F_TLOCK, 0) < 0) { +# ifdef EREMOTE + if (errno == EREMOTE) + return(TRUE); +# endif EREMOTE + return(FALSE); + } +# endif LOCKF_ONLY +# endif NOFLOCK +#endif XENIX_LOCKING + return(true); +} +#endif + +boolean +OSUnlockFile(fd) +int fd; +{ +#ifdef USEFCNTLLOCK + { struct flock flck; + + flck.l_type = F_UNLCK; + flck.l_whence = L_SET; + flck.l_start = 0; + flck.l_len = 0; + + if (fcntl(fd, F_SETLKW, &flck) < 0) + return(FALSE); + } +#else USEFCNTLLOCK +#ifdef hpux + off_t saveoffs; +#endif hpux +#ifdef xenix5 + lseek(fd, 0L, L_SET); +#endif xenix5 +#ifdef XENIX_LOCKING + if (locking(fd, LK_UNLCK, 0L) < 0) { +#ifdef DEBUG + printf("OSUnlockFile"); +#endif DEBUG + return(FALSE); /* problem!!! */ + } +#else XENIX_LOCKING +# ifndef NOFLOCK + if (flock(fd, LOCK_UN) < 0) + return(FALSE); +# else /* NOFLOCK */ +# ifdef LOCKF_ONLY +# ifdef hpux + saveoffs = lseek(fd, 0L, SEEK_CUR); + lseek(fd, 0L, SEEK_SET); +# endif hpux + if (lockf(fd, F_ULOCK, 0) < 0) { +# ifdef hpux + lseek(fd, saveoffs, SEEK_SET); +# endif hpux +# ifdef EREMOTE + if (errno == EREMOTE) + return(TRUE); +# endif /* EREMOTE */ + return(FALSE); + } +# ifdef hpux + lseek(fd, saveoffs, SEEK_SET); +# endif hpux +# endif /* LOCKF_ONLY */ +# endif /* end else NOFLOCK */ +#endif XENIX_LOCKING +#endif USEFCNTLLOCK + return(TRUE); +} + +getlockinfo(haveflock,havelockf) +int *haveflock; +int *havelockf; +{ +#ifdef NOFLOCK + *haveflock = 0; +#else + *haveflock = 1; +#endif +#ifdef NOLOCKF + *havelockf = 0; +#else + *havelockf = 1; +#endif +} diff --git a/lib/afp/afppacks.c b/lib/afp/afppacks.c new file mode 100644 index 0000000..307c859 --- /dev/null +++ b/lib/afp/afppacks.c @@ -0,0 +1,754 @@ +/* + * $Author: djh $ $Date: 1996/04/25 03:26:19 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afppacks.c,v 2.5 1996/04/25 03:26:19 djh Rel djh $ + * $Revision: 2.5 $ + * + */ + +/* + * afppacks.c - Packing and unpacking templates + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * march 23, 1987 CCKim Created from afpcmd.c + * + */ + +#include +#include +#include +#include + +#ifndef NULL +#define NULL 0 +#endif NULL + +PackEntry EnumPackR[] = { + PACK(ERPPtr,P_WORD,enur_fbitmap), + PACK(ERPPtr,P_WORD,enur_dbitmap), + PACK(ERPPtr,P_WORD,enur_actcnt), + PACKEND() + }; + +PackEntry DirParmPackR[] = { + PACK(FDParmPtr,P_WORD,fdp_fbitmap), + PACK(FDParmPtr,P_WORD,fdp_dbitmap), + PACK(FDParmPtr,P_BYTE,fdp_flg), + PACK(FDParmPtr,P_BYTE,fdp_zero), + PACKEND() + }; + + +PackEntry FilePackR[] = { + PACK(FDParmPtr,P_BMAP,fdp_fbitmap), /* start of bitmap parms */ + PAKB(FDParmPtr,P_WORD,fdp_attr,FP_ATTR), + PAKB(FDParmPtr,P_DWRD,fdp_pdirid,FP_PDIR), + PAKB(FDParmPtr,P_TIME,fdp_cdate,FP_CDATE), + PAKB(FDParmPtr,P_TIME,fdp_mdate,FP_MDATE), + PAKB(FDParmPtr,P_TIME,fdp_bdate,FP_BDATE), + PKSB(FDParmPtr,P_BYTS,fdp_finfo,FP_FINFO), + PKSB(FDParmPtr,P_OSTR,fdp_lname,FP_LNAME), + PKSB(FDParmPtr,P_OSTR,fdp_sname,FP_SNAME), + PAKB(FDParmPtr,P_DWRD,fdp_parms.fp_parms.fp_fileno,FP_FILNO), + PAKB(FDParmPtr,P_DWRD,fdp_parms.fp_parms.fp_dflen,FP_DFLEN), + PAKB(FDParmPtr,P_DWRD,fdp_parms.fp_parms.fp_rflen,FP_RFLEN), + PAKB(FDParmPtr,P_WORD,fdp_prodos_ft,FP_PDOS), + PAKB(FDParmPtr,P_DWRD,fdp_prodos_aux,FP_PDOS), + PACKEND() +}; + + +PackEntry DirPackR[] = { + PACK(FDParmPtr,P_BMAP,fdp_dbitmap), /* start of bitmap parms */ + PAKB(FDParmPtr,P_WORD,fdp_attr,DP_ATTR), + PAKB(FDParmPtr,P_DWRD,fdp_pdirid,DP_PDIR), + PAKB(FDParmPtr,P_TIME,fdp_cdate,DP_CDATE), + PAKB(FDParmPtr,P_TIME,fdp_mdate,DP_MDATE), + PAKB(FDParmPtr,P_TIME,fdp_bdate,DP_BDATE), + PKSB(FDParmPtr,P_BYTS,fdp_finfo,DP_FINFO), + PKSB(FDParmPtr,P_OSTR,fdp_lname,DP_LNAME), + PKSB(FDParmPtr,P_OSTR,fdp_sname,DP_SNAME), + PAKB(FDParmPtr,P_DWRD,fdp_parms.dp_parms.dp_dirid,DP_DIRID), + PAKB(FDParmPtr,P_WORD,fdp_parms.dp_parms.dp_nchild,DP_CHILD), + PAKB(FDParmPtr,P_DWRD,fdp_parms.dp_parms.dp_ownerid,DP_CRTID), + PAKB(FDParmPtr,P_DWRD,fdp_parms.dp_parms.dp_groupid,DP_GRPID), + PAKB(FDParmPtr,P_DWRD,fdp_parms.dp_parms.dp_accright,DP_ACCES), + PAKB(FDParmPtr,P_WORD,fdp_prodos_ft,DP_PDOS), + PAKB(FDParmPtr,P_DWRD,fdp_prodos_aux,DP_PDOS), + PACKEND() + }; + +PackEntry ProtoGVPP[] = { /* FPGetVolParms */ + PACK(GVPPPtr,P_BYTE,gvp_cmd), /* command */ + PACK(GVPPPtr,P_ZERO,gvp_zero), /* always zero */ + PACK(GVPPPtr,P_WORD,gvp_volid), /* volume id */ + PACK(GVPPPtr,P_WORD,gvp_bitmap), /* request bitmap */ + PACKEND() + }; + + +PackEntry ProtoSVPP[] = { /* FPSetVolParms */ + PACK(SVPPPtr,P_BYTE,svp_cmd), /* command */ + PACK(SVPPPtr,P_ZERO,svp_zero), /* always zero */ + PACK(SVPPPtr,P_WORD,svp_volid), /* volume id */ + PACK(SVPPPtr,P_WORD,svp_bitmap), /* set bitmap */ + PACK(SVPPPtr,P_DWRD,svp_backdata), /* backup data to set */ + PACKEND() + }; + +PackEntry ProtoOVP[] = { /* FPOpenVol */ + PACK(OVPPtr,P_BYTE,ovl_cmd), /* command */ + PACK(OVPPtr,P_ZERO,ovl_zero), /* always zero */ + PACK(OVPPtr,P_WORD,ovl_bitmap), /* request bitmap */ + PAKS(OVPPtr,P_PSTR,ovl_name), /* volume name packed */ + PACKEVEN(), /* even out if necessary */ + PAKS(OVPPtr,P_BYTS,ovl_pass), /* password packed */ + PACKEND() + }; + +PackEntry ProtoCVP[] = { /* FPCloseVol */ + PACK(CVPPtr,P_BYTE,cv_cmd), /* command */ + PACK(CVPPtr,P_ZERO,cv_zero), /* always zero */ + PACK(CVPPtr,P_WORD,cv_volid), /* volume ID */ + PACKEND() + }; + +PackEntry ProtoFVP[] = { /* FPFlushVol */ + PACK(FPPtr,P_BYTE,fls_cmd), /* command */ + PACK(FPPtr,P_ZERO,fls_zero), /* always zero */ + PACK(FPPtr,P_WORD,fls_volid), /* volume ID */ + PACKEND() + }; + +PackEntry ProtoMNP[] = { /* FPMapName */ + PACK(MNPPtr,P_BYTE,mpn_cmd), /* MapName command */ + PACK(MNPPtr,P_BYTE,mpn_fcn), /* function */ + PAKS(MNPPtr,P_PATH,mpn_name), /* name */ + PACKEND() + }; + +PackEntry ProtoMIP[] = { /* FPMapID */ + PACK(MIPPtr,P_BYTE,mpi_cmd), /* MapID command */ + PACK(MIPPtr,P_BYTE,mpi_fcn), /* function */ + PACK(MIPPtr,P_DWRD,mpi_id), /* ID to map */ + PACKEND() + }; + + +PackEntry ProtoGFkPP[] = { /* FPGetForkParms */ + PACK(GFkPPPtr,P_BYTE,gfp_cmd), /* command */ + PACK(GFkPPPtr,P_ZERO,gfp_zero), /* zero word */ + PACK(GFkPPPtr,P_WORD,gfp_refnum), /* reference number */ + PACK(GFkPPPtr,P_WORD,gfp_bitmap), /* bitmap */ + PACKEND() + }; + +PackEntry ProtoSFkPP[] = { /* FPSetForkParms */ + PACK(SFkPPPtr,P_BYTE,sfkp_cmd), /* command */ + PACK(SFkPPPtr,P_ZERO,sfkp_zero), /* zero word */ + PACK(SFkPPPtr,P_WORD,sfkp_refnum), /* reference number */ + PACK(SFkPPPtr,P_WORD,sfkp_bitmap), /* bitmap */ + PACK(SFkPPPtr,P_BMAP,sfkp_bitmap), /* for attributes */ + PAKB(SFkPPPtr,P_DWRD,sfkp_dflen,FP_DFLEN), /* fork length */ + PAKB(SFkPPPtr,P_DWRD,sfkp_rflen,FP_RFLEN), /* fork length */ + PACKEND() + }; + +PackEntry ProtoOFkP[] = { /* FPOpenFork */ + PACK(OFkPPtr,P_BYTE,ofk_cmd), /* command */ + PACK(OFkPPtr,P_BYTE,ofk_rdflg), /* resource/data flag */ + PACK(OFkPPtr,P_WORD,ofk_volid), /* volume id */ + PACK(OFkPPtr,P_DWRD,ofk_dirid), /* directory id */ + PACK(OFkPPtr,P_WORD,ofk_bitmap), /* bitmap */ + PACK(OFkPPtr,P_WORD,ofk_mode), /* access mode */ + PACK(OFkPPtr,P_BYTE,ofk_ptype), /* path type */ + PAKS(OFkPPtr,P_PATH,ofk_path), /* path name */ + PACKEND() +}; + +PackEntry ProtoOFkRP[] = { + PACK(OFkRPPtr, P_WORD, ofkr_bitmap), /* file bitmap */ + PACK(OFkRPPtr, P_WORD, ofkr_refnum), /* open fork reference number */ + PACKEND() /* file params follow */ +}; + + +PackEntry ProtoCFkP[] = { /* FPCloseFork */ + PACK(CFkPPtr,P_BYTE,cfk_cmd), /* command */ + PACK(CFkPPtr,P_ZERO,cfk_zero), /* zero byte */ + PACK(CFkPPtr,P_WORD,cfk_refnum), /* reference number */ + PACKEND() +}; + +PackEntry ProtoGFDPP[] = { /* FPGetFileDirParms */ + PACK(GFDPPPtr,P_BYTE,gdp_cmd), /* command */ + PACK(GFDPPPtr,P_ZERO,gdp_zero), /* always zero */ + PACK(GFDPPPtr,P_WORD,gdp_volid), /* volume id */ + PACK(GFDPPPtr,P_DWRD,gdp_dirid), /* directory id */ + PACK(GFDPPPtr,P_WORD,gdp_fbitmap), /* file request bitmap */ + PACK(GFDPPPtr,P_WORD,gdp_dbitmap), /* directory request bitmap */ + PACK(GFDPPPtr,P_BYTE,gdp_ptype), /* path type */ + PAKS(GFDPPPtr,P_PATH,gdp_path), /* path */ + PACKEND() + }; + +PackEntry ProtoCFP[] = { /* FPCreateFile */ + PACK(CFPPtr,P_BYTE,crf_cmd), /* CreateFile command */ + PACK(CFPPtr,P_BYTE,crf_flg), /* flags */ + PACK(CFPPtr,P_WORD,crf_volid), /* volume id */ + PACK(CFPPtr,P_DWRD,crf_dirid), /* directory id */ + PACK(CFPPtr,P_BYTE,crf_ptype), /* path name type */ + PAKS(CFPPtr,P_PATH,crf_path), /* path name */ + PACKEND() +}; + +PackEntry ProtoSFPP[] = { /* FPSetFileParms */ + PACK(SFPPPtr,P_BYTE,sfp_cmd), /* command */ + PACK(SFPPPtr,P_ZERO,sfp_zero), /* always zero */ + PACK(SFPPPtr,P_WORD,sfp_volid), /* volume id */ + PACK(SFPPPtr,P_DWRD,sfp_dirid), /* directory id */ + PACK(SFPPPtr,P_WORD,sfp_bitmap), /* set bitmap */ + PACK(SFPPPtr,P_BMAP,sfp_bitmap), /* for attributes */ + PACK(SFPPPtr,P_BYTE,sfp_ptype), /* path type */ + PAKS(SFPPPtr,P_PATH,sfp_path), /* path + file parameters to set */ + PACKEVEN(), /* even out if necessary */ + PACKEND() + }; + + +PackEntry ProtoCpFP[] = { /* FPCopyFile */ + PACK(CpFPPtr,P_BYTE,cpf_cmd), /* command */ + PACK(CpFPPtr,P_ZERO,cpf_zero), /* always zero */ + PACK(CpFPPtr,P_WORD,cpf_svolid), /* source volume id */ + PACK(CpFPPtr,P_DWRD,cpf_sdirid), /* source directory id */ + PACK(CpFPPtr,P_WORD,cpf_dvolid), /* destination volume id */ + PACK(CpFPPtr,P_DWRD,cpf_ddirid), /* destination directory id */ + PACK(CpFPPtr,P_BYTE,cpf_sptype), /* source path type */ + PAKS(CpFPPtr,P_PATH,cpf_spath), /* source path */ + PACK(CpFPPtr,P_BYTE,cpf_dptype), /* destination path type */ + PAKS(CpFPPtr,P_PATH,cpf_dpath), /* destination path */ + PACK(CpFPPtr,P_BYTE,cpf_newtype), /* new path type */ + PAKS(CpFPPtr,P_PSTR,cpf_newname), /* new name */ + PACKEND() + }; + +PackEntry ProtoRFP[] = { /* FPRenameFile */ + PACK(RPPtr,P_BYTE,ren_cmd), /* command */ + PACK(RPPtr,P_ZERO,ren_zero), /* always zero */ + PACK(RPPtr,P_WORD,ren_volid), /* volume id */ + PACK(RPPtr,P_DWRD,ren_dirid), /* directory id */ + PACK(RPPtr,P_BYTE,ren_ptype), /* path type */ + PAKS(RPPtr,P_PATH,ren_path), /* path name */ + PACK(RPPtr,P_BYTE,ren_ntype), /* new type */ + PAKS(RPPtr,P_PATH,ren_npath), /* new path */ + PACKEND() + }; + +PackEntry ProtoMFP[] = { /* FPMoveFile */ + PACK(MPPtr,P_BYTE,mov_cmd), /* command */ + PACK(MPPtr,P_ZERO,mov_zero), /* always zero */ + PACK(MPPtr,P_WORD,mov_volid), /* volume id */ + PACK(MPPtr,P_DWRD,mov_sdirid), /* source directory id */ + PACK(MPPtr,P_DWRD,mov_ddirid), /* destination directory id */ + PACK(MPPtr,P_BYTE,mov_sptype), /* source path type */ + PAKS(MPPtr,P_PATH,mov_spath), /* source path */ + PACK(MPPtr,P_BYTE,mov_dptype), /* destination path type */ + PAKS(MPPtr,P_PATH,mov_dpath), /* destination path */ + PACK(MPPtr,P_BYTE,mov_newtype), /* new type */ + PAKS(MPPtr,P_PATH,mov_newname), /* new name */ + PACKEND() + }; + +#ifdef notdef +PackEntry ProtoGDPP[] = { /* GetDirParms */ + PACK(GDPPPtr,P_BYTE,gdp_cmd), /* command */ + PACK(GDPPPtr,P_ZERO,gdp_zero), /* always zero */ + PACK(GDPPPtr,P_WORD,gdp_volid), /* volume ID */ + PACK(GDPPPtr,P_DWRD,gdp_dirid), /* directory id */ + PACK(GDPPPtr,P_WORD,gdp_bitmap), /* bitmap */ + PACK(GDPPPtr,P_BYTE,gdp_ptype), /* path type */ + PAKS(GDPPPtr,P_PSTR,gdp_path), /* path */ + PACKEND() + }; +#endif + +PackEntry ProtoSDPP[] = { /* FPSetDirParms */ + PACK(SDPPPtr,P_BYTE,sdp_cmd), /* command */ + PACK(SDPPPtr,P_ZERO,sdp_zero), /* always zero */ + PACK(SDPPPtr,P_WORD,sdp_volid), /* volume ID */ + PACK(SDPPPtr,P_DWRD,sdp_dirid), /* directory id */ + PACK(SDPPPtr,P_WORD,sdp_bitmap), /* bitmap */ +/* PACK(SDPPPtr,P_BMAP,sdp_bitmap), /* for attrib */ + PACK(SDPPPtr,P_BYTE,sdp_ptype), /* path type */ + PAKS(SDPPPtr,P_PATH,sdp_path), /* path */ + PACKEVEN(), /* move to even boundary */ + PACKEND() + }; + + +PackEntry ProtoODP[] = { /* FPOpenDir */ + PACK(ODPPtr,P_BYTE,odr_cmd), /* command */ + PACK(ODPPtr,P_ZERO,odr_zero), /* always zero */ + PACK(ODPPtr,P_WORD,odr_volid), /* volume ID */ + PACK(ODPPtr,P_DWRD,odr_dirid), /* directory ID */ + PACK(ODPPtr,P_BYTE,odr_ptype), /* path type */ + PAKS(ODPPtr,P_PATH,odr_path), /* path */ + PACKEND() + }; + +PackEntry ProtoCDP[] = { /* FPCloseDir */ + PACK(CDPPtr,P_BYTE,cdr_cmd), /* command */ + PACK(CDPPtr,P_ZERO,cdr_zero), /* always zero */ + PACK(CDPPtr,P_WORD,cdr_volid), /* volume id */ + PACK(CDPPtr,P_DWRD,cdr_dirid), /* directory id */ + PACKEND() + }; + +PackEntry ProtoDFP[] = { /* FPDeleteFile */ + PACK(DPPtr,P_BYTE,del_cmd), /* command */ + PACK(DPPtr,P_ZERO,del_zero), /* always zero */ + PACK(DPPtr,P_WORD,del_volid), /* volume id */ + PACK(DPPtr,P_DWRD,del_dirid), /* directory id */ + PACK(DPPtr,P_BYTE,del_ptype), /* path type */ + PAKS(DPPtr,P_PATH,del_path), /* path */ + PACKEND() + }; + +PackEntry ProtoEP[] = { /* FPEnumerate */ + PACK(EPPtr,P_BYTE,enu_cmd), /* command */ + PACK(EPPtr,P_ZERO,enu_zero), /* always zero */ + PACK(EPPtr,P_WORD,enu_volid), /* volume id */ + PACK(EPPtr,P_DWRD,enu_dirid), /* directory id */ + PACK(EPPtr,P_WORD,enu_fbitmap), /* file bitmap */ + PACK(EPPtr,P_WORD,enu_dbitmap), /* directory bitmap */ + PACK(EPPtr,P_WORD,enu_reqcnt), /* request count */ + PACK(EPPtr,P_WORD,enu_stidx), /* start index */ + PACK(EPPtr,P_WORD,enu_maxreply), /* max reply size */ + PACK(EPPtr,P_BYTE,enu_ptype), /* path type */ + PAKS(EPPtr,P_PATH,enu_path), /* path */ + PACKEND() + }; + +PackEntry ProtoEPR[] = { + PACK(ERPPtr, P_WORD, enur_fbitmap), + PACK(ERPPtr, P_WORD, enur_dbitmap), + PACK(ERPPtr, P_WORD, enur_actcnt), + PACKEND() +}; + +PackEntry ProtoCRDP[] = { /* FPCreateDir */ + PACK(CRDPPtr,P_BYTE,crd_cmd), /* command */ + PACK(CRDPPtr,P_ZERO,crd_zero), /* always zero */ + PACK(CRDPPtr,P_WORD,crd_volid), /* volume id */ + PACK(CRDPPtr,P_DWRD,crd_dirid), /* directory id */ + PACK(CRDPPtr,P_BYTE,crd_ptype), /* path type */ + PAKS(CRDPPtr,P_PATH,crd_path), /* path */ + PACKEND() + }; + +PackEntry ProtoODT[] = { /* FPOpenDT */ + PACK(ODTPPtr,P_BYTE,odt_cmd), + PACK(ODTPPtr,P_ZERO,odt_zero), + PACK(ODTPPtr,P_WORD,odt_volid), /* volid */ + PACKEND() + }; + +PackEntry ProtoCDT[] = { /* FPCloseDT */ + PACK(CDTPPtr,P_BYTE,cdt_cmd), + PACK(CDTPPtr,P_ZERO,cdt_zero), + PACK(CDTPPtr,P_WORD,cdt_dtrefnum), /* */ + PACKEND() + }; + +PackEntry ProtoGI[] = { /* GetIcon */ + PACK(GIPPtr,P_BYTE,gic_cmd), + PACK(GIPPtr,P_ZERO,gic_zero), + PACK(GIPPtr,P_WORD,gic_dtrefnum), + PAKS(GIPPtr,P_BYTS,gic_fcreator), + PAKS(GIPPtr,P_BYTS,gic_ftype), + PACK(GIPPtr,P_BYTE,gic_itype), + PACK(GIPPtr,P_ZERO,gic_zero2), + PACK(GIPPtr,P_WORD,gic_length), + PACKEND() +}; + +PackEntry ProtoGII[] = { /* GetIconInfo */ + PACK(GIIPPtr,P_BYTE,gii_cmd), + PACK(GIIPPtr,P_ZERO,gii_zero), + PACK(GIIPPtr,P_WORD,gii_dtrefnum), + PAKS(GIIPPtr,P_BYTS,gii_fcreator), + PACK(GIIPPtr,P_WORD,gii_iidx), /* */ + PACKEND() + }; + +PackEntry ProtoAAP[] = { /* AddAPPL */ + PACK(AAPPtr,P_BYTE,aap_cmd), + PACK(AAPPtr,P_ZERO,aap_zero), + PACK(AAPPtr,P_WORD,aap_dtrefnum), + PACK(AAPPtr,P_DWRD,aap_dirid), + PAKS(AAPPtr,P_BYTS,aap_fcreator), + PACK(AAPPtr,P_DWRD,aap_apptag), + PACK(AAPPtr,P_BYTE,aap_ptype), + PAKS(AAPPtr,P_PATH,aap_path), /* */ + PACKEND() + }; + + +PackEntry ProtoRMA[] = { /* RemoveAppl */ + PACK(RAPPtr,P_BYTE,rma_cmd), + PACK(RAPPtr,P_ZERO,rma_zero), + PACK(RAPPtr,P_WORD,rma_refnum), + PACK(RAPPtr,P_DWRD,rma_dirid), + PAKS(RAPPtr,P_BYTS,rma_fcreator), + PACK(RAPPtr,P_BYTE,rma_ptype), + PAKS(RAPPtr,P_PATH,rma_path), /* */ + PACKEND() + }; + +PackEntry ProtoGAP[] = { /* GetAPPL */ + PACK(GAPPtr,P_BYTE,gap_cmd), + PACK(GAPPtr,P_ZERO,gap_zero), + PACK(GAPPtr,P_WORD,gap_dtrefnum), + PAKS(GAPPtr,P_BYTS,gap_fcreator), + PACK(GAPPtr,P_WORD,gap_applidx), + PACK(GAPPtr,P_WORD,gap_bitmap), /* */ + PACKEND() + }; + + +PackEntry ProtoRP[] = { /* FPRead */ + PACK(ReadPPtr, P_BYTE, rdf_cmd), + PACK(ReadPPtr, P_ZERO, rdf_zero), + PACK(ReadPPtr, P_WORD, rdf_refnum), + PACK(ReadPPtr, P_DWRD, rdf_offset), + PACK(ReadPPtr, P_DWRD, rdf_reqcnt), + PACK(ReadPPtr, P_BYTE, rdf_flag), + PACK(ReadPPtr, P_BYTE, rdf_nlchar), + PACKEND() +}; + + +PackEntry ProtoWP[] = { /* FPWrite */ + PACK(WPPtr, P_BYTE, wrt_cmd), + PACK(WPPtr, P_BYTE, wrt_flag), + PACK(WPPtr, P_WORD, wrt_refnum), + PACK(WPPtr, P_DWRD, wrt_offset), + PACK(WPPtr, P_DWRD, wrt_reqcnt), + PACKEND() +}; + + +PackEntry ProtoLP[] = { /* AFPLogin */ + PACK(LPPtr, P_BYTE, log_cmd), + PAKS(LPPtr, P_PSTR, log_ver), + PAKS(LPPtr, P_PSTR, log_uam), + PKSB(LPPtr, P_PSTR, log_user, UAMP_USER), + PAKB(LPPtr, P_EVEN, log_zero, UAMP_ZERO), + PKSB(LPPtr, P_BYTS, log_passwd, UAMP_PASS), + PACKEND() +}; + +PackEntry ProtoLOP[] = { + PACK(LOPPtr, P_BYTE, lgo_cmd), + PACKEND() +}; + +PackEntry ProtoAuthInfo[] = { + PKSB(LPPtr, P_PSTR, log_user, UAMP_USER), + PAKB(LPPtr, P_EVEN, log_zero, UAMP_ZERO), + PKSB(LPPtr, P_BYTS, log_passwd, UAMP_PASS), + PACKEND() +}; + +PackEntry ProtoLRP[] = { /* FPLogin reply */ + PAKB(LRPPtr, P_WORD, logr_idnum, UAMP_INUM), + PKSB(LRPPtr, P_BYTS, logr_randnum, UAMP_RAND), + PACKEND() +}; + +PackEntry ProtoLCP[] = { /* FPLoginCont */ + PACK(LCPPtr,P_BYTE,lgc_cmd), /* command */ + PACK(LCPPtr,P_ZERO,lgc_zero), /* is this here? */ + PAKB(LCPPtr,P_WORD,lgc_idno, UAMP_INUM), /* ID number */ + PKSB(LCPPtr,P_BYTS,lgc_encrypted, UAMP_ENCR), /* encrypted passwd */ + PKSB(LCPPtr,P_BYTS,lgc_wsencrypt, UAMP_TWAY), /* encrypted passwd */ + PACKEND() +}; + +PackEntry ProtoLCR[] = { /* FPLoginCont Reply */ + PKSB(LCPPtr,P_BYTS,lgc_wsencrypt,UAMP_TWAY), + PACKEND() +}; + +PackEntry ProtoSFDPP[] = { /* FPSetFileDirParms */ + PACK(SFDPPPtr,P_BYTE,scp_cmd), /* command */ + PACK(SFDPPPtr,P_ZERO,scp_zero), /* always zero */ + PACK(SFDPPPtr,P_WORD,scp_volid), /* volume id */ + PACK(SFDPPPtr,P_DWRD,scp_dirid), /* directory id */ + PACK(SFDPPPtr,P_WORD,scp_bitmap), /* set bitmap */ + PACK(SFDPPPtr,P_BMAP,scp_bitmap), /* For attributes */ + PACK(SFDPPPtr,P_BYTE,scp_ptype), /* path type */ + PAKS(SFDPPPtr,P_PATH,scp_path), /* path + file parameters to set */ + PACKEVEN(), /* even out if necessary */ + PACKEND() + }; + + +/* For FPEnumerate, etc. - client */ +PackEntry ProtoFileAttr[] = { + PAKB(FDParmPtr, P_WORD, fdp_attr,FP_ATTR), + PAKB(FDParmPtr, P_DWRD, fdp_pdirid,FP_PDIR), + PAKB(FDParmPtr, P_TIME, fdp_cdate,FP_CDATE), + PAKB(FDParmPtr, P_TIME, fdp_mdate,FP_MDATE), + PAKB(FDParmPtr, P_TIME, fdp_bdate,FP_BDATE), + PKSB(FDParmPtr, P_BYTS, fdp_finfo,FP_FINFO), + PKSB(FDParmPtr, P_OPTH, fdp_lname,FP_LNAME), + PKSB(FDParmPtr, P_OPTH, fdp_sname,FP_SNAME), + PAKB(FDParmPtr,P_DWRD,fdp_parms.fp_parms.fp_fileno,FP_FILNO), + PAKB(FDParmPtr,P_DWRD,fdp_parms.fp_parms.fp_dflen,FP_DFLEN), + PAKB(FDParmPtr,P_DWRD,fdp_parms.fp_parms.fp_rflen,FP_RFLEN), + PAKB(FDParmPtr,P_WORD,fdp_prodos_ft,FP_PDOS), + PAKB(FDParmPtr,P_DWRD,fdp_prodos_aux,FP_PDOS), + PACKEND() +}; + + +/* For FPEnumerate, etc. - client */ +PackEntry ProtoDirAttr[] = { + PAKB(FDParmPtr, P_WORD, fdp_attr,DP_ATTR), + PAKB(FDParmPtr, P_DWRD, fdp_pdirid,DP_PDIR), + PAKB(FDParmPtr, P_TIME, fdp_cdate,DP_CDATE), + PAKB(FDParmPtr, P_TIME, fdp_mdate,DP_MDATE), + PAKB(FDParmPtr, P_TIME, fdp_bdate,DP_BDATE), + PKSB(FDParmPtr, P_BYTS, fdp_finfo,DP_FINFO), + PKSB(FDParmPtr, P_OPTH, fdp_lname,DP_LNAME), + PKSB(FDParmPtr, P_OPTH, fdp_sname,DP_SNAME), + PAKB(FDParmPtr, P_DWRD, fdp_parms.dp_parms.dp_dirid,DP_DIRID), + PAKB(FDParmPtr, P_WORD, fdp_parms.dp_parms.dp_nchild,DP_CHILD), + PAKB(FDParmPtr, P_DWRD, fdp_parms.dp_parms.dp_ownerid,DP_CRTID), + PAKB(FDParmPtr, P_DWRD, fdp_parms.dp_parms.dp_groupid,DP_GRPID), + PAKB(FDParmPtr, P_DWRD, fdp_parms.dp_parms.dp_accright,DP_ACCES), + PAKB(FDParmPtr, P_WORD,fdp_prodos_ft,DP_PDOS), + PAKB(FDParmPtr, P_DWRD,fdp_prodos_aux,DP_PDOS), + PACKEND() +}; + +/* For FPEnumerate, etc. - client */ +PackEntry ProtoFileDirAttr[] = { + PAKB(FDParmPtr, P_WORD, fdp_attr,DP_ATTR), + PAKB(FDParmPtr, P_DWRD, fdp_pdirid,DP_PDIR), + PAKB(FDParmPtr, P_TIME, fdp_cdate,DP_CDATE), + PAKB(FDParmPtr, P_TIME, fdp_mdate,DP_MDATE), + PAKB(FDParmPtr, P_TIME, fdp_bdate,DP_BDATE), + PKSB(FDParmPtr, P_BYTS, fdp_finfo,DP_FINFO), + PKSB(FDParmPtr, P_OPTH, fdp_lname,DP_LNAME), + PKSB(FDParmPtr, P_OPTH, fdp_sname,DP_SNAME), + PAKB(FDParmPtr, P_WORD,fdp_prodos_ft,DP_PDOS), + PAKB(FDParmPtr, P_DWRD,fdp_prodos_aux,DP_PDOS), + PACKEND() +}; + +PackEntry ProtoACP[] = { /* FPAddComment */ + PACK(ACPPtr, P_BYTE, adc_cmd), + PACK(ACPPtr, P_ZERO, adc_zero), + PACK(ACPPtr, P_WORD, adc_dtrefnum), + PACK(ACPPtr, P_DWRD, adc_dirid), + PACK(ACPPtr, P_BYTE, adc_ptype), + PAKS(ACPPtr, P_PATH, adc_path), + PACKEVEN(), +/* PACK(ACPPtr, P_BYTE, adc_clen), */ + PAKS(ACPPtr, P_PATH, adc_comment), + PACKEND() +}; + +PackEntry ProtoBRL[] = { /* FPByteRangeLock */ + PACK(BRLPPtr, P_BYTE, brl_cmd), + PACK(BRLPPtr, P_BYTE, brl_flg), + PACK(BRLPPtr, P_WORD, brl_refnum), + PACK(BRLPPtr, P_DWRD, brl_offset), + PACK(BRLPPtr, P_DWRD, brl_length), + PACKEND() +}; + +PackEntry ProtoFFP[] = { + PACK(FFkPPtr, P_BYTE, flf_cmd), + PACK(FFkPPtr, P_ZERO, flf_zero), + PACK(FFkPPtr, P_WORD, flf_refnum) +}; + +PackEntry ProtoGCP[] = { /* FPGetComment */ + PACK(GCPPtr, P_BYTE, gcm_cmd), + PACK(GCPPtr, P_ZERO, gcm_zero), + PACK(GCPPtr, P_WORD, gcm_dtrefnum), + PACK(GCPPtr, P_DWRD, gcm_dirid), + PACK(GCPPtr, P_BYTE, gcm_ptype), + PAKS(GCPPtr, P_PATH, gcm_path), + PACKEND() +}; + +PackEntry ProtoGSPRP[] = { /* GetSrvrParms Reply */ + PACK(GSPRPPtr, P_TIME, gspr_time), + PACK(GSPRPPtr, P_BYTE, gspr_nvols), + PACKEND() +}; + +PackEntry ProtoGSPRPvol[] = { + PACK(VolParm *, P_BYTE, volp_flag), + PAKS(VolParm *, P_PATH, volp_name), + PACKEND() +}; + +PackEntry ProtoGVPRP[] = { /* GetVolParms Reply */ + PACK(GVPRPPtr,P_WORD,gvpr_bitmap), /* bitmap specifies below items */ + PACK(GVPRPPtr,P_BMAP,gvpr_bitmap), /* bitmap specifies below items */ + PAKB(GVPRPPtr,P_WORD,gvpr_attr,VP_ATTR), /* attributes word */ + PAKB(GVPRPPtr,P_WORD,gvpr_sig,VP_SIG), /* signature word */ + PAKB(GVPRPPtr,P_TIME,gvpr_cdate,VP_CDATE), /* creation date */ + PAKB(GVPRPPtr,P_TIME,gvpr_mdate,VP_MDATE), /* modification date */ + PAKB(GVPRPPtr,P_TIME,gvpr_bdate,VP_BDATE), /* last back date */ + PAKB(GVPRPPtr,P_WORD,gvpr_volid,VP_VOLID), /* volume id */ + PAKB(GVPRPPtr,P_DWRD,gvpr_free,VP_FREE), /* free bytes */ + PAKB(GVPRPPtr,P_DWRD,gvpr_size,VP_SIZE), /* size in bytes */ + PKSB(GVPRPPtr,P_OSTR,gvpr_name,VP_NAME), /* name of volume */ + PKSB(GVPRPPtr,P_BYTS,gvpr_efree,VP_EFREE), /* extended free bytes */ + PKSB(GVPRPPtr,P_BYTS,gvpr_esize,VP_ESIZE), /* extended total bytes */ + PACKEND() +}; + +PackEntry ProtoAIP[] = { /* FPAddIcon */ + PACK(AIPPtr, P_BYTE, adi_cmd), + PACK(AIPPtr, P_ZERO, adi_zero), + PACK(AIPPtr, P_WORD, adi_dtref), + PAKS(AIPPtr, P_BYTS, adi_fcreator), + PAKS(AIPPtr, P_BYTS, adi_ftype), + PACK(AIPPtr, P_BYTE, adi_icontype), + PACK(AIPPtr, P_ZERO, adi_zero2), + PACK(AIPPtr, P_DWRD, adi_icontag), + PACK(AIPPtr, P_WORD, adi_iconsize), + PACKEND() +}; + +PackEntry ProtoGAPR[] = { /* GetAPPL reply */ + PACK(GARPPtr, P_WORD, gapr_bitmap), + PACK(GARPPtr, P_DWRD, gapr_appltag), + PACKEND() +}; + +PackEntry ProtoGIIR[] = { /* GetIconInfo reply */ + PACK(GIIRPPtr, P_DWRD, giir_itag), + PAKS(GIIRPPtr, P_BYTS, giir_ftype), + PACK(GIIRPPtr, P_BYTE, giir_itype), + PACK(GIIRPPtr, P_ZERO, giir_zero), + PACK(GIIRPPtr, P_WORD, giir_size), + PACKEND() +}; + +PackEntry ProtoRMC[] = { /* FPRemoveComment */ + PACK(RCPPtr, P_BYTE, rmc_cmd), + PACK(RCPPtr, P_ZERO, rmc_zero), + PACK(RCPPtr, P_WORD, rmc_dtrefnum), + PACK(RCPPtr, P_DWRD, rmc_dirid), + PACK(RCPPtr, P_BYTE, rmc_ptype), + PAKS(RCPPtr, P_PATH, rmc_path), + PACKEND() +}; + +PackEntry ProtoSRP[] = { /* GetSrvrInfo reply */ + PAKS(GSIRPPtr, P_OSTR, sr_machtype), + PACK(GSIRPPtr, P_OPTR, sr_avo), + PACK(GSIRPPtr, P_OPTR, sr_uamo), + PACK(GSIRPPtr, P_OPTR, sr_vicono), + PACK(GSIRPPtr, P_WORD, sr_flags), + PAKS(GSIRPPtr, P_PATH, sr_servername), + PACKEVEN(), + PACK(GSIRPPtr, P_OPTR, sr_sigo), + PACK(GSIRPPtr, P_OPTR, sr_naddro), + PACKEND() +}; + +PackEntry ProtoCPP[] = { /* ChangePassword */ + PACK(CPPtr, P_BYTE, cp_cmd), /* command */ + PACK(CPPtr, P_ZERO, cp_zero), /* always zero */ + PAKS(CPPtr, P_PSTR, cp_uam), /* authentication method */ + PACK(CPPtr, P_EVEN, cp_pad), /* pad to even */ + PAKS(CPPtr, P_PSTR, cp_user), /* user name */ + PACK(CPPtr, P_EVEN, cp_pad), /* pad to even */ + PAKS(CPPtr, P_BYTS, cp_oldpass), /* 8 bytes for old password */ + PAKS(CPPtr, P_BYTS, cp_newpass), /* 8 bytes for new password */ + PACKEND() +}; + +PackEntry ProtoGUIP[] = { /* GetUserInfo */ + PACK(GUIPtr, P_BYTE, gui_cmd), /* command */ + PACK(GUIPtr, P_BYTE, gui_flag), /* flag word */ + PACK(GUIPtr, P_DWRD, gui_userid), /* user id */ + PACK(GUIPtr, P_WORD, gui_bitmap), /* bitmap of info to return */ + PACKEND() +}; + +PackEntry ProtoGUIRP[] = { /* GetUserInfo Reply */ + PACK(GUIRPtr, P_BMAP, guir_bitmap), /* bitmap to return */ + PAKB(GUIRPtr, P_DWRD, guir_userid, UIP_USERID), + PAKB(GUIRPtr, P_DWRD, guir_pgroup, UIP_PRIMARY_GID), + PACKEND() +}; + +PackEntry ProtoExP[] = { /* ExchangeFiles */ + PACK(EXPtr, P_BYTE, exc_cmd), /* command */ + PACK(EXPtr, P_ZERO, exc_zero), /* always zero */ + PACK(EXPtr, P_WORD, exc_volid), /* volume id */ + PACK(EXPtr, P_DWRD, exc_adirid), /* first directory id */ + PACK(EXPtr, P_DWRD, exc_bdirid), /* second directory id */ + PACK(EXPtr, P_BYTE, exc_aptype), /* first path type */ + PAKS(EXPtr, P_PATH, exc_apath), /* first path */ + PACK(EXPtr, P_BYTE, exc_bptype), /* second path type */ + PAKS(EXPtr, P_PATH, exc_bpath), /* second path */ + PACKEND() +}; + +PackEntry ProtoMsgP[] = { /* GetSrvrMsg */ + PACK(SrvrMsgPtr, P_BYTE, msg_cmd), /* command */ + PACK(SrvrMsgPtr, P_ZERO, msg_zero), /* always zero */ + PACK(SrvrMsgPtr, P_WORD, msg_typ), /* message type */ + PACK(SrvrMsgPtr, P_WORD, msg_bitmap), /* bitmap */ + PACKEND() +}; + +PackEntry ProtoMsgRP[] = { /* GetSrvrMsg Reply */ + PACK(SrvrMsgReplyPtr, P_WORD, msgr_typ), /* message type */ + PACK(SrvrMsgReplyPtr, P_WORD, msgr_bitmap), /* bitmap */ + PAKS(SrvrMsgReplyPtr, P_PSTR, msgr_data), /* message string */ + PACKEND() +}; + +PackEntry ProtoCreateID[] = { /* CreateID */ + PACK(CreateIDPtr, P_BYTE, crid_cmd), /* command */ + PACK(CreateIDPtr, P_ZERO, crid_zero), /* always zero */ + PACK(CreateIDPtr, P_WORD, crid_volid),/* volume id */ + PACK(CreateIDPtr, P_DWRD, crid_dirid),/* directory id */ + PACK(CreateIDPtr, P_BYTE, crid_ptype),/* path type */ + PAKS(CreateIDPtr, P_PATH, crid_path), /* path */ + PACKEND() +}; + +PackEntry ProtoDelID[] = { /* DeleteID */ + PACK(DeleteIDPtr, P_BYTE, did_cmd), /* command */ + PACK(DeleteIDPtr, P_ZERO, did_zero), /* always zero */ + PACK(DeleteIDPtr, P_WORD, did_volid), /* volume id */ + PACK(DeleteIDPtr, P_DWRD, did_fileid), /* file id */ + PACKEND() +}; + +PackEntry ProtoRslvID[] = { /* ResolveID */ + PACK(ResolveIDPtr, P_BYTE, rid_cmd), /* command */ + PACK(ResolveIDPtr, P_ZERO, rid_zero), /* always zero */ + PACK(ResolveIDPtr, P_WORD, rid_volid), /* volume id */ + PACK(ResolveIDPtr, P_DWRD, rid_fileid), /* file id */ + PACK(ResolveIDPtr, P_WORD, rid_fbitmap), /* bitmap */ + PACKEND() +}; diff --git a/lib/afp/afppass.c b/lib/afp/afppass.c new file mode 100644 index 0000000..6fa3224 --- /dev/null +++ b/lib/afp/afppass.c @@ -0,0 +1,771 @@ +/* + * $Author: djh $ $Date: 1995/06/30 11:19:39 $ + * $Header: /mac/src/cap60/lib/afp/RCS/afppass.c,v 2.1 1995/06/30 11:19:39 djh Rel djh $ + * $Revision: 2.1 $ + * + */ + +/* + * AUFS Distributed Passwords + * + * Copyright 1995 - The University of Melbourne. All rights reserved. + * May be used only for CAP/AUFS authentication. Any other use + * requires prior permission in writing from the copyright owner. + * + * djh@munnari.OZ.AU + * June 1995 + * + * afppass.c - AUFS Distributed Password library routines. + * + * User passwords are normally stored in ~user/.afppass in DES encrypted + * form. This file also contains values for password expiry date, minimum + * password length, maximum failed login attempts and number of failed + * login attempts. + * + * For greater security, the file must be owned by the user and be set to + * mode AFP_DISTPW_MODE (usually 0600 or -rw-------), if this is not the + * case, the file is deleted. + * + * The decryption key is stored in a global afppass (defaults to the + * file /usr/local/lib/cap/afppass) which also contains default values + * for expiry date, minimum password length and maximum failed attempts. + * If this file is not owned by root and mode 0600 it will be removed. + * + * Notes: + * 1. In the case of user home directories mounted via NFS, the files must + * be set to mode 0644 (since root cannot read mode 0600 files on remote + * filesystems). You can change the mode using the define + * -DAFP_DISTPW_MODE=0644 + * + * 2. If you prefer to keep the .afppass files centrally, you can define + * the path using the define -DAFP_DISTPW_PATH=\"/usr/local/lib/cap/upw\" + * + * 3. The decryption key for the global afppass is defined by AFP_DIST_PASS + * Should be localized for each site, using -DAFP_DIST_PASS=\"password\". + * + * 4. AFP passwords can only be changed by the user with the AppleShare + * workstation client or by the UNIX superuser using aufsmkusr. + * + * 5. User AFP passwords MUST NOT be identical to UNIX login passwords, + * this restriction is enforced by the library routines. + * + */ + +#ifdef DISTRIB_PASSWDS + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef NEEDFCNTLDOTH +#include +#endif NEEDFCNTLDOTH + +static struct afppass global; +struct afppass *afp_glob = NULL; + +char hex[] = "0123456789ABCDEF"; + +/* + * Initialise data structure + * + * must be called by root to get global password & settings + * from the specified file (which must already exist). + * + */ + +int +afpdp_init(path) +char *path; +{ + int fd, len; + struct stat buf; + char abuf[AFPPDSIZE]; + void afpdp_decr(); + + if (geteuid() != 0) + return(-1); + + bzero(&global, sizeof(struct afppass)); + + if (stat(path, &buf) < 0) + return(-1); + + /* + * check mode, size and owner + * + */ + if ((buf.st_mode&0777) != 0600 + || buf.st_size != AFPPDSIZE + || buf.st_uid != 0) { + unlink(path); + return(-1); + } + + if ((fd = open(path, O_RDONLY, 0644)) < 0) + return(fd); + + len = read(fd, abuf, sizeof(abuf)); + + close(fd); + + if (len != sizeof(abuf)) + return(-1); + + /* + * sanity check on file contents + * + */ + if (abuf[16] != '\n' || abuf[33] != '\n') + return(-1); + + /* + * decrypt each "line" into the structure + * using global key + * + */ + afpdp_decr(abuf, (u_char *)AFP_DISTPW_PASS, (u_char *)&global); + afpdp_decr((abuf+17), (u_char *)AFP_DISTPW_PASS, global.afp_password); + + /* + * another sanity check + * + */ + if (global.afp_magic != AFPDP_MAGIC) + return(-1); + + /* + * make sure password null terminated + * + */ + global.afp_password[KEYSIZE] = '\0'; + + afp_glob = &global; + + return(0); +} + +/* + * return pointer to structure representing ~user/.afppass + * + */ + +struct afppass * +afpdp_read(user, uid, home) +char *user; +int uid; +char *home; +{ + int fd, len; + struct stat buf; + static struct afppass afppass; + char key[KEYSIZE], abuf[AFPPDSIZE], path[MAXPATHLEN]; + void afpdp_decr(); + + if (afp_glob == (struct afppass *)NULL) + return((struct afppass *)NULL); + + bzero(&afppass, sizeof(struct afppass)); + +#ifdef AFP_DISTPW_PATH + sprintf(path, "%s/%s%s", AFP_DISTPW_PATH, user, AFP_DISTPW_USER); +#else /* AFP_DISTPW_PATH */ + sprintf(path, "%s/%s", home, AFP_DISTPW_USER); +#endif /* AFP_DISTPW_PATH */ + + if (stat(path, &buf) < 0) + return((struct afppass *)NULL); + + /* + * check mode, size and owner + * + */ + if ((buf.st_mode&0777) != AFP_DISTPW_MODE + || buf.st_size != AFPPDSIZE) { + unlink(path); /* delete file */ + return((struct afppass *)NULL); + } + if (buf.st_uid != uid) + return((struct afppass *)NULL); + + if ((fd = open(path, O_RDONLY, 0644)) < 0) + return((struct afppass *)NULL); + + len = read(fd, abuf, sizeof(abuf)); + + close(fd); + + if (len != sizeof(abuf)) + return((struct afppass *)NULL); + + /* + * sanity check on file contents + * + */ + if (abuf[16] != '\n' || abuf[33] != '\n') + return((struct afppass *)NULL); + + /* + * copy global key, xor with 'user' to + * prevent interchange of .afppass files + * + */ + bcopy((char *)afp_glob->afp_password, key, KEYSIZE); + if ((len = strlen(user)) > KEYSIZE) + len = KEYSIZE; + while (--len >= 0) + key[len] ^= user[len]; + + /* + * decrypt each "line" into the structure using key + * + */ + afpdp_decr(abuf, key, (u_char *)&afppass); + afpdp_decr((abuf+17), key, afppass.afp_password); + + if (afppass.afp_magic != AFPDP_MAGIC) + return((struct afppass *)NULL); + + /* + * make sure password null terminated + * + */ + afppass.afp_password[KEYSIZE] = '\0'; + + return(&afppass); +} + +/* + * write a (possibly new) ~user/.afppass + * + * fail if UNIX password is used. + * + */ + +int +afpdp_writ(user, uid, home, afppass) +char *user; +int uid; +char *home; +struct afppass *afppass; +{ + int fd, i, j; + char key[KEYSIZE], abuf[AFPPDSIZE], path[MAXPATHLEN]; + void afpdp_encr(); + + if (afp_glob == (struct afppass *)NULL) + return(-1); + + if (afppass == (struct afppass *)NULL) + return(-1); + + if (afppass->afp_magic != AFPDP_MAGIC) + return(-1); + + /* + * ensure password null padded + * + */ + if ((i = strlen(afppass->afp_password)) > KEYSIZE) + i = KEYSIZE; + + for (j = i; j < KEYSIZE; j++) + afppass->afp_password[j] = '\0'; + + /* + * check that the proposed new password + * is NOT identical to the UNIX password + * (and that the user exists ...) + * + */ + if (afpdp_upas(uid, afppass->afp_password) <= 0) + return(-1); + +#ifdef AFP_DISTPW_PATH + sprintf(path, "%s/%s%s", AFP_DISTPW_PATH, user, AFP_DISTPW_USER); +#else /* AFP_DISTPW_PATH */ + sprintf(path, "%s/%s", home, AFP_DISTPW_USER); +#endif /* AFP_DISTPW_PATH */ + + if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, AFP_DISTPW_MODE)) < 0) + return(-1); + + /* + * copy global key, xor with 'user' to + * prevent interchange of .afppass files + * + */ + bcopy((char *)afp_glob->afp_password, key, KEYSIZE); + if ((i = strlen(user)) > KEYSIZE) + i = KEYSIZE; + while (--i >= 0) + key[i] ^= user[i]; + + /* + * encrypt each half of structure into buffer + * + */ + afpdp_encr((u_char *)afppass, key, abuf); + afpdp_encr(afppass->afp_password, key, abuf+17); + + abuf[16] = '\n'; + abuf[33] = '\n'; + + if (write(fd, abuf, sizeof(abuf)) != sizeof(abuf)) + return(-1); + + fchmod(fd, AFP_DISTPW_MODE); + fchown(fd, uid, -1); + + close(fd); + + return(0); +} + +/* + * write a (possibly new) /usr/local/lib/cap/afppass + * + */ + +int +afpdp_make(path, afppass) +char *path; +struct afppass *afppass; +{ + int fd, i, j; + char abuf[AFPPDSIZE]; + void afpdp_encr(); + + if (geteuid() != 0) + return(-1); + + if (afppass == (struct afppass *)NULL) + return(-1); + + if (afppass->afp_magic != AFPDP_MAGIC) + return(-1); + + /* + * ensure password null padded + * + */ + if ((i = strlen(afppass->afp_password)) > KEYSIZE) + i = KEYSIZE; + + for (j = i; j < KEYSIZE; j++) + afppass->afp_password[j] = '\0'; + + if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) + return(-1); + + /* + * encrypt each half of structure into buffer + * + */ + afpdp_encr((u_char *)afppass, (u_char *)AFP_DISTPW_PASS, abuf); + afpdp_encr(afppass->afp_password, (u_char *)AFP_DISTPW_PASS, abuf+17); + + abuf[16] = '\n'; + abuf[33] = '\n'; + + if (write(fd, abuf, sizeof(abuf)) != sizeof(abuf)) + return(-1); + + fchmod(fd, 0600); + fchown(fd, 0, -1); + + close(fd); + + return(0); +} + +/* + * decrypt 'str' using 'key', into 'buf' + * + * 'str' assumed to be 16 hex chars or null + * 'buf' assumed to be 8 bytes long + * + */ + +void +afpdp_decr(str, key, buf) +char *str; +u_char *key, *buf; +{ + int i, j, k; + u_char mykey[KEYSIZE]; + + if ((i = strlen((char *)key)) > KEYSIZE) + i = KEYSIZE; + + /* + * copy key. DES ignores bottom bit, + * so shift one up, add null padding + * + */ + for (j = 0; j < i; j++) + mykey[j] = *(key+j) << 1; + for (j = i; j < KEYSIZE; j++) + mykey[j] = '\0'; + + /* + * copy str. convert hex string to data + * + */ + if (str != NULL) { + for (i = 0, j = 0; i < KEYSIZE; i++) { + buf[i] = 0; + for (k = 0; k < 2; j++, k++) { + if (str[j] >= '0' && str[j] <= '9') + buf[i] += (str[j] - '0'); + if (str[j] >= 'A' && str[j] <= 'F') + buf[i] += (str[j] - 'A' + 10); + if (str[j] >= 'a' && str[j] <= 'f') + buf[i] += (str[j] - 'a' + 10); + if (k == 0) + buf[i] *= 16; + } + } + } + + /* + * initialise and run DES + * + */ +#ifndef DES_AVAIL + desinit(0); + dessetkey(mykey); + dedes(buf); + desdone(); +#else /* DES_AVAIL */ + { + char pass[64], pkey[64]; + + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + pass[(i*8)+j] = (buf[i] >> (7-j)) & 0x01; + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + pkey[(i*8)+j] = (mykey[i] >> (7-j)) & 0x01; + setkey(pkey); + encrypt(pass, 1); + for (i = 0; i < 8; i++) { + buf[i] = 0; + for (j = 0; j < 8; j++) + buf[i] |= ((pass[(i*8)+j] & 0x01) << (7-j)); + } + } +#endif /* DES_AVAIL */ + + return; +} + +/* + * encrypt 'buf' using 'key', into 'str' + * + * 'buf' assumed to be 8 bytes (null padded) + * 'str' assumed have space for 16 characters or null + * + */ + +void +afpdp_encr(buf, key, str) +u_char *buf, *key; +char *str; +{ + int i, j, k; + u_char mykey[KEYSIZE]; + + if ((i = strlen(key)) > KEYSIZE) + i = KEYSIZE; + + /* + * copy key. DES ignores bottom bit, + * so shift one up, add null padding + * + */ + for (j = 0; j < i; j++) + mykey[j] = *(key+j) << 1; + for (j = i; j < KEYSIZE; j++) + mykey[j] = '\0'; + + /* + * initialise and run DES + * + */ +#ifndef DES_AVAIL + desinit(0); + dessetkey(mykey); + endes(buf); + desdone(); +#else /* DES_AVAIL */ + { + char pass[64], pkey[64]; + + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + pass[(i*8)+j] = (buf[i] >> (7-j)) & 0x01; + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + pkey[(i*8)+j] = (mykey[i] >> (7-j)) & 0x01; + setkey(pkey); + encrypt(pass, 0); + for (i = 0; i < 8; i++) { + buf[i] = 0; + for (j = 0; j < 8; j++) + buf[i] |= ((pass[(i*8)+j] & 0x01) << (7-j)); + } + } +#endif /* DES_AVAIL */ + + if (str == NULL) + return; + + /* + * convert to Hex digits + * + */ + for (i = 0, j = 0; i < KEYSIZE; i++) { + str[j++] = hex[(buf[i] >> 4) & 0x0f]; + str[j++] = hex[(buf[i] & 0x0f)]; + } + + return; +} + +/* + * compare password against UNIX account password + * + * returns: -1 if nonexistent, 0 if same, 1 if no match + * + */ + +int +afpdp_upas(uid, passwd) +int uid; +char *passwd; +{ + struct passwd *pw, *getpwuid(); + + if ((pw = getpwuid(uid)) == NULL) + return(-1); + + if (strcmp((char *)crypt(passwd, pw->pw_passwd), pw->pw_passwd) == 0) + return(0); + + return(1); +} + +/* + * check password expiry date (global and user) + * + * return + * 1 if password expired and user allowed to update + * -1 if password expired and user can't update + * 0 if password hasn't expired + * + */ + +int +afpdp_pwex(afp) +struct afppass *afp; +{ + time_t now, then; + + if (afp_glob == (struct afppass *)NULL + || afp == (struct afppass *)NULL) + return(-1); + + time(&now); + then = ntohl(afp_glob->afp_expires); + + /* + * enforce global expiry date + * + */ + if (then > SECS_10_YRS && now > then) + return(-1); + + /* + * otherwise check user expiry date + * + */ + if ((then = ntohl(afp->afp_expires)) == 0) + return(0); + + if (now > then) + return(1); + + return(0); +} + +/* + * update user expiry date + * + */ + +void +afpdp_upex(afp) +struct afppass *afp; +{ + time_t now, then; + + if (afp_glob == (struct afppass *)NULL + || afp == (struct afppass *)NULL) + return; + + time(&now); + then = ntohl(afp_glob->afp_expires); + + if (then > SECS_10_YRS || then == 0) + afp->afp_expires = afp_glob->afp_expires; + else + afp->afp_expires = htonl(now+then); + + return; +} + +/* + * read a positive integer up to 'maxm' + * use 'def' if no input provided + * + */ + +int +afpdp_gnum(def, maxm) +int def, maxm; +{ + int num = 0; + char abuf[80]; + + do { + fgets(abuf, sizeof(abuf), stdin); + if (abuf[0] == '\n') + return(def); + num = atoi(abuf); + if (num > maxm) + printf("Maximum value is %d, try again: [%d] ? ", maxm, def); + if (num < 0) + printf("Number must be positive, try again: [%d] ? ", def); + } while (num > maxm || num < 0); + + return(num); +} + +/* + * read a date or time from standard input + * + * format can be a period in the form + * NNNNd (days) + * NNNNm (months) + * + * or an absolute time + * YY/MM/DD [HH:MM:SS] + * + * return 0xffffffff if null response + * + */ + +time_t +afpdp_gdat() +{ + struct tm tm; + time_t mult = 0; + char abuf[80], *cp; + + bzero(abuf, sizeof(abuf)); + fgets(abuf, sizeof(abuf), stdin); + + if (abuf[0] == '\n') + return(0xffffffff); + + /* + * explicit days ? + * + */ + if ((cp = (char *)index(abuf, 'd')) != NULL) { + *cp = '\0'; + mult = SECS_IN_DAY; + return(mult * atoi(abuf)); + } + + /* + * or months ? + * + */ + if ((cp = (char *)index(abuf, 'm')) != NULL) { + *cp = '\0'; + mult = SECS_IN_MON; + return(mult * atoi(abuf)); + } + + /* + * check for YY/MM/DD + * + */ + cp = abuf; + bzero((char *)&tm, sizeof(struct tm)); + if ((char *)index(cp, '/') != NULL) { + if (cp[2] == '/' && cp[5] == '/') { + cp[2] = cp[5] = cp[8] = '\0'; + if ((tm.tm_year = atoi(cp)) < 95) + tm.tm_year += 100; /* year - 1900 */ + tm.tm_mon = atoi(cp+3) - 1; /* 0 - 11 */ + tm.tm_mday = atoi(cp+6); /* 1 - 31 */ + tm.tm_isdst = 1; + cp += 8; + } else { + printf("Sorry I don't understand that format, use YY/MM/DD\n"); + return(0); + } + } + + /* + * and optional HH:MM:SS + * + */ + if (cp != abuf && *cp++ == '\0') { + if ((char *)index(cp, ':') != NULL) { + if (cp[2] == ':' && cp[5] == ':') { + cp[2] = cp[5] = cp[8] = '\0'; + tm.tm_hour = atoi(cp); /* 0 - 23 */ + tm.tm_min = atoi(cp+3); /* 0 - 59 */ + tm.tm_sec = atoi(cp+6); /* 0 - 59 */ + tm.tm_isdst = 1; + } else { + printf("Sorry I don't understand that format, use HH:MM:SS\n"); + return(0); + } + } + } + + /* + * tm set ? + * + */ + if (tm.tm_isdst) { + tm.tm_isdst = 0; +#if defined(sun) && !defined(SOLARIS) + return(timelocal(&tm)); +#else /* sun && !SOLARIS */ + return(mktime(&tm)); +#endif /* sun && !SOLARIS */ + } + + /* + * default days + * + */ + return(atoi(abuf)*SECS_IN_DAY); +} +#else /* DISTRIB_PASSWDS */ +int pass_dummy_for_ld; +#endif /* DISTRIB_PASSWDS */ diff --git a/lib/afp/makefile b/lib/afp/makefile new file mode 100644 index 0000000..e91ecbf --- /dev/null +++ b/lib/afp/makefile @@ -0,0 +1,58 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 14:00:06 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +DESTDIR=/usr/local/lib +OSDEFS= +LIBAFP=libafp.a +I=/usr/include +DES=../../extras + +LIBAFPSRCS=afperr.c afpcmd.c afppacks.c afposlock.c +LIBAFPOBJS=afperr.o afpcmd.o afppacks.o afposlock.o des.o + +$(LIBAFP): $(LIBAFPOBJS) + ar rv $(LIBAFP) $(LIBAFPOBJS) + +des.o: ${DES}/des.c + (cd ${DES}; make des.o) + cp ${DES}/des.o . + +clean: + -rm -f ${LIBAFPOBJS} ${LIBAFP} core *~ + +install: $(LIBAFP) + ${INSTALLER} $(LIBAFP) $(DESTDIR) + ranlib $(DESTDIR)/$(LIBAFP) + +dist: + @cat todist + +lint: $(LIBAFPSRCS) + lint $(LIBAFPSRCS) + +afposlock.o: afposlock.c + ${CC} ${OSDEFS} ${CFLAGS} -c afposlock.c + +# Dependencies +afpcmd.o: afpcmd.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/afp.h $I/netat/afpcmd.h +afperr.o: afperr.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h $I/netat/afp.h +afppacks.o: afppacks.c $I/netat/appletalk.h \ + $I/netat/aberrors.h $I/netat/abqueue.h \ + $I/netat/sysvcompat.h $I/netat/afp.h \ + $I/netat/afpcmd.h +afposlock.o: afposlock.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/sysvcompat.h \ + $I/netat/afp.h $I/netat/afpcmd.h diff --git a/lib/afpc/Makefile.m4 b/lib/afpc/Makefile.m4 new file mode 100644 index 0000000..81165e5 --- /dev/null +++ b/lib/afpc/Makefile.m4 @@ -0,0 +1,33 @@ +CFLAGS=cflags() bigcflags() specialcflags() +DESTDIR=libdestdir() +LIBAFPC=afpclib() +I=includedir() + +LIBAFPCSRCS=afpc.c afpcc.c +LIBAFPCOBJS=afpc.o afpcc.o + +$(LIBAFPC): $(LIBAFPCOBJS) + ifdef([uselordertsort],[ar cr $(LIBAFPC) `lorder $(LIBAFPCOBJS)|tsort`], + [ar rv $(LIBAFPC) $(LIBAFPCOBJS)]) + +clean: + -rm -f ${LIBAFPCOBJS} ${LIBAFPC} core *~ + +spotless: + -rm -f ${LIBAFPCOBJS} ${LIBAFPC} core *~ *.orig Makefile makefile + +install: $(LIBAFPC) + ifdef([sysvinstall],[install -f $(DESTDIR) $(LIBAFPC)], + [${INSTALLER} $(LIBAFPC) $(DESTDIR)]) +ifdef([uselordertsort],[],[ ranlib $(DESTDIR)/$(LIBAFPC)]) + +dist: + @cat todist + +lint: $(LIBAFPCSRCS) + lint $(LIBAFPCSRCS) + +afpc.o: afpc.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afpc.h +afpcc.o: afpcc.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afpc.h diff --git a/lib/afpc/README b/lib/afpc/README new file mode 100644 index 0000000..4526e52 --- /dev/null +++ b/lib/afpc/README @@ -0,0 +1,22 @@ + afpc.c - primary library + afpc.mss - documentation for afpc.c + afpcc.c - set of "easy" access routines (not documented) + probs - noted problems with appleshare client + +The afpc libraries are consist of two major parts. The first is a set +of routines (afpc) that adheres closely to the Apple standards for +argument passing, etc. The second is a set of routines (afpcc) that +attempts to simplify using the above mentioned routines. afpc.mss +shows the structures and special calling conventions for routines in +(afpc) - be wary of this file - it is out of date. + +Note: if libafp has des routines encoded you will be able to use +randnum exchange in login sequences - otherwise not. + +The afpc libraries are installed into /usr/local/lib/libafpc.a. See +the samples directory for examples of how to load or how to program +using these libraries. + +These routines are braindamaged and unmaintainable the way there are +done. They really need to be rewritten. + diff --git a/lib/afpc/afpc.c b/lib/afpc/afpc.c new file mode 100644 index 0000000..a762302 --- /dev/null +++ b/lib/afpc/afpc.c @@ -0,0 +1,970 @@ +/* + * $Author: djh $ $Date: 1996/04/25 01:12:53 $ + * $Header: /mac/src/cap60/lib/afpc/RCS/afpc.c,v 2.6 1996/04/25 01:12:53 djh Rel djh $ + * $Revision: 2.6 $ + * + */ + +/* + * afpc.c - AFP client calls + * + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 CCKim Created. + * + */ + +#include +#include +#include +#include /* so ntohl, etc work on non-vax */ +#include +#include +#include +#include + +sendspcmd(srn, sbuf, slen, cr) +byte *sbuf; +int slen; +dword *cr; +{ + int rlen, comp; + + SPCommand(srn, sbuf, slen, NULL, 0, cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + /* should we check rlen? */ + return(comp); +} + + +FPAddAPPL(srn, aa, cr) +int srn; +AddAPPLPkt *aa; +{ + char lbuf[sizeof(AddAPPLPkt)+1]; + extern PackEntry ProtoAAP[]; + int len; + + len = htonPackX(ProtoAAP, aa, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPAddComment(srn, ac, cr) +int srn; +AddCommentPkt *ac; +dword *cr; +{ + int len; + char lbuf[sizeof(AddCommentPkt)+1]; + extern PackEntry ProtoACP[]; + + len = htonPackX(ProtoACP, ac, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPAddIcon(srn, adi, icon, il, cr) +int srn; +AddIconPkt *adi; +byte *icon; +int il; +dword *cr; +{ + int len, rlen, wlen, comp; + char lbuf[sizeof(AddIconPkt)+1]; + extern PackEntry ProtoAIP[]; + + len = htonPackX(ProtoAIP, adi, lbuf); + + SPWrite(srn,lbuf,len, icon, il, NULL, 0, cr, &wlen, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != 0) /* keep trying if so */ + return(comp); + return(noErr); +} + + +FPByteRangeLock(srn, brl, rangestart, cr) +int srn; +ByteRangeLockPkt *brl; +dword *rangestart; +dword *cr; +{ + dword reply; + int rlen, len, comp; + char lbuf[sizeof(ByteRangeLockPkt)+1]; + extern PackEntry ProtoBRL[]; + + len = htonPackX(ProtoBRL, brl, lbuf); + SPCommand(srn, lbuf, len, &reply, sizeof(reply), cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + *rangestart = ntohl(reply); + return(comp); +} + + +FPCloseDir(srn, cd, cr) +int srn; +CloseDirPkt *cd; +dword *cr; +{ + char lbuf[sizeof(CloseDirPkt)+1]; + extern PackEntry ProtoCDP[]; + int len; + + len = htonPackX(ProtoCDP, cd, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPCloseDT(srn, cdt, cr) +int srn; +CloseDTPkt *cdt; +dword *cr; +{ + extern PackEntry ProtoCDT[]; + int len; + char lbuf[sizeof(CloseDTPkt)+1]; + + len = htonPackX(ProtoCDT, cdt, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPCloseFork(SRefNum, cfp, cr) +int SRefNum; +CloseForkPkt *cfp; +dword *cr; +{ + char lbuf[sizeof(CloseForkPkt)+1]; + extern PackEntry ProtoCFkP[]; + int len; + len = htonPackX(ProtoCFkP, cfp, lbuf); + + return(sendspcmd(SRefNum, lbuf, len, cr)); +} + + +FPCloseVol(srn, cv, cr) +int srn; +CloseVolPkt *cv; +dword *cr; +{ + int len; + extern PackEntry ProtoCVP[]; + char lbuf[sizeof(CloseVolPkt)+1]; + + len = htonPackX(ProtoCVP, cv, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPCopyFile(srn, cf, cr) +int srn; +CopyFilePkt *cf; +dword *cr; +{ + char lbuf[sizeof(CopyFilePkt)]; + extern PackEntry ProtoCpFP[]; + int len; + + len = htonPackX(ProtoCpFP, cf, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPCreateDir(srn, cd, newdirid, cr) +int srn; +CreateDirPkt *cd; +dword *newdirid; +dword *cr; +{ + dword reply; + int rlen, len, comp; + char lbuf[sizeof(CreateDirPkt)+1]; + extern PackEntry ProtoCRDP[]; + + len = htonPackX(ProtoCRDP, cd, lbuf); + SPCommand(srn, lbuf, len, &reply, sizeof(reply), cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + *newdirid = ntohl(reply); + return(comp); +} + + +FPCreateFile(srn, cf, cr) +int srn; +CreateFilePkt *cf; +dword *cr; +{ + char lbuf[sizeof(CreateFilePkt)+1]; + int len; + extern PackEntry ProtoCFP[]; + + len = htonPackX(ProtoCFP, cf, lbuf); + + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPDelete(srn, dp, cr) +int srn; +DeletePkt *dp; +dword *cr; +{ + char lbuf[sizeof(DeletePkt)+1]; + extern PackEntry ProtoDFP[]; + int len; + + len = htonPackX(ProtoDFP, dp, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPEnumerate(srn, ep, tbuf, tbufsiz, epar, eparcnt, cnt, cr) +int srn; +EnumeratePkt *ep; +byte *tbuf; +int tbufsiz; +FileDirParm *epar; +int eparcnt; +int *cnt; +dword *cr; +{ + char lbuf[sizeof(EnumeratePkt)]; + int rlen, comp, len, i; + word bitmap; + extern PackEntry ProtoEP[], ProtoEPR[], ProtoFileAttr[], ProtoDirAttr[]; + unsigned char *p; + EnumerateReplyPkt epr; + + len = htonPackX(ProtoEP, ep, lbuf); + + SPCommand(srn, lbuf, len, tbuf, tbufsiz, cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + len = ntohPackX(ProtoEPR, tbuf, rlen, &epr); + *cnt = epr.enur_actcnt; + for (i = 0, p = tbuf+len; i<(int)epr.enur_actcnt ; i++) { + len = (int)p[0]; + epar->fdp_flg = p[1]; + bitmap = FDP_ISDIR(p[1]) ? epr.enur_dbitmap : epr.enur_fbitmap; + ntohPackXbitmap(FDP_ISDIR(p[1]) ? ProtoDirAttr : ProtoFileAttr, + &p[2], len-2, epar, bitmap); + epar++; + p+=len; + } + return(noErr); +} + + +FPFlush(srn, fv, cr) +int srn; +FlushPkt *fv; +dword *cr; +{ + char lbuf[sizeof(FlushPkt)+1]; + int len; + extern PackEntry ProtoCFP[]; + + len = htonPackX(ProtoCFP, fv, lbuf); + + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPFlushFork(srn, ff, cr) +int srn; +FlushForkPkt *ff; +dword *cr; +{ + char lbuf[sizeof(FlushForkPkt)+1]; + extern PackEntry ProtoFFP[]; + int len; + + len = htonPackX(ProtoFFP, ff, lbuf); + + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPGetAPPL(srn, gap, gar, cr) +int srn; +GetAPPLPkt *gap; +GetAPPLReplyPkt *gar; +dword *cr; +{ + char lbuf[sizeof(GetAPPLPkt)+1]; + char buf[sizeof(GetAPPLReplyPkt)+1]; + int rlen, comp, len; + extern PackEntry ProtoGAP[], ProtoGAPR[], ProtoFileAttr[]; + + len = htonPackX(ProtoGAP, gap, lbuf); + SPCommand(srn,lbuf,len,buf,sizeof(GetAPPLReplyPkt),cr,&rlen,-1,&comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + len = ntohPackX(ProtoGAPR, buf, rlen, gar); + rlen -= len; + ntohPackXbitmap(ProtoFileAttr, buf+len, rlen, &gar->fdp, gar->gapr_bitmap); + return(noErr); +} + + +FPGetComment(srn, gc, gcr, cr) +int srn; +GetCommentPkt *gc; +GCRPPtr gcr; +dword *cr; +{ + char lbuf[sizeof(GetCommentPkt)+1]; + char buf[sizeof(GetCommentReplyPkt)+1]; + int rlen, comp, len; + extern PackEntry ProtoGCP[]; + + len = htonPackX(ProtoGCP, gc, lbuf); + SPCommand(srn,lbuf,len,buf,sizeof(GetCommentReplyPkt),cr,&rlen,-1,&comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + len = (int)buf[0]; /* get return string len */ + if (len > 199) + len = 199; /* truncate if too long */ + bcopy(buf+1, gcr->gcmr_ctxt, len); + gcr->gcmr_clen = len; + return(noErr); +} + + +FPGetFileDirParms(srn, gfdp, epar, cr) +int srn; +GetFileDirParmsPkt *gfdp; +FileDirParm *epar; +dword *cr; +{ + byte lbuf[sizeof(GetFileDirParmsPkt)+1]; + byte buf[sizeof(FileDirParm)+10]; + int rlen, comp, len; + extern PackEntry ProtoGFDPP[], ProtoFileAttr[], ProtoDirAttr[]; + word rfbitmap, rdbitmap; + byte *p, isfiledir; + + len = htonPackX(ProtoGFDPP, gfdp, lbuf); + + SPCommand(srn,lbuf, len, buf, sizeof(FileDirParm)+10, cr, &rlen,-1,&comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0) + return(comp); + p = buf; + UnpackWord(&p, &rfbitmap); + UnpackWord(&p, &rdbitmap); + epar->fdp_flg = isfiledir = *p++; + p++; /* skip past zero entry */ + if (FDP_ISDIR(isfiledir)) + ntohPackXbitmap(ProtoDirAttr, p, rlen, epar, rdbitmap); /* directory */ + else + ntohPackXbitmap(ProtoFileAttr, p, rlen, epar, rfbitmap); /* file */ + return(noErr); +} + + +FPGetForkParms(srn, gfp, epar, cr) +int srn; +GetForkParmsPkt *gfp; +FileDirParm *epar; +dword *cr; +{ + byte lbuf[sizeof(GetForkParmsPkt)+1]; + byte buf[sizeof(FileDirParm)+10]; + int rlen, comp, len; + extern PackEntry ProtoGFkPP[],ProtoFileAttr[], ProtoDirAttr[]; + byte *p; + word fbitmap; + + len = htonPackX(ProtoGFkPP, gfp, lbuf); + + SPCommand(srn,lbuf, len, buf, sizeof(buf), cr, &rlen,-1,&comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != noErr) + return(comp); + p = buf; /* copy pointer */ + UnpackWord(&p, &fbitmap); /* unpack bitmap */ + ntohPackXbitmap(ProtoFileAttr, p, rlen, epar, fbitmap); + return(noErr); +} + + +FPGetIcon(srn, gi, icon, iconlen, cr) +int srn; +GetIconPkt *gi; +byte *icon; +int iconlen; +dword *cr; +{ + byte lbuf[sizeof(GetIconPkt)+1]; + extern PackEntry ProtoGI[]; + int comp, rlen, len; + + len = htonPackX(ProtoGI, gi, lbuf); + + SPCommand(srn, lbuf, len, icon, iconlen, cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != noErr) + return(comp); + return(noErr); +} + + +FPGetIconInfo(srn, gii, gicr, cr) +int srn; +GetIconInfoPkt *gii; +GetIconInfoReplyPkt *gicr; +dword *cr; +{ + byte buf[sizeof(GetIconInfoPkt)+1], lbuf[sizeof(GetIconInfoReplyPkt)+1]; + int len, rlen, comp; + extern PackEntry ProtoGII[], ProtoGIIR[]; + + len = htonPackX(ProtoGII, gii, lbuf); + SPCommand(srn, lbuf, len, buf, sizeof(buf), cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != noErr) + return(comp); + ntohPackX(ProtoGIIR, buf, rlen, gicr); + return(noErr); +} + + +FPGetSrvrMsg(srn, smp, smrp, cr) +int srn; +SrvrMsgPkt *smp; +SrvrMsgReplyPkt *smrp; +dword *cr; +{ + byte buf[sizeof(SrvrMsgPkt)+1], lbuf[sizeof(SrvrMsgReplyPkt)+1]; + int len, rlen, comp; + extern PackEntry ProtoMsgP[], ProtoMsgRP[]; + + len = htonPackX(ProtoMsgP, smp, lbuf); + SPCommand(srn, lbuf, len, buf, sizeof(buf), cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != noErr) + return(comp); + ntohPackX(ProtoMsgRP, buf, rlen, smrp); + return(noErr); +} + + +FPGetSrvrInfo(addr, sr) +AddrBlock *addr; +GetSrvrInfoReplyPkt *sr; +{ + byte buf[atpMaxData]; + int len, comp; + extern PackEntry ProtoSRP[]; + int avolen, uamolen; + byte *p; + + SPGetStatus(addr, buf, atpMaxData-1, &len, 1, -1, &comp) ; + while (comp > 0) + abSleep(4*9, TRUE); + if (comp < 0) + return(comp); + + ntohPackX(ProtoSRP, buf, len, sr); + /* number of bytes for avo and uamo strings */ + avolen = IndStrLen(sr->sr_avo); + uamolen = IndStrLen(sr->sr_uamo); + if ((p = (byte *)malloc(avolen+uamolen)) == NULL) + return(-1); + /* copy the data */ + bcopy(sr->sr_avo, p, avolen); + bcopy(sr->sr_uamo, p+avolen, uamolen); + /* reset pointers */ + sr->sr_avo = p; + sr->sr_uamo = p+avolen; + return(noErr); +} + + +FPGetSrvrParms(srn, sp, cr) +int srn; +GSPRPPtr *sp; +dword *cr; +{ + byte cmd = AFPGetSrvrParms; + byte buf[atpMaxData]; + int rlen, comp, i, nvols; + extern PackEntry ProtoGSPRP[]; + GSPRPPtr rp; + byte *p; + + SPCommand(srn, &cmd, 1, buf, sizeof(buf)-1, cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + nvols = (int)buf[4]; /* count of vols */ + if (nvols == 0) + *sp = (GSPRPPtr)malloc(sizeof(GetSrvrParmsReplyPkt)); + else + *sp = (GSPRPPtr)malloc(sizeof(GetSrvrParmsReplyPkt)+ + (nvols-1)*sizeof(VolParm)); + if (*sp == NULL) + return(-1); + rp = *sp; + ntohPackX(ProtoGSPRP, buf, rlen, rp); + for (p = &buf[5], i = 0; i < nvols; i++) { + rp->gspr_volp[i].volp_flag = *p; + cpyp2cstr(rp->gspr_volp[i].volp_name, p+1); + p += 2+(int)p[1]; + } + return(noErr); +} + + +FPGetVolParms(srn, gvp, ve, cr) +int srn; +GetVolParmsPkt *gvp; +GetVolParmsReplyPkt *ve; +dword *cr; +{ + byte lbuf[sizeof(GetVolParmsPkt)+1]; + byte buf[sizeof(GetVolParmsReplyPkt)+10]; + int len, rlen, comp; + extern PackEntry ProtoGVPP[]; /* getvolparms */ + extern PackEntry ProtoGVPRP[]; /* volume info */ + + len = htonPackX(ProtoGVPP, gvp, lbuf); + SPCommand(srn, lbuf, len, buf, sizeof(GetVolParmsReplyPkt)+10, + cr, &rlen, -1, &comp); + while (comp > 0) abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + ntohPackX(ProtoGVPRP, buf, rlen, ve); + return(noErr); +} + + +FPLogin(srn, lp, lir, cr) +int srn; +LoginPkt *lp; +LoginReplyPkt *lir; +dword *cr; +{ + byte lbuf[sizeof(LoginPkt)+8]; + int rlen, len, comp; + extern PackEntry ProtoLP[], ProtoLRP[]; + char buf[sizeof(LoginReplyPkt)+1]; + + len = htonPackXbitmap(ProtoLP, lp, lbuf, lp->log_flag); + + SPCommand(srn, lbuf, len, buf, sizeof(buf), cr, &rlen, -1, &comp); + + while (comp > 0) + abSleep(4, TRUE); + ntohPackXbitmap(ProtoLRP, buf, rlen, lir, lir->logr_flag); + return(comp); +} + + +/* For now - assume no response - this is not necessarily true though */ +FPLoginCont(srn, lgc, lir, cr) +int srn; +LoginContPkt *lgc; +LoginReplyPkt *lir; +dword *cr; +{ + extern PackEntry ProtoLCP[], ProtoLRP[]; + char lbuf[sizeof(LoginContPkt)]; + char buf[sizeof(LoginReplyPkt)+1]; + int rlen, len, comp; + + len = htonPackXbitmap(ProtoLCP, lgc, lbuf, lgc->lgc_flags); + SPCommand(srn, lbuf, len, buf, sizeof(buf), cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + ntohPackXbitmap(ProtoLRP, buf, rlen, lir, lir->logr_flag); + return(comp); +} + + +FPLogout(srn, cr) +int srn; +dword *cr; +{ + byte cmd = AFPLogout; + return(sendspcmd(srn, &cmd, 1, cr)); +} + + +FPMapID(srn, mi,mapr, cr) +int srn; +MapIDPkt *mi; +MapIDReplyPkt *mapr; +dword *cr; +{ + byte buf[sizeof(MapIDReplyPkt)+1], lbuf[sizeof(MapIDPkt)+1]; + int len, rlen, comp; + extern PackEntry ProtoMIP[]; + + len = htonPackX(ProtoMIP, mi, lbuf); + SPCommand(srn,lbuf,len,buf,sizeof(MapIDReplyPkt)+1, cr, &rlen, -1, &comp); + while (comp > 0) abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + pstrcpy(mapr->mpir_name, buf); /* copy back name */ + return(noErr); +} + + +FPMapName(srn, mnp, id, cr) +int srn; +MapNamePkt *mnp; +dword *id; +dword *cr; +{ + byte lbuf[sizeof(MapNamePkt)+1], retid[sizeof(dword)], *p; + int len, rlen, comp; + extern PackEntry ProtoMNP[]; + + len = htonPackX(ProtoMNP, mnp, lbuf); + SPCommand(srn, lbuf, len, retid, sizeof(retid), cr, &rlen, -1, &comp); + while (comp > 0) abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + p = retid; + UnpackDWord(&p, id); + return(comp); +} + + +FPMoveFile(srn, mf, cr) +int srn; +MovePkt *mf; +dword *cr; +{ + char lbuf[sizeof(MovePkt)]; + extern PackEntry ProtoMFP[]; + int len; + + len = htonPackX(ProtoMFP, mf, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPOpenDir(srn, od, retdirid, cr) +int srn; +OpenDirPkt *od; +dword *retdirid; +dword *cr; +{ + byte lbuf[sizeof(OpenDirPkt)+1], retid[sizeof(dword)], *p; + int len, rlen, comp; + extern PackEntry ProtoODP[]; + + len = htonPackX(ProtoODP, od, lbuf); + SPCommand(srn, lbuf, len, retid, sizeof(retid), cr, &rlen, -1, &comp); + while (comp > 0) abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + p = retid; + UnpackDWord(&p, retdirid); + return(comp); +} + + +FPOpenDT(srn, odt, dtrefnum, cr) +int srn; +OpenDTPkt *odt; +word *dtrefnum; +dword *cr; +{ + extern PackEntry ProtoODT[]; + word reply; + int rlen, len, comp; + char lbuf[sizeof(OpenDTPkt)+1]; + + len = htonPackX(ProtoODT, odt, lbuf); + SPCommand(srn, lbuf, len, &reply, sizeof(reply), cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != 0) + return(comp); + *dtrefnum = ntohs(reply); + return(comp); +} + + +/* if type then rsrc else data */ +/* should really optionally return bitmap data */ + + +FPOpenFork(srn, of, epar, refnum, cr) +int srn; +OpenForkPkt *of; +FileDirParm *epar; +word *refnum; +dword *cr; +{ + extern PackEntry ProtoOFkP[], ProtoFileAttr[]; + byte lbuf[sizeof(OpenForkPkt)], buf[sizeof(FileDirParm)+20], *p; + int rlen, comp, len; + word bitmap; + + len = htonPackX(ProtoOFkP, of, lbuf); + + SPCommand(srn, lbuf, len, buf, sizeof(buf), cr, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0) + return(comp); + p = buf; + UnpackWord(&p, &bitmap); + UnpackWord(&p, refnum); + if (epar) + ntohPackXbitmap(ProtoFileAttr, p, rlen, epar, bitmap); +#ifdef notdef + printf("openfork CR = %X, %d\n",cr,-(*cr)); + printf("RLEN = %d\n",rlen); + printf("Open file refnum = %d\n",*refnum); +#endif + return(noErr); +} + + +FPOpenVol(srn, ov, op, cr) +int srn; +OpenVolPkt *ov; +GetVolParmsReplyPkt *op; +dword *cr; +{ + byte lbuf[sizeof(OpenVolPkt)+1]; + byte buf[100]; + int rlen, comp, len; + extern PackEntry ProtoGVPRP[]; + extern PackEntry ProtoOVP[]; + + len = htonPackX(ProtoOVP, ov, lbuf); + + SPCommand(srn, lbuf, len, buf, 100, cr, &rlen, -1, &comp); + + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0) + return(comp); + + ntohPackX(ProtoGVPRP, buf, rlen, op); + return(noErr); +} + + +/* + * reads from remote into buf for length at most buflen starting at offset + * file must already be open + * returns length read + * sets eof if eof was returned +*/ + + +FPRead(srn, rp, buf, buflen, rlen, cr) +int srn; +ReadPkt *rp; +byte *buf; +int *rlen; +dword *cr; +{ + char lbuf[sizeof(ReadPkt)+1]; + extern PackEntry ProtoRP[]; + int comp, len; + + len = htonPackX(ProtoRP, rp, lbuf); + + SPCommand(srn, lbuf, len, buf, buflen, cr, rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + return(comp); +} + + +FPRemoveAPPL(srn, ra, cr) +int srn; +RemoveAPPLPkt *ra; +dword *cr; +{ + extern PackEntry ProtoRMA[]; + int len; + byte lbuf[sizeof(RemoveAPPLPkt)+1]; + + len = htonPackX(ProtoRMA, ra, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPRemoveComment(srn, rc, cr) +int srn; +RemoveCommentPkt *rc; +dword *cr; +{ + extern PackEntry ProtoRMC[]; + int len; + byte lbuf[sizeof(RemoveCommentPkt)+1]; + + len = htonPackX(ProtoRMC, rc, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPRename(srn, rn, cr) +int srn; +RenamePkt *rn; +dword *cr; +{ + extern PackEntry ProtoRFP[]; + byte lbuf[sizeof(RenamePkt)+1]; + int len; + + len = htonPackX(ProtoRFP, rn, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPSetDirParms(srn, sdp, fdp, cr) +int srn; +SetDirParmsPkt *sdp; +FileDirParm *fdp; +dword *cr; +{ + extern PackEntry ProtoSDPP[], ProtoDirAttr[]; + byte lbuf[sizeof(SetDirParmsPkt)+sizeof(FileDirParm)+1]; + int len; + + len = htonPackX(ProtoSDPP, sdp, lbuf); + len += htonPackXbitmap(ProtoDirAttr, fdp, lbuf+len, sdp->sdp_bitmap); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPSetFileParms(srn, sfp, fdp, cr) +int srn; +SetFileParmsPkt *sfp; +FileDirParm *fdp; +dword *cr; +{ + extern PackEntry ProtoSFPP[], ProtoFileAttr[]; + byte lbuf[sizeof(SetFileParmsPkt)+sizeof(FileDirParm)+1]; + int len; + + len = htonPackX(ProtoSFPP, sfp, lbuf); + len += htonPackXbitmap(ProtoFileAttr, fdp, lbuf+len, sfp->sfp_bitmap); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPSetFileDirParms(srn, sfdp, fdp, cr) +int srn; +SetFileDirParmsPkt *sfdp; +FileDirParm *fdp; +dword *cr; +{ + extern PackEntry ProtoSDPP[], ProtoFileDirAttr[]; + byte lbuf[sizeof(SetForkParmsPkt)+sizeof(FileDirParm)+1]; + int len; + + len = htonPackX(ProtoSDPP, sfdp, lbuf); + len += htonPackXbitmap(ProtoFileDirAttr, fdp, lbuf+len, sfdp->scp_bitmap); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPSetForkParms(srn, sfp, cr) +int srn; +SetForkParmsPkt *sfp; +dword *cr; +{ + extern PackEntry ProtoSFkPP[]; + byte lbuf[sizeof(SetForkParmsPkt)+1]; + int len; + + len = htonPackX(ProtoSFkPP, sfp, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPSetVolParms(srn, svp, cr) +SetVolParmsPkt *svp; +dword *cr; +{ + extern PackEntry ProtoSVPP[]; + byte lbuf[sizeof(SetVolParmsPkt)+1]; + int len; + + len = htonPackX(ProtoSVPP, svp, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + + +FPWrite(srn, wbuf, wlen, wp, actcnt, lastoffset_written, cr) +int srn; +char *wbuf; +int wlen; +WritePkt *wp; +dword *actcnt; /* actual count written */ +dword *lastoffset_written; /* last offset written */ +dword *cr; +{ + char lbuf[sizeof(WritePkt)+1]; + dword low; + int rlen, comp, len; + extern PackEntry ProtoWP[]; + + len = htonPackX(ProtoWP, wp, lbuf); + + SPWrite(srn, lbuf, len, wbuf, wlen, &low, sizeof(low), + cr, actcnt, &rlen, -1, &comp); + while (comp > 0) + abSleep(4, TRUE); + if (comp < 0 || *cr != 0) /* keep trying if so */ + return(comp); + *lastoffset_written = htonl(low); /* ugh */ + return(noErr); +} + + +FPExchangeFiles(srn, mf, cr) +int srn; +ExchPkt *mf; +dword *cr; +{ + char lbuf[sizeof(ExchPkt)]; + extern PackEntry ProtoExP[]; + int len; + + len = htonPackX(ProtoExP, mf, lbuf); + return(sendspcmd(srn, lbuf, len, cr)); +} + diff --git a/lib/afpc/afpc.mss b/lib/afpc/afpc.mss new file mode 100644 index 0000000..8e42300 --- /dev/null +++ b/lib/afpc/afpc.mss @@ -0,0 +1,965 @@ +@make(manual) +@device(ln03) +@section(FPAddAPPL) +@begin(example) +@tabdivide(8) +Call: FPAddAPPL(SessRefNum, aa, FPError) +Input:@\int SessRefNum; +Input:@\AddAPPLPkt *aa; +Output:@\dword *FPError; +Data Structures: +typedef struct { /* FPAddAPPL */ + byte aap_cmd; /* Command */ + byte aap_zero; /* Always zero */ + word aap_volid; /* volid */ + dword aap_dirid; /* directory id */ + byte aap_fcreator[4]; /* file creator */ + dword aap_apptag; /* application tag */ + byte aap_ptype; /* path type */ + byte aap_path[MAXPATH]; /* path */ +} AddAPPLPkt, *AAPPtr; +@end(Example) + +@section(FPAddComment) +@begin(example) +@tabdivide(8) +Call:@\FPAddComment(SessRefNum, ac, FPError) +Input:@\int SessRefNum; +Input:@\AddCommentPkt *ac; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* AddComment */ + byte adc_cmd; /* Command */ + byte adc_zero; /* always zero */ + word adc_dtrefnum; /* desk top refnum */ + dword adc_dirid; /* directory id */ + byte adc_ptype; /* path type */ + byte adc_path[MAXPATH]; /* path */ + byte adc_clen; /* comment length */ + byte adc_comment[199]; /* comment string (PASCAL) */ +} AddCommentPkt, *ACPPtr; +@end(example) + +@section(FPAddIcon) +@begin(example) +@tabdivide(8) +Call:@\FPAddIcon(SessRefNum, adi, icon, iconlen, FPError) +Input:@\int SessRefNum; +Input:@\AddIconPkt *adi; +Input:@\byte *icon; +Input:@\int iconlen; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* AddIcon */ + byte adi_cmd; /* Command */ + byte adi_zero; + word adi_dtref; /* Desktop refnum */ + byte adi_fcreator[4]; /* file creator */ + byte adi_ftype[4]; /* file type */ + byte adi_icontype; /* icon type */ + byte adi_zero2; + dword adi_icontag; /* user icon tag */ + word adi_iconsize; /* icon size */ +} AddIconPkt, *AIPPtr; +@end(example) + +Add the icon bitmap pointed to by @i of length @i to +the desk top. + + +@section(FPByteRangeLock) +@begin(example) +@tabdivide(8) +Call:@\FPByteRangeLock(SessRefNum, brl, rangestart, FPError) +Input:@\int SessRefNum; +Input:@\ByteRangeLockPkt *brl; +Output:@\dword *rangestart; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* ByteRangeLock */ + byte brl_cmd; /* command */ + byte brl_flg; /* flags */ +#define BRL_START 0x100 /* high bit */ +#define BRL_UNLOCK 0x001 /* low bit */ + word brl_refnum; /* file refnum */ + dword brl_offset; /* offset to start lock */ + dword brl_length; /* number of bytes to lock */ +} ByteRangeLockPkt, *BRLPPtr; +@end(example) + + +@section(FPCloseDir) +@begin(example) +@tabdivide(8) +Call:@\FPCloseDir(SessRefNum, cd, FPError) +Input:@\int SessRefNum; +Input:@\CloseDirPkt *cd; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPCloseDir */ + byte cdr_cmd; /* command */ + byte cdr_zero; /* always zero */ + word cdr_volid; /* volume id */ + dword cdr_dirid; /* directory id */ +} CloseDirPkt, *CDPPtr; +@end(example) + + +@section(FPCloseDT) +@begin(example) +@tabdivide(8) +Call:@\FPCloseDT(SessRefNum, cdt, FPError) +Input:@\int SessRefNum; +Input:@\CloseDTPkt *cdt; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPCloseDT */ + byte cdt_cmd; /* command */ + byte cdt_zero; /* zero byte */ + word cdt_dtrefnum; /* desktop database refnum */ +} CloseDTPkt, *CDTPPtr; +@end(example) + +@section(FPCloseFork) +@begin(example) +@tabdivide(8) +Call:@\FPCloseFork(SRefNum, cfp, FPError) +Input:@\int SRefNum; +Input:@\CloseForkPkt *cfp; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPCloseFork */ + byte cfk_cmd; /* command */ + byte cfk_zero; /* zero byte */ + word cfk_refnum; /* open fork reference number */ +} CloseForkPkt, *CFkPPtr; +@end(example) + +@section(FPCloseVol) +@begin(example) +@tabdivide(8) +Call:@\FPCloseVol(SessRefNum, cv, FPError) +Input:@\int SessRefNum; +Input:@\CloseVolPkt *cv; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPCloseVol */ + byte cv_cmd; /* command */ + byte cv_zero; /* always zero */ + word cv_volid; /* volume ID */ +} CloseVolPkt, *CVPPtr; +@end(example) + + +@section(FPCopyFile) +@begin(example) +@tabdivide(8) +Call:@\FPCopyFile(SessRefNum, cf, FPError) +Input:@\int SessRefNum; +Input:@\CopyFilePkt *cf; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPCopyFile (optional) */ + byte cpf_cmd; /* command */ + byte cpf_zero; /* always zero */ + word cpf_svolid; /* source volume id */ + sdword cpf_sdirid; /* source directory id */ + word cpf_dvolid; /* destination volume id */ + sdword cpf_ddirid; /* destination directory id */ + byte cpf_sptype; /* source path type */ + byte cpf_spath[MAXPATH]; /* source path */ + byte cpf_dptype; /* destination path type */ + byte cpf_dpath[MAXPATH]; /* destination path */ + byte cpf_newtype; /* new path type */ + byte cpf_newname[MAXPATH]; /* new name */ +} CopyFilePkt, *CpFPPtr; +@end(example) + + +@section(FPCreateDir) +@begin(example) +@tabdivide(8) +Call:@\FPCreateDir(SessRefNum, cd, newdirid, FPError) +Input:@\int SessRefNum; +Input:@\CreateDirPkt *cd; +Output:@\dword *newdirid; +Input:@\dword *FPError; +Data Structures: + +typedef struct { /* FPCreateDir */ + byte crd_cmd; /* command */ + byte crd_zero; /* always zero */ + word crd_volid; /* volume id */ + dword crd_dirid; /* directory id */ + byte crd_ptype; /* path type */ + byte crd_path[MAXPATH]; /* path */ +} CreateDirPkt, *CRDPPtr; +@end(example) + +The directory id of the new directory is returned through @i +if the call is successful. + + +@section(FPCreateFile) +@begin(example) +@tabdivide(8) +Call:@\FPCreateFile(SessRefNum, cf, FPError) +Input:@\int SessRefNum; +Input:@\CreateFilePkt *cf; +Output:@\dword *FPError; +Data Structures: +typedef struct { /* FPCreateFile */ + byte crf_cmd; /* command */ + byte crf_flg; /* flags */ +#define CRF_HARD 0x01 /* hard create */ + word crf_volid; /* volume id */ + sdword crf_dirid; /* directory id */ + byte crf_ptype; /* path name type */ + byte crf_path[MAXPATH]; /* path name */ +} CreateFilePkt, *CFPPtr; +@end(example) + + +@section(FPDelete) +@begin(example) +@tabdivide(8) +Call:@\FPDelete(SessRefNum, dp, FPError) +Input:@\int SessRefNum; +Input:@\DeletePkt *dp; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPDelete */ + byte del_cmd; /* command */ + byte del_zero; /* always zero */ + word del_volid; /* volume id */ + sdword del_dirid; /* directory id */ + byte del_ptype; /* path type */ + byte del_path[MAXPATH]; /* path */ +} DeletePkt, *DPPtr; +@end(example) + + +@section(FPEnumerate) +@begin(example) +@tabdivide(8) +Call:@\FPEnumerate(SessRefNum, ep, tbuf, tbufsiz, fdparms, +@\fdparmslength, cnt, FPError) +Input:@\int SessRefNum; +Input:@\EnumeratePkt *ep; +Input:@\byte *tbuf; +Input:@\int tbufsiz; +Output:@\FileDirParm *fdparms; +Input:@\int fdparmslength; +Output:@\int *cnt; +Output:@\dword *FPError; +Data Structures: +typedef struct { /* FPEnumerate */ + byte enu_cmd; /* command */ + byte enu_zero; /* always zero */ + word enu_volid; /* volume id */ + dword enu_dirid; /* directory id */ + word enu_fbitmap; /* file bitmap */ + word enu_dbitmap; /* directory bitmap */ + word enu_reqcnt; /* request count */ + word enu_stidx; /* start index */ + word enu_maxreply; /* max reply size */ + byte enu_ptype; /* path type */ + byte enu_path[MAXPATH]; /* path */ +} EnumeratePkt, *EPPtr; +@end(example) + +@i should be a buffer of size @i that is used to hold +the reply from the remote side. The enumerated items are filled in in +the array of length @i pointed to by @i. +Note: @i should be at least as large as ep->enu_maxreply. + + +@section(FPFlush) +@begin(example) +@tabdivide(8) +Call:@\FPFlush(SessRefNum, fv, FPError) +Input:@\int SessRefNum; +Input:@\FlushPkt *fv; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPFlush */ + byte fls_cmd; /* command */ + byte fls_zero; /* always zero */ + word fls_volid; /* volume ID */ +} FlushPkt, *FPPtr; +@end(example) + + +@section(FPFlushFork) +@begin(example) +@tabdivide(8) +Call:@\FPFlushFork(SessRefNum, ff, FPError) +Input:@\int SessRefNum; +Input:@\FlushForkPkt *ff; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPFlushFork */ + byte flf_cmd; /* command */ + byte flf_zero; /* always zero */ + word flf_refnum; /* open fork reference number */ +} FlushForkPkt, *FFkPPtr; +@end(example) + + +@section(FPGetAPPL) +@begin(example) +@tabdivide(8) +Call:@\FPGetAPPL(SessRefNum, gap, gar, FPError) +Input:@\int SessRefNum; +Input:@\GetAPPLPkt *gap; +Output:@\GetAPPLReplyPkt *gar; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPGetAPPL */ + byte gap_cmd; + byte gap_zero; + word gap_dtrefnum; /* desk top reference number */ + byte gap_fcreator[4]; /* creator type of the appl */ + word gap_applidx; /* index of the APPL entry */ + word gap_bitmap; /* bitmap of parms to return */ +} GetAPPLPkt, *GAPPtr; + +typedef struct { /* FPGetAPPL Reply */ + word gapr_bitmap; /* returned bitmap */ + dword gapr_appltag; /* appl tag */ + FileDirParm fdp; /* file parms */ +} GetAPPLReplyPkt, *GARPPtr; +@end(example) + + +@section(FPGetComment) +@begin(example) +@tabdivide(8) +Call:@\FPGetComment(SessRefNum, gc, gcr, FPError) +Input:@\int SessRefNum; +Input:@\GetCommentPkt *gc; +Output:@\GetCommentReplyPkt *gcr; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPGetComment */ + byte gcm_cmd; /* command */ + byte gcm_zero; + word gcm_dtrefnum; /* desktop reference number */ + dword gcm_dirid; /* directory id */ + byte gcm_ptype; /* path type */ + byte gcm_path[MAXPATH]; /* path */ +} GetCommentPkt, *GCPPtr; + +typedef struct { /* FPGetComment Reply */ + byte gcmr_clen; /* comment length */ + byte gcmr_ctxt[199]; /* comment text */ +} GetCommentReplyPkt, *GCRPPtr; +@end(example) + +Important notice: the comment text is a Pascal string, so the first +byte has the length of string and this length is independent of +gcmr_clen. + + +@section(FPGetFileDirParms) +@begin(example) +@tabdivide(8) +Call:@\FPGetFileDirParms(SessRefNum, gfdp, fdp, FPError) +Input:@\int SessRefNum; +Input:@\GetFileDirParmsPkt *gfdp; +Output:@\FileDirParm *fdp; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPGetFileDirParms */ + byte gdp_cmd; /* command */ + byte gdp_zero; /* always zero */ + word gdp_volid; /* volume ID */ + dword gdp_dirid; /* directory id */ + word gdp_fbitmap; /* file bitmap */ + word gdp_dbitmap; /* directory bitmap */ + byte gdp_ptype; /* path type */ + byte gdp_path[MAXPATH]; /* path */ +} GetFileDirParmsPkt, *GFDPPPtr; +@end(example) + + +@section(FPGetForkParms) +@begin(example) +@tabdivide(8) +Call:@\FPGetForkParms(SessRefNum, gfp, fdp, FPError) +Inputs:@\int SessRefNum; +Input:@\GetForkParmsPkt *gfp; +Output:@\FileDirParm *fdp; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPGetForkParms */ + byte gfp_cmd; /* command */ + byte gfp_zero; /* zero word */ + word gfp_refnum; /* open fork reference number */ + word gfp_bitmap; /* bitmap */ +} GetForkParmsPkt, *GFkPPPtr; +@end(example) + + +@section(FPGetIcon) +@begin(example) +@tabdivide(8) +Call:@\FPGetIcon(SessRefNum, gi, icon, iconlen, FPError) +Input:@\int SessRefNum; +Input:@\GetIconPkt *gi; +Output:@\byte *icon; +Input:@\int iconlen; +Outputs:@\dword *FPError; +Data Structures: +typedef struct { /* FPGetIcon */ + byte gic_cmd; + byte gic_zero; + word gic_dtrefnum; /* desktop ref num */ + byte gic_fcreator[4]; /* file creator */ + byte gic_ftype[4]; /* file type */ + byte gic_itype; /* icon type */ + byte gic_zero2; + word gic_length; +} GetIconPkt, *GIPPtr; +@end(example) + +The icon is returned in the array of length @i pointed to by +@i. + + +@section(FPGetIconInfo) +@begin(example) +@tabdivide(8) +Call:@\FPGetIconInfo(SessRefNum, gii, gicr, FPError) +Input:@\int SessRefNum; +Input:@\GetIconInfoPkt *gii; +Output:@\GetIconInfoReplyPkt *gicr; +Output:@\dword *FPError; +Data Structures: +typedef struct { /* FPGetIconInfo */ + byte gii_cmd; + byte gii_zero; + word gii_dtrefnum; + byte gii_fcreator[4]; + word gii_iidx; /* icon index */ +} GetIconInfoPkt, *GIIPPtr; + +typedef struct { /* FPGetIconInfo Reply */ + dword giir_itag; /* icon tag */ + byte giir_ftype[4]; /* file type */ + byte giir_itype; /* icon type */ + byte giir_zero; + word giir_size; /* size of icon */ +} GetIconInfoReplyPkt, *GIIRPPtr; +@end(example) + + +@section(FPGetSrvrInfo) +@begin(example) +@tabdivide(8) +Call:@\FPGetSrvrInfo(addr, gsir) +Input:@\AddrBlock *addr; +Output:@\GetSrvrInfoReplyPkt *gsir; +Data Structures: + +typedef struct { + char sr_machtype[17]; /* machine name */ + byte *sr_avo; /* offset to afp versions */ + byte *sr_uamo; /* user access methods offset (ISTR) */ + char *sr_vicono; /* offset to volume icon */ + word sr_flags; /* flags */ + char sr_servername[33]; /* server name */ +} GetSrvrInfoReplyPkt, *GSIRPPtr; +@end(example) + +FPGetSrvrInfo uses SPGetStatus to get the specified information. + + +@section(FPGetSrvrParms) +@begin(example) +@tabdivide(8) +Call:@\FPGetSrvrParms(SessRefNum, sp, FPError) +Input:@\int SessRefNum; +Output:@\GetSrvrParmsReplyPkt *sp; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* SrvrParm */ + byte volp_flag; /* flags */ +#define SRVRP_PASSWD 0x01 /* password is present */ + byte volp_name[27]; /* volume name */ +} VolParm; + +typedef struct { /* FPGetSrvrParms Reply */ + dword gspr_time; /* server time */ + byte gspr_nvols; /* number of volume parms */ + VolParm gspr_volp[1]; /* one VolParm for each volume */ +} GetSrvrParmsReplyPkt, *GSPRPPtr; +@end(example) + + +@section(FPGetVolParms) +@begin(example) +@tabdivide(8) +Call:@\FPGetVolParms(SessRefNum, gvp, gvpr, FPError) +Input:@\int SessRefNum; +Input:@\GetVolParmsPkt *gvp; +Output:@\GetVolParmsReplyPkt *gvpr; +Output:@\dword *FPError; +Data Structures: +typedef struct { /* FPGetVolParms */ + byte gvp_cmd; /* command */ + byte gvp_zero; /* always zero */ + word gvp_volid; /* volume id */ + word gvp_bitmap; /* request bitmap */ +} GetVolParmsPkt, *GVPPPtr; + +typedef struct { /* FPGetVolParms */ + byte gvpr_bitmap; /* return bitmap */ + word gvpr_attr; /* attributes */ + word gvpr_sig; /* volume signature */ + sdword gvpr_cdate; /* volume creation date */ + sdword gvpr_mdate; /* volume modification date */ + sdword gvpr_bdate; /* volume backup date */ + word gvpr_volid; /* volume id */ + sdword gvpr_size; /* size of volume in bytes */ + sdword gvpr_free; /* free bytes on volume */ + byte gvpr_name[MAXVLEN]; /* advertised name */ +} GetVolParmsReplyPkt, *GVPRPPtr; +@end(example) + + +@section(FPLogin) +@begin(example) +@tabdivide(8) +Call:@\FPLogin(SessRefNum, user, passwd, uam, FPError) +Input:@\int SessRefNum; +Input:@\byte *user; +Input:@\byte *passwd; +Input:@\int uam; +Output:@\dword *FPError; +Data Structures: + +@end(example) + +FPLogin is not yet finished. + + +@section(FPLogout) +@begin(example) +@tabdivide(8) +Call:@\FPLogout(SessRefNum, FPError) +Input:@\int SessRefNum; +Output:@\dword *FPError; + +@end(example) + + +@section(FPMapID) +@begin(example) +@tabdivide(8) +Call:@\FPMapID(SessRefNum, mi,mapr, FPError) +Input:@\int SessRefNum; +Input:@\MapIDPkt *mi; +Output:@\MapIDReplyPkt *mapr; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPMapID */ + byte mpi_cmd; /* MapID command */ + byte mpi_fcn; /* function */ + sdword mpi_id; /* ID to map */ +} MapIDPkt, *MIPPtr; + +typedef struct { /* FPMapID Reply */ + byte mpir_name[MAXPSTR]; +} MapIDReplyPkt, *MIRPPtr; +@end(example) + + +@section(FPMapName) +@begin(example) +@tabdivide(8) +Call:@\FPMapName(SessRefNum, mnp, id, FPError) +Input:@\int SessRefNum; +Input:@\MapNamePkt *mnp; +Output:@\dword *id; +Outputs:@\dword *FPError; +Data Structures: + +typedef struct { /* FPMapName */ + byte mpn_cmd; /* command */ + byte mpn_fcn; /* function */ + byte mpn_name[MAXPSTR]; /* name */ +} MapNamePkt, *MNPPtr; +@end(example) + +The user or group id is returned through @i. + +@section(FPMoveFile) +@begin(example) +@tabdivide(8) +Call:@\FPMoveFile(SessRefNum, mf, FPError) +Inputs:@\int SessRefNum; +Input:@\MovePkt *mf; +Outputs:@\dword *FPError; +Data Structures: + +typedef struct { /* FPMove */ + byte mov_cmd; /* command */ + byte mov_zero; /* always zero */ + word mov_volid; /* volume id */ + sdword mov_sdirid; /* source directory id */ + sdword mov_ddirid; /* destination directory id */ + byte mov_sptype; /* source path type */ + byte mov_spath[MAXPATH]; /* source path */ + byte mov_dptype; /* destination path type */ + byte mov_dpath[MAXPATH]; /* destination path */ + byte mov_newtype; /* new type */ + byte mov_newname[MAXPATH]; /* new name */ +} MovePkt, *MPPtr; +@end(example) + + +@section(FPOpenDir) +@begin(example) +@tabdivide(8) +Call:@\FPOpenDir(SessRefNum, od, retdirid, FPError) +Inputs:@\int SessRefNum; +Input:@\OpenDirPkt *od; +Output:@\dword *retdirid; +Outputs:@\dword *FPError; +Data Structures: + +typedef struct { /* FPOpenDir */ + byte odr_cmd; /* command */ + byte odr_zero; /* always zero */ + word odr_volid; /* volume ID */ + dword odr_dirid; /* directory ID */ + byte odr_ptype; /* path type */ + byte odr_path[MAXPATH]; /* path */ +} OpenDirPkt, *ODPPtr; +@end(example) + +The directory id is returned through @i. + + +@section(FPOpenDT) +@begin(example) +@tabdivide(8) +Call:@\FPOpenDT(SessRefNum, odt, dtrefnum, FPError) +Input:@\int SessRefNum; +Input:@\OpenDTPkt *odt; +Output:@\word *dtrefnum; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPOpenDT */ + byte odt_cmd; /* command */ + byte odt_zero; + word odt_volid; /* desktop volume id */ +} OpenDTPkt, *ODTPPtr; +@end(example) + +The desk top reference number is returned through @i. + + +@section(FPOpenFork) +@begin(example) +@tabdivide(8) +Call:@\FPOpenFork(SessRefNum, of, epar, refnum, FPError) +Input:@\int SessRefNum; +Input:@\OpenForkPkt *of; +Output:@\FileDirParm *epar; +Output:@\word *refnum; +Output:@\dword *FPError; +Data Structures: +typedef struct { /* FPOpenFork */ + byte ofk_cmd; /* command */ + byte ofk_rdflg; /* resource/data flag */ +#define OFK_RSRC 0x01 /* resource fork */ + word ofk_volid; /* volume id */ + sdword ofk_dirid; /* directory id */ + word ofk_bitmap; /* bitmap */ + word ofk_mode; /* access mode */ + byte ofk_ptype; /* path type */ + byte ofk_path[MAXPATH]; /* path name */ +} OpenForkPkt, *OFkPPtr; +@end(example) + +The open fork reference number is returned through @i. + + +@section(FPOpenVol) +@begin(example) +@tabdivide(8) +Call:@\FPOpenVol(SessRefNum, ov, op, FPError) +Inputs:@\int SessRefNum; +Input:@\OpenVolPkt *ov; +Output:@\GetVolParmsReplyPkt *op; +Outputs:@\dword *FPError; +Data Structures: + +typedef struct { /* FPGetVolParms */ + byte gvp_cmd; /* command */ + byte gvp_zero; /* always zero */ + word gvp_volid; /* volume id */ + word gvp_bitmap; /* request bitmap */ +} GetVolParmsPkt, *GVPPPtr; + +typedef struct { /* FPGetVolParms */ + byte gvpr_bitmap; /* return bitmap */ + word gvpr_attr; /* attributes */ + word gvpr_sig; /* volume signature */ + sdword gvpr_cdate; /* volume creation date */ + sdword gvpr_mdate; /* volume modification date */ + sdword gvpr_bdate; /* volume backup date */ + word gvpr_volid; /* volume id */ + sdword gvpr_size; /* size of volume in bytes */ + sdword gvpr_free; /* free bytes on volume */ + byte gvpr_name[MAXVLEN]; /* advertised name */ +} GetVolParmsReplyPkt, *GVPRPPtr; +@end(example) + + +@section(FPRead) +@begin(example) +@tabdivide(8) +Call:@\FPRead(SessRefNum, buf, buflen, rp, rlen, FPError) +Input:@\int SessRefNum; +Output:@\byte *buf; +Input:@\int buflen; +Input:@\ReadPkt *rp; +Output:@\int *rlen; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPRead */ + byte rdf_cmd; + byte rdf_zero; + word rdf_refnum; /* fork reference number */ + dword rdf_offset; /* offset for read */ + dword rdf_reqcnt; /* request count */ + byte rdf_flag; +#define RDF_NEWLINE 0x01 + byte rdf_nlchar; /* newline char */ +} ReadPkt, *ReadPPtr; +@end(example) + +The FPRead results are placed in the array pointed to by @i. The +size of the buffer is buflen. Number of bytes read is returned in +rlen. + +@section(FPRemoveAPPL) +@begin(example) +@tabdivide(8) +Call:@\FPRemoveAPPL(SessRefNum, ra, FPError) +Input:@\int SessRefNum; +Input:@\RemoveAPPLPkt *ra; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPRemoveAPPL */ + byte rma_cmd; + byte rma_zero; + word rma_refnum; + dword rma_dirid; + byte rma_fcreator[4]; + byte rma_ptype; + byte rma_path[MAXPATH]; +} RemoveAPPLPkt, *RAPPtr; +@end(example) + + +@section(FPRemoveComment) +@begin(example) +@tabdivide(8) +Call:@\FPRemoveComment(SessRefNum, rc, FPError) +Input:@\int SessRefNum; +Input:@\RemoveCommentPkt *rc; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPRemoveComment */ + byte rmc_cmd; + byte rmc_zero; + word rmc_dtrefnum; /* dest top ref num */ + dword rmc_dirid; + byte rmc_ptype; + byte rmc_path[MAXPATH]; +} RemoveCommentPkt, *RCPPtr; +@end(example) + + +@section(FPRename) +@begin(example) +@tabdivide(8) +Call:@\FPRename(SessRefNum, rn, FPError) +Input:@\int SessRefNum; +Input:@\RenamePkt *rn; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPRename */ + byte ren_cmd; /* command */ + byte ren_zero; /* always zero */ + word ren_volid; /* volume id */ + sdword ren_dirid; /* directory id */ + byte ren_ptype; /* path type */ + byte ren_path[MAXPATH]; /* path name */ + byte ren_ntype; /* new type */ + byte ren_npath[MAXPATH]; /* new path */ +} RenamePkt, *RPPtr; +@end(example) + + +@section(FPSetDirParms) +@begin(example) +@tabdivide(8) +Call:@\FPSetDirParms(SessRefNum, sdp, fdp, FPError) +Input:@\int SessRefNum; +Input:@\SetDirParmsPkt *sdp; +Input:@\FileDirParm *fdp; +Outputs:@\dword *FPError; +Data Structures: + +typedef struct { /* FPSetDirParms */ + byte sdp_cmd; /* command */ + byte sdp_zero; /* always zero */ + word sdp_volid; /* volume ID */ + dword sdp_dirid; /* parent directory id */ + word sdp_bitmap; /* bitmap */ + byte sdp_ptype; /* path type */ + byte sdp_path[MAXPATH]; /* path */ +} SetDirParmsPkt, *SDPPPtr; +@end(example) + +The directory parameters in @i are set according to the bitmap. + + +@section(FPSetFileParms) +@begin(example) +@tabdivide(8) +Call:@\FPSetFileParms(SessRefNum, sfp, fdp, FPError) +Input:@\int SessRefNum; +Input:@\SetFileParmsPkt *sfp; +Input:@\FileDirParm *fdp; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPSetFileParms */ + byte sfp_cmd; /* command */ + byte sfp_zero; /* always zero */ + word sfp_volid; /* volume id */ + dword sfp_dirid; /* directory id */ + word sfp_bitmap; /* set bitmap */ + byte sfp_ptype; /* path type */ + byte sfp_path[MAXPATH]; /* path + file parameters to set */ +} SetFileParmsPkt, *SFPPPtr; +@end(example) + +The file parameters in @i are set according to the bitmap. + +@section(FPSetFileDirParms) +@begin(example) +@tabdivide(8) +Call:@\FPSetFileDirParms(SessRefNum, sfdp, fdp, FPError) +Inputs:@\int SessRefNum; +Input:@\SetFileDirParmsPkt *sfdp; +Input:@\FileDirParm *fdp; +Outputs:@\dword *FPError; +Data Structures: +typedef struct { /* FPSetFileDirParms */ + byte scp_cmd; /* set common parms command */ + byte scp_zero; + word scp_volid; + dword scp_dirid; + word scp_bitmap; + byte scp_ptype; + byte scp_path[MAXPATH]; +} SetFileDirParmsPkt, *SFDPPPtr; +@end(example) + +The file or directory parameters in @i are set according to the +bitmap. + +@section(FPSetForkParms) +@begin(example) +@tabdivide(8) +Call:@\FPSetForkParms(SessRefNum, sfp, FPError) +Input:@\int SessRefNum; +Input:@\SetForkParmsPkt *sfp; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPSetForkParms */ + byte sfkp_cmd; /* command */ + byte sfkp_zero; /* zero word */ + word sfkp_refnum; /* reference number */ + word sfkp_bitmap; /* bitmap */ + sdword sfkp_rflen; /* resource fork length */ + sdword sfkp_dflen; /* data fork length */ +} SetForkParmsPkt, *SFkPPPtr; +@end(example) + +@section(FPSetVolParms) +@begin(example) +@tabdivide(8) +Call:@\FPSetVolParms(SessRefNum, svp, FPError) +Input:@\SetVolParmsPkt *svp; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPSetVolParms */ + byte svp_cmd; /* command */ + byte svp_zero; /* always zero */ + word svp_volid; /* volume id */ + word svp_bitmap; /* set bitmap */ + dword svp_backdata; /* backup data to set */ +} SetVolParmsPkt, *SVPPPtr; +@end(example) + + +@section(FPWrite) +@begin(example) +@tabdivide(8) +Call:@\FPWrite(SessRefNum, wbuf, wlen, wp, actcnt, +@\loff_written, FPError) +Input:@\int SessRefNum; +Input:@\char *wbuf; +Input:@\int wlen; +Input:@\WritePkt *wp; +Output:@\dword *actcnt; +Output:@\dword *loff_written; +Output:@\dword *FPError; +Data Structures: + +typedef struct { /* FPWrite */ + byte wrt_cmd; + byte wrt_flag; +#define WRT_START 0x01 + word wrt_refnum; + dword wrt_offset; + dword wrt_reqcnt; +} WritePkt, *WPPtr; +@end(example) + +The buffer pointed to by @i is written to the remote. The +buffer length is specified by wlen. The count of bytes actually +written is returned through @i. The last offset written is +returned in loff_written. diff --git a/lib/afpc/afpcc.c b/lib/afpc/afpcc.c new file mode 100644 index 0000000..048add4 --- /dev/null +++ b/lib/afpc/afpcc.c @@ -0,0 +1,879 @@ +/* + * $Author: djh $ $Date: 1996/04/25 01:18:16 $ + * $Header: /mac/src/cap60/lib/afpc/RCS/afpcc.c,v 2.5 1996/04/25 01:18:16 djh Rel djh $ + * $Revision: 2.5 $ +*/ + +/* + * afpcc.c - easy interface to AFP client calls + * + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1987 CCKim Created. + * + */ + +/* PATCH: Rutgers1/*, djh@munnari.OZ.AU, 19/11/90 */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef SUNOS4_FASTDES +#include +#endif SUNOS4_FASTDES + +eFPAddAPPL(srn, dtr, dirid, fcreator, appltag, path, cr) +int srn; +word dtr; +dword dirid; +char fcreator[]; +dword appltag; +byte *path; +dword *cr; +{ + AddAPPLPkt aa; + + aa.aap_cmd = AFPAddAPPL; + aa.aap_zero = 0; + aa.aap_dtrefnum = dtr; + aa.aap_dirid = dirid; + bcopy(fcreator, aa.aap_fcreator, 4); + aa.aap_apptag = appltag; + aa.aap_ptype = 2; + pstrcpy(aa.aap_path, path); /* copy in path */ + return(FPAddAPPL(srn, &aa, cr)); +} + +eFPAddComment(srn, dtr, dirid, path, comment, cr) +int srn; +word dtr; +dword dirid; +byte *path; +byte *comment; +dword *cr; +{ + AddCommentPkt ac; + int len; + + ac.adc_cmd = AFPAddComment; + ac.adc_zero = 0; + ac.adc_dtrefnum = dtr; + ac.adc_dirid = dirid; + ac.adc_ptype = 2; + pstrcpy(ac.adc_path, path); + if ((len = pstrlen(comment)) > 199) + len = 199; + ac.adc_clen = len; + pstrcpy(ac.adc_comment, comment); + return(FPAddComment(srn, &ac, cr)); +} + +eFPAddIcon(srn, dtr, fcreator, ftype, icontype, icontag, icon, iconlen, cr) +int srn; +word dtr; +byte fcreator[]; +byte ftype[]; +byte icontype; +dword icontag; +byte *icon; +int iconlen; +dword *cr; +{ + AddIconPkt adi; + adi.adi_cmd = AFPAddIcon; + adi.adi_zero = 0; + adi.adi_dtref = dtr; + bcopy(fcreator, adi.adi_fcreator, 4); + bcopy(ftype, adi.adi_ftype, 4); + adi.adi_icontype = icontype; + adi.adi_icontag = icontag; + adi.adi_iconsize = iconlen; + + return(FPAddIcon(srn, &adi, icon, iconlen, cr)); +} + +eFPByteRangeLock(srn, oforkrefnum, offset, length, flags, rangestart, cr) +int srn; +word oforkrefnum; +dword offset, length; +word flags; +dword *rangestart; +dword *cr; +{ + ByteRangeLockPkt brl; + + brl.brl_cmd = AFPByteRangeLock; + brl.brl_flg = flags; + brl.brl_refnum = oforkrefnum; + brl.brl_offset = offset; + brl.brl_length = length; + return(FPByteRangeLock(srn, &brl, rangestart, cr)); +} + +eFPCloseDir(srn, volid, dirid, cr) +int srn; +word volid; +dword dirid; +dword *cr; +{ + CloseDirPkt cd; + cd.cdr_cmd = AFPCloseDir; + cd.cdr_zero = 0; + cd.cdr_volid = volid; + cd.cdr_dirid = dirid; + return(FPCloseDir(srn, &cd, cr)); +} + +eFPCloseDT(srn, dtrefnum, cr) +int srn; +word dtrefnum; +dword *cr; +{ + CloseDTPkt cdt; + + cdt.cdt_cmd = AFPCloseDT; + cdt.cdt_zero = 0; + cdt.cdt_dtrefnum = dtrefnum; + + return(FPCloseDT(srn, &cdt, cr)); +} + +eFPCloseFork(SRefNum, OForkRefnum, cr) +int SRefNum; +word OForkRefnum; +dword *cr; +{ + CloseForkPkt cfp; + + cfp.cfk_cmd = AFPCloseFork; + cfp.cfk_zero = 0; + cfp.cfk_refnum = OForkRefnum; + + return(FPCloseFork(SRefNum, &cfp, cr)); +} + +eFPCloseVol(srn, volid, cr) +int srn; +word volid; +dword *cr; +{ + CloseVolPkt cv; + + cv.cv_cmd = AFPCloseVol; + cv.cv_zero = 0; + cv.cv_volid = volid; + return(FPCloseVol(srn, &cv, cr)); +} + +eFPCopyFile(srn, svolid, sdirid, spath, dvolid, ddirid, dpath, newname, cr) +int srn; +word svolid; +dword sdirid; +byte *spath; +word dvolid; +dword ddirid; +byte *dpath; +byte *newname; +dword *cr; +{ + CopyFilePkt cf; + + cf.cpf_cmd = AFPCopyFile; + cf.cpf_zero = 0; + cf.cpf_svolid = svolid; + cf.cpf_sdirid = sdirid; + cf.cpf_dvolid = dvolid; + cf.cpf_ddirid = ddirid; + cf.cpf_sptype = 2; + pstrcpy(cf.cpf_spath, spath); + cf.cpf_dptype = 2; + pstrcpy(cf.cpf_dpath, dpath); + cf.cpf_newtype = 2; + pstrcpy(cf.cpf_newname, newname); + return(FPCopyFile(srn, &cf, cr)); +} + +eFPCreateDir(srn, volid, dirid, path, newdirid, cr) +int srn; +word volid; +dword dirid; +byte *path; +dword *newdirid; +dword *cr; +{ + CreateDirPkt cd; + + cd.crd_cmd = AFPCreateDir; + cd.crd_zero = 0; + cd.crd_volid = volid; + cd.crd_dirid = dirid; + cd.crd_ptype = 2; + pstrcpy(cd.crd_path, path); + return(FPCreateDir(srn, &cd, newdirid, cr)); +} + +eFPCreateFile(srn, volid, dirid, createflag, pathname, cr) +int srn; +word volid; +dword dirid; +byte createflag; +byte *pathname; +dword *cr; +{ + CreateFilePkt cf; + + cf.crf_cmd = AFPCreateFile; + cf.crf_flg = createflag ? CRF_HARD : 0; + cf.crf_volid = volid; + cf.crf_dirid = dirid; + cf.crf_ptype = 0x2; /* always long name */ + pstrcpy(cf.crf_path, pathname); /* file name */ + return(FPCreateFile(srn, &cf, cr)); +} + +eFPDelete(srn, volid, dirid, path, cr) +int srn; +word volid; +dword dirid; +byte *path; +dword *cr; +{ + DeletePkt dp; + + dp.del_cmd = AFPDelete; + dp.del_zero = 0; + dp.del_volid = volid; + dp.del_dirid = dirid; + dp.del_ptype = 0x2; /* long name */ + pstrcpy(dp.del_path, path); + return(FPDelete(srn, &dp, cr)); +} + +/* call with epars, returns filled in */ +eFPEnumerate(srn, volid, dirid, path, idx, fbitmap, dbitmap, + epar, eparcnt, cnt, cr) +int srn; +word volid; +dword dirid; +byte *path; +int idx; +word fbitmap; +word dbitmap; +FileDirParm *epar; +int eparcnt; +int *cnt; +dword *cr; +{ + EnumeratePkt ep; + byte buf[576*3-1]; + + ep.enu_cmd = AFPEnumerate; + ep.enu_zero = 0; + ep.enu_volid = volid; + ep.enu_dirid = dirid; /* root */ + ep.enu_fbitmap = fbitmap; + ep.enu_dbitmap = dbitmap; + ep.enu_reqcnt = eparcnt; + ep.enu_stidx = idx; + ep.enu_maxreply = 576*3-1; + ep.enu_ptype = 2; + pstrcpy(ep.enu_path, path); + + return(FPEnumerate(srn, &ep, buf, 576*3-1, epar, eparcnt, cnt, cr)); +} + +eFPFlush(srn, volid, cr) +int srn; +word volid; +dword *cr; +{ + FlushPkt fv; + + fv.fls_cmd = AFPFlush; + fv.fls_zero = 0; + fv.fls_volid = volid; + return(FPFlush(srn, &fv, cr)); +} + +eFPFlushFork(srn, oforkrefnum, cr) +int srn; +word oforkrefnum; +dword *cr; +{ + FlushForkPkt ff; + + ff.flf_cmd = AFPFlushFork; + ff.flf_zero = 0; + ff.flf_refnum = oforkrefnum; + return(FPFlushFork(srn, &ff, cr)); +} + +eFPGetAPPL(srn, dtr, fcreator, idx, bitmap, gar, cr) +int srn; +word dtr; +byte fcreator[]; +word idx; +word bitmap; +GetAPPLReplyPkt *gar; +dword *cr; +{ + GetAPPLPkt gap; + + gap.gap_cmd = AFPGetAPPL; + gap.gap_zero = 0; + gap.gap_dtrefnum = dtr; + bcopy(fcreator, gap.gap_fcreator, 4); + gap.gap_applidx = idx; + gap.gap_bitmap = bitmap; + + return(FPGetAPPL(srn, &gap, gar, cr)); +} + +eFPGetComment(srn, dtr, dirid, path, gcr, cr) +int srn; +word dtr; +dword dirid; +byte *path; +GCRPPtr gcr; +dword *cr; +{ + GetCommentPkt gc; + + gc.gcm_cmd = AFPGetComment; + gc.gcm_zero = 0; + gc.gcm_dtrefnum = dtr; + gc.gcm_dirid = dirid; + gc.gcm_ptype = 2; /* long name */ + pstrcpy(gc.gcm_path, path); + return(FPGetComment(srn, &gc, gcr, cr)); +} + +eFPGetFileDirParms(srn, volid, dirid, fbitmap, dbitmap, path, epar, cr) +int srn; +word volid; +dword dirid; +word fbitmap; +word dbitmap; +byte *path; +FileDirParm *epar; +dword *cr; +{ + GetFileDirParmsPkt gfdp; + + gfdp.gdp_cmd = AFPGetFileDirParms; + gfdp.gdp_zero = 0; + gfdp.gdp_volid = volid; + gfdp.gdp_dirid = dirid; /* root */ + gfdp.gdp_fbitmap = fbitmap; + gfdp.gdp_dbitmap = dbitmap; + gfdp.gdp_ptype = 0x2; /* long path type */ + pstrcpy(gfdp.gdp_path,path); + return(FPGetFileDirParms(srn, &gfdp, epar, cr)); +} + +eFPGetForkParms(srn, fref, bitmap, epar, cr) +int srn; +word fref; +word bitmap; +FileDirParm *epar; +dword *cr; +{ + GetForkParmsPkt gfp; + + gfp.gfp_cmd = AFPGetForkParms; + gfp.gfp_zero = 0; + gfp.gfp_refnum = fref; + gfp.gfp_bitmap = bitmap; + return(FPGetForkParms(srn, &gfp, epar, cr)); +} + +eFPGetIcon(srn, dtr, fcreator, ftype, icontype, icon, iconlen, cr) +int srn; +word dtr; +byte fcreator[]; +byte ftype[]; +byte icontype; +byte *icon; +int iconlen; +dword *cr; +{ + GetIconPkt gic; + + gic.gic_cmd = AFPGetIcon; + gic.gic_zero = 0; + gic.gic_dtrefnum = dtr; + bcopy(fcreator, gic.gic_fcreator, 4); + bcopy(ftype,gic.gic_ftype, 4); + gic.gic_itype = icontype; + gic.gic_zero2 = 0; + gic.gic_length = iconlen; + return(FPGetIcon(srn, &gic, icon, iconlen, cr)); +} + +eFPGetIconInfo(srn, dtr, fcreator, iconidx, gicr, cr) +int srn; +word dtr; +byte fcreator[]; +word iconidx; +GetIconInfoReplyPkt *gicr; +dword *cr; +{ + GetIconInfoPkt gii; + + gii.gii_cmd = AFPGetIconInfo; + gii.gii_zero = 0; + gii.gii_dtrefnum = dtr; + bcopy(fcreator, gii.gii_fcreator, 4); + gii.gii_iidx = iconidx; + + return(FPGetIconInfo(srn, &gii, gicr, cr)); +} + +/* maybe make this do a lookup first someday? */ +eFPGetSrvrInfo(addr, sr) +AddrBlock *addr; +GetSrvrInfoReplyPkt *sr; +{ + return(FPGetSrvrInfo(addr, sr)); +} + +eFPGetSrvrParms(srn, sp, cr) +int srn; +GSPRPPtr *sp; +dword *cr; +{ + return(FPGetSrvrParms(srn, sp, cr)); +} + +eFPGetVolParms(srn, volid, vbitmap, ve, cr) +int srn; +word volid; +word vbitmap; +GetVolParmsReplyPkt *ve; +dword *cr; +{ + GetVolParmsPkt gvp; + + gvp.gvp_cmd = AFPGetVolParms; + gvp.gvp_zero = 0; + gvp.gvp_volid = volid; + gvp.gvp_bitmap = vbitmap; + + return(FPGetVolParms(srn, &gvp, ve, cr)); +} + +/* Order matters */ +static char *uam_which[3] = { + "No User Authent", + "Cleartxt passwrd", + "Randnum exchange" +}; + +eFPLogin(srn, user, passwd, uam, cr) +int srn; +byte *user, *passwd; +int uam; +dword *cr; +{ + static byte uam_flags[3] = { + 0, /* no special parms for ANON */ + UAMP_USER|UAMP_PASS|UAMP_ZERO, /* passwd + user... */ + UAMP_USER, /* just pass user for RANDNUM initially */ + }; + LoginPkt lp; + LoginReplyPkt lrp; + LoginContPkt lcp; + byte flgs; + int comp; + + if (desinit(0) < 0) + if (uam == UAM_RANDNUM) { + *cr = aeBadUAM; + return(-1); + } + lp.log_cmd = AFPLogin; + strcpy(lp.log_ver, "AFPVersion 1.1"); + strcpy(lp.log_uam, uam_which[uam]); + flgs = lp.log_flag = uam_flags[uam]; + /* Have to do this - we are going to say this don't have to valid */ + /* if not needed for uam */ + if (flgs & UAMP_USER) + strcpy(lp.log_user,user); + if (flgs & UAMP_PASS) + bcopy(passwd, lp.log_passwd, 8); /* copy in password */ + if (uam == UAM_RANDNUM) + lrp.logr_flag = UAMP_RAND|UAMP_INUM; /* expect these back */ + comp = FPLogin(srn, &lp, &lrp, cr); + if (comp < 0) + return(comp); + if ((((dword)*cr) != aeAuthContinue) && (uam != UAM_RANDNUM)) + return(comp); + if (desinit(0) < 0) { + /* here handle randnum exchange */ + *cr = aeBadUAM; + return(-1); + } +#ifdef SUNOS4_FASTDES + des_setparity(passwd); + bcopy(lrp.logr_randnum, lcp.lgc_encrypted, sizeof(lcp.lgc_encrypted)); + ecb_crypt(passwd,lcp.lgc_encrypted,64,DES_ENCRYPT|DES_HW); +#else SUNOS4_FASTDES + dessetkey(passwd); + bcopy(lrp.logr_randnum, lcp.lgc_encrypted, sizeof(lcp.lgc_encrypted)); + endes(lcp.lgc_encrypted); + desdone(); /* clean up (not used except by login) */ +#endif SUNOS4_FASTDES + lcp.lgc_cmd = AFPLoginCont; + lcp.lgc_zero = 0; + lcp.lgc_idno = lrp.logr_idnum; + lcp.lgc_flags = UAMP_INUM|UAMP_ENCR; + return(FPLoginCont(srn, &lcp, &lrp, cr)); +} + +eFPMapID(srn, fnc, ugid, mapr, cr) +int srn; +byte fnc; +dword ugid; +MapIDReplyPkt *mapr; +dword *cr; +{ + MapIDPkt mi; + + mi.mpi_cmd = AFPMapID; + mi.mpi_fcn = fnc; + mi.mpi_id = ugid; + + return(FPMapID(srn, &mi, mapr, cr)); +} + +eFPMapName(srn, fnc, name, id, cr) +int srn; +byte fnc; +byte *name; +dword *id; +dword *cr; +{ + MapNamePkt mnp; + + mnp.mpn_cmd = AFPMapName; + mnp.mpn_fcn = fnc; + pstrcpy(mnp.mpn_name, name); + return(FPMapName(srn, &mnp, id, cr)); +} + +eFPMoveFile(srn, volid, sdirid, spath, ddirid, dpath, newname, cr) +int srn; +word volid; +dword sdirid; +byte *spath; +dword ddirid; +byte *dpath; +byte *newname; +dword *cr; +{ + MovePkt mf; + + mf.mov_cmd = AFPMove; + mf.mov_zero = 0; + mf.mov_volid = volid; + mf.mov_sdirid = sdirid; + mf.mov_ddirid = ddirid; + mf.mov_sptype = 2; + pstrcpy(mf.mov_spath, spath); + mf.mov_dptype = 2; + pstrcpy(mf.mov_dpath, dpath); + mf.mov_newtype = 2; + pstrcpy(mf.mov_newname, newname); + return(FPMoveFile(srn, &mf, cr)); +} + +eFPOpenDir(srn, volid, dirid, path, retdirid, cr) +int srn; +word volid; +dword dirid; +byte *path; +dword *retdirid; +dword *cr; +{ + OpenDirPkt od; + + od.odr_cmd = AFPOpenDir; + od.odr_zero = 0; + od.odr_dirid = dirid; + od.odr_volid = volid; + od.odr_ptype = 2; + pstrcpy(od.odr_path, path); + return(FPOpenDir(srn, &od, retdirid, cr)); +} + +eFPOpenDT(srn, volid, dtrefnum, cr) +int srn; +word volid; +word *dtrefnum; +dword *cr; +{ + OpenDTPkt odt; + + odt.odt_cmd = AFPOpenDT; + odt.odt_zero = 0; + odt.odt_volid = volid; + return(FPOpenDT(srn, &odt, dtrefnum, cr)); +} + +eFPOpenFork(srn, volid, dirid, mode, path, type, bitmap, epar, refnum, cr) +int srn; +word volid; +dword dirid; +word mode; +char *path; +byte type; +word bitmap; +FileDirParm *epar; +word *refnum; +dword *cr; +{ + OpenForkPkt of; + + of.ofk_cmd = AFPOpenFork; + of.ofk_rdflg = type ? OFK_RSRC : 0; + of.ofk_volid = volid; + of.ofk_dirid = dirid; /* root */ + of.ofk_bitmap = bitmap; /* no info */ + of.ofk_mode = mode; /* read/write */ + of.ofk_ptype = 0x2; /* long name */ + pstrcpy(of.ofk_path, path); /* file name */ + return(FPOpenFork(srn, &of, epar, refnum, cr)); +} + +eFPOpenVol(srn, bitmap, dovol, passwd, op, cr) +int srn; +word bitmap; +char *dovol; +byte *passwd; +GetVolParmsReplyPkt *op; +dword *cr; +{ + OpenVolPkt ov; + + ov.ovl_cmd = AFPOpenVol; + ov.ovl_zero = 0; + ov.ovl_bitmap = bitmap; + strcpy(ov.ovl_name, dovol); + if (passwd) + pstrcpy(ov.ovl_pass, passwd); + else + ov.ovl_pass[0] = 0; + return(FPOpenVol(srn, &ov, op, cr)); +} + +eFPRead(srn, fd, buf, buflen, toget, offset, cr) +int srn; +word fd; +char *buf; +int buflen; +int toget; +dword offset; +dword *cr; +{ + ReadPkt rp; + int err; + int rlen; + + rp.rdf_cmd = AFPRead; + rp.rdf_zero = 0; + rp.rdf_refnum = fd; + rp.rdf_offset = offset; + rp.rdf_reqcnt = toget; + rp.rdf_flag = 0; + rp.rdf_nlchar = 0; + err = FPRead(srn, &rp, buf, buflen, &rlen, cr); + if (err < 0) + return(err); + if (*cr && ((dword)*cr) != aeEOFErr) + return(0); + return(rlen); +} + +eFPRemoveAPPL(srn, dtr, dirid, fcreator, path, cr) +int srn; +word dtr; +dword dirid; +byte fcreator[]; +byte *path; +dword *cr; +{ + RemoveAPPLPkt rma; + + rma.rma_cmd = AFPRmvAPPL; + rma.rma_zero = 0; + rma.rma_refnum = dtr; + rma.rma_dirid = dirid; + bcopy(fcreator, rma.rma_fcreator, 4); + rma.rma_ptype = 0x2; + pstrcpy(rma.rma_path, path); + return(FPRemoveAPPL(srn, &rma, cr)); +} + +eFPRemoveComment(srn, dtr, dirid, path, cr) +int srn; +word dtr; +dword dirid; +byte *path; +dword *cr; +{ + RemoveCommentPkt rmc; + + rmc.rmc_cmd = AFPRmvComment; + rmc.rmc_zero = 0; + rmc.rmc_dtrefnum = dtr; + rmc.rmc_dirid = dirid; + rmc.rmc_ptype = 0x2; + pstrcpy(rmc.rmc_path, path); + return(FPRemoveComment(srn, &rmc, cr)); +} + +eFPRename(srn, volid, dirid, path, newname, cr) +int srn; +word volid; +dword dirid; +byte *path, *newname; +dword *cr; +{ + RenamePkt rn; + rn.ren_cmd = AFPRename; + rn.ren_zero = 0; + rn.ren_volid = volid; + rn.ren_dirid = dirid; + rn.ren_ptype = 0x2; + pstrcpy(rn.ren_path, path); + rn.ren_ntype = 0x2; + pstrcpy(rn.ren_npath, newname); + return(FPRename(srn, &rn, cr)); +} + +eFPSetDirParms(srn, volid, dirid, bitmap, path, di, cr) +int srn; +word volid; +dword dirid; +word bitmap; +byte *path; +FileDirParm *di; +dword *cr; +{ + SetDirParmsPkt sdp; + sdp.sdp_cmd = AFPSetDirParms; + sdp.sdp_zero = 0; + sdp.sdp_volid = volid; + sdp.sdp_dirid = dirid; + sdp.sdp_bitmap = bitmap; + sdp.sdp_ptype = 0x2; + pstrcpy(sdp.sdp_path, path); + return(FPSetDirParms(srn, &sdp, di, cr)); +} + +eFPSetFileParms(srn, volid, dirid, bitmap, path, fi, cr) +int srn; +word volid; +dword dirid; +word bitmap; +byte *path; +FileDirParm *fi; +dword *cr; +{ + SetFileParmsPkt sfp; + + sfp.sfp_cmd = AFPSetFileParms; + sfp.sfp_zero = 0; + sfp.sfp_volid = volid; + sfp.sfp_dirid = dirid; + sfp.sfp_bitmap = bitmap; + sfp.sfp_ptype = 0x2; + pstrcpy(sfp.sfp_path, path); + return(FPSetFileParms(srn, &sfp, fi, cr)); +} + +eFPSetFileDirParms(srn, volid, dirid, bitmap, path, fdi, cr) +int srn; +word volid; +dword dirid; +word bitmap; +byte *path; +FileDirParm *fdi; +dword *cr; +{ + SetFileDirParmsPkt scp; + + scp.scp_cmd = AFPSetFileParms; + scp.scp_zero = 0; + scp.scp_volid = volid; + scp.scp_dirid = dirid; + scp.scp_bitmap = bitmap; + scp.scp_ptype = 0x2; + pstrcpy(scp.scp_path, path); + return(FPSetFileDirParms(srn, &scp, fdi, cr)); +} + +eFPSetForkParms(srn, fd, bitmap, fi, cr) +int srn; +word fd; +word bitmap; +FileParm *fi; +dword *cr; +{ + SetForkParmsPkt sfp; + sfp.sfkp_cmd = AFPSetForkParms; + sfp.sfkp_zero = 0; + sfp.sfkp_refnum = fd; + sfp.sfkp_bitmap = bitmap; + sfp.sfkp_rflen = fi->fp_rflen; + sfp.sfkp_dflen = fi->fp_dflen; + return(FPSetForkParms(srn, &sfp, cr)); +} + +eFPSetVolParms(srn, volid, backupdate, cr) +int srn; +word volid; +dword backupdate; +dword *cr; +{ + SetVolParmsPkt svp; + + svp.svp_cmd = AFPSetVolParms; + svp.svp_zero = 0; + svp.svp_volid = volid; + svp.svp_bitmap = VP_BDATE; + svp.svp_backdata = backupdate; + return(FPSetVolParms(srn, &svp, cr)); +} + +eFPWrite(srn, fd, wbuf, wlen, towrite, actcnt, offset, cr) +int srn, fd; +char *wbuf; +int towrite; +dword *actcnt; +int *offset; +dword *cr; +{ + WritePkt wp; + dword pactcnt, pwritten; + int comp; + + *actcnt = 0; + wp.wrt_cmd = AFPWrite; + wp.wrt_flag = 0; + wp.wrt_refnum = fd; + wp.wrt_offset = *offset; + wp.wrt_reqcnt = towrite; + + return(FPWrite(srn, wbuf, wlen, &wp, actcnt, offset, cr)); +} diff --git a/lib/afpc/makefile b/lib/afpc/makefile new file mode 100644 index 0000000..d83a1d9 --- /dev/null +++ b/lib/afpc/makefile @@ -0,0 +1,39 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 14:00:08 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +DESTDIR=/usr/local/lib +LIBAFPC=libafpc.a +I=/usr/include + +LIBAFPCSRCS=afpc.c afpcc.c +LIBAFPCOBJS=afpc.o afpcc.o + +$(LIBAFPC): $(LIBAFPCOBJS) + ar rv $(LIBAFPC) $(LIBAFPCOBJS) + +clean: + -rm -f ${LIBAFPCOBJS} ${LIBAFPC} core *~ + +install: $(LIBAFPC) + ${INSTALLER} $(LIBAFPC) $(DESTDIR) + ranlib $(DESTDIR)/$(LIBAFPC) + +dist: + @cat todist + +lint: $(LIBAFPCSRCS) + lint $(LIBAFPCSRCS) + +afpc.o: afpc.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afpc.h +afpcc.o: afpcc.c $I/netat/appletalk.h $I/netat/aberrors.h \ + $I/netat/abqueue.h $I/netat/afpc.h diff --git a/lib/afpc/probs b/lib/afpc/probs new file mode 100644 index 0000000..5fd1f3d --- /dev/null +++ b/lib/afpc/probs @@ -0,0 +1,33 @@ +Revised: Aug. 6, 1987 + +Comments apply to AppleShare version 1.0, Jan. 12, 1987 and protocol +specification "AppleTalk File Protocol (AFP) Engineering Technical +Notes, Protocol Version 1.1, February 17, 1987" + +Additional comments apply to AppleShare version 1.1. + +FPAddAPPL packet display says we are to send volid in a request - this +doesn't seem right - probably should be DTRefNum. + +AppleShare seems to use Mac time, with earliest time at 0. This is at +|odds with the specification. This seems to be fixed in AppleShare 1.1. + +At least for FPEnumerate AppleShare client seems to reverse creation +and modification dates. Either the spec or code is out of sync. +Probably the code since the AppleShare server seems to get them in the +|correct order. This may be fixed in AppleShare 1.1. + +Spec does not make clear how FPAddIcon is to proceed - it simply says +"The bitmap is sent to the server in a subsequent intermediate +exchange of the Session Protocol packets". + +SPAttention calls from the server are not documented - not sure if you +think this should be part of the specification, but the calls should +be documented. From observations, we can guess that some number <=4 +of the high order bits of the attention code define the event. For +example, (0x => hex) 0x8xxx seems to mean server is going down in xxx +minutes unless xxx == fff in which case it indicates that a shutdown +has been cancelled. + + + diff --git a/lib/cap/Makefile.m4 b/lib/cap/Makefile.m4 new file mode 100644 index 0000000..12c6df7 --- /dev/null +++ b/lib/cap/Makefile.m4 @@ -0,0 +1,90 @@ +CFLAGS=cflags() caposdefs() specialcflags() +NBPFLAGS=nbpflags() +I=includedir() +LIBCAP=caplib() +DESTDIR=libdestdir() +AUTHCONFIG=authconfig() + +LIBABSRCS=abatp.c abddp.c abmisc.c abnbp.c abauxddp.c abauxnbp.c \ + abpap.c abpapc.c abpaps.c abpp.c abqueue.c abasp.c \ + abzip.c abversion.c atalkdbm.c absched.c abkip.c \ + authenticate.c ablog.c scandir.c +LIBABOBJS=abatp.o abmisc.o abzip.o abversion.o absched.o \ + abpap.o abpapc.o abpaps.o abpp.o abqueue.o abasp.o \ + authenticate.o ablog.o scandir.o + +# LABOBJ defines the various low level delivery mechanisms +# default: abkip.o abddp.o abnbp.o atalkdbm.o +# with UAB: abmkip.o abddp.o abnbp.o atalkdbm.o +# for A/UX: abauxddp.o abauxnbp.o +# for EtherTalk: abetalk.o abddp.o abnbp.o atalkdbm.o +LAPOBJ=lapobj() + +# USEVPRINTF - use vprintf in logging +ifdef([usevprintf],[LOGDEFS=-DUSEVPRINTF],[LOGDEFS=]) + +DEPENDS=$I/netat/appletalk.h $I/netat/aberrors.h $I/netat/abqueue.h + +all: $(LIBCAP) + +$(LIBCAP): $(LIBABOBJS) $(LAPOBJ) + ifdef([uselordertsort], + [ar cr $(LIBCAP) `lorder $(LIBABOBJS) $(LAPOBJ) | tsort`], + [ar rv $(LIBCAP) $(LIBABOBJS) $(LAPOBJ)]) + +clean: + -rm -f *.o *.a core + +spotless: + -rm -f *.o *.a *.orig core Makefile makefile + +install: $(LIBCAP) + ifdef([sysvinstall],[install -f $(DESTDIR) $(LIBCAP)], + [${INSTALLER} $(LIBCAP) $(DESTDIR)]) + ifdef([uselordertsort],[],[(cd $(DESTDIR);ranlib $(LIBCAP))]) + +dist: + @cat todist + +lint: $(LIBABSRCS) + lint $(LIBABSRCS) + +abetalk.o: + (cd ../../support/ethertalk; make abetalk.o) + mv ../../support/ethertalk/abetalk.o abetalk.o + +abmkip.o: abkip.c ${DEPENDS} $I/netat/abnbp.h $I/netat/compat.h + cp abkip.c abmkip.c + ${CC} ${CFLAGS} -DUAB_MKIP -c abmkip.c + /bin/rm abmkip.c + +atalkdbm.o: atalkdbm.c ${DEPENDS} + ${CC} ${CFLAGS} -DTAB=atalklocal() -DETAB=etalklocal() \ + -DCONFIGDIR=configdir() -c atalkdbm.c + +authenticate.o: authenticate.c ${DEPENDS} + ${CC} ${CFLAGS} -DAUTHCONFIG=${AUTHCONFIG} -c authenticate.c + +ablog.o: ablog.c ${DEPENDS} + ${CC} ${CFLAGS} ${LOGDEFS} -c ablog.c + +abnbp.o: abnbp.c ${DEPENDS} $I/netat/abnbp.h + ${CC} ${CFLAGS} ${NBPFLAGS} -c abnbp.c + +abkip.o: abkip.c ${DEPENDS} $I/netat/abnbp.h $I/netat/compat.h +abddp.o: abddp.c ${DEPENDS} cap_conf.h +abatp.o: abatp.c ${DEPENDS} abatp.h +abatpaux.o: abatpaux.c ${DEPENDS} abatp.h +abasp.o: abasp.c ${DEPENDS} abasp.h +abpap.o: abpap.c ${DEPENDS} abpap.h cap_conf.h +abpapc.o: abpapc.c ${DEPENDS} abpap.h cap_conf.h +abpaps.o: abpaps.c ${DEPENDS} abpap.h cap_conf.h +abzip.o: abzip.c ${DEPENDS} +abmisc.o: abmisc.c ${DEPENDS} +abpp.o: abpp.c ${DEPENDS} +abversion.o: abversion.c ${DEPENDS} +abauxddp.o: abauxddp.c ${DEPENDS} cap_conf.h +abauxnbp.o: abauxnbp.c ${DEPENDS} $I/netat/abnbp.h +absched.o: absched.c ${DEPENDS} $I/netat/compat.h +atalkdbm.o: atalkdbm.c ${DEPENDS} $I/netat/compat.h atalkdbm.h +abqueue.o: abqueue.c $I/netat/abqueue.h diff --git a/lib/cap/abasp.c b/lib/cap/abasp.c new file mode 100644 index 0000000..06d6fdd --- /dev/null +++ b/lib/cap/abasp.c @@ -0,0 +1,2490 @@ +/* + * $Author: djh $ $Date: 91/03/14 13:45:20 $ + * $Header: abasp.c,v 2.2 91/03/14 13:45:20 djh Exp $ + * $Revision: 2.2 $ +*/ + +/* + * abasp.c - Appletalk Session Protocol + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * July 28, 1986 CCKim Created + * Aug 4, 1986 CCKim Verified: level 0 +*/ + +#include +#include +#include +#include +#include "abasp.h" + +int aspInit(); +int SPGetParms(); +int SPInit(); +int SPGetNetworkInfo(); +private void handle_aspserver(); +private void asp_doopensess(); +private void sessopenreply(); +int SPGetSession(); +int SPCloseSession(); +int SPGetRequest(); +int SPCmdReply(); +int SPWrtReply(); +private int spreply(); +int SPWrtContinue(); +int SPNewStatus(); +int SPAttention(); + +int SPGetStatus(); +int SPOpenSession(); +private void handle_aspclient(); +int SPCommand(); +int SPWrite(); +private void asp_do_write(); + +private void handle_asp_sndreq(); +private void handle_asp_getreq(); /* for SPGetRequest */ +private void handle_asp_rspdone(); /* for SPWRtReply, SPCmdReply and */ + /* intermediate part of SPWrite */ +private void handle_asp_special(); + +private void do_sendclosesessreply(); +private void do_sendreply(); +private void start_client_aspskt(); +private void shutdown_aspskt(); + +void delete_aq(); +ASPQE *create_aq(); +private ASPQE *get_aq(); +private boolean match_aspwe(); +private ASPQE *find_aspawe(); + +private void startasptickle(); +void stopasptickle(); +private void ttimeout(); +private void start_ttimer(); +private void reset_ttimer(); +void stop_ttimer(); + +int SPFork(); +OSErr SPShutdown(); +#ifdef ASPPID +int SPFindPid(); +#endif +private OSErr spshutdown(); + +private int aspskt_init(); /* initialize skts */ +private OSErr aspskt_new(); +private void aspskt_free(); +private ASPSkt *aspskt_find_notrunning(); +private ASPSkt *aspskt_find_sessid(); +#ifdef ASPPID +private ASPSkt *aspskt_find_pid(); +#endif +private OSErr aspsskt_new(); +ASPSkt *aspskt_find_active(); +ASPSkt *aspskt_find_sessrefnum(); +ASPSSkt *aspsskt_find_slsrefnum(); +private boolean aspsskt_isactive(); + +private void sizeof_abr_bds_and_req(); +private void sizeof_bds_and_req(); +private OSErr asp_cksndrq_err(); + +private int sessid_not_inited = TRUE; +private word next_sessid = 0; /* use word to prevent overflows */ +/* this allows us to keep code around in case this should be done */ +/* differently at some point. */ + +#define AD_SKT 1 +#define AD_HANDLERS 2 +#define AD_CALLS 4 +#define AD_TICKLE 8 +private int asp_dbug = AD_SKT|AD_HANDLERS|AD_CALLS|AD_TICKLE; + +#define isdskt (dbug.db_asp && (asp_dbug & AD_SKT)) +#define isdhand (dbug.db_asp && (asp_dbug & AD_SKT)) +#define isdcalls (dbug.db_asp && (asp_dbug & AD_SKT)) +#define isdtickle (dbug.db_asp && (asp_dbug & AD_SKT)) + +private char *asptypes[9] = { + "Unknown", + "aspCloseSession", + "aspCommand", + "aspGetStat", + "aspOpenSess", + "aspTickle", + "aspWrite", + "aspWriteData", + "aspAttention" +}; + +private char *aspevents[] = { + "tSPGetRequest", + "tSPCmdReply", + "tSPWrtContinue", + "tSPWrtReply", + "tSPAttention", + "tSP_Special_DROP", + "tSPGetStat", + "tSPOpenSess", + "tSPCommand", + "tSPWrite", + "tSPWrite2 ", + "tSPClose" +}; + +/* + * Initialize asp - only args is the minimun number of sessions to allow + * + * You don't have to call this, but if you do, be sure to do it before + * any other ASP calls. + * +*/ +int +aspInit(n) +int n; +{ + return(aspskt_init(n)); +} + +/* + * Get server operating parameters + * +*/ +SPGetParms(MaxCmdSize, QuantumSize) +int *MaxCmdSize; +int *QuantumSize; +{ + if (isdcalls) + fprintf(stderr,"asp: SPGetParms\n"); + *MaxCmdSize = atpMaxData; + *QuantumSize = atpMaxData * atpMaxNum; +} + +/* + * Initialize for Server Listening Socket + * +*/ +OSErr +SPInit(SLSEntityIdentifier, ServiceStatusBlock, ServiceStatusBlockSize, + SLSRefNum) +AddrBlock *SLSEntityIdentifier; /* SLS Net id */ +char *ServiceStatusBlock; /* block with status info */ +int ServiceStatusBlockSize; /* size of status info */ +int *SLSRefNum; /* sls ref num return place */ +{ + int err; + atpProto *ap; + ASPSSkt *sas; + OSErr tmp; + + if (isdcalls) + fprintf(stderr,"asp: SPInit called\n"); + if ((tmp = aspsskt_new(SLSRefNum, &sas)) != noErr) + return(tmp); + if (ServiceStatusBlockSize > atpMaxData*atpMaxNum) + return(SizeErr); + + sas->ssb = ServiceStatusBlock; + sas->ssbl = ServiceStatusBlockSize; + sas->addr = *SLSEntityIdentifier; + /* start listener */ + ap = &sas->abr.proto.atp; + ap->atpSocket = sas->addr.skt; + ap->atpReqCount = 0; /* don't need to see the data */ + ap->atpDataPtr = NULL; + err = cbATPGetRequest(&sas->abr, handle_aspserver, *SLSRefNum); + if (err != noErr) + return(noATPResource); + return(err); +} + +/* + * returns address of remote ss + * +*/ +SPGetNetworkInfo(SessRefNum, addr) +int SessRefNum; +AddrBlock *addr; +{ + ASPSkt *as; + + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) + return(ParamErr); + if (as->state != SP_STARTED) + return(noATPResource); + + *addr = as->addr; + return(noErr); +} + +/* + * Handle an incoming request on SLS socket. Can only be of type: + * Tickle, GetStat, or OpenSess +*/ +private void +handle_aspserver(abr, SLSRefNum) +ABusRecord *abr; +int SLSRefNum; +{ + ASPUserBytes *aub; + atpProto *ap; + ASPSkt *as; + ASPSSkt *sas; + OSErr err; + + if ((sas = aspsskt_find_slsrefnum(SLSRefNum)) == NULL) { + if (isdhand) + fprintf(stderr, "asp: ASP_SLS: SLS %d invalid (prob. child cleaning)\n", + SLSRefNum); + return; /* nothing to do - sls is invalid */ + } + aub = (ASPUserBytes *)&abr->proto.atp.atpUserData; + if (isdhand) + fprintf(stderr, "asp: [ASP_SLS: ASPTYPE %s]\n", asptypes[aub->std.b1]); + + if (abr->abResult == noErr) { + switch (aub->std.b1) { /* get command */ + case aspOpenSess: + asp_doopensess(SLSRefNum, aub, abr); + break; + case aspTickle: + if ((as = aspskt_find_sessid(SLSRefNum, aub->std.b2)) == NULL) { + if (isdhand) + fprintf(stderr,"asp: Got tickle for sessid %d, but no ses\n", + aub->std.b2); + break; + } + if (isdhand) + fprintf(stderr,"asp: Got tickle on %d\n",aub->std.b2); + reset_ttimer(as); + break; + case aspGetStat: + asp_dosendstatus(SLSRefNum, abr); + break; + default: + if (isdhand) + fprintf(stderr, "asp: Misdirected request on ASP SLS\n"); + } + } + + if (abr->abResult == sktClosed) { + while ((as = aspskt_find_active(SLSRefNum)) != NULL) + *as->comp = sktClosed; + return; + } + + ap = &sas->abr.proto.atp; + ap->atpSocket = sas->addr.skt; + ap->atpReqCount = 0; /* don't need to see the data */ + ap->atpDataPtr = NULL; + err = cbATPGetRequest(&sas->abr, handle_aspserver, SLSRefNum); + /* what to do with error? should report if we get a really bad one */ + if (err != noErr) + fprintf(stderr, "asp: GetRequest fails on SLS %d! Server is dead!\n", + SLSRefNum); +} + +/* + * Try to open a session - server only + * +*/ +private void +asp_doopensess(SLSRefNum, aub, abr) +int SLSRefNum; +ASPUserBytes *aub; +ABusRecord *abr; +{ + ASPSkt *as; + int err; + + if (isdhand) + fprintf(stderr,"asp: Server: remote wants connection: protocol level %x\n", + ntohs(aub->std.data)); + if (ntohs(aub->std.data) != ASP_PROTOCOL_VERSION) { + if (isdhand) + fprintf(stderr,"asp: Server: connection refused - protocol level %x\n", + ASP_PROTOCOL_VERSION); + sessopenreply(SLSRefNum, abr, BadVersNum, 0, (byte)0); + return; + } + + if ((as = aspskt_find_active(SLSRefNum)) == NULL) { + /* no getsessions active */ + if (isdhand) + fprintf(stderr,"asp: Server: no get session active, server busy\n"); +#ifdef DEBUGAUFS + logit(0, "asp: Server %d: no get session active, server busy", SLSRefNum); + dumpsockets(SLSRefNum); +#endif + sessopenreply(SLSRefNum, abr, ServerBusy, 0, (byte)0); + return; + } + + as->addr = abr->proto.atp.atpAddress; + as->addr.skt = 0; /* accept for any socket on remote */ + + if (as->ss == -1) + as->ss = 0; /* use zero to indicate dynamic allocation */ + if ((err = ATPOpenSocket(&as->addr, &as->ss)) < 0) { + /* woops */ + as->ss = -1; + as->state = SP_INACTIVE; /* close down srn */ + *as->comp = NoMoreSessions; + aspskt_free(as); /* get rid of it */ + if (isdhand) + fprintf(stderr,"asp: Server: out of sockets! atp err %d\n", err); +#ifdef DEBUGAUFS + logit(0, "asp: Server %d: out of sockets: err %d", SLSRefNum, err); +#endif + sessopenreply(SLSRefNum, abr, ServerBusy, 0, (byte)0); + return; + } + as->addr.skt = aub->std.b2; /* wss */ + if (isdhand) + fprintf(stderr,"asp: Server: conn. initiated: id %d on wss %d, ss %d\n", + as->SessID, as->addr.skt, as->ss); +#ifdef DEBUGAUFS + logit(0, "asp: Server: conn. initiated: id %d on wss %d, ss %d", + as->SessID, as->addr.skt, as->ss); +#endif + sessopenreply(SLSRefNum, abr, noErr, as->ss, (byte)as->SessID); + as->state = SP_STARTED; + as->tickle_abr.proto.atp.atpAddress = as->addr; + as->tickle_abr.proto.atp.atpSocket = as->ss; /* remote WSS */ + startasptickle(as); + start_ttimer(as); + *as->comp = noErr; /* done */ +} + +/* + * reply to an open session call from a client + * +*/ +private void +sessopenreply(SLSRefNum, abr, errcode, ss, sessid) +int SLSRefNum; +ABusRecord *abr; +int errcode; +int ss; +byte sessid; +{ + ASPQE *aspqe; + atpProto *ap; + ASPUserBytes *aub; + int cnt; + ASPSSkt *sas = aspsskt_find_slsrefnum(SLSRefNum); + + if (isdhand) + fprintf(stderr,"asp: Server: opensessionreply\n"); + if (sas == NULL) /* slsrefnum invalid */ + return; + + aspqe = create_aspaqe(); + aspqe->type = tSP_Special_DROP; + ap = &aspqe->abr.proto.atp; + ap->atpSocket = sas->addr.skt; + ap->atpAddress = abr->proto.atp.atpAddress; + ap->atpTransID = abr->proto.atp.atpTransID; + cnt = setup_bds(aspqe->bds, atpMaxNum, atpMaxData, (char *)NULL,0, (dword)0); + aspqe->bds[0].userData = 0; + aub = (ASPUserBytes *)&aspqe->bds[0].userData ; + aub->std.b1 = ss; + aub->std.b2 = sessid; + aub->std.data = htons(errcode); + ap->atpRspBDSPtr = aspqe->bds; + ap->fatpEOM = (abr->proto.atp.atpBitMap >> cnt) != 0 ? 1 : 0 ; + ap->atpNumBufs = cnt; + ap->atpBDSSize = cnt; + if (cbATPSndRsp(&aspqe->abr, handle_asp_special, aspqe) != noErr) { + /* well, we can get rid of the unused pointer at least */ + delete_aspaqe(aspqe); + } +} + +/* + * + * Send a status report back + * +*/ +asp_dosendstatus(SLSRefNum, abr) +int SLSRefNum; +ABusRecord *abr; +{ + ASPSSkt *sas = aspsskt_find_slsrefnum(SLSRefNum); + ASPQE *aspqe; + atpProto *ap; + int cnt; + + if (isdhand) + fprintf(stderr,"asp: Server: sendstatus\n"); + if (sas == NULL) /* nothing to do */ + return; + + aspqe = create_aspaqe(); + aspqe->type = tSP_Special_DROP; + ap = &aspqe->abr.proto.atp; + ap->atpSocket = sas->addr.skt; + ap->atpAddress = abr->proto.atp.atpAddress; + ap->atpTransID = abr->proto.atp.atpTransID; + cnt = setup_bds(aspqe->bds, atpMaxNum, atpMaxData, sas->ssb, sas->ssbl, + (dword)0); + ap->atpRspBDSPtr = aspqe->bds; + ap->fatpEOM = (abr->proto.atp.atpBitMap >> cnt) != 0 ? 1 : 0 ; + ap->atpNumBufs = cnt; + ap->atpBDSSize = cnt; + if (cbATPSndRsp(&aspqe->abr, handle_asp_special, aspqe) != noErr) { + delete_aspaqe(aspqe); /* get rid */ + } + /* what to do with err? just ignore*/ +} + +/* + * Watch SLS for a open to transfer to the Server Service Socket (SSS) + * +*/ +OSErr +SPGetSession(SLSRefNum, SessRefNum, comp) +int SLSRefNum; +int *SessRefNum; +int *comp; +{ + ASPSkt *as; + OSErr tmp; + int i; + + if (isdcalls) + fprintf(stderr,"asp: SPGetSession - SLS %d\n",SLSRefNum); + if (!aspsskt_isactive(SLSRefNum)) { + *comp = ParamErr; + return(ParamErr); + } + if ((tmp=aspskt_new(SessRefNum, &as)) != noErr) { + *comp = tmp; + return(tmp); + } + + as->type = SP_SERVER; + as->wqueue = NULL; + as->state = SP_STARTING; + as->SLSRefNum = SLSRefNum; + as->ss = -1; /* unknown at present */ + as->comp = comp; + /* check for in use? should be no prob */ + if (sessid_not_inited) { + next_sessid = time(0L) & 0xff; /* random hopefully */ + sessid_not_inited = FALSE; + } + /* make sure sessid is unique on sls refnum being careful to stop */ + /* after all the sessids have been checked */ + i = 0; + while (aspskt_find_sessid(SLSRefNum, (byte)next_sessid) != NULL) { + next_sessid = ++next_sessid & 0xff; /* single byte */ + if (i++ > 255) + return(NoMoreSessions); + } + as->SessID = (byte)next_sessid; + next_sessid = ++next_sessid & 0xff; /* single byte */ +#ifdef DEBUGAUFS + logit(0, "asp: getsession: looking for connection on %d with sessid %d", + as->SessRefNum, as->SessID); +#endif + *comp = 1; + return(noErr); +} + +/* + * Close down a Service Socket socket + * +*/ +OSErr +SPCloseSession(SessRefNum, atpretries, atptimeout, comp) +int SessRefNum; +int atpretries; +int atptimeout; +int *comp; +{ + ASPSkt *as; + atpProto *ap; + ASPUserBytes *aub; + ASPQE *aspqe; + int cnt, err; + + if (isdcalls) + fprintf(stderr,"asp: SPCloseSession - srn %d\n",SessRefNum); + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + + switch (as->state) { + case SP_STARTED: + break; + case SP_HALFCLOSED: +#ifdef notdef + /* this is wrong, want the close to go out if server calls aspclose */ + return(spshutdown(SessRefNum)); +#endif + /* allow halfclosed sockets to be closed by server */ + break; + default: + aspskt_free(as); + return(noErr); + } + + aspqe = create_aspaqe(); + aspqe->type = tSPClose; + aspqe->comp = comp; + aspqe->SessRefNum = SessRefNum; + + ap = &aspqe->abr.proto.atp; + ap->atpUserData = (dword)0; + aub = (ASPUserBytes *)&ap->atpUserData; + aub->std.b1 = aspCloseSession; + aub->std.b2 = as->SessID; + ap->atpAddress = as->addr; + if (as->state == SP_HALFCLOSED) { + /* In case we are half-closed, we use the sls to send sp attn */ + ASPSSkt *sas = aspsskt_find_slsrefnum(as->SLSRefNum); + if (sas) + ap->atpSocket = sas->addr.skt; + else { + delete_aspaqe(aspqe); + *comp = ParamErr; + return(ParamErr); + } + } else + ap->atpSocket = as->ss; + ap->atpReqCount = 0; + ap->atpDataPtr = NULL; + cnt = setup_bds(aspqe->bds, 1, atpMaxData, (char *)NULL, 0, (dword)0); + ap->atpRspBDSPtr = aspqe->bds; + ap->atpNumBufs = cnt; + ap->fatpXO = FALSE; + ap->atpRetries = atpretries; + ap->atpTimeOut = atptimeout <= 0 ? ASPCLOSESESSIONTIMEOUT : atptimeout; + *comp = 1; /* mark waiting */ + err = cbATPSndRequest(&aspqe->abr, handle_asp_sndreq, aspqe); + if (err == noErr) + return(noErr); + delete_aspaqe(aspqe); /* get rid of it */ + return(asp_cksndrq_err("ASPClose", err, comp)); +} + +/* + * Get a request on a SSS + * +*/ +SPGetRequest(SessRefNum, ReqBuff, ReqBuffSize, ReqRefNum, SPReqType, + ActRcvdReqLen, comp) +int SessRefNum; +char *ReqBuff; +int ReqBuffSize; +ASPQE **ReqRefNum; +int *SPReqType; +int *ActRcvdReqLen; +int *comp; +{ + atpProto *ap; + ASPSkt *as; + ASPQE *aspqe; + int err; + + if (isdcalls) + fprintf(stderr,"asp: SPGetRequest - srn %d\n",SessRefNum); + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + if (as->state != SP_STARTED) { + *comp = SessClosed; + return(SessClosed); + } + if (as->ss == -1) { /* bad call */ + *comp = ParamErr; + return(ParamErr); + } + aspqe = create_aspaqe(); /* will never return bad, dies instead */ + aspqe->SessRefNum = SessRefNum; + aspqe->type = tSPGetRequest; + aspqe->ReqRefNum = ReqRefNum; + aspqe->SPReqType = SPReqType; + aspqe->ActRcvdReqLen = ActRcvdReqLen; + aspqe->comp = comp; + + ap = &aspqe->abr.proto.atp; + ap->atpReqCount = ReqBuffSize; + ap->atpDataPtr = ReqBuff; + ap->atpSocket = as->ss; + + *comp = 1; + err = cbATPGetRequest(&aspqe->abr, handle_asp_getreq, aspqe); + if (err != noErr) { + delete_aspaqe(aspqe); + *comp = noATPResource; + return(noATPResource); + } + return(noErr); +} + +/* + * handle completion of the SPGetRequest command + * +*/ +private void +handle_asp_getreq(abr, aspqe) +ABusRecord *abr; +ASPQE *aspqe; +{ + ASPUserBytes *aub; + ASPSkt *as; + + if (isdhand) + fprintf(stderr, "asp: handle_sndreq with aspqe %x\n",aspqe); + if (aspqe == NULL) + return; /* drop */ + if (abr == NULL || aspqe == NULL) { + fprintf(stderr,"asp: fatal error: handle_asp_getreq - abr or aspqe NIL\n"); + exit(255); + return; + } + + if (aspqe->type != tSPGetRequest) { + if (isdhand) + fprintf(stderr,"asp: GetReq handler with bad aspqe %x - type %s\n", + aspqe, aspevents[aspqe->type]); + delete_aspaqe(aspqe); + } + + as = aspskt_find_sessrefnum(aspqe->SessRefNum); + + switch (abr->abResult) { + case noErr: + if (!as) { /* no sess? ugh */ +#ifdef DEBUGAUFS + logit(0, "Session %d not active!!! Return SessClosed", aspqe->SessRefNum); +#endif + *aspqe->comp = SessClosed; + break; + } + aub = (ASPUserBytes *)&abr->proto.atp.atpUserData; + /*** change aub->std.data to aub->std.b2 ****/ + if (as->SessID != aub->std.b2) { + if (isdhand) + fprintf(stderr,"asp: Bad Req - Sessid = %d, ours is %d\n", + aub->std.b2, as->SessID); +#ifdef DEBUGAUFS + logit(0, "asp: Bad Req - Sessid = %d, ours is %d", + aub->std.b2, as->SessID); +#endif + *aspqe->comp = BadReqRcvd; + break; + } + *aspqe->comp = abr->abResult; + if (isdhand) + fprintf(stderr, "asp: hgetreq: Sessid %d, reqrefnum %x, type %s\n", + as->SessID, aspqe, asptypes[aub->std.b1]); + *aspqe->ReqRefNum = aspqe; /* cheap, but very bad */ + *aspqe->ActRcvdReqLen = abr->proto.atp.atpActCount; + *aspqe->comp = noErr; + *aspqe->SPReqType = aub->std.b1; /* mark */ + switch (aub->std.b1) { + case aspCommand: + case aspWrite: + return; /* just return, everything else is done */ + case aspCloseSession: +#ifdef DEBUGAUFS + logit(0, "asp: Close on sessid %d, session %d", + aub->std.b2, as->SessRefNum); +#endif + *aspqe->comp = SessClosed; + do_sendclosesessreply(as, abr); + break; + default: + /* what to do? */ + if (isdhand) + fprintf(stderr,"asp: SPGetReq: Received unexpected request %d\n", + aub->std.b1); + *aspqe->comp = BadReqRcvd; + break; + } + break; + case sktClosed: + *aspqe->comp = SessClosed; + break; + default: + if (isdhand) + fprintf(stderr, "asp: SPGetReq: bad atp completion %d\n",abr->abResult); + *aspqe->comp = aspFault; + break; + } + delete_aspaqe(aspqe); +} + + + +/* + * Reply to a request to an SSS from a WSS + * +*/ +SPCmdReply(SessRefNum, ReqRefNum, CmdResult, CmdReplyData, CmdReplyDataSize, + comp) +int SessRefNum; +ASPQE *ReqRefNum; +dword CmdResult; +char *CmdReplyData; +int CmdReplyDataSize; +int *comp; +{ + if (isdcalls) + fprintf(stderr,"asp: SPCmdReply - srn %d, rrn %x, reply size %d\n", + SessRefNum, ReqRefNum, CmdReplyDataSize); + return(spreply(tSPCmdReply, SessRefNum, ReqRefNum, CmdResult, CmdReplyData, + CmdReplyDataSize, comp)); +} + +/* + * final reply to a SPWrite request to an SSS from an WSS. + * +*/ +SPWrtReply(SessRefNum, ReqRefNum, CmdResult, CmdReplyData, CmdReplyDataSize, + comp) +int SessRefNum; +ASPQE *ReqRefNum; +dword CmdResult; +char *CmdReplyData; +int CmdReplyDataSize; +int *comp; +{ + if (isdcalls) + fprintf(stderr,"asp: SPWrtReply - srn %d, rrn %x, reply size %d\n", + SessRefNum, ReqRefNum, CmdReplyDataSize); + return(spreply(tSPWrtReply, SessRefNum, ReqRefNum, CmdResult, CmdReplyData, + CmdReplyDataSize, comp)); +} + +private int +spreply(type, SessRefNum, ReqRefNum, CmdResult, CmdReplyData, CmdReplyDataSize, + comp) +int type; +int SessRefNum; +ASPQE *ReqRefNum; +dword CmdResult; +char *CmdReplyData; +int CmdReplyDataSize; +int *comp; +{ + atpProto *ap; + ASPSkt *as; + ASPQE *aspqe; + int cnt, err; + + if (CmdReplyDataSize < 0) { + *comp = ParamErr; + return(ParamErr); + } + if (CmdReplyDataSize > atpMaxNum*atpMaxData) { + *comp = SizeErr; + return(SizeErr); + } + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + if (as->state != SP_STARTED) { /* really means srn isn't active yet */ + *comp = ParamErr; + return(ParamErr); + } + if (as->ss == -1) { /* bad call */ + *comp = ParamErr; + return(ParamErr); + } + + aspqe = create_aspaqe(); + aspqe->type = type; + aspqe->SessRefNum = SessRefNum; + aspqe->comp = comp; + + /* setup bds */ + ap = &aspqe->abr.proto.atp; + ap->atpSocket = as->ss; + + ap->atpAddress = ReqRefNum->abr.proto.atp.atpAddress; + ap->atpTransID = ReqRefNum->abr.proto.atp.atpTransID; + + /* We blithely attempt to send out all the data, regardless of the */ + /* bitmap sent by the remote. According to the ASP document, the */ + /* client should have been smart enough to ask for one more response */ + /* than data if we are on a 578 (atpmaxdata) boundary and will be able */ + /* figure out there is size error - the extra pkts outside the bitmap */ + /* should simply be dropped */ + cnt = setup_bds(aspqe->bds, atpMaxNum, atpMaxData, CmdReplyData, + CmdReplyDataSize, (dword)0); + aspqe->bds[0].userData = htonl(CmdResult); /* only for first */ + ap->atpRspBDSPtr = aspqe->bds; + /* since we only send a response once, we should always set EOM */ + ap->fatpEOM = 1; + ap->atpNumBufs = cnt; + ap->atpBDSSize = cnt; + *comp = 1; /* mark waiting */ + err = cbATPSndRsp(&aspqe->abr, handle_asp_rspdone, aspqe); + delete_aspaqe(ReqRefNum); /* is this right? Suppose so... */ + if (err != noErr) { + delete_aspaqe(aspqe); /* get rid of it */ + if (err == badBuffNum) { + *comp = ParamErr; /* bad ReqRefNum */ + return(ParamErr); + } + *comp = noATPResource; + return(noATPResource); + } + return(noErr); +} + + +/* + * Allow a write to continue (equiv - this is a read call) based upon + * request to an SSS from a WSS (client) + * +*/ +SPWrtContinue(SessRefNum, ReqRefNum, Buffer, BufferSize, ActLenRcvd, + atptimeout, comp) +int SessRefNum; +ASPQE *ReqRefNum; +char *Buffer; +int BufferSize; +int *ActLenRcvd; +int atptimeout; +int *comp; +{ + atpProto *ap; + ASPUserBytes *aub; + ASPSkt *as; + ASPQE *aspqe; + int cnt, err; + + if (isdcalls) + fprintf(stderr,"asp: SPWrtContinue: srn %d, rrn %x, bufsize %d\n", + SessRefNum, ReqRefNum, BufferSize); + if (BufferSize < 0) { + *comp = ParamErr; + return(ParamErr); + } + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + if (as->state != SP_STARTED) { + *comp = SessClosed; + return(SessClosed); + } + if (as->ss == -1) { /* bad call */ + *comp = ParamErr; + return(ParamErr); + } + + aspqe = create_aspaqe(); + aspqe->type = tSPWrtContinue; + aspqe->SessRefNum = SessRefNum; + aspqe->ActRcvdReplyLen = ActLenRcvd; /* overload */ + aspqe->comp = comp; + + ap = &aspqe->abr.proto.atp; + /* get sessid, seqno info */ + ap->atpUserData = ReqRefNum->abr.proto.atp.atpUserData; + aub = (ASPUserBytes *)&ap->atpUserData; + aub->std.b1 = aspWriteData; +/* ap->atpAddress = ReqRefNum->abr.proto.atp.atpAddress; */ + ap->atpAddress = as->addr; + ap->atpSocket = as->ss; + + + ap->atpReqCount = sizeof(word); + aspqe->availableBufferSize = htons((word)BufferSize); + ap->atpDataPtr = (char *)&aspqe->availableBufferSize; + ap->atpRspBDSPtr = aspqe->bds; + cnt = setup_bds(aspqe->bds, atpMaxNum, atpMaxData, Buffer, + BufferSize, (dword) 0); + ap->atpNumBufs = cnt; + ap->fatpXO = TRUE; + ap->atpRetries = 255; /* infinite retries */ + ap->atpTimeOut = atptimeout <= 0 ? ASPWRITETIMEOUT : atptimeout; + *comp = 1; /* mark waiting */ + err = cbATPSndRequest(&aspqe->abr, handle_asp_sndreq, aspqe); + if (err == noErr) + return(noErr); + delete_aspaqe(aspqe); /* get rid of it */ + return(asp_cksndrq_err("SPWrtContinue", err, comp)); +} + +/* + * establish new status on the SSS + * +*/ +SPNewStatus(SLSRefNum, ServiceStatusBlock, ServiceStatusBlockSize) +int SLSRefNum; +char *ServiceStatusBlock; +int ServiceStatusBlockSize; +{ + ASPSSkt *sas = aspsskt_find_slsrefnum(SLSRefNum); + + if (isdcalls) + fprintf(stderr,"asp: SPNewStatus: SLS %d\n",SLSRefNum); + if (sas == NULL) + return(ParamErr); + + sas->ssb = ServiceStatusBlock; + sas->ssbl = ServiceStatusBlockSize; + return(noErr); +} + +/* + * Send attn signal to WSS. + * +*/ +SPAttention(SessRefNum, AttentionCode, atpretries, atptimeout, comp) +int SessRefNum; +word AttentionCode; +int atpretries; +int *comp; +{ + atpProto *ap; + ASPUserBytes *aub; + ASPSkt *as; + ASPQE *aspqe; + int cnt, err; + + if (isdcalls) + fprintf(stderr,"asp: SPattention: srn %d, code %x\n",SessRefNum, AttentionCode); + + if (AttentionCode == (word)0) { + *comp = ParamErr; + return(ParamErr); + } + + if ((as=aspskt_find_sessrefnum(SessRefNum))==NULL ||as->state==SP_STARTING) { + *comp = ParamErr; + return(ParamErr); + } + + aspqe = create_aspaqe(); + aspqe->type = tSPAttention; + aspqe->comp = comp; + + ap = &aspqe->abr.proto.atp; + /* get sessid, seqno info */ + ap->atpUserData = 0; + aub = (ASPUserBytes *)&ap->atpUserData; + aub->std.b1 = aspAttention; + aub->std.b2 = as->SessID; + aub->std.data = htons(AttentionCode); + ap->atpAddress = as->addr; + if (as->state == SP_HALFCLOSED) { + /* In case we are half-closed, we use the sls to send sp attn */ + ASPSSkt *sas = aspsskt_find_slsrefnum(as->SLSRefNum); + if (sas) + ap->atpSocket = sas->addr.skt; + else { + delete_aspaqe(aspqe); + *comp = ParamErr; + return(ParamErr); + } + } else + ap->atpSocket = as->ss; + + ap->atpReqCount = 0; + ap->atpDataPtr = NULL; + ap->atpRspBDSPtr = aspqe->bds; + cnt = setup_bds(aspqe->bds, atpMaxNum, atpMaxData, (char *)NULL,0,(dword)0); + ap->atpNumBufs = cnt; + ap->fatpXO = FALSE; + ap->atpRetries = atpretries; + ap->atpTimeOut = atptimeout <= 0 ? ASPATTENTIONTIMEOUT : atptimeout; + *comp = 1; /* mark waiting */ + err = cbATPSndRequest(&aspqe->abr, handle_asp_sndreq, aspqe); + if (err == noErr) + return(noErr); + delete_aspaqe(aspqe); + return(asp_cksndrq_err("SPAttention", err, comp)); +} + +/* workstation calls */ + +/* spgetparms as above */ +SPGetStatus(SLSEntityIdentifier, StatusBuffer, StatusBufferSize, + ActRcvdStatusLen, atpretries, atptimeout, comp) +AddrBlock *SLSEntityIdentifier; +char *StatusBuffer; +int StatusBufferSize; +int *ActRcvdStatusLen; +int atpretries; +int atptimeout; +int *comp; +{ + atpProto *ap; + ASPUserBytes *aub; + ASPQE *aspqe; + int cnt, err; + + + if (isdcalls) + fprintf(stderr,"asp: SPGetStatus called\n"); + aspqe = create_aspaqe(); + aspqe->type = tSPGetStat; + aspqe->ActRcvdStatusLen = ActRcvdStatusLen; + aspqe->comp = comp; + + ap = &aspqe->abr.proto.atp; + ap->atpUserData = (dword)0; + aub = (ASPUserBytes *)&ap->atpUserData; + aub->std.b1 = aspGetStat; + ap->atpSocket = 0; + ap->atpAddress = *SLSEntityIdentifier; + ap->atpReqCount = 0; + ap->atpDataPtr = NULL; + ap->atpRspBDSPtr = aspqe->bds; + cnt = setup_bds(aspqe->bds, atpMaxNum, atpMaxData, StatusBuffer, + StatusBufferSize, (dword)0); + /* we need this to ensure that we can figure out if a size error occurs */ + if (cnt < atpMaxNum && ((StatusBufferSize % atpMaxData) == 0)) { + /* empty bds entry */ + aspqe->bds[cnt].buffPtr = NULL; + aspqe->bds[cnt].dataSize = 0; /* init */ + aspqe->bds[cnt].buffSize = 0; /* no data here */ + cnt++; + } + ap->fatpXO = FALSE; + ap->atpTimeOut = atptimeout <= 0 ? ASPGETSTATTIMEOUT : atptimeout; + ap->atpRetries = atpretries; + ap->atpNumBufs = cnt; + *comp = 1; /* mark waiting */ + err = cbATPSndRequest(&aspqe->abr, handle_asp_sndreq, aspqe); + if (err == noErr) + return(noErr); + delete_aspaqe(aspqe); + return(asp_cksndrq_err("SPGetStatus", err, comp)); +} + +SPOpenSession(SLSEntityIdentifier, AttnRoutine, SessRefNum, atpretries, + atptimeout, comp) +AddrBlock *SLSEntityIdentifier; +int (*AttnRoutine)(); +int *SessRefNum; +int atptimeout; +int *comp; +{ + atpProto *ap; + ASPUserBytes *aub; + ASPSkt *as; + ASPQE *aspqe; + int cnt, err; + OSErr tmp; + AddrBlock useaddr; + + if (isdcalls) + fprintf(stderr,"asp: SPOpenSession called\n"); + + if ((tmp=aspskt_new(SessRefNum, &as)) != noErr) { + *comp = tmp; + return(tmp); + } + + as->type = SP_CLIENT; + as->next_sequence = 0; + as->wqueue = NULL; + as->state = SP_STARTING; + as->addr = *SLSEntityIdentifier; + + as->ss = 0; + useaddr = *SLSEntityIdentifier; + useaddr.skt = 0; + if ((err = ATPOpenSocket(&useaddr, &as->ss)) != noErr) { + aspskt_free(as); /* get rid of this */ + *comp = noATPResource; + return(noATPResource); + } + + as->attnroutine = AttnRoutine; + + aspqe = create_aspaqe(); + aspqe->type = tSPOpenSess; + aspqe->SessRefNum = *SessRefNum; + aspqe->comp = comp; + + ap = &aspqe->abr.proto.atp; + ap->atpUserData = (dword)0; + aub = (ASPUserBytes *)&ap->atpUserData; + aub->std.b1 = aspOpenSess; + aub->std.b2 = as->ss; + aub->std.data = htons(ASP_PROTOCOL_VERSION); + ap->atpAddress = *SLSEntityIdentifier; + ap->atpSocket = as->ss; + + ap->atpReqCount = 0; + ap->atpDataPtr = NULL; + ap->atpRspBDSPtr = aspqe->bds; + cnt = setup_bds(aspqe->bds, atpMaxNum, atpMaxData, (char *)NULL, 0,(dword)0); + ap->fatpXO = TRUE; + ap->atpTimeOut = atptimeout <= 0 ? ASPOPENSESSTIMEOUT : atptimeout; + ap->atpRetries = atpretries; + ap->atpNumBufs = cnt; + *comp = 1; /* mark waiting */ + err = cbATPSndRequest(&aspqe->abr, handle_asp_sndreq, aspqe); + if (err == noErr) + return(noErr); + delete_aspaqe(aspqe); + return(asp_cksndrq_err("SPOpenSess",err,comp)); +} + +/* + * Handle incoming requests for a client process + * +*/ +private void +handle_aspclient(abr, SessRefNum) +ABusRecord *abr; +int SessRefNum; +{ + ASPSkt *as; + ASPUserBytes *aub; + atpProto *ap; + + aub = (ASPUserBytes *)&abr->proto.atp.atpUserData; + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + if (isdhand) + fprintf(stderr, "asp: ASP_WSS: srn %d not found, sessid sent %d\n", + SessRefNum, aub->std.b2); + return; + } + if (isdhand) + fprintf(stderr,"asp: [ASP_WSS: ASPTYPE %s, SESSID sent %d, local %d]\n", + asptypes[aub->std.b1], aub->std.b2,as->SessID); + + + switch (abr->abResult) { + case noErr: + if (aub->std.b2 != as->SessID) { + if (isdhand) + fprintf(stderr, "asp: Misdirected request on ASP WSS\n"); + break; + } + switch (aub->std.b1) { + case aspTickle: + reset_ttimer(as); + break; + case aspCloseSession: + do_sendclosesessreply(as, abr); + return; /* don't restart */ + case aspWriteData: + asp_do_write(abr, (word)ntohs(aub->std.data), as); + break; + case aspAttention: + do_sendreply(as, abr); + (*as->attnroutine)(SessRefNum, aub->std.b2, (word)ntohs(aub->std.data)); + break; + } + break; + case sktClosed: + if (isdhand) + fprintf(stderr, "asp: handle_aspclient: skt closed\n"); + return; + default: + if (isdhand) + fprintf(stderr, "asp: handle_aspclient: bad atp completion %d\n", + abr->abResult); + break; + } + + ap = &as->rabr.proto.atp; + ap->atpSocket = as->ss; + ap->atpReqCount = sizeof(as->reqdata); + ap->atpDataPtr = (char *)&as->reqdata; + cbATPGetRequest(&as->rabr, handle_aspclient, SessRefNum); + /* ignore error */ +} + +SPCommand(SessRefNum, CmdBlock, CmdBlockSize, ReplyBuffer, ReplyBufferSize, + CmdResult, ActRcvdReplyLen, atptimeout, comp) +int SessRefNum; +char *CmdBlock; +int CmdBlockSize; +char *ReplyBuffer; +int ReplyBufferSize; +dword *CmdResult; +int *ActRcvdReplyLen; +int atptimeout; +int *comp; +{ + atpProto *ap; + ASPUserBytes *aub; + ASPSkt *as; + ASPQE *aspqe; + int cnt, err; + + if (isdcalls) + fprintf(stderr,"asp: SPCommand: srn %d, cmdsize %d, replysize %d\n", + SessRefNum, CmdBlockSize, ReplyBufferSize); + if ((as=aspskt_find_sessrefnum(SessRefNum))==NULL ||as->state==SP_STARTING) { + *comp = ParamErr; + return(ParamErr); + } + if (as->ss == -1) { /* bad call */ + *comp = ParamErr; + return(ParamErr); + } + + aspqe = create_aspaqe(); + aspqe->type = tSPCommand; + aspqe->SessRefNum = SessRefNum; + aspqe->comp = comp; + aspqe->CmdResult = CmdResult; + aspqe->ActRcvdReplyLen = ActRcvdReplyLen; + + ap = &aspqe->abr.proto.atp; + ap->atpUserData = (dword)0; + aub = (ASPUserBytes *)&ap->atpUserData; + aub->std.b1 = aspCommand; + aub->std.b2 = as->SessID; + aub->std.data = htons(as->next_sequence); + as->next_sequence = ++as->next_sequence % 65536; + ap->atpAddress = as->addr; + ap->atpSocket = as->ss; + + ap->atpReqCount = CmdBlockSize; + ap->atpDataPtr = CmdBlock; + ap->atpRspBDSPtr = aspqe->bds; + cnt = setup_bds(aspqe->bds, atpMaxNum, atpMaxData, ReplyBuffer, + ReplyBufferSize, (dword)0); + /* we need this to ensure that we can figure out if a size error occurs */ + if (cnt < atpMaxNum && ((ReplyBufferSize % atpMaxData) == 0)) { + /* empty bds entry */ + aspqe->bds[cnt].buffPtr = NULL; + aspqe->bds[cnt].dataSize = 0; /* init */ + aspqe->bds[cnt].buffSize = 0; /* no data here */ + cnt++; + } + ap->fatpXO = TRUE; + ap->atpTimeOut = atptimeout <= 0 ? ASPCOMMANDTIMEOUT : atptimeout; + ap->atpRetries = 255; /* infinite */ + ap->atpNumBufs = cnt; + *comp = 1; /* mark waiting */ + err = cbATPSndRequest(&aspqe->abr, handle_asp_sndreq, aspqe); + if (err == noErr) + return(noErr); + delete_aspaqe(aspqe); + return(asp_cksndrq_err("SPCommand", err, comp)); +} + + +SPWrite(SessRefNum, CmdBlock, CmdBlockSize, WriteData, WriteDataSize, + ReplyBuffer, ReplyBufferSize, CmdResult, ActLenWritten, + ActRcvdReplyLen, atptimeout, comp) +int SessRefNum; +char *CmdBlock; +int CmdBlockSize; +char *WriteData; +int WriteDataSize; +char *ReplyBuffer; +int ReplyBufferSize; +dword *CmdResult; +int *ActLenWritten; +int *ActRcvdReplyLen; +int atptimeout; +int *comp; +{ + atpProto *ap; + ASPUserBytes *aub; + ASPSkt *as; + ASPQE *aspqe, *aspwe; + int cnt, err; + + if (isdcalls) + fprintf(stderr,"asp: SPWrite: srn %d, cmdsize %d, wds %d, replysize %d\n", + SessRefNum, CmdBlockSize, WriteDataSize, ReplyBufferSize); + + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) { + *comp = ParamErr; + return(ParamErr); + } + if (as->state == SP_STARTING) { + *comp = ParamErr; + return(ParamErr); + } + if (as->ss == -1) { /* bad call */ + *comp = ParamErr; + return(ParamErr); + } + + aspwe = create_aspawe(as); + aspwe->type = tSPWrite2; + aspwe->SessRefNum = SessRefNum; + aspwe->WriteData = WriteData; + aspwe->WriteDataSize = WriteDataSize; + aspwe->ActLenWritten = ActLenWritten; + + aspqe = create_aspaqe(); + aspqe->type = tSPWrite; + aspqe->SessRefNum = SessRefNum; + aspqe->comp = comp; + aspqe->CmdResult = CmdResult; + aspqe->ActRcvdReplyLen = ActRcvdReplyLen; + + ap = &aspqe->abr.proto.atp; + ap->atpUserData = (dword)0; + aub = (ASPUserBytes *)&ap->atpUserData; + aub->std.b1 = aspWrite; + aub->std.b2 = as->SessID; + aub->std.data = htons(as->next_sequence); + aspwe->seqno = as->next_sequence; + as->next_sequence = ++as->next_sequence % 65536; + ap->atpAddress = as->addr; + ap->atpSocket = as->ss; + + ap->atpReqCount = CmdBlockSize; + ap->atpDataPtr = CmdBlock; + ap->atpRspBDSPtr = aspqe->bds; + cnt = setup_bds(aspqe->bds, atpMaxNum, atpMaxData, ReplyBuffer, + ReplyBufferSize, (dword)0); + /* we need this to ensure that we can figure out if a size error occurs */ + if (cnt < atpMaxNum && ((ReplyBufferSize % atpMaxData) == 0)) { + /* empty bds entry */ + aspqe->bds[cnt].buffPtr = NULL; + aspqe->bds[cnt].dataSize = 0; /* init */ + aspqe->bds[cnt].buffSize = 0; /* no data here */ + cnt++; + } + ap->fatpXO = TRUE; + ap->atpTimeOut = atptimeout <= 0 ? ASPWRITETIMEOUT : atptimeout; + ap->atpRetries = 255; /* infinite */ + ap->atpNumBufs = cnt; + *comp = 1; /* mark waiting */ + err = cbATPSndRequest(&aspqe->abr, handle_asp_sndreq, aspqe); + if (err == noErr) + return(noErr); + delete_aspaqe(aspqe); + delete_aspawe(aspwe, as); + return(asp_cksndrq_err("SPWrite", err, comp)); +} + +/* + * continue the write started by SPWrite - send the data after + * a wrtcontinue from the remote + * +*/ +private void +asp_do_write(abr, seqno, as) +ABusRecord *abr; +word seqno; +ASPSkt *as; +{ + ASPQE *aspwe; + atpProto *ap; + int cnt, towrite; + + + if (isdhand) + fprintf(stderr,"asp: Respond to wrtcontinue: with seqno %d on as %x\n", + seqno, as); + aspwe = find_aspawe(as, seqno); + if (aspwe == NULL) /* drop then */ + return; + + if (isdhand) + fprintf(stderr,"asp: Response is with aspawe %x\n",aspwe); + + if (abr->proto.atp.atpActCount < sizeof(as->reqdata)) { + /* This means we got a bad request here */ + /* because we don't have the count to write */ + return; + } + + /* setup bds */ + ap = &aspwe->abr.proto.atp; + ap->atpSocket = as->ss; + + ap->atpAddress = abr->proto.atp.atpAddress; + ap->atpTransID = abr->proto.atp.atpTransID; + + towrite = min(ntohs(as->reqdata), aspwe->WriteDataSize); + if (isdhand) + fprintf(stderr,"asp: Writting %d on aspawe %x\n",towrite,aspwe); + *aspwe->ActLenWritten = towrite; + cnt = setup_bds(aspwe->bds, atpMaxNum, atpMaxData, aspwe->WriteData, + towrite, (dword)0); + ap->atpRspBDSPtr = aspwe->bds; + ap->fatpEOM = 1; + ap->atpNumBufs = cnt; + ap->atpBDSSize = cnt; + cbATPSndRsp(&aspwe->abr, handle_asp_special, aspwe); + /* we should really figure out how to handle errors here */ +} + + +/* + * handle asp protocol events on ss sockets for sndrequests + * + * For now: SPWrite, SPCommand, SPWrtContinue, SPGetStat, SPOpenSession, + * SPAttention + * +*/ +private void +handle_asp_sndreq(abr, aspqe) +ABusRecord *abr; +ASPQE *aspqe; +{ + atpProto *ap; + ASPSkt *as; + ASPUserBytes *aub; + int rds, rrs; + + if (isdhand) + fprintf(stderr, "asp: handle_asp_sndreq with aspqe %x\n",aspqe); + if (aspqe == NULL) + return; /* drop */ + if (abr == NULL || aspqe == NULL) { + fprintf(stderr,"asp: fatal error: handle_asp_sndreq - abr or aspqe NIL\n"); + exit(255); + return; + } + if (isdhand) + fprintf(stderr, "asp: hsndreq: event code %s\n",aspevents[aspqe->type]); + + if ((as = aspskt_find_sessrefnum(aspqe->SessRefNum)) == NULL) + if (isdhand) + fprintf(stderr, "asp: hsndreq: srn %d not found\n",aspqe->SessRefNum); + + /* should check type (server/client) */ + switch (aspqe->type) { + case tSPWrite: + case tSPCommand: + case tSPWrtContinue: + switch (abr->abResult) { + case noErr: + if (aspqe->type != tSPWrtContinue) + *aspqe->CmdResult = ntohl(aspqe->bds[0].userData); + sizeof_abr_bds_and_req(abr, &rds, &rrs); + if (rds > rrs) { + /* Data should be okay since each bds element is atpMaxData */ + /* except for the last, however, last bit of data is missing */ + *aspqe->comp = BufTooSmall; + *aspqe->ActRcvdReplyLen = rrs; /* really only got this */ + } else { + *aspqe->comp = noErr; + *aspqe->ActRcvdReplyLen = rds; /* phew! everything okay */ + } + break; + case sktClosed: + *aspqe->comp = SessClosed; + break; + case reqFailed: + if (isdhand) + fprintf(stderr, "asp: hsndreq: reqFailed %x\n",aspqe); + *aspqe->comp = NoAck; + break; +#ifdef notdef + /* Request failed - could just die like here, but rather return */ + /* an error and let upper layer decide */ + *aspqe->comp = SessClosed; + if (!as) /* can't */ + break; + as->state = SP_INACTIVE; + shutdown_aspskt(as); + aspskt_free(as); + break; +#endif + default: + if (isdhand) + fprintf(stderr,"asp: hsndreq: bad atp completion %d\n",abr->abResult); + *aspqe->comp = aspFault; + } + break; + case tSPGetStat: /* almost like above... */ + /* probably should check that the atp user bytes are all zero, but.. */ + if (abr->abResult == noErr) { + sizeof_abr_bds_and_req(abr, &rds, &rrs); + if (rds > rrs) { + *aspqe->comp = BufTooSmall; + *aspqe->ActRcvdStatusLen = rrs; /* really only got this */ + } else { + *aspqe->comp = noErr; + *aspqe->ActRcvdStatusLen = rds; /* phew! everything okay */ + } + } else if (abr->abResult == reqFailed || abr->abResult == sktClosed) + *aspqe->comp = NoServers; /* assume atpReqFailed */ + else { + if (isdhand) + fprintf(stderr, "asp: SPGetStat: bad atp completion %d\n",abr->abResult); + *aspqe->comp = aspFault; + } + break; + case tSPOpenSess: + switch (abr->abResult) { + case noErr: + if (!as) { /* no matching session???? */ + *aspqe->comp = NoServers; + break; + } + aub = (ASPUserBytes *)&aspqe->bds[0].userData; + *aspqe->comp = (sword)ntohs(aub->std.data); + if (*aspqe->comp != noErr) + break; + ap = &as->rabr.proto.atp; + ap->atpSocket = as->ss; + ap->atpReqCount = sizeof(as->reqdata); + ap->atpDataPtr = (char *)&as->reqdata; + as->state = SP_STARTED; + as->SessID = aub->std.b2; /* remember sessid!! */ + /* set address for tickle to go to */ + as->tickle_abr.proto.atp.atpAddress = as->addr; /* remote SLS not SSS! */ + as->tickle_abr.proto.atp.atpSocket = as->ss; + startasptickle(as); + /* this is purposely deferred until after the startasptickle */ + as->addr.skt = aub->std.b1; /* get remote sss */ + cbATPGetRequest(&as->rabr, handle_aspclient, aspqe->SessRefNum); + start_ttimer(as); + break; + case reqFailed: + case sktClosed: + *aspqe->comp = NoServers; + break; + default: + if (isdhand) + fprintf(stderr, "asp: SPOpenSess: bad atp completion %d\n", + abr->abResult); + *aspqe->comp = aspFault; + } + break; + case tSPAttention: + /* should probably check that atp user bytes are all zero, but.. */ + if (abr->proto.atp.atpUserData != 0) + if (isdhand) + fprintf(stderr, "asp: SPAttention: bad userdata in attention ack\n"); + switch (abr->abResult) { + case noErr: + *aspqe->comp = noErr; + break; + case reqFailed: + case sktClosed: + *aspqe->comp = NoAck; + break; + default: + if (isdhand) + fprintf(stderr, "asp: SPAttenion: bad atp completion %d\n", + abr->abResult); + *aspqe->comp = aspFault; + } + break; + case tSPClose: + switch (abr->abResult) { + case sktClosed: + *aspqe->comp = SessClosed; + break; + case reqFailed: + case noErr: + if (isdhand) + fprintf(stderr, "asp: hsndreq: remote close %x\n",aspqe); + /* ignore error, etc */ + if (!as) { + *aspqe->comp = SessClosed; + break; + } + /* Inactive before so we protocol running won't cause as many problems */ + if (as->state == SP_INACTIVE) /* someone has already inactived */ + break; + as->state = SP_INACTIVE; /* close should inactive session :-) */ + shutdown_aspskt(as); + aspskt_free(as); + *aspqe->comp = noErr; + break; + default: + if (isdhand) + fprintf(stderr, "asp: SPClose: bad atp completion %d\n",abr->abResult); + *aspqe->comp = aspFault; + } + default: + break; /* just drop the thing */ + } + delete_aspaqe(aspqe); /* get rid of it */ +} + +/* + * Handle response done event - right now - just SPCmdReply and SPWrtReply + * +*/ +private void +handle_asp_rspdone(abr, aspqe) +ABusRecord *abr; +ASPQE *aspqe; +{ + ASPSkt *as; + + if (isdhand) + fprintf(stderr, "asp: handle_rspdone with aspqe %x\n",aspqe); + + if (aspqe == NULL) + return; /* drop */ + if (abr == NULL || aspqe == NULL) { + fprintf(stderr,"asp: fatal error: handle_rspdone - abr or aspqe NIL\n"); + exit(255); + return; + } + + if (isdhand) + fprintf(stderr, "asp: rspdone: event code %s\n",aspevents[aspqe->type]); + + if ((as = aspskt_find_sessrefnum(aspqe->SessRefNum)) == NULL) { + if (isdhand) + fprintf(stderr, "asp:rspdone - session %s not found\n", + aspqe->SessRefNum); + *aspqe->comp = SessClosed; + delete_aspaqe(aspqe); + return; + } + + switch (abr->abResult) { + case noErr: + switch (aspqe->type) { + case tSPWrtReply: + case tSPCmdReply: + *aspqe->comp = noErr; + break; + } + break; + case sktClosed: + *aspqe->comp = SessClosed; + break; + case noRelErr: + /* is this the right thing to do???? */ + if (isdhand) + fprintf(stderr, "asp: hrspdone: no rel received %x\n",aspqe); + *aspqe->comp = NoAck; /* overload */ +#ifdef notdef + /* This is a viable option, but will not do it.... */ + *aspqe->comp = SessClosed; + as->state = SP_INACTIVE; + shutdown_aspskt(as); + aspskt_free(as); +#endif + break; + default: + if (isdhand) + fprintf(stderr, "asp: hrspdone: Unexpected comp %d\n",abr->abResult); + *aspqe->comp = aspFault; + break; + } + delete_aspaqe(aspqe); +} + +/* + * Handle "Special" events - really handle cases where we wanted to + * run ATP async and need to drop the aspqe +*/ +private void +handle_asp_special(abr, aspqe) +ABusRecord *abr; +ASPQE *aspqe; +{ + ASPSkt *as; + + if (isdhand) + fprintf(stderr, "asp: hspecial: aspqe %x event code %s\n", + aspqe, aspevents[aspqe->type]); + + if (aspqe->type == tSPWrite2) { + if ((as = aspskt_find_sessrefnum(aspqe->SessRefNum)) == NULL) { + if (isdhand) + fprintf(stderr,"asp: WriteContinue reply done, but session %s invalid\n", + aspqe->SessRefNum); + } else delete_aspawe(aspqe, as); + } else delete_aspaqe(aspqe); +} + +/* + * send a reply to a close session call + * +*/ +private void +do_sendclosesessreply(as, abr) +ASPSkt *as; +ABusRecord *abr; +{ + if (isdhand) + fprintf(stderr, "asp: responding to remote closesession\n"); + if (as->state == SP_INACTIVE) + return; /* already done */ + /* Inactive before so we protocol running won't cause as many problems */ + abr->proto.atp.atpUserData = 0; /* clear these */ + as->state = SP_INACTIVE; + do_sendreply(as, abr); + shutdown_aspskt(as); + aspskt_free(as); +} + +/* + * Send an empty reply message + * +*/ +private void +do_sendreply(as, abr) +ASPSkt *as; +ABusRecord *abr; +{ + ABusRecord rabr; + BDS bds[1]; + atpProto *ap; + int cnt; + + ap = &rabr.proto.atp; + ap->atpSocket = as->ss; + + ap->atpAddress = abr->proto.atp.atpAddress; + ap->atpTransID = abr->proto.atp.atpTransID; + cnt = setup_bds(bds, atpMaxNum, atpMaxData, (char *)NULL, 0, (dword)0); + ap->atpRspBDSPtr = bds; + ap->fatpEOM = (abr->proto.atp.atpBitMap >> cnt) != 0 ? 1 : 0 ; + ap->atpNumBufs = cnt; + ap->atpBDSSize = cnt; + ATPSndRsp(&rabr, FALSE); +} + +/* + * Close down activity on an ASP connection. + * +*/ +private void +shutdown_aspskt(as) +ASPSkt *as; +{ + ASPQE *aspqe; + + if (isdhand) + fprintf(stderr, "asp: socketshutdown on srn %d/%d (sess %d) started\n", + as->SLSRefNum, as->SessRefNum, as->SessID); + if (as->tickling) + stopasptickle(as); /* stop tickling remote */ + stop_ttimer(as); + /* close down getrequests */ + while ((aspqe = get_aspaqe()) != NULL) { + switch (aspqe->type) { + /* atpsndResponse */ + case tSPWrite2: + /* shouldn't be in this queue */ + break; + case tSPCmdReply: + case tSPWrtReply: + case tSP_Special_DROP: + /* just break - will be shutup by atpclose */ + break; + /* atpgetrequest */ + case tSPGetRequest: + /* just break - will be shutup by atpclose */ + break; + /* atpsndReqeust */ + case tSPWrtContinue: + case tSPGetStat: + case tSPAttention: + case tSPOpenSess: + case tSPCommand: + case tSPWrite: + case tSPClose: + ATPReqCancel(&aspqe->abr, TRUE); /* run async, say why not? */ + break; + } + } + while ((aspqe = get_aspawe(as)) != NULL) + ; + if (as->ss != -1) + ATPCloseSocket(as->ss); /* close the socket */ + as->ss = -1; /* nothing here anymore */ +} + +/* + * SPFork(SessRefNum) + * + * Do a fork, returning -1 on an error + * + * For Client: + * Close off everything in toplevel + * For Server: + * Close off everything but handling for incoming tickles in + * toplevel - the SLS which remains in top-level is the one that + * sees them. + * In child, remember that we no longer wish to see the SLS + * + * Outgoing tickles are under the control of stickle, ctickle -- TRUE + * for either means to tickle or not to tickle for the base process + * (half-closed) connection and forked processes respectively + * +*/ +int +SPFork(SessRefNum, stickle, ctickle) +int SessRefNum; +int stickle; +int ctickle; +{ + int pid; + ASPSkt *as, *as1; + ASPSSkt *sas; + + if (isdcalls) + fprintf(stderr, "asp: SPFork called\n"); + if ((as = aspskt_find_sessrefnum(SessRefNum)) == NULL) + return(-1); + if (as->state != SP_STARTED) + return(-1); /* nothing to do */ + + if ((pid = fork()) < 0) /* check for error */ + return(pid); + if (pid) { + if (isdhand) + fprintf(stderr, "asp: child pid is %d\n",pid); + /* Parent doesn't want to know about the SSS */ +#ifdef ASPPID + as->pid = pid; +#endif + shutdown_aspskt(as); + if (as->type == SP_SERVER) { + /* an tickle user routines are still there though! */ + start_ttimer(as); /* restart tickle timer */ + as->state = SP_HALFCLOSED; + /* in parent: want to tickle them :-) */ + if (stickle) { + if (isdtickle) + fprintf(stderr, "asp: parent is tickling\n"); + /* remote address should have been set already */ + as->tickle_abr.proto.atp.atpSocket = 0; + startasptickle(as); + } + } else { + aspskt_free(as); + } + } else { + /* Child shouldn't see SLS if it exists */ + if (isdhand) + fprintf(stderr, "asp: child is running\n"); + if (as->type != SP_SERVER) + return(pid); + if ((sas = aspsskt_find_slsrefnum(as->SLSRefNum)) == NULL) + return(pid); + + /* kill off any unstarted ones - usually left over half-closed */ + /* if srn isn't started, then had no business forking anyway */ + while ((as1 = aspskt_find_notrunning()) != NULL) { + if (as1->state == SP_HALFCLOSED) + spshutdown(as1); + else { + shutdown_aspskt(as1); + aspskt_free(as1); + } + } + /* this is at the crux - we are not privy to incoming tickles on */ + /* session since they go to the sss! */ + stop_ttimer(as); /* close tickle timer since we are not */ + /* privy to incoming request */ + if (!ctickle) { + if (isdtickle) + fprintf(stderr, "asp: no child tickling\n"); + stopasptickle(as); + } + ATPCloseSocket(sas->addr.skt); /* close down server listener here */ + dsiTCPIPCloseSLS(); /* and the AppleShareIP SLS */ + } + return(pid); +} + +/* + * Complete shutdown of half-closed session. (E.g. parent side of + * spfork session). Should only be done when known child is dead. + * +*/ +OSErr +SPShutdown(srn) +int srn; +{ + ASPSkt *as = aspskt_find_sessrefnum(srn); + if (isdcalls) + fprintf(stderr, "asp: SPShutdown called with srn %d\n",srn); + return(spshutdown(as)); +} + +private OSErr +spshutdown(as) +ASPSkt *as; +{ + if (isdcalls) + fprintf(stderr, "asp: spshutdown called\n"); + + if (as == NULL || as->state != SP_HALFCLOSED) + return(ParamErr); + /* gotta do stopasptickle because server sends tickles as well */ + /* as child. This keeps client from timing out when child blocks */ + /* assumes that we trust that we will "know" when child processes die */ + if (as->tickling) + stopasptickle(as); /* stop tickling remote */ + stop_ttimer(as); + as->state = SP_INACTIVE; + aspskt_free(as); + return(noErr); +} + +#ifdef ASPPID +int +SPFindPid(pid) +int pid; +{ + ASPSkt *as = aspskt_find_pid(pid); + if (as == NULL) + return(-1); + return(as->SessRefNum); +} +#endif + +/* + * Set callback for Tickle Timeout - our timer expired for tickles + * from remote + * +*/ +OSErr +SPTickleUserRoutine(SessRefNum, routine, arg) +int SessRefNum; +int (*routine)(); +int arg; +{ + ASPSkt *as = aspskt_find_sessrefnum(SessRefNum); + + if (isdcalls) + fprintf(stderr, "asp: SPTickleUser called\n"); + if (as == NULL) + return(ParamErr); + as->tickle_timeout_user = routine; + as->ttu_arg = arg; + return(noErr); +} + +/* mappings for sessrefnum/slsrefnum to aspskt/aspsskt */ +private ASPSkt *asplist = NULL; +private ASPSSkt aspslist[NUMSASP]; +int numasp = -1; /* initially */ + +private int +aspskt_init(n) +int n; +{ + ASPSkt *as; + + if (n < NUMASP) + n = NUMASP; /* min to alloc */ + if (asplist != NULL && isdskt) + fprintf(stderr, "asp: asplist wasn't empty - AWK\n"); + asplist = (ASPSkt *)malloc(sizeof(ASPSkt)*n); + numasp = n; + if (asplist == NULL) { + fprintf(stderr, "asp: PANIC! out of memory in aspskt_init\n"); + exit(998); + } + as = asplist; + while (n--) { + as->active = FALSE; + as++; + } + return(numasp); +} + +private OSErr +aspskt_new(srn, retas) +int *srn; +ASPSkt **retas; +{ + int i; + ASPSkt *as; + + if (asplist == NULL) /* hasn't been inited yet? */ + aspskt_init(NUMASP); /* then do it */ + for (i = 0, as = asplist; i < numasp; i++, as++) + if (!as->active) { + as->active = TRUE; + as->tickle_timeout_user = NILPROC; /* make sure null */ +#ifdef ASPPID + as->pid = 0; /* base process */ +#endif + as->tickling = FALSE; /* we are not tickling yet! */ + as->SessRefNum = i; + *retas = as; + *srn = i; + return(noErr); + } + return(NoMoreSessions); +} + +private void +aspskt_free(as) +ASPSkt *as; +{ + if (as == NULL) + return; + as->active = FALSE; + /* possibly do a callback */ +} + +private ASPSkt * +aspskt_find_sessid(SLSRefNum, sessid) +int SLSRefNum; +byte sessid; +{ + int i; + ASPSkt *as; + + for (i = 0, as=asplist; i < numasp; i++, as++) + if (as->active && SLSRefNum == as->SLSRefNum && as->SessID == sessid) + return(as); + return(NULL); +} + +private ASPSkt * +aspskt_find_notrunning() +{ + int i; + ASPSkt *as; + + for (i = 0, as=asplist; i < numasp; i++, as++) + if (as->active && as->state != SP_STARTED) + return(as); + return(NULL); +} + +#ifdef ASPPID +private ASPSkt * +aspskt_find_pid(pid) +int pid; +{ + ASPSkt *as; + int i; + for (i=0, as=asplist; i < numasp; i++, as++) + if (as->active && as->state == SP_HALFCLOSED && as->pid == pid) + return(as); + return(NULL); +} +#endif + +#ifdef DEBUGAUFS +dumpsockets(sls) +{ + int i; + ASPSkt *as; + for (i = 0, as = asplist ; i < numasp; i++, as++) { +#ifdef ASPPID + logit(0, "aspskt %d %sactive, sls %d, state %d, type %d, ss %d, pid %d", + i, as->active ? "" : "not ", + as->SLSRefNum, as->state, as->type, as->ss, as->pid); +#else + logit(0, "aspskt %d %sactive, sls %d, state %d, type %d, ss %d", + i, as->active ? "" : "not ", + as->SLSRefNum, as->state, as->type, as->ss); +#endif + } +} +#endif + +ASPSkt * +aspskt_find_active(SLSRefNum) +int SLSRefNum; +{ + int i; + ASPSkt *as; + + for (i = 0, as=asplist; i < numasp; i++, as++) + if (as->active && as->SLSRefNum == SLSRefNum && as->state == SP_STARTING) + return(as); + return(NULL); +} + +ASPSkt * +aspskt_find_sessrefnum(srn) +int srn; +{ + ASPSkt *as; + + if (srn < 0 || srn >= numasp) + return(NULL); + as = &asplist[srn]; + if (as->active) + return(as); + return(NULL); +} + +private OSErr +aspsskt_new(sls, sas) +int *sls; +ASPSSkt **sas; +{ + int cno; + for (cno = 0; cno < NUMSASP; cno++) + if (!aspslist[cno].active) { + aspslist[cno].active = TRUE; + *sls = cno; + *sas = &aspslist[cno]; + return(noErr); + } + return(TooManyClients); +} + + +/* + * locate SLSRefNum structure + * (non-private for DSI access) + * + */ + +ASPSSkt * +aspsskt_find_slsrefnum(sls) +int sls; +{ + if (sls >= NUMSASP || sls < 0 || !aspslist[sls].active) + return(NULL); + return(&aspslist[sls]); +} + +private boolean +aspsskt_isactive(sls) +{ + if (sls > NUMSASP || sls < 0 || !aspslist[sls].active) + return(FALSE); + return(TRUE); +} + + +/* + * ASP Tickle managment functions + * +*/ + +/* + * startpaptickle - start a tickle for the specified connection outgoing + * on the designated socket + * remote address and local socket MUST be set before calling + * +*/ +private void +startasptickle(as) +ASPSkt *as; +{ + atpProto *ap; + ASPUserBytes *aub; + static BDS bds[1]; + int err; + + + ap = &as->tickle_abr.proto.atp; + ap->atpUserData = 0; + aub = (ASPUserBytes *) &ap->atpUserData; + aub->std.b1 = aspTickle; + aub->std.b2 = as->SessID; +#ifdef notdef + /* must be set before called */ + ap->atpAddress = as->addr; /* remote wss or sls socket */ + ap->atpSocket = skt; +#endif + if (isdtickle) { + fprintf(stderr, "asp: starting tickle on connection %d/%d socket %d\n", + as->SLSRefNum, as->SessRefNum, ap->atpSocket); + } + + ap->atpReqCount = 0; + ap->atpDataPtr = 0; + ap->atpRspBDSPtr = bds; + bds[0].buffPtr = NULL; + bds[0].buffSize = 0; + ap->atpNumBufs = 1; + ap->fatpXO = FALSE; + ap->atpTimeOut = ASPTICKLETIMEOUT; + ap->atpRetries = 255; /* means infinity */ + err = ATPSndRequest(&as->tickle_abr, TRUE); + if (err != noErr) { + fprintf(stderr,"asp: Problems starting tickle\n"); + as->tickling = FALSE; + } else + as->tickling = TRUE; +} + +/* + * stopasptickle - cancel the tickle on the specified connection + * +*/ +void +stopasptickle(as) +ASPSkt *as; +{ + OSErr err; + + if (isdtickle) { + fprintf(stderr, "asp: killing tickle on connection %d/%d\n", + as->SLSRefNum, as->SessRefNum); + } + err = ATPReqCancel(&as->tickle_abr, FALSE); /* run async? */ + if (err == cbNotFound && err != sktClosed) { + if (isdtickle) + fprintf(stderr, "asp: Tickle request completed - should never happen\n"); + } + as->tickling = FALSE; +} + +/* + * Timeout handler for remote tickle + */ +private void +ttimeout(as) +ASPSkt *as; +{ + if (isdtickle) { + fprintf(stderr, "asp: Tickle timeout\n"); + fprintf(stderr, "asp: Timeout on connection %d\n", as->SessID); + } + if (as->tickle_timeout_user != NULL) + (*as->tickle_timeout_user)(as->SessRefNum, as->ttu_arg); + else { + as->state = SP_INACTIVE; + shutdown_aspskt(as); + aspskt_free(as); + } +} + +/* + * Start the remote tickle timeout + * +*/ +private void +start_ttimer(as) +ASPSkt *as; +{ + Timeout(ttimeout, (u_long)as, ASPCONNECTIONTIMEOUT); +} + +/* + * reset the remote tickle timeout + * +*/ +private void +reset_ttimer(as) +ASPSkt *as; +{ + remTimeout(ttimeout, (u_long)as); + Timeout(ttimeout, (u_long)as, ASPCONNECTIONTIMEOUT); +} + +/* + * cancel the remote tickle timeout + * +*/ +void +stop_ttimer(as) +ASPSkt *as; +{ + remTimeout(ttimeout, (u_long)as); +} + + + + + +private ASPQE *aspqe_list; +private QElemPtr aspqe_free; + +ASPQE * +create_aq(which, as) +int which; +ASPSkt *as; +{ + ASPQE *aspqe; + QHead head; + + switch (which) { + case ASPAQE: + head = (QHead)&aspqe_list; + break; + case ASPAWE: + head = &as->wqueue; + break; + } + + if ((aspqe = (ASPQE *)dq_head(&aspqe_free)) == NULL && + (aspqe =(ASPQE *)malloc(sizeof(ASPQE))) == NULL) { + fprintf(stderr,"asp: panic: create_aq: out of memory\n"); + exit(8); + } + if (isdskt) + fprintf(stderr,"asp: create_aq: create %x on %s\n", + aspqe,which == ASPAQE ? "main queue" : "local writeq"); + q_tail(head, &aspqe->link); + return(aspqe); +} + +void +delete_aq(aspqe, which, as) +ASPQE *aspqe; +int which; +ASPSkt *as; +{ + QHead head; + + switch (which) { + case ASPAQE: + head = (QHead)&aspqe_list; + break; + case ASPAWE: + head = &as->wqueue; + break; + } + if (isdskt) + fprintf(stderr,"asp: delete_aq: delete %x on %s\n", + aspqe,which == ASPAQE ? "main queue" : "local writeq"); + dq_elem(head, &aspqe->link); + q_tail(&aspqe_free, &aspqe->link); +} + +private ASPQE * +get_aq(which, as) +int which; +ASPSkt *as; +{ + ASPQE *aspqe; + QHead head; + + switch (which) { + case ASPAQE: + head = (QHead)&aspqe_list; + break; + case ASPAWE: + head = &as->wqueue; + break; + } + + if ((aspqe = (ASPQE *)dq_head(head)) != NULL) + q_tail(&aspqe_free, &aspqe->link); + return(aspqe); +} + +private boolean +match_aspwe(aspqe, seqno) +ASPQE *aspqe; +word seqno; +{ + return(aspqe->seqno == seqno); +} + +private ASPQE * +find_aspawe(as, seqno) +ASPSkt *as; +word seqno; +{ + return((ASPQE *)q_mapf(as->wqueue, match_aspwe, seqno)); +} + + +/* some aux stuff that should eventually be moved out */ +/* + * return size of dataSize and size of original requested data + * +*/ +private void +sizeof_bds_and_req(bds, numbds, rds, rrs) +BDS bds[]; +int numbds; +int *rds; +int *rrs; +{ + int i, ds, rs; + + if (bds == NULL || numbds < 0) { + *rds = 0; + *rrs = 0; + return; + } + if (numbds > atpMaxNum) + numbds = atpMaxNum; /* cheap */ + + for (i = 0, ds = 0, rs=0; i < numbds; i++) { + ds += bds[i].dataSize; + rs += bds[i].buffSize; + } + *rds = ds; + *rrs = rs; +} + +/* + * Same as sizeof_bds_and_req, but takes ABusrecord instead of + * bds and bdssize + * +*/ +private void +sizeof_abr_bds_and_req(abr, rds, rrs) +ABusRecord *abr; +int *rds; +int *rrs; +{ + if (abr == NULL) { + *rds = 0; + *rrs = 0; + return; + } + sizeof_bds_and_req(abr->proto.atp.atpRspBDSPtr, abr->proto.atp.atpNumRsp, + rds, rrs); +} + +/* + * Check for possible ATPsendrequest errors and convert to an approriate + * asp error +*/ +private OSErr +asp_cksndrq_err(s, err, comp) +char *s; +OSErr err; +int *comp; +{ + if (err == noErr) + return(noErr); + if (err == atpLenErr) { + *comp = SizeErr; + return(SizeErr); + } + if (err == tooManySkts) { + *comp = noATPResource; + return(noATPResource); + } + else if (err == badATPSkt) { + fprintf(stderr, "asp: ASP Internal error: %s: badATPSkt\n",s); + exit(1); + } + fprintf(stderr, "asp: ASP Internal error: %s: unexpected error %d\n",s,err); + exit(1); +} + +/* set asp debug flags */ +aspdebug(s) +char *s; +{ + asp_dbug = 0; /* default to zero */ + while (*s) { + switch (*s) { + case 's': + asp_dbug |= AD_SKT; + break; + case 'h': + asp_dbug |= AD_HANDLERS; + break; + case 'c': + asp_dbug |= AD_CALLS; + break; + case 't': + asp_dbug |= AD_TICKLE; + break; + } + s++; + } +} diff --git a/lib/cap/abasp.h b/lib/cap/abasp.h new file mode 100644 index 0000000..abf550c --- /dev/null +++ b/lib/cap/abasp.h @@ -0,0 +1,146 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:45:42 $ + * $Header: abasp.h,v 2.1 91/02/15 22:45:42 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * abasp.c - Appletalk Session Protocol + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * July 28, 1986 CCKim Created + * Aug 4, 1986 CCKim Verified: level 0 +*/ + + +/* definition of ATP User Bytes */ +typedef union { + struct { /* used by Tickle, CloseSess, GetStat, etc. */ + byte b1; + byte b2; + word data; + } std; + dword CmdResult; /* 4 bytes, for vax, must use ntohl, htonl */ +} ASPUserBytes; + + +/* Definition of ASP Server Listener Socket block */ +typedef struct { + int active; + char *ssb; + int ssbl; + AddrBlock addr; + ABusRecord abr; /* for getrequest */ +} ASPSSkt; +#define NUMSASP 1 + +/* Definition of ASP Station Socket block */ +typedef struct { + int active; /* true if in use */ + int state; /* state of connection */ + int type; /* server/client */ + int SLSRefNum; /* which SLS owns us */ + int SessRefNum; /* our session refnum - really */ + /* redundant info right now */ + AddrBlock addr; /* complete address of remote ss */ + + ABusRecord tickle_abr; /* for tickle */ + int tickling; /* mark whether we are tickling */ + + ABusRecord rabr; /* request abr */ + word reqdata; /* for writedata data */ + int ss; /* service socket */ + byte SessID; /* session id (per sls) */ + int (*attnroutine)(); /* attention callback */ + int next_sequence; /* for write/command */ + QElemPtr wqueue; /* queue of write's */ + int *comp; /* pointer to completion var */ + + int (*tickle_timeout_user)(); /* for tickle timeout */ + int ttu_arg; /* an argument to call with */ + +#ifdef ASPPID + int pid; /* for spfork */ +#endif +} ASPSkt; +#define NUMASP 5 + +/* Definitions for possible asp connection states */ +#define SP_INACTIVE 0 +#define SP_STARTING 1 +#define SP_STARTED 2 +#define SP_HALFCLOSED 3 + +/* ASP Connection types */ +#define SP_SERVER 0 +#define SP_CLIENT 1 + +/* Internal ASP queue elements - used to "remember" things */ +typedef struct ASPQE { + QElem link; + int type; /* what was the command type */ + int SessRefNum; /* traceback */ + + word seqno; + + struct ABusRecord abr; /* request abr */ + BDS bds[atpMaxNum]; + word availableBufferSize; /* data for wrtcontinue packet */ + + struct ASPQE **ReqRefNum; + int *SPReqType; + int *ActRcvdReqLen; + int *ActLenRcvd; + int *comp; + + int *ActRcvdReplyLen; + dword *CmdResult; + int *ActRcvdStatusLen; + + char *WriteData; + int WriteDataSize; + int *ActLenWritten; +} ASPQE; /* asp queue element */ + + +/* QUeue element types */ +#define tSPGetRequest 0 +#define tSPCmdReply 1 +#define tSPWrtContinue 2 +#define tSPWrtReply 3 +#define tSPAttention 4 +#define tSP_Special_DROP 5 +#define tSPGetStat 6 +#define tSPOpenSess 7 +#define tSPCommand 8 +#define tSPWrite 9 +#define tSPWrite2 10 +#define tSPClose 11 + +/* Defines write v.s. std. queue element */ +#define ASPAQE 0 +#define ASPAWE 1 + +#define create_aspaqe() create_aq(ASPAQE, (ASPSkt *)0) +#define create_aspawe(as) create_aq(ASPAWE, (ASPSkt *)(as)) +#define delete_aspaqe(item) delete_aq((ASPQE *)(item),ASPAQE,(ASPSkt *)0) +#define delete_aspawe(item,as) delete_aq((ASPQE *)(item),ASPAWE,(ASPSkt *)(as)) +#define get_aspaqe() get_aq(ASPAQE, (ASPSkt *)0) +#define get_aspawe(as) get_aq(ASPAWE, (ASPSkt *)(as)) + +/* should go into cap_conf.h? */ +#define ASPTICKLETIMEOUT 30*4 /* defined by Spec (30 seconds) */ +#define ASPCONNECTIONTIMEOUT 120*4 /* defined by Spec ( 2 minutes) */ +#define ASPGETSTATTIMEOUT 2*4 /* arbitrary (2 seconds) */ +#define ASPOPENSESSTIMEOUT 2*4 /* arbitrary (2 seconds) */ +#define ASPCLOSESESSIONTIMEOUT 2*4 /* arbitrary (2 seconds) */ +#define ASPCOMMANDTIMEOUT 2*4 /* arbitrary (2 seconds) */ +#define ASPWRITETIMEOUT 2*4 /* arbitrary (2 seconds) */ +#define ASPATTENTIONTIMEOUT 2*4 /* arbitrary (2 seconds) */ + diff --git a/lib/cap/abatp.c b/lib/cap/abatp.c new file mode 100644 index 0000000..1f3ab60 --- /dev/null +++ b/lib/cap/abatp.c @@ -0,0 +1,1525 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:48:17 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abatp.c,v 2.9 1996/06/18 10:48:17 djh Rel djh $ + * $Revision: 2.9 $ + * + */ + +/* + * abatp.c - Appletalk Transaction Protocol + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Edit History: + * + * June 15, 1986 CCKim Created + * June 30, 1986 CCKim Clean up, finish XO + * July 15, 1986 CCKim Some more cleanup, fix bug with rsptimeout, + * allow incoming socket in atpsndreq + * July 28, 1986 CCKim Make sure auto req socket gets deleted in child + * July 28, 1986 CCKim Wasn't handling case when rspcb didn't have + * valid response very well, fix it. + * + */ + +#include +#include +#ifndef linux +#include +#endif /* linux */ +#include +#include +#include +#include "abatp.h" + + +/* + * both suns and pyramids are such that byte alignment issues force + * use to copy around the atp header. Reason it is ifdef'ed is that + * we really don't want to do this if it isn't necessary + * + * Actually, what would be real nice is if the read was done with + * a readv and we had a chain of buffers, etc, but things weren't done + * that way. Problem with this is that you really have to know too + * much at the lower levels to do it... + * + * CCK: actually not, we can assume we only get ddp packets + * at the lower levels (when reading off a particular fd) or else + * the base implementation is brain damaged. + * +*/ + +OSErr ATPSndRequest(); +OSErr cbATPSndRequest(); +OSErr ATPOpenSocket(); +OSErr ATPCloseSocket(); +OSErr ATPGetRequest(); +OSErr cbATPGetRequest(); +OSErr ATPRspCancel(); +OSErr ATPSndRsp(); +OSErr cbATPSndRsp(); +void ATPSetResponseTimeout(); +/* OSErr ATPAddRsp(); */ +private void atp_listener(); +private void tcb_timeout(); +private void rsptimeout(); +private boolean handle_request(); +private boolean handle_release(); +private boolean handle_response(); +private OSErr atpreqsend(); +private OSErr atprelsend(); +private OSErr atpxmitres(); + +private int atpreqskt = -1; +private int atpreqsktpid = 1; /* pid 1 is init - hope it never runs this */ +/* baseline value */ +private dword atpresptimeout = RESPONSE_CACHE_TIMEOUT; +/* room for atp + user data + extra to make count vs. index */ +#define ATPIOVLEN (IOV_ATP_SIZE+1) +private ATP atph; /* atp header */ +private byte atpdata[atpMaxData]; /* room for ATP data bytes */ +private struct iovec ratpiov[ATPIOVLEN] = { + {NULL, 0}, /* lap */ + {NULL, 0}, /* ddp */ + {(caddr_t)&atph, atpSize}, /* atp */ + {(caddr_t)atpdata, atpMaxData} /* atp user data */ +}; +private int delete_tcb_skt(); +private int ATPWrite(); + +/* + * ATPSndRequest + * + * Send a request to the remote ATP. As documented by Apple, except + * a non-zero atp request socket means to use that socket instead of + * some socket we generate + * +*/ + +OSErr +ATPSndRequest(abr, async) +ABusRecord *abr; +int async; /* boolean - true means runs async */ +{ + int to; + + if ((to = cbATPSndRequest(abr, NULL, 0L)) != noErr) + return(to); + + if (async) + return(noErr); /* all done if asynchronous */ + + to = abr->proto.atp.atpTimeOut; + while (abr->abResult == 1) + abSleep(to,TRUE); /* wakeup on events */ + + return(abr->abResult); +} + +OSErr +cbATPSndRequest(abr, callback, cbarg) +ABusRecord *abr; +int (*callback)(); +caddr_t cbarg; +{ + atpProto *atpproto; + TCB *tcb; + + atpproto = &abr->proto.atp; + if ((atpproto->atpReqCount > atpMaxData) || + (atpproto->atpRspBDSPtr == NULL)) + return(atpLenErr); + + if (atpproto->atpSocket == 0) { + if (atpreqskt < 0 || atpreqsktpid != getpid()) { + if (atpreqskt >= 0) { + delete_tcb_skt(atpreqskt); /* get rid of outstanding requests */ + DDPCloseSocket(atpreqskt); /* make sure child cleans up */ + } + atpreqskt = 0; + if (DDPOpenSocketIOV(&atpreqskt,atp_listener,ratpiov,ATPIOVLEN)!=noErr) { + atpreqskt = -1; /* make sure */ + return(tooManySkts); + } + atpreqsktpid = getpid(); /* mark pid */ + } + atpproto->atpSocket = atpreqskt; + } + + /* should check buffer list */ + /* get a free tcb */ + if ((tcb = create_tcb(atpproto->atpSocket, abr, callback, cbarg)) == NULL) + return(noDataArea); + + if (atpreqsend(tcb) < 0) { + tcb->callback = NULL; /* no callback! */ + delete_tcb(tcb); + return(badATPSkt); + } + atpproto->atpNumRsp = 0; /* make sure zero */ + abr->abResult = 1; + Timeout(tcb_timeout,tcb,atpproto->atpTimeOut); /* q a timeout */ + return(noErr); +} + +/* + * ATPKillGetReq + * + * cancel an outstanding ATPGetRequest + * + */ + +OSErr +ATPKillGetReq(abr, async) +ABusRecord *abr; +boolean async; +{ + RqCB *rqcb; + + rqcb = find_rqcb_abr(abr); + if (rqcb == NULL) + return(cbNotFound); + rqcb->abr->abResult = sktClosed; + delete_rqcb(rqcb); + return(noErr); +} + +/* + * ATPReqCancel + * + * cancel an outstanding ATPSndRequest + * + */ + +OSErr +ATPReqCancel(abr, async) +ABusRecord *abr; +boolean async; +{ + TCB *tcb; + + tcb = find_tcb_abr(abr); + if (tcb == NULL) + return(cbNotFound); + tcb->abr->abResult = sktClosed; + /* should we actually close the socket? */ + remTimeout(tcb_timeout, tcb); + delete_tcb(tcb); + return(noErr); +} + +/* + * + * Responder code + * +*/ + +/* + * ATPOpenSocket(AddrBlock *addr,int *skt) + * + * ATPOpenSocket opens a socket for the purpose of receiving requests. + * "skt" contains the socket number of the socket to open, or zero if + * dynamic allocation is desired. "addr" contains a filter from which + * requests will be accepts. A 0 in the network number, node ID, or + * socket number field of the "addr" record acts as a "wild card." + * + * Note: if you are only going to send requests and receive response + * from these requests, you do not need to open an ATP socket with + * ATPOpenSocket. + * +*/ + +OSErr +ATPOpenSocket(addr,skt) +AddrBlock *addr; +int *skt; +{ + if (DDPOpenSocketIOV(skt,atp_listener,ratpiov, ATPIOVLEN) != noErr) + return(tooManySkts); + if (create_atpskt(*skt, addr, NULL) == NULL) + return(noDataArea); + return(noErr); +} + +/* + * ATPCloseSocket(int skt) + * + * ATPCloseSocket closes the responding socket whose number is + * specified by "skt." It releases the data structure associated + * with all pending asynchronous calls involving that socket; these + * calls are completed immediately and return the result code + * sktClosed. + * +*/ +OSErr +ATPCloseSocket(skt) +int skt; +{ + int v; + RspCB *rspcb; + RqCB *rqcb; + + while ((rspcb = find_rspcb_skt(skt)) != NULL) { + if (rspcb->abr != NULL) + rspcb->abr->abResult = sktClosed; /* completed */ + /* completion code set before rspcb so completion code set */ + killrspcb(rspcb); + } + while ((rqcb = find_rqcb(skt)) != NULL) { + rqcb->abr->abResult = sktClosed; + delete_rqcb(rqcb); + } + v = delete_atpskt(skt); /* v is nominal amount of work to do */ + if (dbug.db_atp && v) + fprintf(stderr,"atp: ****atpclose with %d on the queue...\n",v); + (void)DDPCloseSocket(skt); /* close the socket (drop codes) */ + return(noErr); /* ignore any ddp error */ +} + +#ifdef ATPREQCACHE +private int Have_CPkt = 0; /* 1 for pkt, -1 for re-using, 0 for none */ +private u_char CPkt_skt; +private ATP CPkt_atp; +private u_char CPkt_buf[atpMaxData]; +private int CPkt_buflen; +private AddrBlock CPkt_addr; + +/* + * The timeout handling is used for replaying + * the cache packet or purging it. + * + */ + +void +CPkt_timeout(arg) +caddr_t arg; +{ + if (dbug.db_atp) + fprintf(stderr, "atp: cache req pkt timeout p=%d c=%d\n",arg,Have_CPkt); + + /* + * if a replay timeout and have a cache packet + * then replay it + * + */ + if (arg == (caddr_t)0 && Have_CPkt) { + if (dbug.db_atp) + fprintf(stderr, "atp: re-handling cache pkt p=%d c=%d\n",arg,Have_CPkt); + Have_CPkt = -1; + handle_request(CPkt_skt, &CPkt_atp, CPkt_buf, CPkt_buflen, &CPkt_addr); + remTimeout(CPkt_timeout, (caddr_t)1); + } + + /* + * no more cache packet + * + */ + Have_CPkt = 0; + + return; +} +#endif ATPREQCACHE + +/* + * ATPGetRequest(int skt, ABusRecord *abr, int async) + * + * ATPGetRequest sets up the mechanism to receive a request sent by + * a remote node issuing ATPSndRequest or ATPRequest. "skt" contains + * the socket number of the socket that should listen for a request; + * this socket must have been opened by calling ATPOpenSocket. +*/ +OSErr +ATPGetRequest(abr,async) +ABusRecord *abr; +int async; /* boolean - true means runs async */ +{ + int to; + + if ((to = cbATPGetRequest(abr, NULL, 0L)) != noErr) + return(to); + if (async) + return(noErr); + while (abr->abResult == 1) /* wait for completion */ + abSleep(400,TRUE); /* wakeup on events */ + return(abr->abResult); /* and return result */ +} + +OSErr +cbATPGetRequest(abr, callback, cbarg) +ABusRecord *abr; +int (*callback)(); +caddr_t cbarg; +{ + /* + * Only one listen request per socket is allowed - more than one + * really does lead to an ambiguity problem - maybe queue them + * up in the future? (What to do if one is blocking and other is + * not?) + */ + + abr->abResult = 1; /* not completed */ + if (create_rqcb(abr->proto.atp.atpSocket,abr,callback,cbarg) == NULL) + return(noDataArea); +#ifdef ATPREQCACHE + /* + * if cache entry exists and matches, + * fire up immediate timeout + * + */ + if (Have_CPkt && (CPkt_skt == abr->proto.atp.atpSocket)) { + remTimeout(CPkt_timeout, (caddr_t)1); + Timeout(CPkt_timeout, (caddr_t)0, 0); + } +#endif ATPREQCACHE + /* no Timeout on this operation... */ + return(noErr); +} + + +/* + * Cancel a previous SndRsp + * + * (Possibly kill off socket?) + * +*/ +OSErr +ATPRspCancel(abr, async) +ABusRecord *abr; +int async; +{ + RspCB *rspcb; + + if ((rspcb = find_rspcb_abr(abr)) == NULL) + return(cbNotFound); + abr->abResult = noErr; /* must be done before killrspcb because */ + /* of callback */ + return(killrspcb(rspcb)); /* ignore error for now */ +} + +/* + * kill off a RSPCB transation + * +*/ +killrspcb(rspcb) +RspCB *rspcb; +{ + remTimeout(rsptimeout, rspcb); + delete_rspcb(rspcb); /* remove it from the list */ + return(noErr); /* no errror */ +} + + +/* + * ATP Send Response + * +*/ +OSErr +ATPSndRsp(abr, async) +ABusRecord *abr; +int async; /* boolean - true means runs async */ +{ + int to ; + if ((to = cbATPSndRsp(abr, NULL, 0L)) < 0) + return(to); + if (!async) { + while (abr->abResult == 1) + abSleep(400, TRUE); + return(abr->abResult); + } + return(noErr); +} + +OSErr +cbATPSndRsp(abr, callback, cbarg) +ABusRecord *abr; +int (*callback)(); +caddr_t cbarg; +{ + atpProto *atpproto; + RspCB *rspcb; + + /* check socket and data lengths */ + + /* should check atpNumBufs and atpBDSSize conform */ + /* note ddp errors are supposed to be ignored...*/ + /* try sending first to ensure socket is open, ow we will have */ + /* it running back there without being able to function properly */ + atpproto = &abr->proto.atp; + + if (atpxmitres(abr, 0xff >> (8-atpproto->atpNumBufs)) < 0) + return(badATPSkt); + /* Find active rspcb and put responses in cache for rexmit, start timeout */ + rspcb = find_rspcb(atpproto->atpSocket, atpproto->atpTransID, + &atpproto->atpAddress); + if (rspcb != NULL) { + rspcb->abr = abr; /* save this away */ + rspcb->callback = callback; + rspcb->cbarg = cbarg; + abr->abResult = 1; + remTimeout(rsptimeout, rspcb); + if (atpresptimeout) + Timeout(rsptimeout, rspcb, atpresptimeout); + } else { + rspcb = create_rspcb(0, 0, NULL); /* create a dummy rspcb */ + rspcb->abr = abr; /* save this away */ + rspcb->callback = callback; + rspcb->cbarg = cbarg; + abr->abResult = noErr; /* if we are at least once.... */ + /* timeout immediately */ + Timeout(rsptimeout, rspcb, 0); + } + + return(noErr); +} + + +/* + * Set the atp response cache timeout value + * +*/ +void +ATPSetResponseTimeout(value) +dword value; +{ + atpresptimeout = value; +} + + +#ifdef NOTDEFINEDATALL +/* + * DO NOT USE THIS ROUTINE - SOME RETHINKING IS NEEDED TO ALLOW + * IT TO BE INTEGRATED!!!! +*/ +OSErr +ATPAddRsp(fd, abr, async) +int fd; +ABusRecord *abr; +int async; /* boolean - true means runs async */ +{ + ATP atp; + atpProto *atpproto; + RspCB *rspcb; + + /* check socket and data lengths */ + + atp.control = atpRspCode; /* response */ + atpproto = &abr->proto.atp; + atp.control |= (atpproto->fatpEOM) ? atpEOM : 0; + atp.transID = atpproto->atpTransID; + atp.bitmap = atpproto->atpNumRsp; + atp.userData = atpproto->atpUserData; + return(ATPWrite(atpproto,&atp, + atpproto->atpDataPtr,atpproto->atpReqCount)); +} + +#endif /* NOT YET IMPLEMENTED */ + + +/* + * atp_Listener - + * here we watch for incoming ATP packets and demux them to the + * appropriate handler (request, response, release) + * + * Since we opened with DDPOpenSocketIOV, our input will be in an + * iovec with the first pointing to the atp header, second to the + * atp data. + * +*/ +private void +atp_listener(skt,type,iov,iovlen,packet_length,addr) +u_char skt; +u_char type; +struct iovec *iov; +int iovlen; +int packet_length; +AddrBlock *addr; +{ + ATP *atp; + char *pkt_data; /* pointer to user data */ + + /* Check the packet type - see if it TReq or TRel (others are */ + /* considered illegal?) */ + + if (type != ddpATP || iovlen < 1 || packet_length < atpSize) + return; /* drop it */ + + atp = (ATP *)iov->iov_base; /* get atp header */ + iov++; /* move past atp header */ + iovlen--; /* move past atp header */ + packet_length -= atpSize; /* reduce to data Size */ + if (iovlen < 1) { /* can't be from us! */ + if (dbug.db_atp) { + fprintf(stderr,"atp: [ATP_LISTENER: net=%d.%d, node=%d, skt=%d]\n", + nkipnetnumber(addr->net),nkipsubnetnumber(addr->net), + addr->node,addr->skt); + fprintf(stderr,"atp: internal error: iovlen < 1 before handle\n"); + } + return; + } + pkt_data = iov->iov_base; /* get user data */ + if (dbug.db_atp) + fprintf(stderr,"atp: [ATP_LISTENER: net=%d.%d, node=%d, skt=%d]\n", + nkipnetnumber(addr->net),nkipsubnetnumber(addr->net), + addr->node,addr->skt); + + switch (atp->control & atpCodeMask) { + default: + return; /* drop packet */ + case atpRspCode: + handle_response(skt, atp, pkt_data, packet_length, addr); + break; + case atpReqCode: /* TReq case: */ + handle_request(skt, atp, pkt_data, packet_length, addr); + break; + case atpRelCode: + handle_release(skt, atp->transID, addr); + break; + } +} + +/* + * tcb_timeout(int tcbno) + * + * tcb_timeout is called via the Timeout() mechanism when + * an ATP response has been pending for too long. + * +*/ +private void +tcb_timeout(tcb) +TCB *tcb; +{ + u_char *retries; + + if (dbug.db_atp) + fprintf(stderr,"atp: tcb_timeout: here with TCB %x\n",tcb); + + retries = &tcb->abr->proto.atp.atpRetries; /* get retries pointer */ + if (*retries != 0) { /* exceeded retries? */ + *retries -= (*retries == 255) ? 0 : 1; + atpreqsend(tcb); /* no, queue up another */ + Timeout(tcb_timeout,tcb,tcb->abr->proto.atp.atpTimeOut); + } else { + tcb->abr->abResult = reqFailed; + delete_tcb(tcb); + } +} + +/* + * rsptimeout + * + * Handle the timeout of a rspcb. Basically, just note that + * a release wasn't sent by the remote if we ever responded + * to the incoming request. Not so clear what we should + * do if the response was never issued, so we just drop in + * that case.... +*/ +private void +rsptimeout(rspcb) +RspCB *rspcb; +{ + /* if skt is zero, then dummy rspcb */ + if (rspcb->atpsocket != 0) { + if (dbug.db_atp) + fprintf(stderr,"atp: removing rspcb %x for timeout\n", rspcb); + /* assuming we tried to respond! */ + if (rspcb->abr != NULL) + rspcb->abr->abResult = noRelErr; /* completed */ + } else { + if (dbug.db_atp) + fprintf(stderr,"atp: removing dummy rspcb %x\n", rspcb); + } + delete_rspcb(rspcb); /* okay! */ +} + + +/* + * handle_request + * + * handle an incoming ATP request packet + * +*/ +private boolean +handle_request(skt, atp, databuf, dblen, addr) +int skt; +ATP *atp; +char *databuf; +int dblen; +AddrBlock *addr; +{ + RspCB *rspcb; + RqCB *rqcb; + atpProto *ap; + + if (find_atpskt(skt, addr) == NULL) { + if (dbug.db_atp) + fprintf(stderr,"atp: handle_request: Socket was never opened or \ +address mismatch\n"); + return(FALSE); + } + + if (dbug.db_atp) + fprintf(stderr, "atp: Incoming request on socket %d with TID %d\n", + skt, ntohs(atp->transID)); + + /* + * If the packets has its XO bit set and a matching RspCB exists + * then: + * - retransmit all response pkts REQUESTED + * - restart release timer + * - exit + */ + if (atp->control & atpXO) { + rspcb = find_rspcb(skt, atp->transID, addr); + if (rspcb != NULL) { + if (dbug.db_atp) + fprintf(stderr,"atp: exactly once: rspcb %x\n", rspcb); + /* we should really record the average number of requests that */ + /* have "lost" packets and average number lost per response size */ + if (dbug.db_atp) + fprintf(stderr,"atp: *incoming bitmap: %x*\n",atp->bitmap); + if (rspcb->abr != NULL) /* response to rexmit? */ + atpxmitres(rspcb->abr, atp->bitmap); /* do it */ + remTimeout(rsptimeout, rspcb); + if (atpresptimeout) + Timeout(rsptimeout, rspcb, atpresptimeout); + /* if we allowed "parts" of response at a time, then we would */ + /* have to return the bitmap if sts was set previously */ + /* (e.g. need atpAddRsp for this to be meaningful) */ + return(FALSE); + } else { + if (dbug.db_atp) + fprintf(stderr,"atp: exactly once transaction: no rspcb\n"); + } + } + /* If RqCB doesn't exist for the local socket or if the packet's */ + /* source address doesn't match the admissible requestor address in */ + /* the RqCB then ignore the packet and exit */ + /*XXX should do address filtering here */ +#ifdef ATPREQCACHE + if ((rqcb = find_rqcb(skt)) == NULL) { + /* + * a RqCB does not exist, maybe it will exist + * shortly so cache this packet for reply if + * it is an XO packet + * + */ + if ((atp->control & atpXO) && Have_CPkt >= 0) { + if (dbug.db_atp) + fprintf(stderr, "atp: cacheing XO req packet\n"); + /* + * clear purge timer if we have + * an old cache packet + * + */ + if (Have_CPkt) + remTimeout(CPkt_timeout, (caddr_t)1); + CPkt_skt = skt; + CPkt_atp = *atp; + bcopy(databuf, (char *)CPkt_buf, dblen); + CPkt_buflen = dblen; + CPkt_addr = *addr; + Have_CPkt = 1; + /* + * set purge timer, retransmit + * rate is 2 seconds + * + */ + Timeout(CPkt_timeout, (caddr_t)1, sectotick(2)-1); + } + return(FALSE); /* drop pkt */ + } +#else ATPREQCACHE + if ((rqcb = find_rqcb(skt)) == NULL) + return(FALSE); /* drop pkt */ +#endif ATPREQCACHE + + /* + If packet's XO bit is set then create a RspCB and start its + release timer + */ + if (atp->control & atpXO) { + rspcb = create_rspcb(skt, atp->transID, addr); + if (dbug.db_atp) + fprintf(stderr,"atp: XO: created rspcb %x on socket %d, TID %d\n", + rspcb, skt, ntohs(atp->transID)); + if (atpresptimeout) + Timeout(rsptimeout, rspcb, atpresptimeout); + } + + /* Notify the client about the arrival of the request and destroy */ + /* the corresponding RqCB */ + + ap = &rqcb->abr->proto.atp; /* handle on the protocol */ + + ap->atpAddress = *addr; /* copy address for user */ + ap->atpBitMap = atp->bitmap; + ap->atpTransID = atp->transID; + ap->atpUserData = atp->userData; + ap->fatpXO = (atp->control & atpXO) ? 1 : 0; + ap->atpActCount = min(ap->atpReqCount, dblen); + /* NULL dataPtr just means he doesn't care about the data - just header */ + if (ap->atpDataPtr && databuf) + bcopy(databuf,ap->atpDataPtr,ap->atpActCount); + else + ap->atpActCount = 0; + rqcb->abr->abResult = noErr; + delete_rqcb(rqcb); + return(TRUE); +} + +/* + * handle_release + * + * handle in incoming ATP release packet + * +*/ +private boolean +handle_release(skt, tid, addr) +int skt; +int tid; +AddrBlock *addr; +{ + RspCB *rspcb; + + if (dbug.db_atp) + fprintf(stderr,"atp: removing rspcb for trel on skt %d, TID %d\n", + skt, ntohs(tid)); + + rspcb = find_rspcb(skt, tid, addr); + if (rspcb == NULL) { /* nothing to do */ + if (dbug.db_atp) + fprintf(stderr, + "atp: rqcb_listener: atp rel on skt %d, TID %d, with no rspcb\n", + skt, ntohs(tid)); + return(FALSE); + } + if (rspcb->abr != NULL) /* never tried to respond... */ + rspcb->abr->abResult = noErr; /* completed */ + remTimeout(rsptimeout, rspcb); + delete_rspcb(rspcb); /* remove it from the list */ + return(TRUE); +} + +/* + * handle_response + * + * handle an incoming ATP response packet + * +*/ +private boolean +handle_response(skt, atp, databuf, dblen, addr) +int skt; +ATP *atp; +char *databuf; +int dblen; +AddrBlock *addr; +{ + TCB *tcb; + int seqno; + BDSPtr bds; + atpProto *ap; + + /* a) find matching TCB */ + /* b) no matching tcb means drop pkt */ + if ((tcb = find_tcb(skt, atp->transID)) == NULL) { + if (dbug.db_atp) + fprintf(stderr, "atp: no matching tid for response tid %d\n"); + return(FALSE); /* drop packet */ + } + + ap = &tcb->abr->proto.atp; + + if (bcmp(addr, &ap->atpAddress, sizeof(AddrBlock)) != 0) { + if (dbug.db_atp) { + fprintf(stderr, "atp: security: response not from requested address\n"); + fprintf(stderr, "atp: expected: [net %d.%d, node %d, skt %d]\n", + nkipnetnumber(ap->atpAddress.net), + nkipsubnetnumber(ap->atpAddress.net), + ap->atpAddress.node, ap->atpAddress.skt); + } + return(FALSE); /* drop packet */ + } + + /* Check pkt is expected by checking pkt sequence no. against bitmap */ + if (((1 << atp->bitmap) &tcb->atp.bitmap) == 0) { + if (dbug.db_atp) + fprintf(stderr, + "atp: response sequence %d not expected or already received\n", + atp->bitmap); + return(FALSE); /* drop packet */ + } + + /* Clear corresponding bit in bitmap to note we got the packet */ + tcb->atp.bitmap &= ~(1<bitmap); /* okay, got our packet */ + ap->atpNumRsp++; /* increment count */ + + /* EOM - don't expect any pkts with higher sequence number */ + if (atp->control & atpEOM) + tcb->atp.bitmap &= ~((0xff >> atp->bitmap) << atp->bitmap); + + seqno = atp->bitmap; /* get sequence number */ + remTimeout(tcb_timeout,tcb); /* remove timeout... */ + + /* move data into correct response buffer */ + /* error checking is minimal, but keeps code from core dumping */ + /* don't check seqno against numbufs - all okay if bitmap matches */ + /* numbufs okay (should check at call time, but tuff) */ + bds = &(tcb->abr->proto.atp.atpRspBDSPtr[seqno]); + bds->userData = atp->userData; + bds->dataSize = dblen; /* set size to what came in */ + if (bds->buffPtr && databuf) /* keep us from doing bad things */ + /* but only copy what fits */ + bcopy(databuf,bds->buffPtr,min(bds->buffSize, dblen)); + else + bds->dataSize = 0; + /* This will probably cause problems, but if we have a incoming pkt */ + /* that is larger than the bds size, then we simply truncate the incoming */ + /* pkt to bds->buffSize, but record the actual length */ + if (dbug.db_atp) + fprintf(stderr,"atp: resp: tid %d, seqno %d, len %d\n", + ntohs(tcb->atp.transID), seqno, bds->dataSize); + + if (atp->control & atpSTS) { /* handle status control */ + if (dbug.db_atp) + fprintf(stderr, "atp: handle_response: sts set, resending request\n"); + atpreqsend(tcb); /* by resending request */ + } + + /* bitmap = 0 means that we are all done */ + if (tcb->atp.bitmap == 0) { + ap->fatpEOM = atp->control & atpEOM ? 1 : 0; + tcb->abr->abResult = noErr; + /* handle XO with trel's */ + if (ap->fatpXO) + atprelsend(tcb); + delete_tcb(tcb); + return(TRUE); + } + + Timeout(tcb_timeout,tcb,ap->atpTimeOut); /* else new timer */ + return(FALSE); +} + +/* + * atpreqsend + * + * Send the request packet specified by the current TCB. + * +*/ +private OSErr +atpreqsend(tcb) +TCB *tcb; +{ + atpProto *ap; + + if (dbug.db_atp) + fprintf(stderr,"atp: Sending request: tid %d\n",ntohs(tcb->atp.transID)); + + ap = &tcb->abr->proto.atp; + tcb->atp.control = atpReqCode | (ap->fatpXO ? atpXO : 0); + return(ATPWrite(ap,&tcb->atp,ap->atpDataPtr,ap->atpReqCount)); +} + +/* + * atprelsend + * + * Send a release on current request specified by the TCB + * + * Assumes that we need not send data if any was associated with packet. +*/ +private OSErr +atprelsend(tcb) +TCB *tcb; +{ + atpProto *ap; + + if (dbug.db_atp) + fprintf(stderr,"atp: Sending rel: tid %d\n",ntohs(tcb->atp.transID)); + + ap = &tcb->abr->proto.atp; + tcb->atp.control = atpRelCode; /* release on request */ + return(ATPWrite(ap, &tcb->atp, (char *)0, 0)); +} + + +/* + * + * atpxmitres - transmit response + * + * Send back a response to a request. Send only responses specified + * by the bitmap + * +*/ +private OSErr +atpxmitres(abr, bitmap) +ABusRecord *abr; +BitMapType bitmap; +{ + int i, err; + BDS *bds; + ATP atp; + atpProto *atpproto; + + atpproto = &abr->proto.atp; + atp.control = atpRspCode; /* mark as response */ + atp.transID = atpproto->atpTransID; /* give TID */ + for (i=0; i < (int)atpproto->atpNumBufs; i++) { + if ( ( (bitmap >> i) & 0x1) == 0) + continue; + if (i==(atpproto->atpNumBufs-1)) + atp.control |= (atpproto->fatpEOM) ? atpEOM : 0; + atp.bitmap = i; /* sequence */ + bds = &atpproto->atpRspBDSPtr[i]; + atp.userData = bds->userData; + err = ATPWrite(atpproto,&atp,bds->buffPtr,bds->buffSize); + if (err < 0) + return(err); + } + return(0); +} + + + +/* + * abatpaux.c - Appletalk Transaction Protocol Auxillary routines + * + * Provides management of: + * o RspCB (response control block) + * o Atp responding sockets + * o Request control blocks + * o Transmission Control Blocks + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986 by The Trustees of Columbia University in the + * City of New York. + * + * + * Edit History: + * + * June 30, 1986 CCKim Created + * Aug 1, 1986 CCKim Make p-v on sockets history + * March 1986 CCKim Merge into abatp module... + * +*/ + +#ifdef notdef +#include +#include +#include +#include +#include +#include +#include "abatp.h" +#endif +/* + * ATPSKT management routines - used to manage the sockets + * opened by ATPOpenSocket. + * + * Dynamically allocate new space for sockets in multiples of NUMATPSKT + * +*/ +private AtpSkt *atpsktlist = NULL; +private int numatpskt = 0; + +/* + * Establish an ATP responding socket block + * +*/ +private AtpSkt * +create_atpskt(skt, raddr) +int skt; +AddrBlock *raddr; +{ + int i; + char *calloc(); + AtpSkt *atpskt; + register AtpSkt *ap; + + for (i=0; i < numatpskt; i++) + if (atpsktlist[i].inuse == 0) + break; + if (i == numatpskt) { + /* make more, use calloc() instead of realloc() */ + if ((ap = (AtpSkt *)calloc(numatpskt+NUMATPSKT, sizeof(AtpSkt))) == NULL) + return(NULL); + if (numatpskt > 0) + bcopy((char *) atpsktlist, (char *)ap, numatpskt*sizeof(AtpSkt)); + if (atpsktlist != NULL) + free((char *) atpsktlist); + atpsktlist = ap; + numatpskt += NUMATPSKT; + } + atpskt = &atpsktlist[i]; + atpskt->inuse = 1; /* true! */ + atpskt->skt = skt; + atpskt->addr = *raddr; + atpskt->usecount = 0; + return(atpskt); +} + +/* + * Find a ATP responding socket and return the address of the block + * if the address mask of the socket specified in the ATPOpenSocket + * allows receiving requests from the specified remote address (raddr) + * Returns NULL o.w. +*/ +private AtpSkt * +find_atpskt(skt, raddr) +int skt; +AddrBlock *raddr; +{ + int i; + AddrBlock *ab; + + for (i=0; i < numatpskt; i++) + if (atpsktlist[i].inuse != 0 && skt == atpsktlist[i].skt) + break; + if (i==numatpskt) + return(NULL); + ab = &atpsktlist[i].addr; + if ((ab->net == 0 || ab->net == raddr->net) && + (ab->node==0 || ab->node == raddr->node) && + (ab->skt==0 || ab->skt==raddr->skt)) + return(&atpsktlist[i]); + else return(NULL); +} + +/* + * remove a ATP responding socket block + */ +private int +delete_atpskt(skt) +int skt; +{ + int i; + for (i=0; i < numatpskt; i++) + if (atpsktlist[i].inuse && atpsktlist[i].skt == skt) { + atpsktlist[i].inuse = 0; + if (dbug.db_atp) + fprintf(stderr,"atp: delete_atpskt: deleting socket %d\n",skt); + return(0); + } + return(-1); +} + + +/* + * Response Control Block handling routines + * + * Organized as a hash table with lists hanging off the buckets + * + * Assumptions: the hash function assumes that skt's tend to be "clustered" + * +*/ +/* RSPCBLIST is a hash list with one bucket for each socket at present */ +/* Bucket lists are not ordered */ +private QElemPtr rspcblist[NUMRspCB]; +private QElemPtr rspcb_free; /* list of free items */ +#define rspcb_hash(skt) ((skt) % NUMRspCB) + +/* + * Create a rspcb and insert it into rspcblist at the access point + * defined by socket. + * + * Doesn't check to see if the rspcb already exists +*/ +private RspCB * +create_rspcb(skt, tid, raddr) +int skt, tid; +AddrBlock *raddr; +{ + RspCB *rspcb; + + if ((rspcb = (RspCB *)dq_head(&rspcb_free)) == NULL && + (rspcb = (RspCB *) malloc(sizeof(RspCB))) == NULL) { + fprintf(stderr,"atp: panic: create_rspcb: out of memory\n"); + exit(8); + } + + if (dbug.db_atp) + fprintf(stderr,"atp: create_rspcb: create %x\n",rspcb); + + rspcb->atpTransID = tid; + if (raddr) /* dummy rspcb doesn't have */ + rspcb->atpAddress = *raddr; /* save remote address */ + rspcb->atpsocket = skt; /* remember this */ + rspcb->callback = NULL; + rspcb->cbarg = (caddr_t)0; + rspcb->abr = NULL; + q_tail(&rspcblist[rspcb_hash(skt)], &rspcb->link); /* add to queue */ + return(rspcb); +} + +/* + * remove a rspcb from the active list + * +*/ +private +delete_rspcb(rspcb) +RspCB *rspcb; +{ + if (dbug.db_atp) + fprintf(stderr,"atp: delete_rspcb: deleting %x\n",rspcb); + + dq_elem(&rspcblist[rspcb_hash(rspcb->atpsocket)], &rspcb->link); + if (rspcb->callback != NULL) + (*rspcb->callback)(rspcb->abr, rspcb->cbarg); + q_tail(&rspcb_free, &rspcb->link); /* add to free list */ +} + +/* + * Find the rscb corresponding to the specified TID and raddr + * +*/ +struct rspcb_match_info { + int skt, tid; + AddrBlock *addr; +}; + +private boolean +match_rspcb(rspcb, info) +RspCB *rspcb; +struct rspcb_match_info *info; +{ + return(info->skt == rspcb->atpsocket && + info->tid == rspcb->atpTransID && + bcmp(info->addr, &rspcb->atpAddress, sizeof(AddrBlock)) == 0); + +} + +private RspCB * +find_rspcb(skt, tid, raddr) +int skt, tid; +AddrBlock *raddr; +{ + struct rspcb_match_info info; + + info.tid = tid, info.skt = skt, info.addr = raddr; + return((RspCB *)q_mapf(rspcblist[rspcb_hash(skt)], match_rspcb, &info)); +} + +/* + * Find the rscb corresponding to the specified abr + * +*/ +private boolean +match_rspcb_abr(rspcb, abr) +RspCB *rspcb; +ABusRecord *abr; +{ + return(abr == rspcb->abr); +} + +private RspCB * +find_rspcb_abr(abr) +ABusRecord *abr; +{ + + return((RspCB *)q_mapf(rspcblist[rspcb_hash(abr->proto.atp.atpSocket)], + match_rspcb_abr, abr)); +} + +/* + * find any rspcb associated with the specified socket + * +*/ +private boolean +match_rspcb_skt(rspcb, skt) +RspCB *rspcb; +void *skt; +{ + int sk = (int)skt; + return(sk == rspcb->atpsocket); +} + +private RspCB * +find_rspcb_skt(skt) +int skt; +{ + return((RspCB *)q_mapf(rspcblist[rspcb_hash(skt)], match_rspcb_skt, + (void *)skt)); +} + + + +/* + * Request control block handling routines + * + * Organized as a hash table with queues off each bucket + * +*/ + +/* RQCBLIST is a hash list with one bucket for each socket at present */ +/* Bucket lists are not ordered */ + +private QElemPtr rqcblist[NUMRqCB]; +private QElemPtr rqcb_free; /* list of free items */ +#define rqcb_hash(skt) ((skt) % NUMRqCB) + +/* + * Create a rqcb and insert it into rqcblist at the access point + * defined by socket. + * + * Doesn't check to see if the rqcb already exists +*/ +private RqCB * +create_rqcb(skt, abr, callback, cbarg) +int skt; +ABusRecord *abr; +int (*callback)(); +caddr_t cbarg; +{ + RqCB *rqcb; + + if ((rqcb = (RqCB *)dq_head(&rqcb_free)) == NULL && + (rqcb = (RqCB *) malloc(sizeof(RqCB))) == NULL) { + fprintf(stderr,"atp: panic: create_rqcb: out of memory\n"); + exit(8); + } + + if (dbug.db_atp) + fprintf(stderr,"atp: creat_rqcb: create %x\n",rqcb); + + rqcb->atpsocket = skt; + rqcb->abr = abr; + rqcb->callback = callback; + rqcb->cbarg = cbarg; + q_tail(&rqcblist[rqcb_hash(skt)], &rqcb->link); /* add to queue */ + return(rqcb); +} + +/* + * Find the first RqCB found for the socket + * + */ + +private boolean +match_rqcb_abr(rqcb, abr) +RqCB *rqcb; +ABusRecord *abr; +{ + return(abr == rqcb->abr); +} + +private RqCB * +find_rqcb_abr(abr) +ABusRecord *abr; +{ + return((RqCB *)q_mapf(rqcblist[rqcb_hash(abr->proto.atp.atpSocket)], + match_rqcb_abr, abr)); +} + +private boolean +match_rqcb(rqcb, skt) +RqCB *rqcb; +int skt; +{ + return(skt == rqcb->atpsocket); +} + +private RqCB * +find_rqcb(skt) +int skt; +{ + return((RqCB *)q_mapf(rqcblist[rqcb_hash(skt)], match_rqcb, (void *)skt)); +} + +/* + * remove the specified rqcb from the active list + * + */ + +private +delete_rqcb(rqcb) +RqCB *rqcb; +{ + + if (dbug.db_atp) + fprintf(stderr,"atp: delete_rqcb: deleting %x\n",rqcb); + + dq_elem(&rqcblist[rqcb_hash(rqcb->atpsocket)], &rqcb->link); + if (rqcb->callback != NULL) + (*rqcb->callback)(rqcb->abr, rqcb->cbarg); + q_tail(&rqcb_free, &rqcb->link); /* add to free list */ +} + + + + +/* + * TCBlist is a simple list of items + * +*/ + +private QElemPtr tcblist; +private QElemPtr tcb_free; + +private u_short next_TID = 0; /* 16 bits of tids */ +private int tidded = 0; /* have we randomized the tid yet? */ + +private TCB * +create_tcb(skt, abr, callback, cbarg) +int skt; +ABusRecord *abr; +int (*callback)(); +caddr_t cbarg; +{ + TCB *tcb; + atpProto *atpproto; + int i; + + if ((tcb = (TCB *)dq_head(&tcb_free)) == NULL && + (tcb = (TCB *) malloc(sizeof(TCB))) == NULL) { + fprintf(stderr,"atp: panic: create_tcb: out of memory\n"); + exit(8); + } + + if (dbug.db_atp) + fprintf(stderr,"atp: create_tcb: creating %x\n",tcb); + + tcb->abr = abr; + atpproto = &abr->proto.atp; + if (tidded) { + for (i=0; i < 65536; i++) { + next_TID = (u_short)((int)next_TID+1) % 65535; /* mod 2^16-1 */ + if (find_tcb(skt, next_TID) == NULL) + break; + } + if (i==65536) { + fprintf(stderr,"atp: Fatal error:\n"); + fprintf(stderr,"atp: All TIDs are in use, this is highly improbable\n"); + exit(9); + } + } else { + /* + * randomly get first tid (preferably not numerically + * close to another process started at the same time) + * + */ + next_TID = (time(0) % 65535) ^ ((getpid() & 0xff) << 8); + tidded = TRUE; + } + tcb->atp.transID = htons(next_TID); + tcb->atp.bitmap = 0xff >> (8-atpproto->atpNumBufs); + tcb->atp.userData = atpproto->atpUserData; + tcb->callback = callback; + tcb->cbarg = cbarg; + tcb->skt = skt; + q_tail(&tcblist, &tcb->link); /* add to queue */ + return(tcb); +} + + +struct tcb_match_info { + int tid; + int skt; +}; + +private boolean +match_tcb(tcb, info) +TCB *tcb; +struct tcb_match_info *info; +{ + return(info->tid == tcb->atp.transID && info->skt == tcb->skt); +} + +private TCB * +find_tcb(skt, tid) +int skt; +int tid; +{ + struct tcb_match_info info; + info.tid = tid, info.skt = skt; + return((TCB *)q_mapf(tcblist, match_tcb, &info)); +} + + +private boolean +match_tcb_abr(tcb, abr) +TCB *tcb; +ABusRecord *abr; +{ + return(abr == tcb->abr); +} + +private TCB * +find_tcb_abr(abr) +ABusRecord *abr; +{ + return((TCB *)q_mapf(tcblist, match_tcb_abr, abr)); +} + +private +delete_tcb(tcb) +TCB *tcb; +{ + if (dbug.db_atp) + fprintf(stderr,"atp: delete_tcb: deleting %x\n",tcb); + + dq_elem(&tcblist, &tcb->link); + if (tcb->callback != NULL) + (*tcb->callback)(tcb->abr, tcb->cbarg); + q_tail(&tcb_free, &tcb->link); /* add to free list */ +} + +private boolean +match_tcb_skt(tcb, skt) +TCB *tcb; +int skt; +{ + return(skt == tcb->skt); +} + +private +delete_tcb_skt(skt) +int skt; +{ + TCB *tcb; + + while ((tcb = (TCB *)q_mapf(tcblist, match_tcb_skt, (void *)skt)) != NULL) { + if (dbug.db_atp) + fprintf(stderr,"atp: delete_tcb_skt: deleting %x\n",tcb); + dq_elem(&tcblist, &tcb->link); + q_tail(&tcb_free, &tcb->link); /* add to free list */ + } +} + + + + +private int +ATPWrite(ap,atp,dp,dl) +atpProto *ap; +ATP *atp; +char *dp; +int dl; +{ + ABusRecord abr; + ddpProto *ddpr; + struct iovec iov[IOV_ATPU_SIZE]; /* io vector upto atp user level */ + int lvl; + + ddpr = &abr.proto.ddp; + ddpr->ddpType = ddpATP; + ddpr->ddpSocket = ap->atpSocket; + ddpr->ddpAddress = ap->atpAddress; + ddpr->ddpReqCount = dl+atpSize; + + iov[IOV_ATP_LVL].iov_base = (caddr_t) atp; + iov[IOV_ATP_LVL].iov_len = atpSize; + lvl = IOV_ATP_LVL; + /* Don't include a data element if there is none - 4.2 doesn't like it */ + if (dl > 0) { + lvl++; + iov[lvl].iov_base = (caddr_t) dp; + iov[lvl].iov_len = dl; + } + lvl++; /* make offset into count */ + return(DDPWriteIOV(&abr,iov,lvl)); +} + + diff --git a/lib/cap/abatp.h b/lib/cap/abatp.h new file mode 100644 index 0000000..437a73f --- /dev/null +++ b/lib/cap/abatp.h @@ -0,0 +1,121 @@ +/* + * $Author: djh $ $Date: 1996/03/11 09:47:07 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abatp.h,v 2.3 1996/03/11 09:47:07 djh Rel djh $ + * $Revision: 2.3 $ + * + */ + +/* + * abatp.c - Appletalk Transaction Protocol header file (internal only) + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * June 30, 1986 CCKim Created + * + */ + +#define atpheaderlength (atpSize+lapSize+ddpSize) + + +#define atpCodeMask 0xc0 /* to get controls */ +#define atpReqCode 0x40 +#define atpRspCode 0x80 +#define atpRelCode 0xC0 +#define atpXO 0x20 +#define atpEOM 0x10 +#define atpSTS 0x08 +#define atpSendChk 0x01 +#define atpTIDValid 0x02 +#define atpFlagMask 0x3F +#define atpControlMask 0xF8 + +typedef struct { + byte lapddp[lapSize+ddpSize]; + ATP atp; +} ATPpkt; + + +typedef struct TCB { + QElem link; + ATP atp; /* atp header */ + int skt; /* local side socket */ + int (*callback)(); + caddr_t cbarg; /* call back argument */ + ABusRecord *abr; +} TCB; + +#define NUMTCB ddpMaxSkt /* max connections */ + + +/* + * Request Control Block + * +*/ + +typedef struct { + QElem link; /* point to queue header */ + int atpsocket; /* socket request went out on */ + ABusRecord *abr; /* ABusRecords */ + int (*callback)(); + caddr_t cbarg; /* call back argument */ +} RqCB; + +#define NUMRqCB 3 /* should suffice */ + +/* + * Response Control Block + * + * Note: we don't need to copy the reponse data because the sndresponse + * routines will not complete until the rel packet is received or + * we get rscb timeout. Thus, the user MUST NOT reuse the buffers until + * the given routine completes! + * +*/ +typedef struct { + QElem link; /* point to queue header */ + struct timeval ctime; /* time created */ + int atpsocket; /* socket of rsp */ + int atpTransID; /* requesting transaction id */ + AddrBlock atpAddress; /* address of remote */ + ABusRecord *abr; /* pointer to abus record */ + int (*callback)(); + caddr_t cbarg; /* call back argument */ +} RspCB; + +#define NUMRspCB 20 + +#define RESPONSE_CACHE_TIMEOUT 4*30 /* timeout is 30 seconds */ + +typedef struct { + int inuse; /* zero if not */ + int skt; + AddrBlock addr; /* filter */ + int usecount; /* times in use */ +} AtpSkt; + +#define NUMATPSKT 5 /* up to 5 responding circuits */ + +private RspCB *find_rspcb(); +private RspCB *find_rspcb_abr(); +private RspCB *create_rspcb(); +private RspCB *find_rspcb_skt(); +private int delete_rspcb(); + +private TCB *create_tcb(); +private delete_tcb(); +private TCB *find_tcb(); +private TCB *find_tcb_abr(); + +private RqCB *create_rqcb(); +private RqCB *find_rqcb_abr(); +private RqCB *find_rqcb(); +private delete_rqcb(); + +private AtpSkt *create_atpskt(); +private AtpSkt *find_atpskt(); +private int delete_atpskt(); diff --git a/lib/cap/abauxddp.c b/lib/cap/abauxddp.c new file mode 100644 index 0000000..87fd18e --- /dev/null +++ b/lib/cap/abauxddp.c @@ -0,0 +1,552 @@ +/* + * $Author: djh $ $Date: 1993/11/29 06:48:59 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abauxddp.c,v 2.2 1993/11/29 06:48:59 djh Rel djh $ + * $Revision: 2.2 $ + * + */ + +/* + * abauxddp.c - Datagram Delivery Protocol for native appletalk under A/UX + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 19, 1986 Schilit Created. + * July 9, 1986 CCKim Clean up some of Bill's stuff and allow + * Appletalk protocols on ethernet with CAP + * Feb. 1988 Charlie - don't like the way the encapsulation code runs + * into the DDP code. Drop out all the encapsulation code into + * another module (interface dep) and drop out part of DDP into it. + * + * 1990 William Roberts Add support for A/UX native appletalk + * + */ + +#include +#include +#include + +/* Use the direct Apple support for ddp. We insist on a long form + * DDP header, and we can open our own file descriptor instead of + * calling abOpen to do it for us. iDDPOpenSocketIOV and DDPClose + * are modified to set up the fdlistener stuff that abOpen/abClose + * used to deal with. + */ +#include +#include +#include +#include /* for strioctls */ +#include /* for ntohs */ + +/* Fortunately the CAP include and the Apple ones don't conflict */ + +#include /* for private, boolean etc */ +#include "cap_conf.h" + +#ifndef DONT_DOCHKSUM +# define DO_CHKSUM 1 +#else +# define DO_CHKSUM 0 +#endif +boolean dochecksum = DO_CHKSUM; /* can be patched if necessary */ + +short lap_proto = LAP_KERNEL; /* kernel appletalk support */ + +extern int errno; + +/* room for up to ddp + 1 for data buffer */ +#define DDPIOVLEN (IOV_DDP_SIZE+1) +private DDP ddph; +private byte ddpdatabuffer[ddpMaxData]; +private struct iovec rddpiov[DDPIOVLEN] = { + {NULL, 0}, /* LAP header */ + {(caddr_t)&ddph, ddpSize}, /* DDP header (me) (redundant) */ + {(caddr_t)ddpdatabuffer, ddpMaxData} /* ddp data */ +}; + +typedef struct { + int (*lproc)(); /* socket listener routine */ + int flags; /* flags */ +#define DDPL_OLDINTERFACE 1 + int fd; /* DDP file descriptor */ +} LISENTRY; /* listener entry */ + +private LISENTRY ddpl[ddpMaxSkt+1]; /* table of listeners */ + + +/* abInit(disp) + * + * Get things started and find out our Node and Net + * Maybe we should wait until later in case a seed bridge starts up? + */ + +export word this_net; +export byte this_node; +export byte this_intfno; +export word bridge_net; /* do we care about this any more? */ +export byte bridge_node; +export word nis_net; /* do we care about this any more? */ +export byte nis_node; +export char this_zone[34]; +export char async_zone[34]; +export char this_intf[50]; + +export DBUG dbug; + +abInit(disp) +int disp; +{ + int fd, x; + at_ddp_cfg_t cfg; + at_socket socket; + struct strioctl si; + + init_fdlistening(); + DDPInit(); + + /* find out our address */ + + socket = 0; /* don't care */ + fd = ddp_open(&socket); + if (fd < 0) { + perror("ddp_open"); + exit(0); + } + + si.ic_cmd = DDP_IOC_GET_CFG; + si.ic_timout = 1; + si.ic_len = sizeof(cfg); + si.ic_dp = (char *)&cfg; + + x = ioctl(fd, I_STR, &si); + if (x < 0) { + perror("abInit: strioctl failed"); + exit(-1); + } + + if (disp || dbug.db_lap) { + printf("abInit: [ddp %3d.%02d, %d] starting\n", + ntohs(cfg.node_addr.net)>>8, + ntohs(cfg.node_addr.net)&0x0FF, + cfg.node_addr.node); + } + this_net = cfg.node_addr.net; + this_node = cfg.node_addr.node; + bridge_net = cfg.router_addr.net; + bridge_node = cfg.router_addr.node; + + x = ddp_close(fd); + if (x < 0) { + perror("ddp_close"); + exit(0); + } +} + +OSErr +GetNodeAddress(mynode, mynet) +int *mynode, *mynet; +{ + *mynode = this_node; + *mynet = this_net; + return (noErr); +} + +/* + * add these for compatibility with other changes ... + * + */ + +GetMyAddr(addr) +AddrBlock *addr; +{ + addr->net = this_net; + addr->node = this_node; +} + +SetMyAddr(addr) +AddrBlock *addr; +{ + this_net = addr->net; + this_node = addr->node; +} + +GetNisAddr(addr) +AddrBlock *addr; +{ + addr->net = nis_net; + addr->node = nis_node; +} + +SetNisAddr(addr) +AddrBlock *addr; +{ + nis_net = addr->net; + nis_node = addr->node; +} + + +DDPInit() +{ + int i; + + /* initialize ddp listener array */ + for (i = 0; i < ddpMaxSkt+1; i++) { + ddpl->lproc = NILPROC; + ddpl->flags = 0; + } + +} + + +/* + * OSErr DDPOpenSocket(int *skt, ProcPtr sktlis) + * OSErr DDPOpenSocketIOV(int *skt,ProcPtr sktlis,struct iovec *iov,int iovlen) + * + * Open a DDP socket and optionally install a socket listener to the + * listener table. If skt is nonzero (it must be in the range of + * 1 to 127) it specifies the socket's number. If skt is 0 then + * DDPOpenSocket dynamically assigns a socket number in the range + * 128 to 254, and returns it in skt. You can actually specify a socket + * in the 128 to 254 range, but it's not a good idea :-). + * + * sktlis contains a pointer (ProcPtr) to the socket listener; if + * it is NILPROC, the default listener will be used (NYI) + * + * If calling DDPOpenSocketIOV, then the iovector passed must be of + * size (IOV_DDP_SIZE+1) (length for ddp+lap) plus one (data for caller). + * In addition, it must be filled in from DDP_LVL+1 to the end + * + * + * The listener is defined as: + * XXX_listener(int skt, PKT *pkt, int len, AddrBlock *addr) + * if called from DDPOpenSocket and: + * XXX_listener(int skt, struct iovec *iov, int iovlen, AddrBlock *addr) + * if called from DDPOpenSocketIOV + * + * The iov passed back to the listener will start after the ddp header + * block + * +*/ +OSErr +DDPOpenSocketIOV(skt, sktlis, iov, iovlen) +int *skt; +ProcPtr sktlis; +struct iovec *iov; +int iovlen; +{ + int fd; + at_socket s; + int defDDPlis(); /* which always fails anyway - clearly a throwback */ + int ddp_upcall(); + + /* allow 0 - means dynamic assignment */ + s = *skt; /* socket wanted */ + if (s >= ddpMaxSkt) { + fprintf(stderr,"ddpOpenSocket: skt out of range\n"); + exit(0); + } + /* open the socket please */ + if ((fd = ddp_open(&s)) < 0) { + if (dbug.db_ddp) + fprintf(stderr,"ddp: open socket - socket open failed: %d\n", errno); + return(fd); /* return error if failed */ + } + *skt = s; /* socket number actually obtained */ + if (dbug.db_ddp) { + fprintf(stderr, "ddp: open socket: opened socket %d\n", s); + } + + iov[IOV_DDP_LVL].iov_base = (caddr_t)&ddph; /* install */ + iov[IOV_DDP_LVL].iov_len = ddpSize; /* install */ + + /* add default or user's listener */ + ddpl[s].lproc = ((sktlis == NILPROC) ? defDDPlis : sktlis); + ddpl[s].flags = 0; + ddpl[s].fd = fd; + + /* add the file descriptor to the list of those listened for */ + fdlistener(fd, ddp_upcall, iov+IOV_DDP_LVL, iovlen-IOV_DDP_LVL); + + return(noErr); /* and return */ +} + + +OSErr +DDPOpenSocket(skt,sktlis) +int *skt; +ProcPtr sktlis; +{ + OSErr err; + + err = DDPOpenSocketIOV(skt, sktlis, rddpiov, DDPIOVLEN); + if (err == noErr) { + ddpl[*skt].flags = DDPL_OLDINTERFACE; + } + return(err); +} + + +/* + * OSErr DDPCloseSocket(int skt) + * + * DDPCloseSocket closes the skt, cancels all pending DDPRead calls + * that have been made on that socket, and removes the socket listener + * procedure. + * +*/ + +OSErr +DDPCloseSocket(skt) +int skt; +{ + if (skt == 0 || skt >= ddpMaxSkt) { + if (dbug.db_ddp) + fprintf(stderr, "ddpCloseSocket: Socket %d out of range\n", skt); + return; + } + ddpl[skt].lproc = NILPROC; /* no procedure */ + + fdunlisten(ddpl[skt].fd); /* stop listening to the socket */ + return(ddp_close(ddpl[skt].fd)); +} + +OSErr +DDPWrite(abr) +abRecPtr abr; +{ + struct iovec iov[IOV_DDP_SIZE+1]; + + iov[IOV_DDP_LVL+1].iov_base = (caddr_t) abr->proto.ddp.ddpDataPtr; + iov[IOV_DDP_LVL+1].iov_len = abr->proto.ddp.ddpReqCount; + + return(DDPWriteIOV(abr,iov,IOV_DDP_SIZE+1)); +} + +/* + * DDPWriteIOV + * + * DDPWriteIOV builds up DDP header and then passes off to routeddp + * who decides where to send it. In the most cases, we'll probably + * have a version of routeddp per "network" type so we can "optimize" + * +*/ +/*ARGSUSED*/ +OSErr +DDPWriteIOV(abr,iov,iovl) +abRecPtr abr; +struct iovec iov[]; +int iovl; +{ + at_ddp_t ddp; + ddpProto *dpr; + at_socket skt; + int err; + + dpr = &abr->proto.ddp; + skt = dpr->ddpSocket; /* our socket number */ + + if (skt == 0 || skt >= ddpMaxSkt || ddpl[skt].lproc == NILPROC) { + return -1; + } + + /* the DDP streams driver fills in everything else given + * just checksum, dst_net, dst_node, dst_socket, type and data. + * The length is inferred from the write or writev command. + */ + ddp.dst_net = dpr->ddpAddress.net; + ddp.dst_node = dpr->ddpAddress.node; + ddp.dst_socket = dpr->ddpAddress.skt; + ddp.type = dpr->ddpType; + ddp.checksum = dochecksum; /* driver computes the correct value */ + + /* The DDP streams driver deals with LAP headers etc, so must + * adjust the iov to point to the start of the DDP stuff + */ + iov += IOV_DDP_LVL; iovl -= IOV_DDP_LVL; + + iov->iov_base = (caddr_t) &ddp; /* DDP header */ + iov->iov_len = ddpSize; + + err = writev(ddpl[skt].fd, iov, iovl); + if (err <= 0) { + if (dbug.db_ddp) { + perror("ddp_write failed"); + } + return -1; + } + return 0; +} + +/*ARGSUSED*/ +OSErr +DDPRead(abr,retCkSumErrs,async) +abRecPtr abr; +int retCkSumErrs,async; +{ + fprintf(stderr,"DDPRead NYI\n"); +} + +OSErr +DDPRdCancel(abr) +abRecPtr abr; +{ + fprintf(stderr,"DDPRdCancel NYI\n"); +} + +defDDPlis(skt,ddp,len,addr) +DDP *ddp; +AddrBlock *addr; +{ + fprintf(stderr,"defDDPlis NYI\n"); +/***** copy data into user buffer *****/ +} + +/* + * ddp_protocol(ddp,len) + * + * ddp_protocol is the installed LAP protocol handler for protocol + * type lapDDP (2). This routine gets called by LAP when a packet + * is received with lapDDP in the protocol field. ddp_protocol + * passes the packet to the socket listener installed by the + * DDPOpenSocket call. + * + * With direct support for DDP, this routine is entered from the + * very simple ddp_upcall routine installed as the fdlistener for + * the appropriate file descriptor. + * + * Caller provides an iov pointing to DDP header +*/ + +int ddp_protocol(iov, iovlen, plen) +struct iovec *iov; +int iovlen; +int plen; +{ + at_socket skt; + AddrBlock addr; + int len, cnt, oldstyle; + at_ddp_t *ddp; + + /* iovlen == 1 means just a ddp header */ + if (iovlen < 1 || iov->iov_len != ddpSize) { + return -1; + } + + ddp = (at_ddp_t *)iov->iov_base; /* it must be aligned ok already*/ + len = ntohs(ddp->length) & 0x3ff; /* get the "real" length */ + if (plen < len) { /* not enought data? */ + if (dbug.db_ddp) + fprintf(stderr, "BAD PACKET: ddp reports more data than came in\n"); + return; /* drop pkt */ + } + + skt = ddp->dst_socket; + + if (ddpl[skt].lproc == NILPROC) { /* listener proc for socket */ + if (dbug.db_ddp) + fprintf(stderr,"ddp_protocol: no socket listener!\n"); + return; + } + + addr.net = ddp->src_net; + addr.node = ddp->src_node; + addr.skt = ddp->src_socket; + iov++; /* skip ddp header */ + iovlen--; + plen -= ddpSize; /* reduce to data size */ + if (iovlen < 1) { + return 0; /* nothing to send, so drop it */ + } + if (ddpl[skt].lproc) { /* be postitive */ + if (ddpl[skt].flags & DDPL_OLDINTERFACE) + (*ddpl[skt].lproc)(skt,ddp->type,iov->iov_base,plen,&addr); + else + (*ddpl[skt].lproc)(skt, ddp->type, iov, iovlen, plen, &addr); + } + return 0; +} + +private int ddp_upcall(fd, iov, iovlen) +int fd; +struct iovec *iov; +int iovlen; +{ + int nbytes; + + nbytes = readv(fd, iov, iovlen); + return (ddp_protocol(iov, iovlen, nbytes)); +} + + +/* + * Call with TRUE or FALSE - FALSE means ignore, TRUE means don't ignore + * +*/ +checksum_error(which) +boolean which; +{ + /* who cares - we certainly don't */ +} + + +/* Utility routine to sprintf an address in a standard format + * + * net_hi.net_lo,node socket + * + * It returns its first argument, for convenient use in printf. + */ +char *appletalk_ntoa(buf, addr) +char buf[]; +AddrBlock *addr; +{ + sprintf(buf, "%d.%02d,%d %d", + ((addr->net)>>8)& 0x0FF, (addr->net)& 0x0FF, + addr->node, addr->skt); + return buf; +} + +/* int ddp_skt2fd(skt) + * + * Converts a socket number into a file descriptor, or returns -1. + * Used by upper layers which have converted over to A/UX support, + * since CAP is otherwise keen only to pass around socket numbers. + */ + +int ddp_skt2fd(skt) +byte skt; +{ + if (skt > 0 && skt <= ddpMaxSkt && ddpl[skt].lproc != NILPROC) { + return ddpl[skt].fd; + } + + /* invlaid skt or no socket listener - presumed not open */ + return -1; +} + +/* + * avoid using global variables + * + */ + +OSErr +GetBridgeAddress(addr) +AddrBlock *addr; +{ + addr->net = bridge_net; + addr->node = bridge_node; + return(noErr); +} + +OSErr +SetBridgeAddress(addr) +AddrBlock *addr; +{ + bridge_net = addr->net; + bridge_node = addr->node; + return(noErr); +} diff --git a/lib/cap/abauxnbp.c b/lib/cap/abauxnbp.c new file mode 100644 index 0000000..bdb107d --- /dev/null +++ b/lib/cap/abauxnbp.c @@ -0,0 +1,582 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:46:46 $ + * $Header: abauxnbp.c,v 2.1 91/02/15 22:46:46 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * abauxnbp.c - nbp access for native appletalk under A/UX + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 13, 1986 Schilit Created + * June 15, 1986 CCKim move to abnbp.c, add extract + * July 1, 1986 Schilit rewrite with async and NBPConfirm + * July 9, 1986 CCKim Clean up and rewrite create_entity + * July 15, 1986 CCKim Add nbpregister, nbpdelete + * + * 1990 William Roberts Add support for A/UX native appletalk + */ + +#include +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else +# include +#endif + +/* Include files for A/UX NBP support + * + * at_nvestr_t corresponds to str32, but uses Pascal Strings + * Subject to that proviso, and the changes of field names, the + * sizes and layouts of the following are the same: + * + * at_entity_t matches EntityName + * at_inet_t matches AddrBlock + * at_nbptuple_t matches NBPTEntry + */ +#include +#include +#include +extern int errno; + +extern char *appletalk_ntoa(); + +private nbpProto *nbpQ; +private byte next_nbpid = 0; +private int nbpSkt = -1; + +/* Public functions */ + +OSErr nbpInit(); /* initialize NBP */ +OSErr NBPLookup(); /* lookup a name or group of names */ +OSErr NBPConfirm(); /* confirm a name/address pair */ +OSErr NBPExtract(); /* extract entity information after lookup */ + +void zoneset(); /* absorbed from atalkdbm.c */ + +/* Internal functions */ + +private OSErr nbpFcn(); /* common NBP function */ +private void SndNBP(); /* send request to appletalk */ +private void nbp_timeout(); /* timeout monitor */ +private void nbp_listener(); /* DDP listener process */ +private void LkUpReply(); /* handle LkUpReply response */ +private int nbp_match(); /* find matching request upon response */ +private int nbpcpy(); /* copy entity into user buffer */ +private int c2pkt_ename(); /* convert entity name from c to packed */ +private int pkt2c_ename(); /* convert entity name from packed to c */ +private int c2pas_ename(); /* convert entity name from c to pascal */ +private int pas2c_ename(); /* convert entity name from c to pascal */ + +/* + * OSErr nbpInit() + * + * Redundant under A/UX AppleTalk support. + */ + +OSErr +nbpInit() +{ + return noErr; +} + +void +zoneset(zonename) +char *zonename; +{ + /* ignored */ +} + +/* + * Close currently open NBP socket + * +*/ +OSErr +nbpShutdown() +{ + return(noErr); +} + +/* + * NBPLookup(nbpProto *pr,int async) + * + * NBPLookup returns the address of all entities with a specified + * name. The nbpProto structure contains + * + * nbpBufSize, nbpBufPtr - data area for result + * nbpDataField - number of entries to look for/entries found. + * nbpEntityName - name of the entity we are interested in + * + * Result code noErr or -1 (for some major failure). +*/ + +OSErr +NBPLookup(abr,async) +nbpProto *abr; +int async; +{ + int maxx; + at_entity_t lookup_ent; + at_nbptuple_t *ep; + at_retry_t retry_info; + EntityName *entp; + char buf[30]; /* for printing addresses */ + + c2pas_ename(abr->nbpEntityPtr, &lookup_ent); + + maxx = abr->nbpBufSize/sizeof(NBPTEntry); /* max entries */ + abr->nbpMaxEnt = /* set it up */ + (maxx < abr->nbpDataField) ? maxx : abr->nbpDataField; + abr->nbpDataField = 0; + + retry_info.interval = abr->nbpRetransmitInfo.retransInterval; + retry_info.retries = abr->nbpRetransmitInfo.retransCount; + retry_info.backoff = 1; /* ??? */ + + if (async) { + fprintf(stderr, "NBP: async NBPLookup not implemented\n"); + return -1; + } + + if (dbug.db_nbp) { + printf("NBP: calling NBP lookup %s:%s@%s\n", + abr->nbpEntityPtr->objStr.s, + abr->nbpEntityPtr->typeStr.s, + abr->nbpEntityPtr->zoneStr.s); + } + maxx = nbp_lookup(&lookup_ent, + abr->nbpBufPtr, abr->nbpMaxEnt, &retry_info); + if (maxx < 0) { + return -1; + } + if (dbug.db_nbp) { + printf("nbp: NBP lookup returned %d entries\n", maxx); + } + + abr->nbpDataField = maxx; + ep = (at_nbptuple_t *)(abr->nbpBufPtr); + while (maxx--) { + entp = (EntityName *)(&(ep->enu_entity)); + pas2c_ename(&(ep->enu_entity), entp); + if (dbug.db_nbp) { + printf("NBP: tuple %02d = %s:%s@%s at %s\n", + abr->nbpDataField - maxx, + entp->objStr.s, entp->typeStr.s, entp->zoneStr.s, + appletalk_ntoa(buf, (AddrBlock *)(&ep->enu_addr))); + } + ep++; + } + + return(noErr); +} + +/* + * OSErr NBPConfirm(nbpProto *pr, int async) + * + * NBPConfirm confirms that an entity known by name and address still + * exists. + * + * nbpEntityPtr - points to a variable of type EntityName that contains + * the name to confirm. No meta characters are allowed in the entity + * name (otherwise the result would be ambigous). + * + * nbpAddress - specifies the address to be confirmed. + * + * nbpRetransmitInfo - contains the retry interval and retry count. + * + * The correct socket number is returned in nbpDataField. + * + * NBPConfirm is more efficient than NBPLookup in terms of network + * traffic. Since NBPConfirm only waits for a single response it + * is also quicker (i.e. doesn't need to wait for the timeout period). + * + * Result Codes: + * noErr No error + * nbpConfDiff Name confirmed for different socket + * nbpNoConfirm Name not confirmed + * +*/ + +OSErr +NBPConfirm(abr,async) +nbpProto *abr; +int async; +{ + int answer; + at_entity_t lookup_ent; + at_retry_t retry_info; + at_inet_t address; + char buf[30]; + + c2pas_ename(abr->nbpEntityPtr, &lookup_ent); + + retry_info.interval = abr->nbpRetransmitInfo.retransInterval; + retry_info.retries = abr->nbpRetransmitInfo.retransCount; + retry_info.backoff = 1; /* ??? */ + + address.net = abr->nbpAddress.net; + address.node = abr->nbpAddress.node; + address.socket = abr->nbpAddress.skt; + + if (async) { + fprintf(stderr, "NBP: async NBPConfirm not implemented\n"); + return -1; + } + if (dbug.db_nbp) { + printf("NBP: calling NBP Confirm %s:%s@%s as %s\n", + abr->nbpEntityPtr->objStr.s, + abr->nbpEntityPtr->typeStr.s, + abr->nbpEntityPtr->zoneStr.s, + appletalk_ntoa(buf, &abr->nbpAddress)); + } + + answer = nbp_confirm(&lookup_ent, &address, &retry_info); + switch (answer) { + + case 1: /* successful */ + abr->nbpDataField = address.socket; + if (dbug.db_nbp) { + printf("NBP: Confirmed OK on socket %d\n", address.socket); + } + return (address.socket == abr->nbpAddress.skt)? + noErr : nbpConfDiff; + + + case 0: /* not confirmed */ + if (dbug.db_nbp) { + printf("NBP: Not confirmed\n"); + } + return(nbpNoConfirm); + } + if (dbug.db_nbp) { + printf("NBP: confirm error (errno=%d)\n", errno); + } + return(-1); +} + +/* + * OSErr NBPExtract(NBPTEntry t[],int nument,int whichone, + * EntityName *en, AddrBlock *addr) + * + * NBPExtract returns one address from the list of addresses returned + * by NBPLookup. + * + * t - is a table of entries in the form NBPTEntry as returned by + * NBPLookUp. + * + * nument - is the number of tuples in "t" as returned in nbpDataField + * by NBPLookUp + * + * whichone - specifies which one of the tuples in the buffer should + * be returned. + * + * en, addr - are pointers to an EntityName and AddrBlock into which + * NBPExtract stores the selected entity information. + * + * Result Codes: + * noErr No error + * extractErr Can't find tuple in buffer + * + */ + +OSErr +NBPExtract(t,nument,whichone,ent,addr) +NBPTEntry t[]; +EntityName *ent; +AddrBlock *addr; +{ + if (whichone > nument) { + fprintf(stderr,"NBPExtract: whichone too large!"); + return(extractErr); /* return error code, not found */ + } else { + *ent = t[whichone-1].ent; /* pretty simple */ + *addr = t[whichone-1].addr; /* stuff... */ + } + return(noErr); +} + +/* + * register a nve + * +*/ +NBPRegister(abr, async) +nbpProto *abr; +boolean async; +{ + int err, fd; + at_entity_t new_ent; + at_retry_t retry_info; + char buf[30]; + +#ifdef no_longer_needed + if ((err = NBPLookup(abr, FALSE)) < 0) /* i guess this is the right */ + return(err); /* thing to do */ +#endif + + c2pas_ename(abr->nbpEntityPtr, &new_ent); + + retry_info.interval = abr->nbpRetransmitInfo.retransInterval; + retry_info.retries = abr->nbpRetransmitInfo.retransCount; + retry_info.backoff = 1; /* ??? */ + + if (async) { + fprintf(stderr, "NBP: async NBPRegister not implemented\n"); + return -1; + } + if (dbug.db_nbp) { + printf("NBP: calling NBP Register %s:%s@%s for socket %d\n", + abr->nbpEntityPtr->objStr.s, + abr->nbpEntityPtr->typeStr.s, + abr->nbpEntityPtr->zoneStr.s, + abr->nbpAddress.skt); + } + + fd = ddp_skt2fd(abr->nbpAddress.skt); + if (fd < 0) { + fprintf(stderr, "NBP: can't register unopened socket %d\n", + abr->nbpAddress.skt); + return -1; + } + + err = nbp_register(&new_ent, fd, &retry_info); + + if (err == -1 && errno == EADDRNOTAVAIL) { + if (dbug.db_nbp) { + printf("NBP: %s:%s@%s already registered\n", + abr->nbpEntityPtr->objStr.s, + abr->nbpEntityPtr->typeStr.s, + abr->nbpEntityPtr->zoneStr.s); + } + return nbpDuplicate; + } + + /* record the entity name and the file descriptor for NBPRemove + * Record the entity name in contiguous pascal form for simple + * comparisons. + */ + + return err; +} + +/* + * remove a nve + * +*/ +NBPRemove(abEntity) +EntityName *abEntity; +{ + int err, fd; + at_entity_t new_ent; + + c2pas_ename(abEntity, &new_ent); + + /* need to look up a file descriptor to go with the entity name + * Either we keep the list of names we've registered, or we + * use NBPLookup to find them. The former is probably better. + */ + + err = nbp_remove(&new_ent, fd); + + if (dbug.db_nbp) { + printf("NBP: NBP Remove %s:%s@%s (fd=%d) returns %d\n", + abEntity->objStr.s, abEntity->objStr.s, + abEntity->zoneStr.s, fd, err); + } + return err; +} + + + +/* + * private int c2pkt_ename(EntityName *cn, u_char *pn) + * + * Copy entity name from c form into contiguous Apple Pascal + * form (packet form). + * + * return: length of pascal form entity name + * + */ +private int +c2pkt_ename(cn,pn) +byte *pn; +EntityName *cn; +{ + int i, cnt; + byte *s; + byte *pc; + + cnt = 0; + for (s = cn->objStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) { + *pn = *s; + if (*s == '\0') + break; + } + if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */ + i = ENTITYSIZE; /* too large: turncated to 32 chars */ + *pc = i; + cnt += (i+1); + for (s = cn->typeStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) { + *pn = *s; + if (*s == '\0') + break; + } + if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */ + i = ENTITYSIZE; /* too large: turncated to 32 chars */ + *pc = i; + cnt += (i+1); + for (s = cn->zoneStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) { + *pn = *s; + if (*s == '\0') + break; + } + if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */ + i = ENTITYSIZE; /* too large: turncated to 32 chars */ + *pc = i; + cnt += (i+1); + return(cnt); /* return number of bytes used */ +} + +/* + * private int pkt2c_enames(u_char *pn, EntityName *cn); + * + * Copy entity names from packet form (abutting Apple Pascal + * strings) to c form into structure of type EntityName. + * + * return: the length of the packed string. + * + */ + +private int +pkt2c_ename(pn,cn) +byte *pn; +EntityName *cn; +{ + int ol,tl,zl; + + ol = *pn; /* length of object */ + tl = *(pn+ol+1); /* length of type */ + zl = *(pn+ol+tl+2); /* length of zone */ + if (ol > ENTITYSIZE || tl > ENTITYSIZE || zl > ENTITYSIZE) { + fprintf(stderr,"pkt2c_entity_names: invalid length!\n"); + return(0); + } + cpyp2cstr(cn->objStr.s,pn); /* copy them... */ + cpyp2cstr(cn->typeStr.s,pn+ol+1); + cpyp2cstr(cn->zoneStr.s,pn+ol+tl+2); + return(ol+tl+zl+3); /* return length */ +} + +/* + * Convert name in the form 'LaserWriter:LaserWriter@*' (object, type, + * zone) to entity form (LaserWriter, LaserWriter, *). + * + * Assumes no ':' in object name , and no '@' in object or type name + * +*/ +void +create_entity(name, en) +char *name; +EntityName *en; +{ + char *zs, *ts; + int ol, tl, zl; + + ts = index(name, ':'); + zs = index(name, '@'); + ol = ts ? (ts - name) : (zs ? (zs - name) : strlen(name)); + tl = ts == NULL ? 0 : ((zs == NULL) ? strlen(ts+1) : (zs - ts - 1)); + zl = zs == NULL ? 0 : strlen(zs+1); + /* make foo@bar be =:foo@bar */ + /* make foo be =:=@foo */ + /* make foo@ be =:foo@* */ + if (ol != 0 && tl == 0 && ts == NULL) { + if (zl != 0 || zs) + tl = ol, ts = name - 1; + else + zs = name - 1, zl = ol; + ol = 0; + } + + bzero(en->objStr.s, sizeof(en->objStr.s)); + bzero(en->typeStr.s, sizeof(en->typeStr.s)); + bzero(en->zoneStr.s, sizeof(en->zoneStr.s)); + strncpy(en->objStr.s, name, min(ENTITYSIZE, ol)); /* just copy */ + if (ts) + strncpy(en->typeStr.s, ts+1, min(ENTITYSIZE, tl)); + if (zs) + strncpy(en->zoneStr.s, zs+1, min(ENTITYSIZE, zl)); +} + + + +/* c2pas_str(unsigned char *cn, at_nvestr_t *pn) + * pas2c_str(at_nvestr_t *pn, unsigned char *cn); + * + * Converts between C strings and Pascal strings. Will work + * correctly even if cn and pn point to the same place. + */ + +private void c2pas_str(cn, pn) +unsigned char *cn; +at_nvestr_t *pn; +{ + int length; + register unsigned char *from, *to; + + length = strlen(cn); + if (length > NBP_NVE_STR_SIZE) length = NBP_NVE_STR_SIZE; + + /* copy from last character, working backwards */ + if (length) { + from = cn + length - 1; /* last char */ + to = (pn->str) + length - 1; + do { + *to-- = *from; + } while (from-- >= cn); + } + pn->len = length; +} + +private void pas2c_str(pn, cn) +at_nvestr_t *pn; +register unsigned char *cn; +{ + int length; + register unsigned char *from; + + from = pn->str; + length = pn->len; + + while (length--) { + *cn++ = *from++; + } + *cn = '\0'; +} + +private int c2pas_ename(cn, pn) +EntityName *cn; +at_entity_t *pn; +{ + c2pas_str(&cn->objStr, &pn->object); + c2pas_str(&cn->typeStr, &pn->type); + c2pas_str(&cn->zoneStr, &pn->zone); +} + +private int pas2c_ename(cn, pn) +at_entity_t *pn; +EntityName *cn; +{ + pas2c_str(&pn->object, &cn->objStr); + pas2c_str(&pn->type, &cn->typeStr); + pas2c_str(&pn->zone, &cn->zoneStr); +} diff --git a/lib/cap/abddp.c b/lib/cap/abddp.c new file mode 100644 index 0000000..7067cf5 --- /dev/null +++ b/lib/cap/abddp.c @@ -0,0 +1,459 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:48:17 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abddp.c,v 2.3 1996/06/18 10:48:17 djh Rel djh $ + * $Revision: 2.3 $ +*/ + +/* + * abddp.c - Datagram Delivery Protocol + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 19, 1986 Schilit Created. + * July 9, 1986 CCKim Clean up some of Bill's stuff and allow + * Appletalk protocols on ethernet with CAP + * Feb. 1988 Charlie - don't like the way the encapsulation code runs + * into the DDP code. Drop out all the encapsulation code into + * another module (interface dep) and drop out part of DDP into it. + * + */ + +#include +#include +#ifndef linux +#include +#endif /* linux */ +#include +#include + +#include "cap_conf.h" + +import byte this_node, nis_node, bridge_node; +import word this_net, nis_net, bridge_net; + +#ifndef DONT_DOCHKSUM +# define DO_CHKSUM 1 +#else +# define DO_CHKSUM 0 +#endif +boolean dochecksum = DO_CHKSUM; +private boolean checksumerror_drop = TRUE; + +/* Following are the major allowed defines in this module */ + +/* -DINLINECHKSUM */ +/* define if you want to the checksum inline. Only been tested for a vax */ +/* and definitely doesn't work correctly on some machines */ + +/* -DNOINLINECHKSUM */ +/* Turns off inlinechecksumming if turned on for vax - this is to allow us */ +/* to debug the code */ + +/* machine dependecies should be encoded here */ + +#ifdef vax +# ifndef NOINLINECHKSUM +# define INLINECHKSUM +# endif +#endif + +#ifdef INLINECHKSUM +/* Dan Tappan, BBN */ +/* + * This macro seems to produce close to optimal code on a VAX (after -O ) + */ +#define ddp_chksum(xp, l, is, s) { \ + register unsigned char *ddp_chksum_p_var = (u_char *)xp; \ + register int ddp_chksum_cnt_var = l; \ + s = is; \ + while(--ddp_chksum_cnt_var >= 0) \ + if ((s = (s + *ddp_chksum_p_var++) << 1) & (1<<16)) ++s; \ + s &= 0xffff; /* make off extra bits */ \ + } +#define ddpchksumtype register int +#else +#define ddp_chksum(xp, l, is, s) s = do_ddp_chksum(xp, l, is) +private word do_ddp_chksum(); +#define ddpchksumtype word +#endif + +/* room for up to ddp + 1 for data buffer */ +#define DDPIOVLEN (IOV_DDP_SIZE+1) +private DDP ddph; +private byte ddpdatabuffer[ddpMaxData]; +private struct iovec rddpiov[DDPIOVLEN] = { + {NULL, 0}, /* LAP header */ + {(caddr_t)&ddph, ddpSize}, /* DDP header (me) (redundant) */ + {(caddr_t)ddpdatabuffer, ddpMaxData} /* ddp data */ +}; + +typedef struct { + int (*lproc)(); /* socket listener routine */ + int flags; /* flags */ +#define DDPL_OLDINTERFACE 1 +} LISENTRY; /* listener entry */ + +private LISENTRY ddpl[ddpMaxSkt+1]; /* table of listeners */ + +DDPInit() +{ + int i; + + /* initialize ddp listener array */ + for (i = 0; i < ddpMaxSkt+1; i++) { + ddpl->lproc = NILPROC; + ddpl->flags = 0; + } + +} + + +/* + * OSErr DDPOpenSocket(int *skt, ProcPtr sktlis) + * OSErr DDPOpenSocketIOV(int *skt,ProcPtr sktlis,struct iovec *iov,int iovlen) + * + * Open a DDP socket and optionally install a socket listener to the + * listener table. If skt is nonzero (it must be in the range of + * 1 to 127) it specifies the socket's number. If skt is 0 then + * DDPOpenSocket dynamically assigns a socket number in the range + * 128 to 254, and returns it in skt. You can actually specify a socket + * in the 128 to 254 range, but it's not a good idea :-). + * + * sktlis contains a pointer (ProcPtr) to the socket listener; if + * it is NILPROC, the default listener will be used (NYI) + * + * If calling DDPOpenSocketIOV, then the iovector passed must be of + * size (IOV_DDP_SIZE+1) (length for ddp+lap) plus one (data for caller). + * In addition, it must be filled in from DDP_LVL+1 to the end + * + * + * The listener is defined as: + * XXX_listener(int skt, PKT *pkt, int len, AddrBlock *addr) + * if called from DDPOpenSocket and: + * XXX_listener(int skt, struct iovec *iov, int iovlen, AddrBlock *addr) + * if called from DDPOpenSocketIOV + * + * The iov passed back to the listener will start after the ddp header + * block + * +*/ +private OSErr +iDDPOpenSocketIOV(skt, iov, iovlen) +int *skt; +struct iovec *iov; +int iovlen; +{ + int refcd; + int s; + + /* allow 0 - means dymanic assignment */ + s = *skt; /* socket wanted */ + if (s >= ddpMaxSkt) { + fprintf(stderr,"ddpOpenSocket: skt out of range\n"); + exit(0); + } + /* open the socket please */ + if ((refcd = abOpen(skt, s, iov, iovlen)) != 0) { + if (dbug.db_ddp) + fprintf(stderr,"ddp: open socket - socket open failed: %d\n", refcd); + return(refcd); /* return error if failed */ + } + s = *skt; /* real socket */ + if (dbug.db_ddp) + fprintf(stderr, "ddp: open socket: opened socket %d\n", s); + /* add default or user's listener */ + iov[IOV_DDP_LVL].iov_base = (caddr_t)&ddph; /* install */ + iov[IOV_DDP_LVL].iov_len = ddpSize; /* install */ + ddpl[s].lproc = NILPROC; + ddpl[s].flags = 0; + return(noErr); +} + + +ddpinstlistener(s, sktlis, flags) +int s; +ProcPtr sktlis; +int flags; +{ + int defDDPlis(); + + ddpl[s].lproc = ((sktlis == NILPROC) ? defDDPlis : sktlis); + ddpl[s].flags = flags; + return(noErr); /* and return */ +} + + +OSErr +DDPOpenSocket(skt,sktlis) +int *skt; +ProcPtr sktlis; +{ + OSErr err; + + /* call iov routine with default DDP iov */ + err = iDDPOpenSocketIOV(skt, rddpiov, DDPIOVLEN); + if (err == noErr) { + ddpinstlistener(*skt, sktlis, DDPL_OLDINTERFACE); + } + return(err); +} + +OSErr +DDPOpenSocketIOV(skt, sktlis, iov, iovlen) +int *skt; +ProcPtr sktlis; +struct iovec *iov; +int iovlen; +{ + OSErr err; + + err = iDDPOpenSocketIOV(skt, iov, iovlen); + if (err == noErr) { + ddpinstlistener(*skt, sktlis, 0); + } + return(err); +} + +/* + * OSErr DDPCloseSocket(int skt) + * + * DDPCloseSocket closes the skt, cancels all pending DDPRead calls + * that have been made on that socket, and removes the socket listener + * procedure. + * +*/ + +OSErr +DDPCloseSocket(skt) +int skt; +{ + if (skt == 0 || skt >= ddpMaxSkt) { + if (dbug.db_ddp) + fprintf(stderr, "ddpRemLis: Socket out of range\n"); + return(ddpSktErr); + } + ddpl[skt].lproc = NILPROC; /* no procedure */ + /* close out the socket and return any errors */ + return(abClose(skt)); +} + +OSErr +DDPWrite(abr) +abRecPtr abr; +{ + struct iovec iov[IOV_DDP_SIZE+1]; + + iov[IOV_DDP_LVL+1].iov_base = (caddr_t) abr->proto.ddp.ddpDataPtr; + iov[IOV_DDP_LVL+1].iov_len = abr->proto.ddp.ddpReqCount; + + return(DDPWriteIOV(abr,iov,IOV_DDP_SIZE+1)); +} + +/* + * DDPWriteIOV + * + * DDPWriteIOV builds up DDP header and then passes off to routeddp + * who decides where to send it. In the most cases, we'll probably + * have a version of routeddp per "network" type so we can "optimize" + * +*/ +/*ARGSUSED*/ +OSErr +DDPWriteIOV(abr,iov,iovl) +abRecPtr abr; +struct iovec iov[]; +int iovl; +{ + DDP ddp; + ddpProto *dpr; + int i; + ddpchksumtype chksum; + + dpr = &abr->proto.ddp; + ddp.length = htons(ddpSize+dpr->ddpReqCount); + ddp.dstNet = dpr->ddpAddress.net; + ddp.dstNode = dpr->ddpAddress.node; + ddp.dstSkt = dpr->ddpAddress.skt; + ddp.srcNet = this_net; + ddp.srcNode = this_node; + ddp.srcSkt = dpr->ddpSocket; + ddp.type = dpr->ddpType; + if (dochecksum) { + ddp_chksum(&ddp.dstNet, ddpSize-4, 0, chksum); + for (i=IOV_DDP_LVL+1; i < iovl; i++) + ddp_chksum(iov[i].iov_base, iov[i].iov_len, chksum, chksum); + if (chksum == 0) chksum = 0xffff; + ddp.checksum = htons(chksum); + } else { + ddp.checksum = 0; + } + iov[IOV_DDP_LVL].iov_base = (caddr_t) &ddp; /* DDP header */ + iov[IOV_DDP_LVL].iov_len = ddpSize; + return(routeddp(iov, iovl)); +} + +/*ARGSUSED*/ +OSErr +DDPRead(abr,retCkSumErrs,async) +abRecPtr abr; +int retCkSumErrs,async; +{ + fprintf(stderr,"DDPRead NYI\n"); +} + +OSErr +DDPRdCancel(abr) +abRecPtr abr; +{ + fprintf(stderr,"DDPRdCancel NYI\n"); +} + +defDDPlis(skt,ddp,len,addr) +DDP *ddp; +AddrBlock *addr; +{ + fprintf(stderr,"defDDPlis NYI\n"); +/***** copy data into user buffer *****/ +} + +/* + * ddp_protocol(ddp,len) + * + * ddp_protocol is the installed LAP protocol handler for protocol + * type lapDDP (2). This routine gets called by LAP when a packet + * is received with lapDDP in the protocol field. ddp_protocol + * passes the packet to the socket listener installed by the + * DDPOpenSocket call. + * + * In the case of UDP encapsulated DDP packets, there is no LAP layer + * and the upcall comes from the "network gateway" level (abkip) + * + * Can in with iov pointing to DDP header +*/ + +ddp_protocol(iov, iovlen, plen) +struct iovec *iov; +int iovlen; +int plen; +{ + byte skt; + byte *p; + AddrBlock addr; + int i; + int len; + int cnt; + int oldstyle; /* hack */ + DDP *ddp; + ddpchksumtype chksum; + + if (iovlen < 1 || iov->iov_len != ddpSize) /* iovlen==1 means just ddph */ + return; + + ddp = (DDP *)iov->iov_base; /* know aligned okay */ + len = ntohs(ddp->length) & 0x3ff; /* get the "real" length */ + if (plen < len || len < ddpSize) { /* not enought data? */ + if (dbug.db_ddp) + fprintf(stderr, "BAD PACKET: ddp reports more data than came in\n"); + return; /* drop pkt */ + } else plen = len; /* truncate if len < plen */ + + if (dochecksum) { + if (ddp->checksum != 0) { + ddp_chksum(&ddp->dstNet, ddpSize-4, 0, chksum); + len -= ddpSize; /* drop ddp size off */ + for (i = 1 ; i < iovlen; i++) { + cnt = min(len, iov[i].iov_len); + ddp_chksum(iov[i].iov_base, cnt, chksum, chksum); + if (cnt != iov[i].iov_len) /* out of data */ + break; + len -= cnt; + } + if (chksum == 0) + chksum = 0xffff; + if (ntohs(ddp->checksum) != chksum) { + if (checksumerror_drop) { + fprintf(stderr, + "Checksum error: Incoming: %x, calculated %x [%d.%d]\n", + ntohs(ddp->checksum), chksum, ntohs(ddp->srcNet), + ddp->srcNode); + fprintf(stderr, "Dropping packet\n"); + return; /* drop packet */ + } + } + } + } + + /* pass down the srcNet and srcNode of the incoming packet, so we can */ + /* cache information below based on the transport */ + abnet_cacheit(ddp->srcNet,ddp->srcNode); + skt = ddp->dstSkt; + + if (ddpl[skt].lproc == NILPROC) { /* listener proc for socket */ + if (dbug.db_ddp) + fprintf(stderr,"ddp_protocol: no socket listener for %d!\n", skt); + return; + } + + addr.net = ddp->srcNet; + addr.node = ddp->srcNode; + addr.skt = ddp->srcSkt; + iov++; /* skip ddp header */ + iovlen--; + plen -= ddpSize; /* reduce to data size */ + if (iovlen < 1) /* nothing to send */ + return; /* drop it */ + if (ddpl[skt].lproc) { /* be postitive */ + if (ddpl[skt].flags & DDPL_OLDINTERFACE) + (*ddpl[skt].lproc)(skt,ddp->type,iov->iov_base,plen,&addr); + else + (*ddpl[skt].lproc)(skt, ddp->type, iov, iovlen, plen, &addr); + } +} + +#ifndef INLINECHKSUM +/* + * Compute a 16 bit checksum via the following algorithm: + * for each byte: sum = byte + sum (unsigned), rotate sum left + * + * Note: to complete the algorithm, the caller must use a value of 0xffff + * if the checksum is zero + * + * note: the algorithm below works efficently on a vax, may not work + * particularly well on other machines + * +*/ +private word +do_ddp_chksum(p, cnt, sum) +register byte *p; +register int cnt; +word sum; +{ + register dword xsum = sum; + + while (cnt-- > 0) { + /* add in new byte, clip off extraneous info, shift as half of rotate */ + xsum = ((xsum + *p++) & 0xffff) << 1; + /* add in the 16th bit (in 17th position) */ + xsum |= (xsum >> 16); + } + return((word)xsum); +} +#endif + +/* + * Call with TRUE or FALSE - FALSE means ignore, TRUE means don't ignore + * + */ + +checksum_error(which) +boolean which; +{ + checksumerror_drop = which; +} + diff --git a/lib/cap/abkas.c b/lib/cap/abkas.c new file mode 100644 index 0000000..9e1ad34 --- /dev/null +++ b/lib/cap/abkas.c @@ -0,0 +1,1287 @@ +/* + * $Author: djh $ $Date: 1996/09/06 12:06:32 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abkas.c,v 2.4 1996/09/06 12:06:32 djh Rel djh $ + * $Revision: 2.4 $ + */ + +/* + * abkas.c - use UNIX Kernel AppleTalk Support via AF_APPLETALK sockets + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Copyright (c) 1992 The University of Melbourne. + * + * Edit History: + * + * February 1992 djh Created + * May, 1996 djh Updated + * + */ + +/* + * The following list of exported routines is provided so you'll know what + * have to be done to do another interface type (ethertalk, etc) + * + * EXPORTED ROUTINES: + * + * OSErr GetNodeAddress(int *mynode, int *mynet) + * Return node addresses + * OSErr GetBridgeAddress(AddrBlock *addr) + * Return bridge addresses + * OSErr SetBridgeAddress(AddrBlock *addr) + * Set bridge addresses + * OSErr SetNetRange(u_short range_start, u_short range_end) + * Set Network Range (Phase 2) + * int abInit(boolean dispay_message) + * Initialize AppleTalk + * int abOpen(int *returnsocket, int wantsocket, struct iovec iov[], iovlen) + * Open a DDP socket + * int abClose(int socket) + * Close a DDP socket + * void abnet_cacheit(word srcNet, byte srcNode) + * Call in DDP protocol layer to tell the lower layer that + * the last packet that came in was from srcNet, srcNode + * int routeddp(struct iovec *iov, int iovlen) + * This is the DDP incursion. With a full AppleTalk implementation, + * this would be part of DDP (abddp2). This routes the DDP packet: + * normally would decide where to send and then send via lap, with KIP + * decides where and sends via UDP. + * + */ + +#include +#include +#include +#include +#ifdef linux +#include +#include +#else /* linux */ +#include +#include +#endif /* linux */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* to cover difference between bsd systems */ +#include + +#define ATADDR_ANYNET (u_short)0x0000 +#define ATADDR_ANYNODE (u_char)0x00 +#define ATADDR_ANYPORT (u_char)0x00 +#define ATADDR_BCAST (u_char)0xff + +#ifdef s_net +#undef s_net +#endif s_net + +/* AppleTalk Address */ + +struct at_addr { + u_short s_net; + u_char s_node; +}; + +/* Socket Address for AppleTalk */ + +struct sockaddr_at { + short sat_family; + u_char sat_port; + struct at_addr sat_addr; + char sat_zero[8]; +}; + +/* Range info for extended networks */ + +struct net_range { + u_char phase; + u_short net_lo; + u_short net_hi; +}; + +/* imported network information */ + +extern word this_net, bridge_net, nis_net, async_net; +extern byte this_node, bridge_node, nis_node; +extern char this_zone[34], async_zone[34], interface[50]; + +extern struct in_addr bridge_addr; + +#ifdef PHASE2 +extern word net_range_start, net_range_end; +#endif PHASE2 + +short lap_proto = LAP_KERNEL; /* kernel appletalk support */ + +/* + * Configuration defines + * + * NORECVMSG - no recvmsg() + * NOSENDMSG - no sendmsg() + * MEEDMSGHDR - no msghdr in sockets.h - define our own + * + */ + +#ifdef NORECVMSG +# ifndef NEEDNETBUF +# define NEEDNETBUF +# endif NEEDNETBUF +#endif NORECVMSG +#ifdef NOSENDMSG +# ifndef NEEDNETBUF +# define NEEDNETBUF +# endif NEEDNETBUF +#endif NOSENDMSG + +import int ddp_protocol(); /* DDP protocol handler */ + +private int ddp_get(); /* kernel packet listener */ +private int kip_get(); /* loopback packet listener */ + +private struct sockaddr_at addr; /* local appletalk socket addr */ +private struct sockaddr_at raddr;/* input appletalk socket addr */ +private struct sockaddr_in from; /* loopback receive address */ +private struct sockaddr_in sndl; /* send packet to loopback */ + +private int skt2fd[ddpMaxSkt+1]; /* translate socket to file descriptor */ +private int skt2kip[ddpMaxSkt+1];/* loopback socket to file descriptor */ +private int ddpskt2udpport[ddpMaxWKS+1]; /* ddp "wks" skt to udp port */ + +private LAP laph; + +export DBUG dbug; + +/* + * initialize + * + */ + +export +abInit(disp) +int disp; +{ + int i; + static int here_before = 0; + + for (i = 0; i < ddpMaxSkt+1; i++) { + skt2fd[i] = -1; /* mark all these as unused */ + skt2kip[i] = -1; /* mark all these as unused */ + } + + for (i = 0; i < ddpMaxWKS+1; i++) + ddpskt2udpport[i] = -1; /* mark as unused */ + + if (!here_before) { + openetalkdb(NULL); /* set up variables */ + here_before = 1; + } + + init_fdlistening(); + + if (disp) { + printf("abInit: [ddp: %3d.%02d, %d]", + ntohs(this_net) >> 8, ntohs(this_net) & 0xff, this_node); + if (this_net != nis_net || this_node != nis_node) + printf(", [NBP (atis) Server: %3d.%02d, %d]", + ntohs(nis_net) >> 8, ntohs(nis_net) & 0xff, nis_node); + if (bridge_node) + printf(", [GW: %3d.%02d, %d]", + ntohs(this_net) >> 8, ntohs(this_net) & 0xff, bridge_node); + printf(" starting\n"); + } + + DDPInit(); + return(0); +} + +/* + * int abOpen(int *skt,rskt, iov, iovlen) + * + * abOpen opens the ddp socket in "skt" or if "skt" is zero allocates + * and opens a new socket. Upon return "skt" contains the socket number + * and the returned value is >=0 if no error, < 0 if error. + * + * iov should be an array of type "struct iov" of length at least + * IOV_LAP_SIZE+1. Levels after IOV_LAP_LVL are assume to filled. + * + */ + +int abOpen(skt,rskt, iov, iovlen) +int *skt; +int rskt; +struct iovec *iov; +int iovlen; +{ + int len, fd, err; + + if (iov == NULL || iovlen < IOV_LAP_SIZE+1 || iovlen > IOV_READ_MAX) + return(-1); + + if ((fd = socket(AF_APPLETALK, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return(fd); + } + + bzero(&addr, sizeof(struct sockaddr_at)); + addr.sat_family = AF_APPLETALK; + addr.sat_addr.s_net = htons(ATADDR_ANYNET); + addr.sat_addr.s_node = ATADDR_ANYNODE; + addr.sat_port = (rskt == 0) ? ATADDR_ANYPORT : rskt; + + if ((err = bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_at))) < 0) { + perror("bind()"); + close(fd); + return(err); + } + + len = sizeof(struct sockaddr_at); + if ((err = getsockname(fd, (struct sockaddr *)&addr, &len)) < 0) { + perror("getsockname()"); + close(fd); + return(err); + } + + *skt = addr.sat_port; + + iov[IOV_LAP_LVL].iov_base = (caddr_t)&laph; /* remember this */ + iov[IOV_LAP_LVL].iov_len = lapSize; /* and this */ + + fdlistener(fd, ddp_get, iov, iovlen); /* remember for later */ + + skt2fd[*skt] = fd; /* remember file descriptor for socket */ + + /* open KIP loopback socket */ + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + fdunlisten(skt2fd[*skt]); + close(skt2fd[*skt]); + return(fd); + } + + sndl.sin_family = AF_INET; + sndl.sin_addr.s_addr = INADDR_ANY; + if ((sndl.sin_port = htons(ddp2ipskt(*skt))) == 0) { + fdunlisten(skt2fd[*skt]); + close(skt2fd[*skt]); + close(fd); + return(ddpSktErr); + } + + if ((err = bind(fd, (struct sockaddr *)&sndl, sizeof(struct sockaddr))) < 0) { + perror("bind()"); + fdunlisten(skt2fd[*skt]); + close(skt2fd[*skt]); + close(fd); + return(ddpSktErr); + } + + fdlistener(fd, kip_get, iov, iovlen); + + skt2kip[*skt] = fd; + + return(noErr); +} + +/* + * close off socket opened by abOpen() + * + */ + +export int +abClose(skt) +int skt; +{ + int fd; + + if (skt < 0 || skt > ddpMaxSkt) { + fprintf(stderr,"abClose: skt out of range\n"); + exit(0); + } + + /* close AppleTalk skt */ + if ((fd = skt2fd[skt]) >= 0) { + fdunlisten(fd); + close(fd); + } + + /* close KIP loopback */ + if ((fd = skt2kip[skt]) >= 0) { + fdunlisten(fd); + close(fd); + } + + skt2fd[skt] = -1; /* mark as unused */ + skt2kip[skt] = -1; /* mark as unused */ + + return(0); +} + +int +abnet_cacheit() +{ +} + +/* + * get a packet from the network + * + * construct appropriate DDP header. + * + */ + +private int +ddp_get(fd, iov, iovlen) +int fd; +struct iovec *iov; +int iovlen; +{ + struct msghdr msg; + int len, err; + LAP *lap; + DDP *ddp; + + /* want DDP type in byte 0 */ + iov[IOV_DDP_LVL].iov_len = 1; + +#ifdef linux + bzero((char *)&msg, sizeof(msg)); +#endif linux + msg.msg_name = (caddr_t) &raddr; + msg.msg_namelen = sizeof(struct sockaddr_at); + msg.msg_iov = iov+1; + msg.msg_iovlen = iovlen-1; +#ifndef linux + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; +#endif /* linux */ + + len = sizeof(struct sockaddr_at); + if ((err = getsockname(fd, (struct sockaddr *)&addr, &len)) < 0) { + perror("getsockname()"); + return(err); + } + + /* + * getsockname returns net byte order + * + */ + addr.sat_addr.s_net = ntohs(addr.sat_addr.s_net); + + if ((len = recvmsg(fd, &msg, 0)) < 0) { + perror("recvmsg()"); + return(len); + } + + lap = (LAP *)iov[IOV_LAP_LVL].iov_base; + lap->type = lapDDP; + lap->dst = addr.sat_addr.s_node; + lap->src = raddr.sat_addr.s_node; + iov[IOV_LAP_LVL].iov_len = lapSize; + + ddp = (DDP *)iov[IOV_DDP_LVL].iov_base; + ddp->type = *(char *)iov[IOV_DDP_LVL].iov_base; + ddp->length = htons(len-1+ddpSize); + ddp->checksum = 0; + ddp->dstNet = addr.sat_addr.s_net; + ddp->srcNet = raddr.sat_addr.s_net; + ddp->dstNode = addr.sat_addr.s_node; + ddp->srcNode = raddr.sat_addr.s_node; + ddp->dstSkt = addr.sat_port; + ddp->srcSkt = raddr.sat_port; + iov[IOV_DDP_LVL].iov_len = ddpSize; + + return(ddp_protocol(iov+1, iovlen-1, len-1+ddpSize)); +} + +/* + * read a KIP packet via loopback + * + */ + +private int +kip_get(fd, iov, iovlen) +int fd; +struct iovec *iov; +int iovlen; +{ + struct msghdr msg; + int len; + LAP *lap; + +#ifdef linux + bzero((char *)&msg, sizeof(msg)); +#endif linux + msg.msg_name = (caddr_t) &from; + msg.msg_namelen = sizeof(from); + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; +#ifndef linux + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; +#endif /* linux */ + + if ((len = recvmsg(fd, &msg, 0)) < 0) { + perror("recvmsg()"); + return(len); + } + if (iov->iov_len != lapSize) /* check */ + return(-1); + + lap = (LAP *)iov->iov_base; + + if (lap->type == lapDDP) + return(ddp_protocol(iov+1, iovlen-1, len-lapSize)); + + return(-1); +} + +export int +routeddp(iov, iovlen) +struct iovec *iov; +int iovlen; +{ + struct sockaddr_at taddr; + struct sockaddr_in tokip; + struct msghdr msg; + DDP *ddp; + LAP lap; + int err; + int fd; + + ddp = (DDP *)iov[IOV_DDP_LVL].iov_base; /* pick out ddp header */ + + /* check ddp socket(s) for validity */ + if ( ddp->srcSkt == 0 || ddp->srcSkt == ddpMaxSkt || + ddp->dstSkt == 0 || ddp->dstSkt == ddpMaxSkt || + skt2fd[ddp->srcSkt] == -1 || skt2kip[ddp->srcSkt] == -1) + return(ddpSktErr); + + /* check for loopback */ + if ((ddp->dstNet == this_net) + && (ddp->dstNode == this_node || ddp->dstNode == 0xff)) { + lap.type = lapDDP; + lap.dst = ddp->dstNode; + lap.src = this_node; + iov[IOV_LAP_LVL].iov_base = (caddr_t)⪅ + iov[IOV_LAP_LVL].iov_len = lapSize; + + tokip.sin_family = AF_INET; + tokip.sin_addr.s_addr = bridge_addr.s_addr; + if ((tokip.sin_port = htons(ddp2ipskt(ddp->dstSkt))) == 0) + return(ddpSktErr); + +#ifdef linux + bzero((char *)&msg, sizeof(msg)); +#endif linux + msg.msg_name = (caddr_t)&tokip; + msg.msg_namelen = sizeof(tokip); + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; +#ifndef linux + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; +#endif /* linux */ + fd = skt2kip[ddp->srcSkt]; + + if ((err = sendmsg(fd, &msg, 0)) < 0) + perror("sendmsg()"); + + if (ddp->dstNode != 0xff) + return(noErr); + } + + /* send via kernel EtherTalk */ + + bzero(&taddr, sizeof(struct sockaddr_at)); + taddr.sat_family = AF_APPLETALK; + taddr.sat_addr.s_net = ddp->dstNet; + taddr.sat_addr.s_node = ddp->dstNode; + taddr.sat_port = ddp->dstSkt; + + /* collapse header, except for DDP Type */ + iov[IOV_DDP_LVL].iov_base += (ddpSize-1); + iov[IOV_DDP_LVL].iov_len = 1; /* ddp type */ + +#ifdef linux + bzero((char *)&msg, sizeof(msg)); +#endif linux + msg.msg_name = (caddr_t)&taddr; + msg.msg_namelen = sizeof(struct sockaddr_at); + msg.msg_iov = iov+1; + msg.msg_iovlen = iovlen-1; +#ifndef linux + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; +#endif /* linux */ + fd = skt2fd[ddp->srcSkt]; + + if ((err = sendmsg(fd, &msg, 0)) < 0) { + perror("sendmsg()"); + return(err); + } + + return(noErr); +} + +/* + * configure the network interface + * + * (expects network numbers in host byte order + * and returns network numbers in net byte order) + * + */ + +int +ifconfig(node, net, net_lo, net_hi) +int *node, *net, *net_lo, *net_hi; +{ + struct sockaddr_at addr; + struct sockaddr_at *sat; + struct net_range *nr; + struct ifreq ifreq; + int len, err; + int fd; + + if ((fd = socket(AF_APPLETALK, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return(fd); + } + + strncpy(ifreq.ifr_name, interface, sizeof(ifreq.ifr_name)); + sat = (struct sockaddr_at *)&ifreq.ifr_addr; + bzero((char *)sat, sizeof(struct sockaddr_at)); + sat->sat_family = AF_APPLETALK; + sat->sat_addr.s_net = htons(*net); + sat->sat_addr.s_node = *node; + sat->sat_port = ATADDR_ANYPORT; + +#ifdef PHASE2 + nr = (struct net_range *)&sat->sat_zero; + nr->net_lo = htons(*net_lo); + nr->net_hi = htons(*net_hi); + nr->phase = 2; +#endif /* PHASE2 */ + + if ((err = ioctl(fd, SIOCSIFADDR, &ifreq)) < 0) { + perror("SIOCSIFADDR"); + return(err); + } + + bzero((char *)&addr, sizeof(struct sockaddr_at)); + addr.sat_family = AF_APPLETALK; + addr.sat_addr.s_net = htons(ATADDR_ANYNET); + addr.sat_addr.s_node = ATADDR_ANYNODE; + addr.sat_port = ATADDR_ANYPORT; + + if ((err = bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_at))) < 0) { + perror("bind()"); + return(err); + } + + len = sizeof(struct sockaddr_at); + if ((err = getsockname(fd, (struct sockaddr *)&addr, &len)) < 0) { + perror("getsockname()"); + return(err); + } + + /* + * getsockname returns net byte order + * + */ + *net = addr.sat_addr.s_net; + *node = addr.sat_addr.s_node; + + close(fd); + return(0); +} + +/* + * ddp to udp wks translate table + * + */ + +struct wks { + char *name; /* name of wks (as in /etc/services) */ + int ddpport; /* ddp port to map from */ + int udpport; /* udp port to map to */ + int notinited; /* tried /etc/services? */ +}; + +/* + * udpport is initially set to the old + * (768) values for compatibility + * + */ + +#define WKS_entry(name, ddpsock) {(name), (ddpsock), ddpWKSUnix+(ddpsock), 1} + +private struct wks wks[] = { + WKS_entry("at-rtmp", rtmpSkt), + WKS_entry("at-nbp", nbpNIS), + WKS_entry("at-echo", echoSkt), + WKS_entry("at-zis", zipZIS), + WKS_entry(NULL, 0) +}; + +/* + * Translate ddp socket to UDP port: returns 0 if no mapping + * + */ + +ddp2ipskt(ddpskt) +int ddpskt; +{ + struct wks *wksp; + struct servent *serv; + + if (ddpskt < 0 || ddpskt > ddpMaxSkt) + return(0); + + if (ddpskt & ddpWKS) /* 128+x means non-wks */ + return(ddpskt + ddpNWKSUnix); + +#ifdef STEAL_PORTS + if (ddpskt >= ddpEWKS) /* 64-128 experimental */ + return(ddpskt + ddpNWKSUnix); +#endif STEAL_PORTS + + if (ddpskt2udpport[ddpskt] < 0) { + for (wksp = wks; wksp->name != NULL; wksp++) + if (wksp->ddpport == ddpskt) { + if ((serv = getservbyname(wksp->name, "udp")) != NULL) + wksp->udpport = ntohs(serv->s_port); /* replace with new */ + if (dbug.db_ini) + fprintf(stderr, "port for %s is %d\n",wksp->name,wksp->udpport); + endservent(); + ddpskt2udpport[ddpskt] = wksp->udpport; + return(wksp->udpport); + } + ddpskt2udpport[ddpskt] = 0; + } + return(ddpskt2udpport[ddpskt]); +} + +#ifdef NEEDNETBUF +#ifdef NEEDMSGHDR +struct msghdr { + caddr_t msg_name; /* name to send to */ + int msg_namelen; /* size of name */ + struct iovec *msg_iov; /* io vec */ + int msg_iovlen; /* length */ + int msg_accrights; /* dummy */ + int msg_accrightslen; +}; +#endif NEEDMSGHDR + +/* + * buffer larger than maximum ddp pkt by far + * + */ + +private char net_buffer[ddpMaxData*2]; + +#ifdef NOSENDMSG +/* + * limited sendmsg - limits to sizeof(net_buffer) + * + */ + +sendmsg(fd, msg, flags) +int fd; +struct msghdr *msg; +int flags; +{ + int err; + int i, pos, len; + struct iovec *iov; + + iov = msg->msg_iov; + for (i=0, pos=0; i < msg->msg_iovlen; i++, iov++) { + len = iov->iov_len; + if (len+pos > sizeof(net_buffer)) /* if overflow */ + len = sizeof(net_buffer)-pos; /* then limit */ + bcopy(iov->iov_base, net_buffer+pos, len); + pos+= len; + if (len != iov->iov_len) /* we don't have any more space */ + break; + } + len = pos; + if ((err=sendto(fd,net_buffer,len,0,msg->msg_name,msg->msg_namelen)) < 0) + perror("sendmsg()"); + return(err); +} +#endif NOSENDMSG + +#ifdef NORECVMSG +recvmsg(fd, msg, flags) +int fd; +struct msghdr *msg; +int flags; +{ + int err; + int i, pos, len, blen; + struct iovec *iov; + + err = recvfrom(fd, net_buffer, sizeof(net_buffer), 0, + msg->msg_name, &msg->msg_namelen); + if (err < 0) + perror("recvfrom()"); + for (blen=err,pos=0,i=0,iov=msg->msg_iov; i < msg->msg_iovlen; i++, iov++) { + len = min(iov->iov_len, blen); + if ((pos + len) > sizeof(net_buffer)) /* if asking for too much */ + len = sizeof(net_buffer) - pos; /* then limit */ + bcopy(net_buffer+pos, iov->iov_base, len); + pos += len; + blen -= len; + /* either no more room or no more data */ + if (len != iov->iov_len) + break; + } + return(err); +} +#endif NORECVMSG +#endif NEEDNETBUF + +/* + * OSErr GetNodeAddress(int *myNode,*myNet) + * + * GetNodeAddress returns the net and node numbers for the current host. + * + */ + +export OSErr +GetNodeAddress(myNode,myNet) +int *myNode,*myNet; +{ + *myNode = this_node; + *myNet = this_net; + return(noErr); /* is ok */ +} + +/* + * Set Node Address + * + */ + +OSErr +SetNodeAddress(myNet, myNode) +int myNet, myNode; +{ + int net_lo, net_hi; + + net_lo = 0x0000; + net_hi = 0xfffe; + + if (ifconfig(&myNode, &myNet, &net_lo, &net_hi) == 0) { + this_net = myNet; + this_node = myNode; + etalkdbupdate(NULL); + return(noErr); + } + return(-1); +} + +/* + * Get Bridge Address + * + */ + +OSErr +GetBridgeAddress(baddr) +AddrBlock *baddr; +{ + baddr->net = bridge_net; + baddr->node = bridge_node; + return(noErr); +} + +/* + * Set Bridge Address + * + */ + +OSErr +SetBridgeAddress(baddr) +AddrBlock *baddr; +{ + bridge_node = baddr->node; + bridge_net = baddr->net; + if (this_net == 0) + SetNodeAddress(bridge_net, 0); + etalkdbupdate(NULL); + return(noErr); +} + +#ifdef PHASE2 +/* + * Set Network Range + * + */ + +OSErr +SetNetRange(range_start, range_end) +u_short range_start, range_end; +{ + this_net = nis_net = net_range_start = range_start; + net_range_end = range_end; + etalkdbupdate(NULL); + return(noErr); +} +#endif PHASE2 + +/* + * Maintain a simple routing table for use with Kernel AppleTalk + * + * Copyright 1996, The University of Melbourne + * + */ + +struct rtmp { + u_short net_lo; /* network range start */ + u_short net_hi; /* network range end */ + u_short dist; /* distance to destination */ + u_short rnet; /* router network address */ + u_char rnode; /* router node address */ + u_char state; /* entry state */ +#define RTMP_GOOD 0 +#define RTMP_SUSP 1 +#define RTMP_BAD1 2 +#define RTMP_BAD2 3 + u_char flags; /* flag bits for this entry */ +#define RTMP_EXTENDED 0x01 + u_char dummy; /* padding to longword */ + struct rtmp *next; /* next in linked list */ +}; + +#define RTMPNULL ((struct rtmp *)NULL) + +#define RTMPTABSIZ 256 +#define RTMPTABMSK RTMPTABSIZ-1 + +#define RTMP_MODIFIED 1 +#define RTMP_DELETED 2 + +struct rtmp *rtmpTab[RTMPTABSIZ]; + +/* + * pkt points to RTMP tuples + * from router rnode.rnet + * + */ + +void +rtmp_data(rnode, rnet, pkt, len) +u_char rnode; +u_short rnet; +u_char *pkt; +int len; +{ + int i; + u_char dist; + u_char *data; + int tuplelen; + int modified; + static int init = 0; + u_short net_lo, net_hi; + struct rtmp *rtmp_new(); + struct rtmp *rtmp_delete(); + struct rtmp *rtmp, *rtmp_find(); + void rtmp_kernel_update(); + void rtmp_insert(); + + if (!init) { + for (i = 0; i < RTMPTABSIZ; i++) + rtmpTab[i] = RTMPNULL; + init = 1; + } + + data = pkt; + rnet = ntohs(rnet); + + while (data < (pkt+len)) { + net_lo = (data[0] << 8) | data[1]; + dist = data[2] & 0x1f; + if (data[2] & 0x80) { + /* extended tuples */ + net_hi = (data[3] << 8) | data[4]; + tuplelen = 6; + } else { + /* non-extended */ + net_hi = net_lo; + tuplelen = 3; + } + + modified = 0; + + if ((rtmp = rtmp_find(net_lo, net_hi)) != RTMPNULL) { + /* update the entry */ + if (rtmp->state == RTMP_BAD1 && dist < 15) { + /* replace entry */ + if (rtmp->rnet != rnet + || rtmp->rnode != rnode) + modified = 1; + rtmp->dist = dist+1; + rtmp->rnet = rnet; + rtmp->rnode = rnode; + rtmp->state = RTMP_GOOD; + } else { + if (rtmp->dist >= dist+1 && dist < 15) { + /* replace entry */ + if (rtmp->rnet != rnet + || rtmp->rnode != rnode) + modified = 1; + rtmp->dist = dist+1; + rtmp->rnet = rnet; + rtmp->rnode = rnode; + rtmp->state = RTMP_GOOD; + } else { + if (rtmp->rnet == rnet + && rtmp->rnode == rnode) { + /* net further away now */ + if (dist != 31) { + rtmp->dist = dist+1; + if (rtmp->dist < 16) + rtmp->state = RTMP_GOOD; + else { + rtmp_kernel_update(rtmp, RTMP_DELETED); + (void)rtmp_delete(rtmp); + } + } else + rtmp->state = RTMP_BAD1; + } + } + } + } else { + /* create a new entry */ + if ((rtmp = rtmp_new()) != RTMPNULL) { + rtmp->net_lo = net_lo; + rtmp->net_hi = net_hi; + rtmp->dist = dist+1; + rtmp->rnet = rnet; + rtmp->rnode = rnode; + rtmp->state = (dist == 31) ? RTMP_BAD1 : RTMP_GOOD; + rtmp_kernel_update(rtmp, RTMP_MODIFIED); + rtmp_insert(rtmp); + } + } + + if (data[2] & 0x80) + if (rtmp != RTMPNULL) + rtmp->flags |= RTMP_EXTENDED; + + if (modified) + rtmp_kernel_update(rtmp, RTMP_MODIFIED); + + data += tuplelen; + } + + return; +} + +/* + * RTMP validity timer, check & age each entry. + * + */ + +void +rtmp_timer() +{ + int i, nrtmp; + struct rtmp *rtmp, *rtmp_delete(); + void Timeout(), rtmp_timer(); + void rtmp_kernel_update(); + + for (i = 0, nrtmp = 0; i < RTMPTABSIZ; i++) { + rtmp = rtmpTab[i]; + while (rtmp != RTMPNULL) { + switch (rtmp->state) { + case RTMP_GOOD: + if (rtmp->dist != 0) + rtmp->state = RTMP_SUSP; + break; + case RTMP_SUSP: + rtmp->state = RTMP_BAD1; + break; + case RTMP_BAD1: + rtmp->state = RTMP_BAD2; + break; + case RTMP_BAD2: + rtmp_kernel_update(rtmp, RTMP_DELETED); + rtmp = rtmp_delete(rtmp); + continue; + break; + } + rtmp = rtmp->next; + nrtmp++; + } + } + +#ifdef DEBUG + for (i = 0; i < RTMPTABSIZ; i++) { + rtmp = rtmpTab[i]; + while (rtmp != RTMPNULL) { + logit(1, "%4x %c %4x -> net %4x node %3d dist %d state %d", + rtmp->net_lo, (rtmp->flags & RTMP_EXTENDED) ? '-' : ' ', + rtmp->net_hi, rtmp->rnet, rtmp->rnode, rtmp->dist, rtmp->state); + rtmp = rtmp->next; + } + } +#endif /* DEBUG */ + + logit(1, "Processed %d entries", nrtmp); + + /* + * schedule another call + * + */ + Timeout(rtmp_timer, 0, 40); + + return; +} + +/* + * exiting, delete all routes from kernel tables + * and reset the network number information. + * + */ + +void +rtmp_release() +{ + int i; + struct rtmp *rtmp; + int node, net, net_lo, net_hi; + void rtmp_kernel_update(); + + for (i = 0; i < RTMPTABSIZ; i++) { + rtmp = rtmpTab[i]; + while (rtmp != RTMPNULL) { + rtmp_kernel_update(rtmp, RTMP_DELETED); + rtmp = rtmp->next; + } + } + + node = 0x00; + net = 0xff00; + net_lo = 0x0000; + net_hi = 0xfffe; + + ifconfig(&node, &net, &net_lo, &net_hi); + + return; +} + +/* + * create a new RTMP element + * + */ + +struct rtmp * +rtmp_new() +{ + struct rtmp *rtmp; + + rtmp = (struct rtmp *)malloc(sizeof(struct rtmp)); + + if (rtmp == RTMPNULL) + return(RTMPNULL); + + rtmp->flags = 0x00; + + return(rtmp); +} + +/* + * free memory occupied by RTMP element + * + */ + +void +rtmp_free(rtmp) +struct rtmp *rtmp; +{ + free((char *)rtmp); + + return; +} + +/* + * insert RTMP tuple in linked list + * + */ + +void +rtmp_insert(rtmp) +struct rtmp *rtmp; +{ + int idx, rtmp_hash(); + struct rtmp *p, *q; + + idx = rtmp_hash(rtmp->net_lo, rtmp->net_hi); + + p = rtmpTab[idx]; + q = RTMPNULL; + + while (p != RTMPNULL) { + if (p->net_lo >= rtmp->net_lo) + break; + q = p; + p = p->next; + } + + rtmp->next = p; + + if (q == RTMPNULL) + rtmpTab[idx] = rtmp; + else + q->next = rtmp; + + return; +} + +/* + * delete RTMP tuple from linked list + * + */ + +struct rtmp * +rtmp_delete(rtmp) +struct rtmp *rtmp; +{ + int idx, rtmp_hash(); + struct rtmp *next, *p, *q; + void rtmp_free(); + + idx = rtmp_hash(rtmp->net_lo, rtmp->net_hi); + + p = rtmpTab[idx]; + q = RTMPNULL; + + while (p != RTMPNULL) { + if (p == rtmp) + break; + q = p; + p = p->next; + } + + if (p == RTMPNULL) + return(p); + + next = rtmp->next; + + if (q == RTMPNULL) + rtmpTab[idx] = next; + else + q->next = next; + + rtmp_free(rtmp); + + return(next); +} + +/* + * find an entry in the RTMP table. + * + */ + +struct rtmp * +rtmp_find(net_lo, net_hi) +u_short net_lo, net_hi; +{ + struct rtmp *rtmp; + int rtmp_hash(); + + rtmp = rtmpTab[rtmp_hash(net_lo, net_hi)]; + + while (rtmp != RTMPNULL) { + if (rtmp->net_lo == net_lo + && rtmp->net_hi == net_hi) + return(rtmp); + rtmp = rtmp->next; + } + + return(RTMPNULL); +} + +/* + * simple hash of high and low net numbers + * + */ + +int +rtmp_hash(net_lo, net_hi) +register u_short net_lo, net_hi; +{ + register int i, idx; + + for (i = 0; i < 17; i++) + if ((idx = ((net_lo>>i) & RTMPTABMSK)) == ((net_hi>>i) & RTMPTABMSK)) + return(idx); + + return(0); +} + +/* + * update (add) or delete entry in kernel routing table + * + */ + +void +rtmp_kernel_update(rtmp, what) +struct rtmp *rtmp; +int what; +{ +#ifdef linux + u_short net; + struct rtentry rt; + struct sockaddr_at *ad; + struct sockaddr_at *gw; + + if (rtmp == RTMPNULL) + return; + + ad = (struct sockaddr_at *)&rt.rt_dst; + gw = (struct sockaddr_at *)&rt.rt_gateway; + + ad->sat_family = AF_APPLETALK; + gw->sat_family = AF_APPLETALK; + ad->sat_port = 0; + gw->sat_port = 0; + ad->sat_addr.s_node = 0; + gw->sat_addr.s_node = rtmp->rnode; + gw->sat_addr.s_net = htons(rtmp->rnet); + + if (skt2fd[1] == -1) + return; + + /* + * the kernel only keeps routes to single networks, + * we have to add an entry for each possible net + * in each network range :-( + * + */ + for (net = rtmp->net_lo; net <= rtmp->net_hi; net++) { + ad->sat_addr.s_net = htons(net); + switch (what) { + case RTMP_MODIFIED: + rt.rt_flags = (RTF_GATEWAY|RTF_UP); + (void)ioctl(skt2fd[1], SIOCADDRT, &rt); + break; + case RTMP_DELETED: + rt.rt_flags = RTF_GATEWAY; + (void)ioctl(skt2fd[1], SIOCDELRT, &rt); + break; + } + } +#endif /* linux */ + + return; +} diff --git a/lib/cap/abkip.c b/lib/cap/abkip.c new file mode 100644 index 0000000..852729c --- /dev/null +++ b/lib/cap/abkip.c @@ -0,0 +1,695 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:48:17 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abkip.c,v 2.8 1996/06/18 10:48:17 djh Rel djh $ + * $Revision: 2.8 $ + * + */ + +/* + * abkip.c - KIP (UDP encapsulated DDP packets) network module, this + * file provides the interface from DDP to the outside world as it + * sees it. It includes the DDP module "routeddp". + * + * We have two delivery mechanisms: + * abkip provides the "standard KIP" DDP interface to a hardware bridge + * (via a range of UDP ports mapped from socket numbers). + * + * abmkip provides the interface from DDP to the UAB bridge using a + * modified form of KIP delivery (via port 903, standard KIP is unusable). + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 14, 1986 Schilit Created. + * June 18, 1986 CCKim Chuck's handler runs protocol + * + */ + +/* + * The following list of exported routines is provided so you'll know what + * have to be done to do another interface type (ethertalk, etc) + * + * EXPORTED ROUTINES: + * + * OSErr GetNodeAddress(int *mynode, int *mynet) + * Return node addresses + * OSErr GetBridgeAddress(AddrBlock *addr) + * Return bridge addresses + * OSErr SetBridgeAddress(AddrBlock *addr) + * Set bridge addresses + * int abInit(boolean dispay_message) + * Initialize AppleTalk + * int abOpen(int *returnsocket, int wantsocket, struct iovec iov[], iovlen) + * Open a DDP socket + * int abClose(int socket) + * Close a DDP socket + * void abnet_cacheit(word srcNet, byte srcNode) + * Call in DDP protocol layer to tell the lower layer that + * the last packet that came in was from srcNet, srcNode + * int routeddp(struct iovec *iov, int iovlen) + * This is the DDP incursion. With a full AppleTalk implementation, + * this would be part of DDP (abddp2). This routes the DDP packet: + * normally would decide where to send and then send via lap, with KIP + * decides where and sends via UDP. + * + */ + +/* + * Define the following if you have problems with arriving data being lost. + * By trial and error, 6k seems like a good place to start. + */ +#define SOCK_BUF_SIZE 6 * 1024 +/* + */ + +#include +#include +#include +#include +#ifndef linux +#include +#endif /* linux */ +#include +#include +#include +#include +#ifndef UAB_MKIP +#include /* for nbpNIS */ +#endif UAB_MKIP +#include /* to cover difference between bsd systems */ + +/* imported network information */ + +extern word this_net, bridge_net, nis_net, async_net; +extern byte this_node, bridge_node, nis_node; +extern char this_zone[34], async_zone[34]; +extern struct in_addr bridge_addr; + +#ifndef UAB_MKIP +short lap_proto = LAP_KIP; /* standard KIP */ +#else UAB_MKIP +short lap_proto = LAP_MKIP; /* modified KIP (UAB) */ +#endif UAB_MKIP + +/* + * Configuration defines + * + * NORECVMSG - no recvmsg + * NOSENDMSG - no sendmsg + * NEEDMSGHDR - no msghdr in sockets.h - define our own + * +*/ +#ifdef NORECVMSG +# ifndef NEEDNETBUF +# define NEEDNETBUF +# endif +#endif +#ifdef NOSENDMSG +# ifndef NEEDNETBUF +# define NEEDNETBUF +# endif +#endif + +#ifdef NEEDNETBUF +#ifdef NEEDMSGHDR +struct msghdr { + caddr_t msg_name; /* name to send to */ + int msg_namelen; /* size of name */ + struct iovec *msg_iov; /* io vec */ + int msg_iovlen; /* length */ + int msg_accrights; /* dummy */ + int msg_accrightslen; +}; +#endif NEEDMSGHDR +#endif NEEDNETBUF + +struct msghdr msgr; +struct msghdr msgw; + +/* for forwarding using ip_resolve */ +private struct in_addr ipaddr_src; /* ip address */ +private word ddp_srcnet; /* ddp network part */ +private byte ddp_srcnode; /* ddp node part */ + +private struct sockaddr_in from_sin; /* network struct of last packet rec. */ +private struct sockaddr_in abfsin; /* apple bus foreign socketaddr/internet */ + +private word portrange; /* old or new (200|768) */ +word rebport; /* used to hold swabbed rebPort */ +#define rebPort 902 /* 0x386 for normal KIP */ +#define mrebPort 903 /* 0x387 for MKIP with UAB */ + +#ifdef UAB_MKIP +private int abfd; /* outgoing fd for UAB */ +struct in_addr xdesthost; /* localhost address */ +#endif UAB_MKIP + +int read_buf_len; /* dummy for absched.c */ + +import int ddp_protocol(); /* DDP protocol handler */ +private int kip_get(); /* our KIP listener */ +export DBUG dbug; /* debug flags */ + +/* BUG: bind doesn't work when lsin is on the stack! */ +private struct sockaddr_in lsin; /* local socketaddr/internet */ +private int skt2fd[ddpMaxSkt+1]; /* translate socket to file descriptor */ + +#ifndef UAB_MKIP +private int ddpskt2udpport[ddpMaxWKS+1]; /* ddp "wks" socket to udp port */ +#endif UAB_MKIP + +private LAP laph; + +/* + * OSErr GetNodeAddress(int *myNode,*myNet) + * + * GetNodeAddress returns the net and node numbers for the current + * host. + * + * N.B. - the myNet address is in net (htons) format. + * +*/ + +export OSErr +GetNodeAddress(myNode,myNet) +int *myNode,*myNet; +{ + *myNode = this_node; + *myNet = this_net; + return(noErr); /* is ok */ +} + +#ifndef UAB_MKIP +/* + * ddp to udp wks translate table + * +*/ +struct wks { + char *name; /* name of wks (as in /etc/services) */ + int ddpport; /* ddp port to map from */ + int udpport; /* udp port to map to */ + int notinited; /* tried /etc/services? */ +}; + +/* udpport is initially set to the old (768) values for compatiblity */ +#define WKS_entry(name, ddpsock) {(name), (ddpsock), ddpWKSUnix+(ddpsock), 1} + +private struct wks wks[] = { + WKS_entry("at-rtmp",rtmpSkt), + WKS_entry("at-nbp",nbpNIS), + WKS_entry("at-echo",echoSkt), + WKS_entry("at-zis",zipZIS), + WKS_entry(NULL, 0) +}; + +/* + * Translate ddp socket to UDP port: returns 0 if no mapping + * +*/ +word +ddp2ipskt(ddpskt) +int ddpskt; +{ + struct wks *wksp; + struct servent *serv; + + if (ddpskt < 0 || ddpskt > ddpMaxSkt) + return(0); + + if (ddpskt & ddpWKS) /* 128+x means non-wks */ + return(ddpskt + ddpNWKSUnix); + +#ifdef STEAL_PORTS + if (ddpskt >= ddpEWKS) /* 64-128 experimental */ + return(ddpskt + ddpNWKSUnix); +#endif STEAL_PORTS + + if (ddpskt2udpport[ddpskt] < 0) { + for (wksp = wks; wksp->name != NULL; wksp++) + if (wksp->ddpport == ddpskt) { + if ((serv = getservbyname(wksp->name, "udp")) != NULL) + wksp->udpport = ntohs(serv->s_port); /* replace with new */ + if (dbug.db_ini) + fprintf(stderr, "port for %s is %d\n",wksp->name,wksp->udpport); + endservent(); + ddpskt2udpport[ddpskt] = wksp->udpport; + return(wksp->udpport); + } + ddpskt2udpport[ddpskt] = 0; + } + return(ddpskt2udpport[ddpskt]); +} + +#else UAB_MKIP + +word +ddp2ipskt(skt) +int skt; +{ +#ifdef STEAL_PORTS + if (skt >= ddpEWKS) + return(skt+ddpNWKSUnix); +#endif STEAL_PORTS + return((skt&0x80) ? (skt+ddpNWKSUnix) : (skt+portrange)); +} +#endif UAB_MKIP + + +/* + * initialize + * +*/ +export +abInit(disp) +{ + int i; + private word getPRange(); + + for (i=0; i < ddpMaxSkt+1; i++) { + skt2fd[i] = -1; /* mark all these as unused */ + } +#ifndef UAB_MKIP + for (i=0; i < ddpMaxWKS; i++) + ddpskt2udpport[i] = -1; /* mark unknown */ +#endif UAB_MKIP + + bzero((char *)&msgr, sizeof(msgr)); + bzero((char *)&msgw, sizeof(msgw)); + + rebport = htons(rebPort); /* swap to netorder */ + portrange = getPRange(); /* which port range to use ? */ + init_fdlistening(); + +#ifdef UAB_MKIP + /* no need to bind since we don't recv on this socket, just send... */ + if ((abfd = socket(AF_INET,SOCK_DGRAM,0)) < 0) { + perror("abinit"); + return(abfd); + } +# ifdef SOCK_BUF_SIZE +# ifdef SO_RCVBUF + { long len = SOCK_BUF_SIZE; + if(setsockopt(abfd, SOL_SOCKET, SO_SNDBUF, (char *)&len, sizeof(long)) != 0) + fprintf(stderr, "Couldn't set socket options\n"); + } +# endif SO_RCVBUF +# endif SOCK_BUF_SIZE +#endif UAB_MKIP + abfsin.sin_family = AF_INET; + abfsin.sin_addr.s_addr = INADDR_ANY; +#ifndef UAB_MKIP + openatalkdb(NULL); /* use default file, sets up variables this_* etc. */ +#else UAB_MKIP + openetalkdb(NULL); /* use default file, sets up variables this_* etc. */ + xdesthost.s_addr = inet_addr("127.0.0.1"); + if (xdesthost.s_addr == -1) + return(-1); +#endif UAB_MKIP + if (disp) { + printf("abInit: [ddp: %3d.%02d, %d]", + ntohs(this_net)>>8, htons(this_net)&0xff, this_node); + if (this_net != nis_net || this_node != nis_node) + printf(", [NBP (atis) Server: %3d.%02d, %d]", + ntohs(nis_net)>>8, htons(nis_net)&0xff, nis_node); + printf(" starting\n"); + } + DDPInit(); + return(0); +} + +/* + * int abOpen(int *skt,rskt, iov, iovlen) + * + * abOpen opens the ddp socket in "skt" or if "skt" is zero allocates + * and opens a new socket. Upon return "skt" contains the socket number + * and the returned value is >=0 if no error, < 0 if error. + * + * iov should be an array of type "struct iov" of length at least + * IOV_LAP_SIZE+1. Levels after IOV_LAP_LVL are assume to filled. + * + */ + +int abOpen(skt,rskt, iov, iovlen) +int *skt; +int rskt; +struct iovec *iov; +int iovlen; +{ + int i,fd,err; + int sktlimit = 128; + word ipskt, ddp2ipskt(); + + /* good enough for now */ + if (iov == NULL || iovlen < IOV_LAP_SIZE+1 || iovlen > IOV_READ_MAX) + return(-1); + + if ((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0) { + perror("abopen"); + return(fd); + } +#ifdef SOCK_BUF_SIZE +# ifdef SO_RCVBUF + { long len = SOCK_BUF_SIZE; + if(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&len, sizeof(long)) != 0) + fprintf(stderr, "Couldn't set socket options\n"); + } +# endif SO_RCVBUF +#endif SOCK_BUF_SIZE + + lsin.sin_family = AF_INET; + lsin.sin_addr.s_addr = INADDR_ANY; + +#ifdef STEAL_PORTS + *skt = (rskt == 0 ? ddpEWKS : rskt); /* zero rskt is free choice */ + sktlimit += 64; +#else STEAL_PORTS + *skt = (rskt == 0 ? ddpWKS : rskt); /* zero rskt is free choice */ +#endif STEAL_PORTS + ipskt = ddp2ipskt(*skt); /* translate into ip socket number */ + if (ipskt == 0) /* bad socket? */ + return(ddpSktErr); + for (i=0; i < sktlimit; i++,ipskt++,(*skt)++) { + lsin.sin_port = htons(ipskt); + if ((err = bind(fd, (struct sockaddr *)&lsin, sizeof(lsin))) == 0) + break; + if (rskt != 0) /* bind failed and wanted exact? */ + return(err); /* yes... */ + } + if (err == 0 && i < sktlimit) { + iov[IOV_LAP_LVL].iov_base = (caddr_t)&laph; /* remember this */ + iov[IOV_LAP_LVL].iov_len = lapSize; /* and this */ + fdlistener(fd, kip_get, iov, iovlen); /* remember for later */ + skt2fd[*skt] = fd; /* remember file descriptor for socket */ + return(noErr); + } + perror("abopen bind"); + close(fd); + return(err); +} + +/* + * close off socket opened by abOpen() + * +*/ +export int +abClose(skt) +int skt; +{ + int fd; + + if (skt < 0 || skt > ddpMaxSkt) { + fprintf(stderr,"abClose: skt out of range\n"); + exit(0); + } + fd = skt2fd[skt]; + if (fd < 0) + return(0); + if (close(fd) != 0) + perror("abClose"); /* some error... */ + fdunlisten(fd); + skt2fd[skt] = -1; /* mark as unused */ + return(0); +} + +#ifdef NEEDNETBUF + +/* buffer larger than maximum ddp pkt by far */ +private char net_buffer[ddpMaxData*2]; + +#ifdef NOSENDMSG +/* + * limited sendmsg - limits to sizeof(net_buffer) + * +*/ +sendmsg(fd, msg, flags) +int fd; +struct msghdr *msg; +int flags; +{ + int err; + int i, pos, len; + struct iovec *iov; + + iov = msg->msg_iov; + for (i=0, pos=0; i < msg->msg_iovlen; i++, iov++) { + len = iov->iov_len; + if (len+pos > sizeof(net_buffer)) /* if overflow */ + len = sizeof(net_buffer)-pos; /* then limit */ + bcopy(iov->iov_base, net_buffer+pos, len); + pos+= len; + if (len != iov->iov_len) /* we don't have any more space */ + break; + } + len = pos; +#ifndef UAB_MKIP + if ((err=sendto(fd,net_buffer,len,0,msg->msg_name,msg->msg_namelen)) < 0) +#else UAB_MKIP + if ((err=sendto(abfd,net_buffer,len,0,msg->msg_name,msg->msg_namelen)) < 0) +#endif UAB_MKIP + perror("abwrite"); + return(err); +} +#endif NOSENDMSG + +#ifdef NORECVMSG +recvmsg(fd, msg, flags) +int fd; +struct msghdr *msg; +int flags; +{ + int err; + int i, pos, len, blen; + struct iovec *iov; + + err = recvfrom(fd, net_buffer, sizeof(net_buffer), 0, + msg->msg_name, &msg->msg_namelen); + if (err < 0) + perror("abread"); + for (blen=err,pos=0,i=0,iov=msg->msg_iov; i < msg->msg_iovlen; i++, iov++) { + len = min(iov->iov_len, blen); + if ((pos + len) > sizeof(net_buffer)) /* if asking for too much */ + len = sizeof(net_buffer) - pos; /* then limit */ + bcopy(net_buffer+pos, iov->iov_base, len); + pos += len; + blen -= len; + /* either no more room or no more data */ + if (len != iov->iov_len) + break; + } + return(err); +} +#endif NORECVMSG +#endif NEEDNETBUF + +#ifdef notdef +abwrite(addr, skt, iov,iovlen) +struct in_addr addr; +unsigned short skt; +struct iovec *iov; +{ + int err; + + abfsin.sin_addr = addr; + abfsin.sin_port = skt; + msgw.msg_name = (caddr_t) &abfsin; + msgw.msg_namelen = sizeof(abfsin); + msgw.msg_iov = iov; + msgw.msg_iovlen = iovlen; + if ((err = sendmsg(abfd,&msgw,0)) < 0) + perror("abwrite"); + return(err); +} + +abread(fd, iov, iovlen) +struct iovec *iov; +{ + int err; + + msgr.msg_name = (caddr_t) &from_sin; + msgr.msg_namelen = sizeof(from_sin); + msgr.msg_iov = iov; + msgr.msg_iovlen = iovlen; + if ((err = recvmsg(fd,&msgr,0)) < 0) + perror("abread"); + return(err); +} +#endif notdef + +/* + * the KIP listener + */ + +private int +kip_get(fd, iov, iovlen) +int fd; +struct iovec *iov; +int iovlen; +{ + int len; + LAP *lap; + + msgr.msg_name = (caddr_t) &from_sin; + msgr.msg_namelen = sizeof(from_sin); + msgr.msg_iov = iov; + msgr.msg_iovlen = iovlen; + if ((len = recvmsg(fd,&msgr,0)) < 0) { + perror("abread"); + return(len); + } + if (iov->iov_len != lapSize) /* check */ + return(-1); + lap = (LAP *)iov->iov_base; + switch (lap->type) { + case lapDDP: + return(ddp_protocol(iov+1, iovlen-1, len-lapSize)); + break; + default: + return(-1); + } + /* return(-1); */ +} + +/* + * This is the DDP/UDP interface + * +*/ + +/* srcNet and node of last incoming packet sent to DDP */ +/* and valid */ +export void +abnet_cacheit(srcNet, srcNode) +word srcNet; +byte srcNode; +{ + ddp_srcnet = srcNet; /* remember where last packet came from */ + ddp_srcnode = srcNode; + ipaddr_src.s_addr = (from_sin.sin_port == rebport) ? 0 : + from_sin.sin_addr.s_addr; +} + +private int +ip_resolve(ddpnet, ddpnode, iphost) +word ddpnet; +byte ddpnode; +struct in_addr *iphost; +{ + if (ipaddr_src.s_addr != 0 && ddpnet == ddp_srcnet && ddpnode == ddp_srcnode) + iphost->s_addr = ipaddr_src.s_addr; + else + iphost->s_addr = bridge_addr.s_addr; +} + +private LAP lap; + +export int +routeddp(iov, iovlen) +struct iovec *iov; +int iovlen; +{ + word destskt, ddp2ipskt(); + struct in_addr desthost; + DDP *ddp; + int err; + int fd; + + ddp = (DDP *)iov[IOV_DDP_LVL].iov_base; /* pick out ddp header */ + + /* check ddp socket(s) for validity */ + if ( ddp->srcSkt == 0 || ddp->srcSkt == ddpMaxSkt || + ddp->dstSkt == 0 || ddp->dstSkt == ddpMaxSkt || + (fd = skt2fd[ddp->srcSkt]) == -1 ) + return(ddpSktErr); + + /* KIP routing code */ + /* establish dest socket */ + destskt = (word)htons(ddp2ipskt(ddp->dstSkt)); + if (destskt == 0) /* byte swapped zero is still zero */ + return(ddpSktErr); + /* resolve mapping */ + ip_resolve(ddp->dstNet, ddp->dstNode, &desthost); + + /* establish a dummy lap header */ + lap.type = lapDDP; + lap.dst = ddp->dstNode; + lap.src = this_node; + iov[IOV_LAP_LVL].iov_base = (caddr_t) ⪅ /* LAP header */ + iov[IOV_LAP_LVL].iov_len = lapSize; /* size */ + + /* send through */ +#ifndef UAB_MKIP + abfsin.sin_addr = desthost; + abfsin.sin_port = destskt; +#else UAB_MKIP + abfsin.sin_addr = xdesthost; + abfsin.sin_port = htons(mrebPort); +#endif UAB_MKIP + msgw.msg_name = (caddr_t) &abfsin; + msgw.msg_namelen = sizeof(abfsin); + msgw.msg_iov = iov; + msgw.msg_iovlen = iovlen; +#ifndef UAB_MKIP + if ((err = sendmsg(fd,&msgw,0)) < 0) +#else UAB_MKIP + if ((err = sendmsg(abfd,&msgw,0)) < 0) +#endif UAB_MKIP + perror("abwrite"); + return(err); +} + +private word +getPRange() +{ + struct servent *getservbyname(); + + if(getservbyname("at-rtmp", "udp") == NULL) + return(ddpWKSUnix); /* 768 */ + else + return(ddpOWKSUnix); /* 200 */ +} + +/* + * avoid using global variables + * + */ + +OSErr +GetBridgeAddress(addr) +AddrBlock *addr; +{ + addr->net = bridge_net; + addr->node = bridge_node; + return(noErr); +} + +OSErr +SetBridgeAddress(addr) +AddrBlock *addr; +{ + bridge_net = addr->net; + bridge_node = addr->node; + return(noErr); +} + +#ifdef linux +/* + * dummy routines for loader + * + */ +void +rtmp_timer() +{ +} +void +rtmp_data() +{ +} +void +rtmp_release() +{ +} +#endif /* linux */ diff --git a/lib/cap/ablap.c b/lib/cap/ablap.c new file mode 100644 index 0000000..5f81068 --- /dev/null +++ b/lib/cap/ablap.c @@ -0,0 +1,179 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:47:37 $ + * $Header: ablap.c,v 2.1 91/02/15 22:47:37 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * ablap.c - Link Access Protocol + * + * As of CAP 5.0, this module has been dropped. It is included for + * reference. It never really made sense with DDP encapsulation anyway + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986 by The Trustees of Columbia University in the + * City of New York. + * + * + * Edit History: + * + * June 19, 1986 Schilit Created. + * July 2, 1986 Schilit Most is not yet implemented + * July 5, 1986 CCKim Most will never be implemented + * March 1988 CCKim DUMP IT! + * + */ + +#include +#include +#include +#include + +/* + * lapp - table of LAP protocol handlers. + * +*/ + +struct { + ProcPtr pproc; /* LAP protocol handler */ +} lapp[lapMaxProto]; /* number of protocols */ + + +typedef struct { + LAP lap; + char data[lapMaxData]; +} ABpkt; /* for lap_read */ + +ABpkt rdpkt; + +/* + * OSErr LAPOpenProtocol(int type, ProcPtr proto) + * + * LAPOpenProtocol adds the LAP protocol type specified in "type" + * to the protocol table. If you provide a pointer to a protocol + * handler in "proto" then each frame with a LAP protocol type of + * "type" will be sent to that handler. + * + * If "proto" is NILPROC then the default handler will be used for + * receiving frames with a LAP protocol type of "type." In this + * case you must call LAPRead to provide the default protocol handler + * with buffer for storing the data. If however you have written your + * own protocol handler and "proto" points to it, your protocol handler + * will be responsible for receiving the frame and it is not necessary + * to call LAPRead. + * +*/ + +OSErr +LAPOpenProtocol(type,proto) +int type; +ProcPtr proto; +{ + int defLAPProto(); + + if (type < 0 || type > 127) { /* type in range? */ + fprintf(stderr,"LAPOpenProtocol: type (%d) not in range 1..127\n",type); + exit(1); + } + lapp[type].pproc = (proto == NILPROC) ? defLAPProto : proto; + return(noErr); +} + +/* + * OSErr LAPCloseProtocol(int type) + * + * LAPCloseProtocol removes the protocol specified by "type" from + * the LAP protocol handler table. + * +*/ + +OSErr +LAPCloseProtocol(type) +int type; +{ + if (type < 2 || type > 127) { /* type in range? */ + fprintf(stderr,"LAPCloseProtocol: type (%d) not in range 3..127\n",type); + exit(1); + } + lapp[type].pproc = NILPROC; /* no more handler */ +} + +/* + * OSErr LAPRead(abRecPtr abr,int async) + * + * LAPRead receives a frame from another node. + * +*/ + +OSErr +LAPRead(abr,async) +abRecPtr abr; +int async; +{ + fprintf(stderr,"LAPRead NYI\n"); +} + +OSErr +LAPRdCancel(abr) +abRecPtr abr; +{ + fprintf(stderr,"LAPRead NYI\n"); +} + +/* + * OSErr LAPWrite(abRecPtr abr) + * + * LAPWrite send a frame to another node. Currently this does + * not work because of the limitations in the kinetics bridge + * box. + * +*/ + +OSErr +LAPWrite(abr) +abRecPtr abr; +{ + fprintf(stderr,"LAPWrite NYI\n"); +} + +/* + * lap_read(int fd) + * + * Read the data off the net and dispatch to the appropriate + * protocol handler. + * +*/ + +lap_read(fd) +int fd; +{ + struct iovec iov[2]; + int lpt,len; + + iov[0].iov_base = (caddr_t) &rdpkt.lap; + iov[0].iov_len = lapSize; + iov[1].iov_base = (caddr_t) rdpkt.data; + iov[1].iov_len = lapMaxData; + if ((len = abread(fd,&iov[0],2)) < 0) /* do the read */ + return(len); /* failed, return now... */ + lpt = rdpkt.lap.type; /* get the protocol type */ + if (lpt < 1 || lpt > 127) + return(-1); /* ugh... */ + if (lapp[lpt].pproc == NILPROC) /* any protocol handler? */ + return(-1); /* no, ugh again */ + return((*lapp[lpt].pproc)(rdpkt.data,len-lapSize)); /* call handler */ +} + +/* + * defLAPProto(int fd) + * + * defLAPProto is not used. + * +*/ + +defLAPProto(fd) +int fd; +{ + fprintf(stderr,"defLAPProto NYI %d\n",fd); +} diff --git a/lib/cap/ablog.c b/lib/cap/ablog.c new file mode 100644 index 0000000..b7c01d9 --- /dev/null +++ b/lib/cap/ablog.c @@ -0,0 +1,182 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/05/29 10:45:18 $"; +static char rcsident[] = "$Header: /mac/src/cap60/lib/cap/RCS/ablog.c,v 2.5 1995/05/29 10:45:18 djh Rel djh $"; +static char revision[] = "$Revision: 2.5 $"; + +/* + * ablog.c - simple logging facility + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * Aug, 1988 CCKim created + * +*/ + +#include +#include +#include +#include + +#ifdef USEVPRINTF +# include +#endif USEVPRINTF +#ifdef USETIMES +# include +#endif USETIMES + +/* current debug level */ +static int dlevel; + +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + +/* + * set debug level + * +*/ +get_debug_level() +{ + return(dlevel); +} +set_debug_level(n) +int n; +{ + dlevel = n; +} + +/* + * print message - use vprintf whenever possible (solves the problem + * of using the varargs macros -- you must interpret the format). + * This is something all machine should, but don't have :-) + */ + +static FILE *lfp = stderr; + + +#ifndef USEVPRINTF +/* Bletch - gotta do it because pyramids don't work the other way */ +/* (using _doprnt and &args) and don't have vprintf */ +/* of course, there will be something that is just one arg larger :-) */ +/*VARARGS1*/ +logit(level, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +int level; +char *fmt; +#else USEVPRINTF +/*VARARGS*/ +logit(va_alist) +va_dcl +#endif USEVPRINTF +{ + static char *mytod(); +#ifdef USEVPRINTF + register char *fmt; + va_list args; + int level; +#endif USEVPRINTF + int saveerr; + extern int errno; + extern int sys_nerr; +#ifndef __FreeBSD__ + extern char *sys_errlist[]; +#endif + + if (lfp == NULL) /* no logging? */ + return; + + saveerr = errno; +#ifdef USEVPRINTF + va_start(args); + level = va_arg(args, int); + fmt = va_arg(args, char *); +#endif USEVPRINTF + + if (dlevel < (level & L_LVL)) + return; + + fprintf(lfp,"%s ",mytod()); + +#ifdef USEVPRINTF + vfprintf(lfp, fmt, args); + va_end(args); +#else USEVPRINTF + fprintf(lfp, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); +#endif USEVPRINTF + if (level & L_UERR) { + if (saveerr < sys_nerr) + fprintf(lfp, ": %s", sys_errlist[saveerr]); + else + fprintf(lfp, ": error %d\n", saveerr); + } + putc('\n', lfp); + fflush(lfp); + if (level & L_EXIT) + exit(1); +} + +islogitfile() +{ + if (lfp == stderr) + return(FALSE); + return(lfp != NULL); +} + +logitfileis(filename, mode) +char *filename; +char *mode; +{ + FILE *fp; + + if ((fp = fopen(filename, mode)) != NULL) { + logit(0, "log file name %s", filename); + } else { + logit(0|L_UERR, "couldn't open logfile %s", filename); + } + lfp = fp; /* reset */ +} + +nologitfile() +{ + if (lfp && lfp != stderr) + fclose(lfp); + lfp = NULL; +} + +/* + * return pointer to formatted tod in static buffer + * +*/ +static char * +mytod() +{ + long tloc; + struct tm *tm, *localtime(); + static char buf[100]; /* should be large enough */ + + (void)time(&tloc); + tm = localtime(&tloc); + if (tm->tm_year > 99) + sprintf(buf, "%02d:%02d:%02d %02d/%02d/%04d ", + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_mon+1, tm->tm_mday, tm->tm_year+1900); + else + sprintf(buf, "%02d:%02d:%02d %02d/%02d/%02d ", + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_mon+1, tm->tm_mday, tm->tm_year); + return(buf); +} diff --git a/lib/cap/abmisc.c b/lib/cap/abmisc.c new file mode 100644 index 0000000..e930c18 --- /dev/null +++ b/lib/cap/abmisc.c @@ -0,0 +1,626 @@ +/* + * $Author: djh $ $Date: 1996/05/01 15:30:26 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abmisc.c,v 2.9 1996/05/01 15:30:26 djh Rel djh $ + * $Revision: 2.9 $ + * + */ + +/* + * abmisc.c - miscellaneous, but nevertheless useful routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 13, 1986 Schilit Created. + * June 15, 1986 CCKim move to abmisc.c + * + */ + +#include +#include +#include +#include +#include + +/* + * cpyc2pstr(char *ps, char *cs) + * + * Copy a C style string to an Apple Pascal style string + * + * Restrictions: sizeof(*ps) must be >= 1+sizeof(*cs) + */ + +void +cpyc2pstr(ps,cs) +char *ps,*cs; +{ + + *ps++ = (u_char) strlen(cs); /* copy in length, one byte */ + while (*ps++ = *cs++) ; /* copy in the rest... */ +} + +/* + * cpyp2cstr(char *cs, char *ps) + * + * Copy a Apple Pascal style string to C string + * + */ + +void +cpyp2cstr(cs,ps) +char *cs,*ps; +{ + bcopy(ps+1,cs,(u_char) *ps); + cs[*ps] = '\0'; /* tie off */ +} + +/* + * pstrcpy(d, s) - like strcpy, but for pascal strings + * +*/ +pstrcpy(d, s) +byte *d, *s; +{ + int len = (int)*s; + + bcopy(s, d, len+1); /* +1 for length too */ +} + +/* like strncpy, but for pascal strings */ +pstrncpy(d, s, n) +byte *s; +byte *d; +int n; +{ + int len = (int)*s; + + if (len > n) + len = n; + bcopy(s+1, d+1, len); + *d = len&0xff; /* single byte */ +} +/* + * pstrlen(s) - like strlen, but for pascal strings + * +*/ +pstrlen(s) +byte *s; +{ + return((int)*s); +} + +/* + * pstrcmp(s1, s2) - like strcmp, but for pascal strings + * +*/ +pstrcmp(s1, s2) +byte *s1; +byte *s2; +{ + int len1 = ((int)*s1)+1; /* account for length */ + + s1++, s2++; /* skip length fields */ + /* no special check for length since this will also do the "right" thing */ + /* (also, know we have at least one) */ + while (len1--) { + if (*s1 != *s2) + return(*s1 - *s2); /* returns right thing? */ + s1++, s2++; + } + return(0); /* equal */ +} + +/* + * dbugarg(char *argv) + * + * Process the -d argument from the command line, setting debug options + * in global dbug structure. + * + * a - db_atp; l - db_lap; d - db_ddp; n - db_nbp; + * i - db_ini; p - db_pap; + * +*/ + +dbugarg(s) +char *s; +{ + int err = 0; + struct cap_version *v, *what_cap_version(); + + while (*s != '\0' && !err) { + switch(*s) { + case 'l': dbug.db_lap = TRUE; printf("debugging LAP\n"); break; + case 'd': dbug.db_ddp = TRUE; printf("debugging DDP\n"); break; + case 'a': dbug.db_atp = TRUE; printf("debugging ATP\n"); break; + case 'n': dbug.db_nbp = TRUE; printf("debugging NBP\n"); break; + case 'p': dbug.db_pap = TRUE; printf("debugging PAP\n"); break; + case 'i': dbug.db_ini = TRUE; printf("debugging INI\n"); break; + case 's': dbug.db_asp = TRUE; printf("debugging ASP\n"); break; + case 'k': dbug.db_skd = TRUE; printf("debugging SKD\n"); break; + case 'v': /* print useful version information and exit */ + v = what_cap_version(); + printf("%s version %d.%d, patch level %d, %s, %s\n%s\n", + v->cv_name, v->cv_version, v->cv_subversion, + v->cv_patchlevel, v->cv_rmonth, v->cv_ryear, v->cv_type); + exit(0); + break; + default: + err++; + } + s++; + } + if (err) { + fprintf(stderr,"l lap, d ddp, a atp, n nbp, p pap, i ini, s asp\n"); + fprintf(stderr,"k scheduler\n"); + return(-1); + } + return(0); +} + +/* + * Establish a bds (of size most numbds) with buffer buf of size bufsiz + * set all bds userdata fields to userdata + * + * return count of bds's. More buffer than bds is okay. + * +*/ +int +setup_bds(bds, numbds, segsize, buf, bufsiz, userdata) +BDS bds[]; +int numbds; +int segsize; +char *buf; +int bufsiz; +atpUserDataType userdata; +{ + int cnt, i, cursize; + + i = cnt = 0; + do { + bds[i].userData = userdata; + cursize = min(segsize, bufsiz-cnt); + bds[i].buffSize = cursize; + bds[i].buffPtr = buf+cnt; + cnt += cursize; + i++; + } while ((cnt - bufsiz) < 0 && i < numbds); + return(i); +} + +int +sizeof_bds(bds, numbds) +BDS bds[]; +int numbds; +{ + int i, cnt; + + for (i = 0, cnt = 0; i < numbds; i++) + cnt += bds[i].dataSize; + return(cnt); +} + +/* routines to deal with "Indexed" strings */ +/**** should be in abmisc ******/ + +/* + * void IniIndStr(byte *istr) + * + * Initialize an Indexed string for calls to AddIndStr + * + */ + +void +IniIndStr(istr) +byte *istr; +{ + *istr = 0; /* index count is zero */ +} + +/* + * void AddIndStr(char *src,byte *istr) + * + * + * Add a c string to a Indexed string. The c string always + * is added to the end of the indexed string. + * + */ + +void +AddIndStr(src,istr) +char *src; +byte *istr; +{ + byte *idx = istr; + int i; + + for (i = *istr++; i > 0; i--) /* step past index, count down on it */ + istr += (*istr)+1; /* move past each pascal string */ + cpyc2pstr(istr,src); /* copy into idx string */ + (*idx)++; /* increment the index */ +} + +/* + * GetIndStr(char *dest,byte *istr,int index) + * + * Copy from an indexed string into a c string. Use index to select + * the string entry within the indexed string type. + * + */ + +void +GetIndStr(dest,istr,idx) +char *dest; +byte *istr; +int idx; +{ + if (idx > 255 || idx >= (int)*istr || idx < 0) { + fprintf(stderr,"GetIndString: idx out of range\n"); + return; + } + + istr++; /* step past idx count */ + while (idx--) + istr += (*istr)+1; /* step past this pstr */ + cpyp2cstr(dest,istr); /* then copy into destination */ +} + + +/* + * int IndStrCnt(byte *istr) + * + * Return the count of entries in an indexed string. + * + */ + +int +IndStrCnt(istr) +byte *istr; +{ + return(*istr); /* this is easy... */ +} + + +/* + * IndStrLen(byte *istr) + * + * Return the length of the indexed string istr including the count byte. + * + */ + +int +IndStrLen(istr) +byte *istr; +{ + byte *idx = istr; + int i; + + istr++; /* step past index */ + for (i=1; i <= (int)*idx; i++) + istr += (*istr)+1; /* move to next pascal string */ + return((istr-idx)); /* here is the count... */ +} + +/* + * PrtIndStr(istr) + * + * For debugging, dump the content indexed string. + * + */ + +PrtIndStr(istr) +byte *istr; +{ + char cstr[256]; + byte *idx = istr; + int i; + + printf("Entries in indexed string: %d\n",*istr); + istr++; /* step past index */ + for (i=1; i <= (int)*idx; i++) { + cpyp2cstr(cstr,istr); /* copy into c string */ + printf("%d: '%s'\n",i,cstr); + istr += (*istr)+1; /* move to next pascal string */ + } +} + + +/* + * int strcmpci(char *s, char *t) + * + * Case insensitive version of strcmp. + * +*/ + +strcmpci(s,t) +u_char *s,*t; +{ + register char c,d; + + for (;;) { + c = *s++; + if (isascii(c) && isupper(c)) + c = tolower(c); + d = *t++; + if (isascii(d) && isupper(d)) + d = tolower(d); + if (c != d) + return(c-d); + if (c == '\0') + return(0); + } +} + +/* + * int strncmpci(char *s, char *t,int n) + * + * Case insensitive version of strcmp. + * +*/ + +strncmpci(s,t,n) +char *s,*t; +int n; +{ + register char c,d; + + for (;n > 0;n--) { + c = *s++; + if (isascii(c) && isupper(c)) + c = tolower(c); + d = *t++; + if (isascii(d) && isupper(d)) + d = tolower(d); + if (c != d) + return(c-d); + if (c == '\0') + return(0); + } + return(0); /* success on runnout */ +} + +/* + * Take a string pointed to by p and duplicate it in a malloc'ed block + * of memory. + * + * If the passed pointer is null, a null string is returned. + * + * returns: pointer to new string, null if no space + * +*/ +char * +strdup(p) +char *p; +{ + char *r = (char *)malloc((p == NULL) ? 1 : (strlen(p)+1)); + + if (r == NULL) + return(NULL); + if (p == NULL) { + *r = 0; + return(r); + } + strcpy(r, p); + return(r); +} + +/* + * int + * cmptime (struct timeval *t1,*t2) + * + * values must be pairwise across or + * + * + * cmptime compares two time values and returns an integer + * greater than, equal to, or less than zero according to + * whether the time represented in t1 is greater than, equal + * to, or less than t2. + * +*/ +int +cmptime(t1,t2) +struct timeval *t1,*t2; +{ + if ((t1->tv_sec == t2->tv_sec)) /* seconds the same? */ + if (t1->tv_usec == t2->tv_usec) /* and usec the same? */ + return(0); /* then equal */ + else /* otherwise depends on usec */ + return((t1->tv_usec < t2->tv_usec) ? -1 : 1); + return((t1->tv_sec < t2->tv_sec) ? -1 : 1); +} + +#ifdef ISO_TRANSLATE +/* + * Macintosh/ISO Character Translation Routines + * + */ + +/* + * Table for translating Macintosh characters 0x80-0xff to ISO + * (Macintosh characters 0x00-0x7f map directly to ISO equivalent). + * + * The top half of the table contains 0x00 in positions where + * no reversible character mapping exists, the bottom half of + * the table contains a reversible mapping for text translation. + * + */ + +u_char Mac2ISO[256] = { + 0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1, /* 80 - 87 */ + 0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8, /* 88 - 8F */ + 0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3, /* 90 - 97 */ + 0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC, /* 98 - 9F */ + 0x84, 0xB0, 0xA2, 0xA3, 0xA7, 0xB7, 0xB6, 0xDF, /* A0 - A7 */ + 0xAE, 0xA9, 0x85, 0xB4, 0xA8, 0xAD, 0xC6, 0xD8, /* A8 - AF */ + 0x86, 0xB1, 0xB2, 0xB3, 0xA5, 0xB5, 0x87, 0x88, /* B0 - B7 */ + 0xBC, 0xB9, 0xBE, 0xAA, 0xBA, 0xBD, 0xE6, 0xF8, /* B8 - BF */ + 0xBF, 0xA1, 0xAC, 0x89, 0x8A, 0x8B, 0x8C, 0xAB, /* C0 - C7 */ + 0xBB, 0x8D, 0xA0, 0xC0, 0xC3, 0xD5, 0x8E, 0x8F, /* C8 - CF */ + 0xD0, 0x90, 0x91, 0x92, 0x93, 0x94, 0xF7, 0xD7, /* D0 - D7 */ + 0xFF, 0xDD, 0x2F, 0xA4, 0x3C, 0x3E, 0xDE, 0x95, /* D8 - DF */ + 0x96, 0x97, 0x98, 0x99, 0x9A, 0xC2, 0xCA, 0xC1, /* E0 - E7 */ + 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4, /* E8 - EF */ + 0xF0, 0xD2, 0xDA, 0xDB, 0xD9, 0x9B, 0x5E, 0x7E, /* F0 - F7 */ + 0xAF, 0x9C, 0x9D, 0x9E, 0xB8, 0xFD, 0xFE, 0x9F, /* F8 - FF */ + + 0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1, /* 80 - 87 */ + 0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8, /* 88 - 8F */ + 0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3, /* 90 - 97 */ + 0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC, /* 98 - 9F */ + 0x00, 0xB0, 0xA2, 0xA3, 0xA7, 0xB7, 0xB6, 0xDF, /* A0 - A7 */ + 0xAE, 0xA9, 0x00, 0xB4, 0xA8, 0x00, 0xC6, 0xD8, /* A8 - AF */ + 0x00, 0xB1, 0x00, 0x00, 0xA5, 0xB5, 0x00, 0x00, /* B0 - B7 */ + 0x00, 0x00, 0x00, 0xAA, 0xBA, 0x00, 0xE6, 0xF8, /* B8 - BF */ + 0xBF, 0xA1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xAB, /* C0 - C7 */ + 0xBB, 0x00, 0xA0, 0xC0, 0xC3, 0xD5, 0x00, 0x00, /* C8 - CF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00, /* D0 - D7 */ + 0xFF, 0x00, 0x2F, 0xA4, 0x3C, 0x3E, 0x00, 0x00, /* D8 - DF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xCA, 0xC1, /* E0 - E7 */ + 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4, /* E8 - EF */ + 0x00, 0xD2, 0xDA, 0xDB, 0xD9, 0x00, 0x5E, 0x7E, /* F0 - F7 */ + 0xAF, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00 /* F8 - FF */ +}; + +/* + * Table for translating ISO characters 0x80-0xff to Macintosh + * (ISO characters 0x00-0x7f map directly to Macintosh equivalent). + * + * The top half of the table contains 0x00 in positions where + * no reversible character mapping exists, the bottom half of + * the table contains a reversible mapping for text translation. + * + */ + +u_char ISO2Mac[256] = { + 0x00, 0x00, 0x00, 0x00, 0xA0, 0xAA, 0xB0, 0xB6, /* 80 - 87 */ + 0xB7, 0xC3, 0xC4, 0xC5, 0xC6, 0xC9, 0xCE, 0xCF, /* 88 - 8F */ + 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xDF, 0xE0, 0xE1, /* 90 - 97 */ + 0xE2, 0xE3, 0xE4, 0xF5, 0xF9, 0xFA, 0xFB, 0xFF, /* 98 - 9F */ + 0xCA, 0xC1, 0xA2, 0xA3, 0xDB, 0xB4, 0x7C, 0xA4, /* A0 - A7 */ + 0xAC, 0xA9, 0xBB, 0xC7, 0xC2, 0xAD, 0xA8, 0xF8, /* A8 - AF */ + 0xA1, 0xB1, 0xB2, 0xB3, 0xAB, 0xB5, 0xA6, 0xA5, /* B0 - B7 */ + 0xFC, 0xB9, 0xBC, 0xC8, 0xB8, 0xBD, 0xBA, 0xC0, /* B8 - BF */ + 0xCB, 0xE7, 0xE5, 0xCC, 0x80, 0x81, 0xAE, 0x82, /* C0 - C7 */ + 0xE9, 0x83, 0xE6, 0xE8, 0xED, 0xEA, 0xEB, 0xEC, /* C8 - CF */ + 0xD0, 0x84, 0xF1, 0xEE, 0xEF, 0xCD, 0x85, 0xD7, /* D0 - D7 */ + 0xAF, 0xF4, 0xF2, 0xF3, 0x86, 0xD9, 0xDE, 0xA7, /* D8 - DF */ + 0x88, 0x87, 0x89, 0x8B, 0x8A, 0x8C, 0xBE, 0x8D, /* E0 - E7 */ + 0x8F, 0x8E, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, /* E8 - EF */ + 0xF0, 0x96, 0x98, 0x97, 0x99, 0x9B, 0x9A, 0xD6, /* F0 - F7 */ + 0xBF, 0x9D, 0x9C, 0x9E, 0x9F, 0xFD, 0xFE, 0xD8, /* F8 - FF */ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80 - 87 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 88 - 8F */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 90 - 97 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 98 - 9F */ + 0xCA, 0xC1, 0xA2, 0xA3, 0xDB, 0xB4, 0x7C, 0xA4, /* A0 - A7 */ + 0xAC, 0xA9, 0xBB, 0xC7, 0xC2, 0x00, 0xA8, 0xF8, /* A8 - AF */ + 0xA1, 0xB1, 0x00, 0x00, 0xAB, 0xB5, 0xA6, 0xA5, /* B0 - B7 */ + 0xFC, 0x00, 0xBC, 0xC8, 0x00, 0x00, 0x00, 0xC0, /* B8 - BF */ + 0xCB, 0xE7, 0xE5, 0xCC, 0x80, 0x81, 0xAE, 0x82, /* C0 - C7 */ + 0xE9, 0x83, 0xE6, 0xE8, 0xED, 0xEA, 0xEB, 0xEC, /* C8 - CF */ + 0x00, 0x84, 0xF1, 0xEE, 0xEF, 0xCD, 0x85, 0x00, /* D0 - D7 */ + 0xAF, 0xF4, 0xF2, 0xF3, 0x86, 0x00, 0x00, 0xA7, /* D8 - DF */ + 0x88, 0x87, 0x89, 0x8B, 0x8A, 0x8C, 0xBE, 0x8D, /* E0 - E7 */ + 0x8F, 0x8E, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, /* E8 - EF */ + 0x00, 0x96, 0x98, 0x97, 0x99, 0x9B, 0x9A, 0xD6, /* F0 - F7 */ + 0xBF, 0x9D, 0x9C, 0x9E, 0x9F, 0x00, 0x00, 0xD8 /* F8 - FF */ +}; + +/* + * Translate Macintosh characters in the supplied C-string + * to their ISO equivalents using a reversible mapping. + * + */ + +void +cMac2ISO(cStr) +u_char *cStr; +{ + while (*cStr) { + if (*cStr & 0x80) + *cStr = Mac2ISO[*cStr & 0x7f]; + cStr++; + } + + return; +} + +/* + * Translate ISO characters in the supplied C-string to + * their Macintosh equivalents using a reversible mapping. + * + */ + +void +cISO2Mac(cStr) +u_char *cStr; +{ + while (*cStr) { + if (*cStr & 0x80) + *cStr = ISO2Mac[*cStr & 0x7f]; + cStr++; + } + + return; +} + +/* + * Translate Macintosh characters in the supplied P-string + * to their ISO equivalents using a reversible mapping. + * + */ + +void +pMac2ISO(pStr) +u_char *pStr; +{ + int i; + int len = *pStr++; + + for (i = 0; i < len; i++) { + if (*pStr & 0x80) + *pStr = Mac2ISO[*pStr & 0x7f]; + pStr++; + } + + return; +} + +/* + * Translate ISO characters in the supplied P-string + * to their Macintosh equivalents using a reversible mapping. + * + */ + +void +pISO2Mac(pStr) +u_char *pStr; +{ + int i; + int len = *pStr++; + + for (i = 0; i < len; i++) { + if (*pStr & 0x80) + *pStr = ISO2Mac[*pStr & 0x7f]; + pStr++; + } + + return; +} + +int +isISOprint(c) +u_char c; +{ + if (c & 0x80) + return(ISO2Mac[c]); + else + return(isprint(c)); +} + +#endif ISO_TRANSLATE diff --git a/lib/cap/abnbp.c b/lib/cap/abnbp.c new file mode 100644 index 0000000..ce86dd3 --- /dev/null +++ b/lib/cap/abnbp.c @@ -0,0 +1,824 @@ +/* + * $Author: djh $ $Date: 1996/03/07 09:13:56 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abnbp.c,v 2.7 1996/03/07 09:13:56 djh Rel djh $ + * $Revision: 2.7 $ +*/ + +/* + * abnbp.c - nbp access. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 13, 1986 Schilit Created + * June 15, 1986 CCKim move to abnbp.c, add extract + * July 1, 1986 Schilit rewrite with async and NBPConfirm + * July 9, 1986 CCKim Clean up and rewrite create_entity + * July 15, 1986 CCKim Add nbpregister, nbpdelete + * April 28,1991 djh Added Phase 2 support + * + */ + +#include +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else +# include +#endif + +private nbpProto *nbpQ; /* NBP queue of nbpProto records */ +private byte next_nbpid = 0; /* NBP transaction ID */ +private int nbpSkt = -1; /* NBP socket number */ + +/* Public functions */ + +OSErr nbpInit(); /* initialize NBP */ +OSErr NBPLookup(); /* lookup a name or group of names */ +OSErr NBPConfirm(); /* confirm a name/address pair */ +OSErr NBPExtract(); /* extract entity information after lookup */ + +int nbpMatch(); /* match obj or type using wildcards */ + +/* Internal functions */ + +private OSErr nbpFcn(); /* common NBP function */ +private void SndNBP(); /* send request to appletalk */ +private void nbp_timeout(); /* timeout monitor */ +private void nbp_listener(); /* DDP listener process */ +private void LkUpReply(); /* handle LkUpReply response */ +private int nbp_match(); /* find matching request upon response */ +private int nbpcpy(); /* copy entity into user buffer */ +private int c2pkt_ename(); /* convert entity name from c to packed */ +private int pkt2c_ename(); /* convert entity name from packed to c */ + +/* + * OSErr nbpInit() + * + * nbpInit initializes NBP; an DDP socket with an NBP listener is + * opened and varaibles are initialized. + * + * Result Codes: + * as returned by DDPOpenSocket() + * + */ + +OSErr +nbpInit() +{ + int err; + + nbpSkt = 0; + nbpQ = (nbpProto *) 0; + next_nbpid = 0; /* NBP transaction ID */ + err = DDPOpenSocket(&nbpSkt,nbp_listener); + if (err != noErr) { + nbpSkt = 0; /* reset */ + fprintf(stderr,"NBPInit: DDPOpenSocket error %d\n",err); + } +} + +/* + * Close currently open NBP socket + * +*/ +OSErr +nbpShutdown() +{ + int err; + err = DDPCloseSocket(nbpSkt); + nbpSkt = -1; + return(err); +} + +/* + * NBPLookup(nbpProto *pr,int async) + * + * NBPLookup returns the address of all entities with a specified + * name. + * + * nbpEntityPtr - points to a variable of type EntityName containing + * the name of the entity whose address should be returned. + * + * nbpBufPtr, nbpBufSize - contain the location and size of an area + * in memory in which the entities' address should be returned. + * + * nbpDataField - indicates the maximum number of matching names to + * find address for; the actual number of addresses is returned in + * nbpDataField. + * + * nbpRetransmitInfo - contains the retry interval and retry count. + * + * Result Codes: + * noErr No error + * nbpBuffOvr Buffer overflow + * +*/ + +OSErr +NBPLookup(abr,async) +nbpProto *abr; +int async; +{ + int maxx; + import word this_net; + OSErr GetBridgeAddress(); + + GetBridgeAddress(&abr->nbpAddress); + if (abr->nbpAddress.node == 0x00) { /* no router */ + abr->nbpAddress.node = 0xff; +#ifdef PHASE2 + abr->nbpAddress.net = 0x0000; /* a local broadcast */ +#else PHASE2 + abr->nbpAddress.net = this_net; +#endif PHASE2 + } + + maxx = abr->nbpBufSize/sizeof(NBPTEntry); /* max entries */ + abr->nbpMaxEnt = /* set it up */ + (maxx < (int)abr->nbpDataField) ? maxx : abr->nbpDataField; + abr->nbpDataField = 0; /* initially no entries */ + + return(nbpFcn(abr,tNBPLookUp,async)); +} + +/* + * OSErr NBPConfirm(nbpProto *pr, int async) + * + * NBPConfirm confirms that an entity known by name and address still + * exists. + * + * nbpEntityPtr - points to a variable of type EntityName that contains + * the name to confirm. No meta characters are allowed in the entity + * name (otherwise the result would be ambigous). + * + * nbpAddress - specifies the address to be confirmed. + * + * nbpRetransmitInfo - contains the retry interval and retry count. + * + * The correct socket number is returned in nbpDataField. + * + * NBPConfirm is more efficient than NBPLookup in terms of network + * traffic. Since NBPConfirm only waits for a single response it + * is also quicker (i.e. doesn't need to wait for the timeout period). + * + * Result Codes: + * noErr No error + * nbpConfDiff Name confirmed for different socket + * nbpNoConfirm Name not confirmed + * +*/ + +OSErr +NBPConfirm(abr,async) +nbpProto *abr; +int async; +{ + return(nbpFcn(abr,tNBPConfirm,async)); /* common function does the work */ +} + +/* + * OSErr NBPExtract(NBPTEntry t[],int nument,int whichone, + * EntityName *en, AddrBlock *addr) + * + * NBPExtract returns one address from the list of addresses returned + * by NBPLookup. + * + * t - is a table of entries in the form NBPTEntry as returned by + * NBPLookUp. + * + * nument - is the number of tuples in "t" as returned in nbpDataField + * by NBPLookUp + * + * whichone - specifies which one of the tuples in the buffer should + * be returned. + * + * en, addr - are pointers to an EntityName and AddrBlock into which + * NBPExtract stores the selected entity information. + * + * Result Codes: + * noErr No error + * extractErr Can't find tuple in buffer + * + */ + +OSErr +NBPExtract(t,nument,whichone,ent,addr) +NBPTEntry t[]; +EntityName *ent; +AddrBlock *addr; +{ + if (whichone > nument) { + fprintf(stderr,"NBPExtract: whichone too large!"); + return(extractErr); /* return error code, not found */ + } else { + *ent = t[whichone-1].ent; /* pretty simple */ + *addr = t[whichone-1].addr; /* stuff... */ + } + return(noErr); +} + +/* + * register a nve + * +*/ +NBPRegister(abr, async) +nbpProto *abr; +boolean async; +{ + import word this_net, nis_net; + import byte this_node, nis_node; + + int err; + + if ((err = NBPLookup(abr, FALSE)) < 0) /* i guess this is the right */ + return(err); /* thing to do */ + + if (abr->nbpDataField != 0) + if (abr->nbpDataField != 1 || + abr->nbpBufPtr[0].addr.net != this_net || + abr->nbpBufPtr[0].addr.node != this_node || + abr->nbpBufPtr[0].addr.skt != abr->nbpAddress.skt) + return(nbpDuplicate); + + abr->nbpAddress.net = nis_net; + abr->nbpAddress.node = nis_node; + /* socket is given */ + + return(nbpFcn(abr,tNBPRegister,async)); /* common function does the work */ +} + +/* + * remove a nve + * +*/ +NBPRemove(abEntity) +EntityName *abEntity; +{ + nbpProto abr; + import word nis_net; + import byte nis_node; + int err; + + abr.nbpEntityPtr = abEntity; + abr.nbpAddress.net = nis_net; + abr.nbpAddress.node = nis_node; + abr.nbpRetransmitInfo.retransInterval = 10; + abr.nbpRetransmitInfo.retransCount = 2; + + err = nbpFcn(&abr,tNBPDelete,FALSE); /* common function does the work */ + if (err != noErr) + return(err); + return(abr.abResult); +} + +/* + * private OSErr nbpFcn(nbpProto *abr, int fcn, async) + * + * nbpFcn is a common function for NBP calls. The function code is + * stored into nbpProto, a unique transaction ID is set, and other + * variables are initialized in the protocol record. The record is + * placed on a Q of NBP requests, the request is sent out to the + * appletalk net, and a retransmission timer is started. + * + * If the async parameter is TRUE nbpFcn returns otherwise it waits + * for abResult to go non-positive. + * +*/ + +private OSErr +nbpFcn(abr,fcn,async) +nbpProto *abr; +int fcn,async; +{ + int rtim; + + if (nbpSkt <= 0) { + fprintf(stderr,"NBP nbpFcn: nbpInit not called"); + exit(1); + } + + abr->abOpcode = fcn; /* set function code */ + abr->abResult = 1; /* result not completed */ + next_nbpid += 1; /* increment request ID */ + abr->nbpID = next_nbpid; /* store request id */ + /* copy so we can modify */ + abr->retranscount = abr->nbpRetransmitInfo.retransCount; + q_head(&nbpQ,abr); /* add entry to Q */ + + SndNBP(abr); /* send out NBP the request */ + + rtim = abr->nbpRetransmitInfo.retransInterval; + Timeout(nbp_timeout,(caddr_t) abr,rtim); + if (async) + return(noErr); + while (abr->abResult > 0) + abSleep(rtim,TRUE); + return(abr->abResult); +} + +/* + * private void SndNBP(nbpProto *abr) + * + * Send out NBP request over appletalk. + * + */ + +private void +SndNBP(nbpr) +nbpProto *nbpr; +{ + ABusRecord ddp; + ddpProto *ddpr; + NBP nbp; + int nsize; + import byte this_node; + import word this_net; + + if (dbug.db_nbp) + printf("NBP SndNBP: sending\n"); + + ddpr = &ddp.proto.ddp; /* handle on DDP protocol args */ + ddpr->ddpType = ddpNBP; + ddpr->ddpDataPtr = (byte *) &nbp; + + ddpr->ddpSocket = (nbpr->abOpcode == tNBPRegister) ? + nbpr->nbpAddress.skt : nbpSkt; + ddpr->ddpAddress = nbpr->nbpAddress; + ddpr->ddpAddress.skt = nbpNIS; /* always talk to NIS servers */ + + nbp.tcnt = 1; /* 1 tuple */ + nbp.id = nbpr->nbpID; + nbp.tuple[0].enume = 0; + nbp.tuple[0].addr.net = this_net; + nbp.tuple[0].addr.node = this_node; + nbp.tuple[0].addr.skt = nbpSkt; /* assume this as our listening socket */ + switch (nbpr->abOpcode) { + case tNBPConfirm: /* this is directed to the node... */ + nbp.control = nbpLkUp; + break; + case tNBPLookUp: + if (nbpr->nbpAddress.node == 0xff) + nbp.control = nbpLkUp; /* no bridge, just do lookup */ + else + nbp.control = nbpBrRq; /* function is lookup with bridge */ + break; + case tNBPDelete: + nbp.tcnt = 0; /* make sure zero */ + nbp.control = nbpDelete; + nbp.tuple[0].addr.skt = nbpr->nbpAddress.skt; + break; + case tNBPTickle: /* say eh for now */ + break; + case tNBPRegister: + nbp.tcnt = 0; /* make sure zero */ + nbp.control = nbpRegister; + break; + } + nsize = c2pkt_ename(nbpr->nbpEntityPtr,nbp.tuple[0].name); +#ifdef PHASE2 + { char *q; + u_char *GetMyZone(); + /* add the zone name for outgoing NBP lookups */ + if (nbpr->abOpcode == tNBPLookUp && nsize >= 2) { + q = (char *) &nbp.tuple[0].name[nsize-2]; + if (*q == 0x01 && *(q+1) == '*') { /* zone "*" */ + strcpy(q+1, (char *)GetMyZone()); + *q = strlen(q+1); + nsize += (*q - 1); + } else { + if (*(q+1) == '\0') { /* null zone */ + strcpy(q+2, (char *)GetMyZone()); + *(q+1) = strlen(q+2); + nsize += *(q+1); + } + } + } + } +#endif PHASE2 + ddpr->ddpReqCount = nbpBaseSize+nsize; + DDPWrite(&ddp); /* write it out... */ +} + +/* + * private void nbp_timeout(nbpProto *nbpr) + * + * Timeout processor called called by the Timeout() mechanism. + * + * nbp_timeout decrements the retransmission counter and if greater + * than zero, retransmits the nbpProto request and requeues the next + * timeout. If the retransmission counter has expired then nbp_timeout + * dequeues the nbpProto request and sets the result code. + * +*/ + +private void +nbp_timeout(nbpr) +nbpProto *nbpr; +{ + if (dbug.db_nbp) + printf("NBP nbp_timeout: %d tick timeout on %d, %d remain\n", + nbpr->nbpRetransmitInfo.retransInterval, nbpr, nbpr->retranscount); + + if (nbpr->retranscount-- > 0) { + SndNBP(nbpr); /* resend */ + Timeout(nbp_timeout,(caddr_t)nbpr,nbpr->nbpRetransmitInfo.retransInterval); + } else { + dq_elem(&nbpQ,nbpr); /* timeout - dq this record */ + switch (nbpr->abOpcode) { + case tNBPRegister: + case tNBPConfirm: + nbpr->abResult = nbpNoConfirm; /* timeout means no confirm */ + break; + case tNBPDelete: + case tNBPTickle: + case tNBPLookUp: + nbpr->abResult = noErr; /* timeout is OK */ + break; + } + } +} + +/* + * private void nbp_listener() + * + * Listener for NBP protocol packets called from DDP level. + * +*/ + +private void +nbp_listener(skt,type,nbp,len,addr) +byte skt; +byte type; +NBP *nbp; +AddrBlock *addr; +{ + /* packet must be large enough and ddp type NBP */ + if (len < nbpMinSize || type != ddpNBP) + return; + + switch (nbp->control) { + case nbpLkUpReply: /* lookup reply? */ + LkUpReply(nbp,len); /* yes, handle it */ + break; + case nbpStatusReply: + StatusReply(nbp, len); + break; + default: + /* anything else should be requests */ + break; + } +} + +/* + * private void nbpspecialdone + * + * +*/ +StatusReply(nbp, len) +NBP *nbp; +int len; +{ + nbpProto *pr; + OSErr errstatus = -1; /* default to generic one */ + +/* find the queued request which matches this NBP reply */ + + pr = (nbpProto *) q_mapf(nbpQ,nbp_match,nbp->id); /* find matching id */ + if (pr == (nbpProto *) 0) /* anything found? */ + return; /* nope... */ + + if (dbug.db_nbp) + fprintf(stderr,"NBP status done: found %d\n",pr); + + switch (pr->abOpcode) { + case tNBPRegister: + switch (nbp->tcnt) { + case nbpSR_noErr: + errstatus = noErr; + break; + case nbpSR_access: + errstatus = nbpDuplicate; + break; + case nbpSR_overflow: + errstatus = nbpBuffOvr; + break; + } + break; + case tNBPDelete: + switch (nbp->tcnt) { + case nbpSR_noErr: + errstatus = noErr; + break; + case nbpSR_access: + errstatus = nbpNoConfirm; /* well, can't do any better??? */ + break; + case nbpSR_nsn: + errstatus = nbpNotFound; + break; + } + break; + } + pr->abResult = errstatus; + remTimeout(nbp_timeout,pr); /* remove timeout as well */ + dq_elem(&nbpQ,pr); /* and unit no longer needed */ +} + +/* + * private void LkUpReply(NBP *nbp, int l) + * + * Called by nbp_listener when a LkUpReply packet comes down the pike. + * + * Take the following action: + * + * 1) Locate queued nbpProto request matching incoming reply's ID. + * + * 2) If this is a response to NBPConfirm, store the responding socket + * number in nbpDataField, set abResult field, remove pending + * timeout, and dq the nbpProto request. All done. + * + * 3) If this is a response to NBPLookUp, copy entity into user + * buffer. If that fails (i.e. overflow or count of responses + * acquired) then set abResult, remove pending timeout, and dq + * the nbpProto request. + * + */ + +private void +LkUpReply(nbp,len) +NBP *nbp; +int len; +{ + nbpProto *pr; + int skt,rslt; + +/* find the queued request which matches this NBP reply */ + + pr = (nbpProto *) q_mapf(nbpQ,nbp_match,nbp->id); /* find matching id */ + if (pr == (nbpProto *) 0) /* anything found? */ + return; /* nope... */ + + if (dbug.db_nbp) + printf("NBP LkUpReply: found %d\n",pr); + + switch(pr->abOpcode) { /* handle according to req type */ + + case tNBPConfirm: /* response to NBPConfirm */ + skt = pr->nbpDataField = /* set datafield to be */ + nbp->tuple[0].addr.skt; /* confirmed socket */ + pr->abResult = (pr->nbpAddress.skt == skt) ? + noErr : nbpConfDiff; /* set completion code */ + remTimeout(nbp_timeout,pr); /* remove timeout as well */ + dq_elem(&nbpQ,pr); /* and unit no longer needed */ + break; /* all done with record */ + + case tNBPLookUp: /* response to NBPLookup */ + rslt = nbpcpy(pr,nbp); /* copy and get result */ + if (rslt <= 0) { /* indicates finished condition */ + pr->abResult = rslt; /* overflow or no error.. */ + remTimeout(nbp_timeout,pr); /* remove timeouts */ + dq_elem(&nbpQ,pr); /* remove request */ + } + break; + } +} + +/* + * private int nbp_match(nbpProto *pr, int id) + * + * nbp_match is the filter routine called via q_mapf (which applies + * the function across all records in a queue). nbp_match locates + * the nbpProto record matching the argument ID. + * +*/ + +private int +nbp_match(pr,id) +nbpProto *pr; +byte id; +{ + return(pr->nbpID == id); +} + + +/* + * private int nbpcpy(nbpProto *pr, NBP *nbp) + * + * nbpcpy copies entities into the user buffer (nbpDataPtr) from + * a network NBP packet. + * + * nbpcpy first checks to see if the entity exists, in which case it + * returns without copying (prevents duplicates). + * + * Returns: + * 1 success + * nbpBuffOvr No room for entity. + * noErr Have acquired requested number of entities. + * + * **BUG** Check for entity already in table should probably use + * more than just the address (name and enum?). In addition the + * entity should always be updated so that a very long term + * NBPLookup can get network changes. + * + */ + +private int +nbpcpy(pr,nbp) +nbpProto *pr; +NBP *nbp; +{ + NBPTuple *ep; + NBPTEntry *en; + int i,len; + +/* Add NBP tuples to user's data structure */ + + en = (NBPTEntry *)pr->nbpBufPtr; + + for (ep = &nbp->tuple[0]; nbp->tcnt != 0; nbp->tcnt--) { + /* check if this tuple entry is already in the table... */ + for (i = 0; i < (int)pr->nbpDataField; i++) + if (bcmp(&en[i].addr, &ep->addr, sizeof(AddrBlock)) == 0) + break; /* found identical */ + if (i != pr->nbpDataField) + continue; /* try next entry */ + if (i > pr->nbpMaxEnt) /* more than wanted? */ + return(nbpBuffOvr); /* yes, return now... */ + bcopy(&ep->addr, &en[i].addr, sizeof(AddrBlock)); + len = pkt2c_ename(ep->name,&en[i].ent); + pr->nbpDataField++; /* one more entry */ + ep = (NBPTuple *)((char *)ep+(len+sizeof(AddrBlock)+1)); + } + + if (pr->nbpDataField == pr->nbpMaxEnt) + return(noErr); + + return(1); +} + + +/* + * private int c2pkt_ename(EntityName *cn, u_char *pn) + * + * Copy entity name from c form into contiguous Apple Pascal + * form (packet form). + * + * return: length of pascal form entity name + * + */ +private int +c2pkt_ename(cn,pn) +byte *pn; +EntityName *cn; +{ + int i, cnt; + byte *s; + byte *pc; + + cnt = 0; + for (s = cn->objStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) { + *pn = *s; + if (*s == '\0') + break; + } + if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */ + i = ENTITYSIZE; /* too large: turncated to 32 chars */ + *pc = i; + cnt += (i+1); + for (s = cn->typeStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) { + *pn = *s; + if (*s == '\0') + break; + } + if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */ + i = ENTITYSIZE; /* too large: turncated to 32 chars */ + *pc = i; + cnt += (i+1); + for (s = cn->zoneStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) { + *pn = *s; + if (*s == '\0') + break; + } + if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */ + i = ENTITYSIZE; /* too large: turncated to 32 chars */ + *pc = i; + cnt += (i+1); + return(cnt); /* return number of bytes used */ +} + +/* + * private int pkt2c_enames(u_char *pn, EntityName *cn); + * + * Copy entity names from packet form (abutting Apple Pascal + * strings) to c form into structure of type EntityName. + * + * return: the length of the packed string. + * + */ + +private int +pkt2c_ename(pn,cn) +byte *pn; +EntityName *cn; +{ + int ol,tl,zl; + + ol = *pn; /* length of object */ + tl = *(pn+ol+1); /* length of type */ + zl = *(pn+ol+tl+2); /* length of zone */ + if (ol > ENTITYSIZE || tl > ENTITYSIZE || zl > ENTITYSIZE) { + fprintf(stderr,"pkt2c_entity_names: invalid lengths! %d:%d@%d\n",ol,tl,zl); + return(0); + } + cpyp2cstr(cn->objStr.s,pn); /* copy them... */ + cpyp2cstr(cn->typeStr.s,pn+ol+1); + cpyp2cstr(cn->zoneStr.s,pn+ol+tl+2); + return(ol+tl+zl+3); /* return length */ +} + +/* + * Convert name in the form 'LaserWriter:LaserWriter@*' (object, type, + * zone) to entity form (LaserWriter, LaserWriter, *). + * + * Assumes no ':' in object name , and no '@' in object or type name + * +*/ +void +create_entity(name, en) +char *name; +EntityName *en; +{ + char *zs, *ts; + int ol, tl, zl; + + ts = index(name, ':'); + zs = index(name, '@'); + ol = ts ? (ts - name) : (zs ? (zs - name) : strlen(name)); + tl = ts == NULL ? 0 : ((zs == NULL) ? strlen(ts+1) : (zs - ts - 1)); + zl = zs == NULL ? 0 : strlen(zs+1); + /* make foo@bar be =:foo@bar */ + /* make foo be =:=@foo */ + /* make foo@ be =:foo@* */ + if (ol != 0 && tl == 0 && ts == NULL) { + if (zl != 0 || zs) + tl = ol, ts = name - 1; + else + zs = name - 1, zl = ol; + ol = 0; + } + + bzero(en->objStr.s, sizeof(en->objStr.s)); + bzero(en->typeStr.s, sizeof(en->typeStr.s)); + bzero(en->zoneStr.s, sizeof(en->zoneStr.s)); + strncpy((char *)en->objStr.s, name, min(ENTITYSIZE, ol)); /* just copy */ + if (ts) + strncpy((char *)en->typeStr.s, ts+1, min(ENTITYSIZE, tl)); + if (zs) + strncpy((char *)en->zoneStr.s, zs+1, min(ENTITYSIZE, zl)); +} + +#ifdef AUTHENTICATE +isNBPInited() +{ + return(nbpSkt > 0); +} +#endif AUTHENTICATE + +/* + * NBP object/type match (taking wild-cards in "pat" into account) + */ + +int +nbpMatch(pat, thing) +register byte *pat, *thing; +{ + register byte *p; + register short pl, tl, hl; + + if ((pat[0] == nbpEquals || pat[0] == nbpWild) && pat[1] == '\0') + return (1); + + if (p = (byte *)index((char *)pat, nbpWild)) { + hl = p - pat; + if (hl && strncmpci((char *)pat, (char *)thing, hl) != 0) + return (0); + pl = strlen((char *)pat) - 1; + tl = strlen((char *)thing); + if (tl < pl) + return (0); + if (hl < pl && + strcmpci((char *)p+1, (char *)thing+(tl-(pl-hl))) != 0) + return (0); + return (1); + } + if (strcmpci((char *)pat, (char *)thing) != 0) + return (0); + return (1); +} diff --git a/lib/cap/abnet.c b/lib/cap/abnet.c new file mode 100644 index 0000000..ef7a639 --- /dev/null +++ b/lib/cap/abnet.c @@ -0,0 +1,455 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:48:22 $ + * $Header: abnet.c,v 2.1 91/02/15 22:48:22 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * abnet.c - appletalk UNIX access. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 14, 1986 Schilit Created. + * June 18, 1986 CCKim Chuck's handler runs protocol + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* to cover difference between bsd systems */ +#include "cap_conf.h" + +/* + * Configuration defines + * + * NEEDNETBUF - system doesn't have scatter/gather recv/send + * NOFFS - no ffs function defined, use our own + * +*/ +#ifdef NORECVMSG +# ifndef NEEDNETBUF +# define NEEDNETBUF +# endif +#endif +#ifdef NOSENDMSG +# ifndef NEEDNETBUF +# define NEEDNETBUF +# endif +#endif + +/* good place to stick the copyright so it shows up in object files */ +char Columbia_Copyright[] = "Copyright (c) 1986,1987,1988 by The Trustees of Columbia University in the City of New York"; + +private struct sockaddr_in abfsin; /* apple bus foreign socketaddr/internet */ +private int abfd; /* apple bus socket */ +import word this_net; /* our network number */ +import byte this_node; /* our node number */ +import word nis_net; /* network number of our nis */ +import byte nis_node; /* node number of our nis */ +import int ddp_protocol(); /* DDP protocol handler */ +export struct sockaddr_in from_sin; /* network struct of last packet rec. */ +export struct in_addr ipaddr_src; /* IP src address of last packet received */ + +export DBUG dbug; /* debug flags */ + +/* BUG: bind doesn't work when lsin is on the stack! */ +private struct sockaddr_in lsin; /* local socketaddr/internet */ + +private int skt2fd[ddpMaxSkt+1]; /* translate socket to file descriptor */ +private gfd_set fdbitmask; /* file descriptors open... */ +private gfd_set *fdbits; +private int fdmaxdesc = 0; + +/* private int subshift, submask, ddpnet; - not used at present */ + +/* + * OSErr GetNodeAddress(int *myNode,*myNet) + * + * GetNodeAddress returns the net and node numbers for the current + * host. + * + * N.B. - the myNet address is in net (htons) format. + * +*/ + +OSErr +GetNodeAddress(myNode,myNet) +int *myNode,*myNet; +{ + *myNode = this_node; + *myNet = this_net; + return(noErr); /* is ok */ +} + +abInit(disp) +{ + int i; + + for (i=0; i < ddpMaxSkt+1; i++) { + skt2fd[i] = -1; /* mark all these as unused */ + } + + fdbits = &fdbitmask; + FD_ZERO(fdbits); /* make sure these are cleared */ + fdmaxdesc = 0; + + /* no need to bind since we don't recv on this socket, just send... */ + if ((abfd = socket(AF_INET,SOCK_DGRAM,0)) < 0) { + perror("abinit"); + return(abfd); + } + abfsin.sin_family = AF_INET; + abfsin.sin_addr.s_addr = INADDR_ANY; + + openatalkdb(); /* sets up this_* */ + + if (disp) { + printf("abInit: [ddp: %3d.%02d, %d]", + ntohs(this_net)>>8, htons(this_net)&0xff, this_node); + if (this_net != nis_net || this_node != nis_node) + printf(", [NBP (atis) Server: %3d.%02d, %d]", + ntohs(nis_net)>>8, htons(nis_net)&0xff, nis_node); + printf(" starting\n"); + } + LAPOpenProtocol(lapDDP,ddp_protocol); /* open protocol */ + + return(0); +} + +/* + * int abOpen(int *skt,rskt) + * + * abOpen opens the ddp socket in "skt" or if "skt" is zero allocates + * and opens a new socket. Upon return "skt" contains the socket number + * and the returned value is the file descriptor for that socket. + * +*/ + +int abOpen(skt,rskt) +int *skt; +int rskt; +{ + int i,fd,err; + word ipskt; + + if ((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0) { + perror("abopen"); + return(fd); + } + lsin.sin_family = AF_INET; + lsin.sin_addr.s_addr = INADDR_ANY; + + *skt = (rskt == 0 ? ddpWKS : rskt); /* zero rskt is free choice */ + ipskt = ddp2ipskt(*skt); /* translate into ip socket number */ + for (i=0; i < 128; i++,ipskt++,(*skt)++) { + lsin.sin_port = htons(ipskt); + if ((err = bind(fd, (caddr_t)&lsin, sizeof(lsin))) == 0) + break; + if (rskt != 0) /* bind failed and wanted exact? */ + return(err); /* yes... */ + } + if (err == 0 && i < 128) { + FD_SET(fd, fdbits); + fdmaxdesc = max(fd, fdmaxdesc); + skt2fd[*skt] = fd; /* remember file descriptor for socket */ + return(noErr); + } + perror("abopen bind"); + close(fd); + return(err); +} + +/* + * returns true if socket is opened by abnet + * +*/ +abIsSktOpen(skt) +{ + if (skt < 0 || skt > ddpMaxSkt) + return(FALSE); + return(!(skt2fd[skt] == -1)); +} + +#ifdef notdef +abSktRdy(skt) +int skt; +{ + if (skt2fd[skt] < 0) + return; + FD_SET(skt2fd[skt],fdbits); +} + +abSktNotRdy(skt) +int skt; +{ + if (skt2fd[skt] < 0) + return; + FD_CLR(skt2fd[skt],fdbits); +} +#endif +abClose(skt) +int skt; +{ + int i; + + if (skt < 0 || skt > ddpMaxSkt) { + fprintf(stderr,"abClose: skt out of range\n"); + exit(0); + } + if (skt2fd[skt] < 0) + return; + if (close(skt2fd[skt]) != 0) + perror("abClose"); /* some error... */ + FD_CLR(skt2fd[skt], fdbits); + skt2fd[skt] = -1; /* mark as unused */ + for (fdmaxdesc = 0, i = 0; i < ddpMaxSkt+1; i++) + fdmaxdesc = max(fdmaxdesc, skt2fd[i]); +} + +#ifndef NEEDNETBUF +abwrite(addr, skt, iov,iovlen) +struct in_addr addr; +unsigned short skt; +struct iovec *iov; +{ + struct msghdr msg; + int err; + + abfsin.sin_addr = addr; + abfsin.sin_port = skt; + msg.msg_name = (caddr_t) &abfsin; + msg.msg_namelen = sizeof(abfsin); + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; + if ((err = sendmsg(abfd,&msg,0)) < 0) + perror("abwrite"); + return(err); +} + +abread(fd, iov, iovlen) +struct iovec *iov; +{ + struct msghdr msg; + int err; + + msg.msg_name = (caddr_t) &from_sin; + msg.msg_namelen = sizeof(from_sin); + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; + if ((err = recvmsg(fd,&msg,0)) < 0) + perror("abread"); +#ifdef notdef +#define rebPort 902 + ipaddr_src.s_addr=(sin.sin_port != htons(rebPort)) ? sin.sin_addr.s_addr:0; +#endif + return(err); +} +#else +/* Think about ways to make this better */ +static char buf[630]; /* buffer */ + +abwrite(addr, skt, iov,iovlen) +struct in_addr addr; +unsigned short skt; +struct iovec *iov; +{ + int err; + int i, pos; + + abfsin.sin_addr = addr; + abfsin.sin_port = skt; + for (i=0, pos=0; i < iovlen; i++) { + bcopy(iov[i].iov_base, &buf[pos], iov[i].iov_len); + pos+= iov[i].iov_len; + } + if ((err = sendto(abfd, buf, pos, 0, &abfsin, sizeof(abfsin))) < 0) + perror("abwrite"); + return(err); +} + +abread(fd, iov, iovlen) +struct iovec *iov; +{ + int i,pos; + int err; + int sinlen; + + sinlen = sizeof(from_sin); + if ((err=recvfrom(fd, buf, 630, 0, &from_sin, &sinlen)) < 0) + perror("abread"); + for (pos=0,i=0; i < iovlen; i++) { + bcopy(&buf[pos], iov[i].iov_base, iov[i].iov_len); + pos += iov[i].iov_len; + } +#ifdef notdef +#define rebPort 902 + ipaddr_src.s_addr=(sin.sin_port != htons(rebPort)) ? sin.sin_addr.s_addr:0; +#endif + return(err); +} +#endif + +/* + * abSleep(int t) + * + * does a sleep via select and handles incomming packets + * +*/ + +/* + * optimization - holds the current gettimeofday() so lower level routines + * can avoid system call overhead - updated when abSleep() is entered and + * after every sleep +*/ +export struct timeval tv_now; +export struct timezone tz_now; + +abSleep(appt,nowait) +int appt,nowait; +{ + struct timeval sleept; + + gettimeofday(&tv_now, &tz_now); /* update current time */ + + apptoabstime(&sleept,appt); /* find absolute time for sleep */ + for (;;) { /* loop until wait time elapsed */ + if (abSelect(&sleept) > 0 && nowait) /* handle packets */ + return; /* and return if not waiting */ + /* abSelect will have updated tv_now */ + if (cmptime(&sleept,&tv_now) < 0) /* past wait time? */ + return; + } +} + +#ifdef NOFFS + +/* Find the First Set bit and return its number. Numbering starts */ +/* at one */ +private int +ffs(pat) +register int pat; +{ + register int j; + register int i; + + for (i = 0; i < 8*sizeof(int); i+=8) { /* each byte */ + if (pat & 0xff) { /* if byte has bits */ + /* do a linear scan */ + for (j = 0; j < 8; j++) { + if (pat & 0x1) + return(i+j+1); + pat >>= 1; + } + } + pat >>= 8; /* go to the next byte */ + } + return(-1); +} + +#endif + +/* + * Find the first active file descriptor + * + * really doesn't belong here + * +*/ +FD_GETBIT(p) +gfd_set *p; +{ + int i, w; + for (w=0,i=0; i < howmany(FD_SETSIZE, NFDBITS); i++) { + if ((w=ffs(p->fds_bits[i])) > 0) + break; + } + if (w < 1) + return(-1); + w += (i*NFDBITS)-1; /* find bit no */ + return((w > FD_SETSIZE) ? -1 : w); +} + +/* + * abSelect(struct timeval *awt) + * + * Call with absolute time. Might return before that... + * Returns > 0 if event occured. + * +*/ +int +abSelect(awt) +struct timeval *awt; +{ + struct timeval rwt,mt; + int fd,rdy; + gfd_set rdybits; + /* + * optimization: if a timeout is less than the argument sleep time + * then we don't have to return after the select() times out. If + * a timeout is not less then we don't have to scan the timeout lists. + * Note: we must return if a timeout is less than the arg. sleep time + * because this may have been an event we were waiting for!!!!! + */ + int timeoutless; /* true if timeout less than sleep time */ + + rdybits = *fdbits; + + if (dbug.db_lap) + fprintf(stderr,"abSelect enter... "); + + timeoutless = 0; + abstoreltime(awt,&rwt); /* convert requested time use requested */ + if (minTimeout(&mt)) /* find min timeout on functions if any */ + if (cmptime(&mt,awt) < 0) { /* smaller than requested? */ + abstoreltime(&mt,&rwt); /* no, yes use it */ + ++timeoutless; /* remember it */ + } + if (rwt.tv_sec < 0) { + if (dbug.db_lap) + fprintf(stderr," negative "); + rdy = 0; + } else rdy = select(fdmaxdesc+1,&rdybits,0,0,&rwt); /* perform wait... */ + + if (rdy > 0) { + do { + if ((fd = FD_GETBIT(&rdybits)) < 0) + break; + FD_CLR(fd, &rdybits); + if (dbug.db_lap) + fprintf(stderr,"lap_read call "); + lap_read(fd); /* process packet */ + } while (1); + } + gettimeofday(&tv_now, &tz_now); + /* + * Optimization: If a packet was waiting don't check the timeout lists - + * odds are that the timeout hasn't expired yet, and we'll catch it the + * next time through anyway + */ + if (rdy <= 0 && timeoutless) + /* An internal timeout occured before the user timeout, */ + /* and there were no packets available */ + doTimeout(); + if (dbug.db_lap) + fprintf(stderr,"exit\n"); + return(rdy); /* return count */ +} + + + diff --git a/lib/cap/abpap.c b/lib/cap/abpap.c new file mode 100644 index 0000000..d0d35e5 --- /dev/null +++ b/lib/cap/abpap.c @@ -0,0 +1,1023 @@ +/* + * $Author: djh $ $Date: 1993/01/14 12:57:35 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abpap.c,v 2.2 1993/01/14 12:57:35 djh Rel djh $ + * $Revision: 2.2 $ +*/ + +/* + * abpap.c - Printer Access protocol access. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Edit History: + * + * June 22, 1986 CCKim Created + * +*/ + +/* PATCH: abpap.c.tickletimer, djh@munnari.OZ.AU, 15/11/90 */ + +#include +#include +#include +#include +#include +#include "abpap.h" +#include "cap_conf.h" + +#define PAP_SHUT_WRITE 0x1 +#define PAP_SHUT_READ 0x2 +#define PAP_SHUT_ALL 0x4 + + +int PAPInit(); /* int PAPInit() */ +int PAPGetNetworkInfo(); /* int PAPGetNetworkInfo(int, Addrblock *) */ +int PAPRead(); /* int PAPRead(int, char*, int*, */ + /* boolean*, int*) */ +int PAPWrite(); /* int PAPWrite(int,char*,int,int,int*) */ +int PAPUnload(); +int PAPClose(); /* int PAPClose(int, boolean) */ +int PAPHalfClose(); +int PAPShutdown(); +private void writedone(); /* private void writedone(ABusRecord *, u_long) */ +private void readdone(); /* private void readdone(ABusRecord *, u_long) */ +private OSErr start_papgetrequest(); +private OSErr stop_papgetrequest(); +private void papremoteclose(); +void start_papc(); +private void stop_papc(); +private void handle_papgetrequest(); +private void papsndresponse(); +private void startpaptickle(); +private void stoppaptickle(); +private void ttimeout(); +private void start_ttimer(); +private void reset_ttimer(); +private void stop_ttimer(); + +private void ppsktinit(); +PAPSOCKET *cnotopapskt(); +int ppskttocno(); +int abskttocno(); +private void ppfreeskt(); +int ppgetskt(); +private void psshutdown(); +private boolean setup_prr(); +private boolean setup_pwr(); + +int +PAPInit() +{ + ppsktinit(); + psinit(); /* init server code */ + pcinit(); /* init client code */ +} + +/* + * returns address of remote + * +*/ +PAPGetNetworkInfo(cno, addr) +int cno; +AddrBlock *addr; +{ + PAPSOCKET *ps; + if ((ps = cnotopapskt(cno)) == NULL) + return(-1); + if (ps->state != PAP_OPENED) + return(-2); + *addr = ps->addr; /* return address */ + return(noErr); +} + +/* + * PAPRead - as documented + * +*/ +int +PAPRead(cno, databuff, datasize, eof, compstate) +int cno; +char *databuff; +int *datasize; +int *eof; +OSErr *compstate; +{ + PAPSOCKET *ps; + int err; + + if (dbug.db_pap) + fprintf(stderr, "pap: papread called: cno %d\n",cno); + if ((ps = cnotopapskt(cno)) == NULL) + return(-1); + if (ps->state != PAP_OPENED) /* connection closed */ + return(-2); /* for now */ + + /* only error is "request already active" */ + if (!setup_prr(cno, databuff, datasize, eof, compstate)) + return(tooManyReqs); + + if ((err = cbATPSndRequest(&ps->prr.abr, readdone, (u_long)cno)) < 0) { + ps->prr.valid = FALSE; + return(err); + } + return(noErr); +} + + + +/* + * PAPWrite - as documented + * +*/ +int +PAPWrite(refnum, databuff, datasize, eof, compstate) +int refnum; +char *databuff; +int datasize; +int eof; +int *compstate; +{ + PAPSOCKET *ps; + + if (dbug.db_pap) + fprintf(stderr,"pap: papwrite: cno %d, datasize %d, eof %s\n", + refnum, datasize, eof ? "yes" : "no"); + if ((ps = cnotopapskt(refnum)) == NULL) + return(-1); + if (ps->state != PAP_OPENED) /* connection closed */ + return(-2); /* for now */ + if (datasize > PAPSegSize*ps->rflowq) + return(-3); + + /* only error is "request already active" */ + if (!setup_pwr(refnum, databuff, datasize, eof, compstate)) + return(tooManyReqs); + papsndresponse(ps); /* try to send if sdc here */ + return(noErr); +} + +/* + * PAPUnload() + * + * Kill off all pap stuff +*/ +PAPUnload() +{ + +} + +/* + * PAPClose + * + * - as documented + * + * +*/ +int +PAPClose(refnum) +int refnum; +{ + PAPSOCKET *ps; + struct ABusRecord abr; + struct atpProto *ap; + PAPUserBytes *pub; + BDS bds[1]; /* one bds */ + int err; + + if (dbug.db_pap) + fprintf(stderr, "pap: papclose: cno %d\n",refnum); + + if ((ps = cnotopapskt(refnum)) == NULL) + return(-1); + + ap = &abr.proto.atp; + ap->atpUserData = 0; + pub = (PAPUserBytes *)&ap->atpUserData; + pub->connid = ps->connid; + pub->PAPtype = papCloseConn; + ap->atpAddress = ps->addr; + ap->atpAddress.skt = ps->rrskt; + ap->atpSocket = 0; + ap->atpReqCount = 0; + ap->atpDataPtr = 0; + ap->atpRspBDSPtr = bds; + bds[0].buffSize = 0; + bds[0].buffPtr = NULL; + ap->fatpXO = 0; /* nyi */ + ap->atpTimeOut = PAPCLOSETIMEOUT; /* retry in seconds */ + ap->atpRetries = PAPCLOSERETRY; /* 3 Retries */ + ap->atpNumBufs = 1; /* number of bds segments */ + err = ATPSndRequest(&abr, FALSE); + + if (err == reqFailed) { + if (dbug.db_pap) + fprintf(stderr,"pap: papclose: remote not responding"); + /* can't return here because we want to do the stop_papc below */ + } + + stop_papc(ps); + + if (err != reqFailed) { + pub = (PAPUserBytes *) &bds[0].userData; + if (pub->PAPtype != papCloseConnReply) { + return(-1); + } + } else return(err); + return(0); +} + +/* + * shutdown write ability on a pap socket + * + * leaves tickle timer (outgoing) active and ability to read from socket + * +*/ +int +PAPHalfClose(refnum) +int refnum; +{ + PAPSOCKET *ps; + + if ((ps = cnotopapskt(refnum)) == NULL) + return(-1); + stop_ttimer(ps); + stop_papgetrequest(ps); + psshutdown(ps, PAP_SHUT_WRITE); + return(0); +} + +/* + * close down a pap connection without telling remote + * useful for forking off the a pap fork without carrying the baggage + * +*/ +int +PAPShutdown(refnum) +int refnum; +{ + PAPSOCKET *ps; + + if ((ps = cnotopapskt(refnum)) == NULL) + return(-1); + stop_papc(ps); + return(0); +} + + + +/* + * callback from atp after sndresp is done + * (use cno as cbarg so we can check papsocket closed without explict + * checks) +*/ +private void +writedone(abr, cbarg) +ABusRecord *abr; +u_long cbarg; +{ + int cno = (int)cbarg; + PAPSOCKET *ps; + + if ((ps = cnotopapskt(cno)) == NULL) /* get pap socket */ + return; /* drop it */ + + if (!ps->pwr.valid) /* nothing to do */ + return; + + *(ps->pwr.comp) = abr->abResult; /* all done! */ + ps->pwr.active = FALSE; + ps->pwr.valid = FALSE; +} + +/* + * call back from atp after sndrequest is done + * +*/ +private void +readdone(abr, cbarg) +ABusRecord *abr; +u_long cbarg; +{ + PRR *prr; + int cno = (int)cbarg; /* get connection */ + int i, cnt; + PAPSOCKET *ps; + PAPUserBytes *pub; + + if ((ps = cnotopapskt(cno)) == NULL) /* get pap socket */ + return; /* drop it */ + + if (ps->state == PAP_OPENED) /* If read is done and ps is open */ + reset_ttimer(ps); /* then do normal reset on tickle timer */ + else { /* else just stop the tickle timeout */ + if (dbug.db_pap) + fprintf(stderr, "pap: readdone cancelling ttimer!\n"); + stop_ttimer(ps); /* just stop the tickle timer! */ + } + + prr = &ps->prr; /* get active prr */ + + if (abr->abResult == noErr) { + /* buffSize < dataSize => data size error (e.g. incoming bigger */ + /* than buffer allocated) should never happen, but we'll simply */ + /* make sure we don't try to use data that's not there for now */ + for (i = 0, cnt = 0; i < (int)abr->proto.atp.atpNumRsp; i++) + cnt += min(prr->bds[i].dataSize,prr->bds[i].buffSize); + *prr->datasize = cnt; + pub = (PAPUserBytes *)&prr->bds[abr->proto.atp.atpNumRsp-1].userData; + if (pub->other.std.eof) { + if (dbug.db_pap) + fprintf(stderr,"pap: Remote signaled EOF\n"); + *prr->eof = 1; + /* assume that any outstanding papwrite request should be canceled */ + if (ps->pwr.valid && ps->pwr.active) { + if (dbug.db_pap) + fprintf(stderr, "pap: cancelling outstanding papwrite due to EOF on stream\n"); + (void)ATPRspCancel(&ps->pwr.abr, FALSE); /* ignore error */ + } + } + } + *prr->comp = abr->abResult; + prr->valid = FALSE; /* not active anymore */ +} + + + +/* + * start_papgetrequest(ps) + * + * issue a atp request call - control falls back and when + * request comes in, we goto handle_papgetrequest + * +*/ +private OSErr +start_papgetrequest(ps) +PAPSOCKET *ps; +{ + atpProto *ap; + int err; + + if (ps->active) /* already started */ + return(noErr); + + ap = &ps->request_abr.proto.atp; + ap->atpSocket = ps->paprskt; + ap->atpReqCount = 0; + ap->atpDataPtr = NULL; + err = cbATPGetRequest(&ps->request_abr, handle_papgetrequest, ps); + ps->request_active = (err == noErr); + return(err); +} + +private OSErr +stop_papgetrequest(ps) +PAPSOCKET *ps; +{ + OSErr err; + + err = ATPReqCancel(&ps->request_abr, FALSE); + return(err); +} + + + +/* + * Used to signal new pap connection and start all requiste maintenace + * functions. Expects all information for the socket to be in place + * at this point. + * +*/ +void +start_papc(ps) +PAPSOCKET *ps; +{ + if (dbug.db_pap) { + fprintf(stderr, "pap: connection %d opened\n", (int)ps->connid); + } + ps->state = PAP_OPENED; + *ps->comp = 0; + startpaptickle(ps); /* start tickler on local */ + start_ttimer(ps); /* start tickle timer on remote */ + start_papgetrequest(ps); /* start up request monitor */ +} + +/* + * used to stop a pap connection maintenance functions +*/ +private void +stop_papc(ps) +PAPSOCKET *ps; +{ + ps->state = PAP_CLOSED; + stoppaptickle(ps); + stop_ttimer(ps); + stop_papgetrequest(ps); + psshutdown(ps, PAP_SHUT_ALL); +} + + + +/* + * handle_papgetrequest + * + * we handle the callback from atp to complete a getrequest + * + * +*/ +private void +handle_papgetrequest(abr, ps) +ABusRecord *abr; +PAPSOCKET *ps; +{ + PAPUserBytes *pub; + + ps->request_active = FALSE; + + if (abr->abResult != noErr) { + if (abr->abResult == sktClosed) + return; + fprintf(stderr, "pap: socket error for socket %x\n",ps); + } + pub = (PAPUserBytes *)&ps->request_abr.proto.atp.atpUserData; + switch (pub->PAPtype) { + default: + fprintf(stderr,"pap: error: unexpected request received\n"); + fprintf(stderr,"pap: conn %d,paptype %d,eof %d, unused %d)\n", + pub->connid, pub->PAPtype, pub->other.std.eof, + pub->other.std.unused); + break; + case papCloseConn: + if (pub->connid != ps->connid) { + if (dbug.db_pap) { + fprintf(stderr, "pap: wrong connection id on incoming pkt\n"); + fprintf(stderr,"pap: correct %d, incoming %d\n", ps->connid, + pub->connid); + } + break; + } + if (dbug.db_pap) + fprintf(stderr,"remote sent close request\n"); + if (ps->state != PAP_OPENED) { + if (dbug.db_pap) { + fprintf(stderr,"pap: remote sent close and conn not open: state: %d\n", + ps->state); + fprintf(stderr, "pap: pap connection ids: rem: %d, local %d\n", + (byte)pub->connid, (byte)ps->connid); + } + /* Correct action is to ignore? */ + break; + } + ps->state = PAP_CLOSED; + /* we are actually obligated to send an immediate reply */ + /* what to do here? - should really start shutting down */ + papremoteclose(ps, abr); /* remote signal shutdown */ + return; /* okay - no don't queue another req */ + case papSendData: + reset_ttimer(ps); + pub->other.seqno = ntohs(pub->other.seqno); + if (dbug.db_pap) + fprintf(stderr,"pap: Seq %d, expected %d\n", + pub->other.seqno, ps->paprseq); + if (pub->connid != ps->connid) { + if (dbug.db_pap) { + fprintf(stderr, "Pap: wrong connection id on incoming pkt\n"); + fprintf(stderr,"Pap: correct %d, incoming %d\n", ps->connid, + pub->connid); + } + break; + } + if (pub->other.seqno != 0) { /* incoming is sequences */ + if (ps->paprseq == 0) /* boundary condition */ + ps->paprseq = pub->other.seqno; + else + if (ps->paprseq != pub->other.seqno) { + if (dbug.db_pap) + fprintf(stderr, "Bad sequence number: expected %d, got %d\n", + ps->paprseq, pub->other.seqno); + break; + } + } + /* remember outstanding send data credit */ + /* could check to make sure that the send data credit isn't used */ + /* before we go on, but... (possible problem) */ +#ifdef notdef + if (dbug.db_pap) { +#endif + if (ps->sdc.valid && (ps->sdc.transID != abr->proto.atp.atpTransID || + ps->sdc.creditno != ps->paprseq)) + fprintf(stderr, + "pap: new send data credit received with one outstanding\n"); + +#ifdef notdef + } +#endif + /* valid pwr and it is active (in a send response) */ + /* this is the "release" handler */ + /* MIGHT be able to do this even if unsequenced by comparing tids */ + if (pub->other.seqno != 0 && ps->pwr.valid && ps->pwr.active) { + if (dbug.db_pap) + fprintf(stderr,"pap: new send data credit: terminating previous papwrite\n"); + (void)ATPRspCancel(&ps->pwr.abr, FALSE); /* ignore error */ + } + /* note: does new sdc means previous papwrite from this end was okay? */ + ps->sdc.transID = abr->proto.atp.atpTransID; + ps->sdc.creditno = ps->paprseq; + /* cannot guarantee that this came in from remote responding skt */ + /* specification doesn't say */ + ps->sdc.skt = abr->proto.atp.atpAddress.skt; + ps->sdc.valid = TRUE; + /* okay to bump here since a rspcb should have been created for */ + /* the current send data credit */ + if (pub->other.seqno != 0) /* if sequenced */ + nextpapseq(ps->paprseq); /* bump pap seqence */ + papsndresponse(ps); /* send any outstanding */ + break; + case papTickle: + reset_ttimer(ps); + break; + } + start_papgetrequest(ps); +} + +/* + * called when the remote signals a closeConn request + * + * we know that no getrequest is active since we can only be called + * from handle_paprequest + * +*/ +private void +papremoteclose(ps, reqabr) +PAPSOCKET *ps; +ABusRecord *reqabr; +{ + atpProto *ap; + ABusRecord abr; + PAPUserBytes *pub; + BDS bds[1]; + int err; + + ap = &abr.proto.atp; + + ap->atpSocket = ps->paprskt; + ap->atpAddress = reqabr->proto.atp.atpAddress; + ap->atpRspBDSPtr = bds; + ap->atpTransID = reqabr->proto.atp.atpTransID; + ap->fatpEOM = 1; /* EOM */ + ap->atpNumBufs = 1; + ap->atpBDSSize = 1; + bds[0].buffSize = 0; + bds[0].buffPtr = NULL; + bds[0].userData = 0L; + pub = (PAPUserBytes *)&bds[0].userData; + pub->connid = ps->connid; + pub->PAPtype = papCloseConnReply; + err = ATPSndRsp(&abr, FALSE); /* send the response */ + if (err < 0 && dbug.db_pap) + fprintf(stderr,"pap: papremoteclose: atpsndrsp returns %d\n",err); + stop_papc(ps); /* close down connection */ +} + +/* + * papsndresponse - send all outstanding write requests if there are + * outstanding sending credits. Call anytime. Will only send if + * we have the requiste send data credits and there is stuff to send. + * [Good places to call are in PAPWrite and the request handler when a + * send data credit is received] + * +*/ +private void +papsndresponse(ps) +PAPSOCKET *ps; +{ + PWR *pwr; + int err; + + if (!ps->sdc.valid || !ps->pwr.valid) + return; /* nothing to do */ + pwr = &ps->pwr; + /* find pwr->abr: get atp address and transid from request abr */ + pwr->abr.proto.atp.atpAddress = ps->addr; + pwr->abr.proto.atp.atpAddress.skt = ps->sdc.skt; + pwr->abr.proto.atp.atpTransID = ps->sdc.transID; + if (dbug.db_pap) + fprintf(stderr,"pap: sending packet (pap %d, atp %d)\n", ps->sdc.creditno, + ps->sdc.transID); + err = cbATPSndRsp(&pwr->abr, writedone, (u_long)pwr->cno); + if (err < 0) { + *pwr->comp = err; /* errror, all done! */ + ps->pwr.valid = FALSE; + /* return here? */ + } else ps->pwr.active = TRUE; + ps->sdc.valid = FALSE; /* used */ +} + + +/* + * PAP Tickle managment functions + * +*/ + +/* + * startpaptickle - start a tickle for the specified connection + * +*/ +private void +startpaptickle(ps) +PAPSOCKET *ps; +{ + atpProto *ap; + PAPUserBytes *pub; + int err; + + ap = &ps->abr.proto.atp; + ap->atpUserData = 0; + pub = (PAPUserBytes *) &ap->atpUserData; + pub->connid = ps->connid; + pub->PAPtype = papTickle; + ap->atpAddress = ps->addr; + ap->atpAddress.skt = ps->rrskt; + ap->atpSocket = 0; + ap->atpReqCount = 0; + ap->atpDataPtr = 0; + ap->atpRspBDSPtr = ps->bds; + ps->bds[0].buffPtr = NULL; + ps->bds[0].buffSize = 0; + ap->atpNumBufs = 1; + ap->fatpXO = 0; + ap->atpTimeOut = PAPTICKLETIMEOUT; + ap->atpRetries = 255; /* means infinity */ + err = ATPSndRequest(&ps->abr, TRUE); + if (err != noErr) { + fprintf(stderr,"pap: problems starting tickle\n"); + } +} + +/* + * stoppaptickle - cancel the tickle on the specified connection + * +*/ +private void +stoppaptickle(ps) +PAPSOCKET *ps; +{ + ATPReqCancel(&ps->abr, FALSE); /* run async? */ +} + +/* + * Timeout handler for remote tickle + */ +private void +ttimeout(arg) +u_long arg; +{ + PAPSOCKET *ps = (PAPSOCKET *)arg; + + if (dbug.db_pap) + fprintf(stderr,"pap: *** TIMEOUT ON PAP SOCKET\n"); + PAPClose(ppskttocno(ps)); /* close off socket */ +} + +/* + * Start the remote tickle timeout + * +*/ +private void +start_ttimer(ps) +PAPSOCKET *ps; +{ + if (dbug.db_pap) + fprintf(stderr,"pap: starting timeout on PAP socket %d\n", + PAPCONNECTIONTIMEOUT); + Timeout(ttimeout, (u_long)ps, PAPCONNECTIONTIMEOUT); +} + +/* + * reset the remote tickle timeout + * +*/ +private void +reset_ttimer(ps) +PAPSOCKET *ps; +{ + remTimeout(ttimeout, (u_long)ps); + Timeout(ttimeout, (u_long)ps, PAPCONNECTIONTIMEOUT); +} + +/* + * cancel the remote tickle timeout + * +*/ +private void +stop_ttimer(ps) +PAPSOCKET *ps; +{ + if (dbug.db_pap) + fprintf(stderr,"pap: cancel timeout on PAP socket\n"); + remTimeout(ttimeout, (u_long)ps); +} + +/* + * The following would be in a seperate file, except we want to + * keep as much as possible private... + * +*/ + + +/* + * abpapaux.c - Printer Access protocol access auxillary routines + * + * pap socket management + * pap read request management + * pap write request management + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * July 1, 1986 CCKim Created + * +*/ +#ifdef singlemodulemode +#include +#include +#include +#include +#include "abpap.h" +#include "cap_conf.h" +#endif + +/* + * pap socket management + * + * Assumes very small list of possible sockets + * +*/ +PAPSOCKET paplist[NUMPAP]; /* list of "sockets" */ + +/* + * initalize pap socket management + * +*/ +private void +ppsktinit() +{ + int i; + + for (i=0; i < NUMPAP; i++) + paplist[i].state = PAP_INACTIVE; +} + +/* + * Convert a refnum to a papsocket + * +*/ +PAPSOCKET * +cnotopapskt(cno) +int cno; +{ + if (cno >= 0 && cno < NUMPAP && paplist[cno].state != PAP_INACTIVE) + return(&paplist[cno]); + else return(NULL); +} + +/* + * convert a pap socket to a refnum + * +*/ +int +ppskttocno(skt) +PAPSOCKET *skt; +{ + int i; + for (i=0; i < NUMPAP; i++) + if (&paplist[i] == skt) + return(i); + return(-1); +} + +/* + * convert a ddp socket to a pap refnum + * +*/ +int +abskttocno(skt) +int skt; +{ + int i; + for (i=0; i < NUMPAP; i++) + if (paplist[i].paprskt == skt) + return(i); + return(-1); +} + +/* + * Free a pap socket + * +*/ +private void +ppfreeskt(cno) +int cno; +{ + ATPCloseSocket(paplist[cno].paprskt); + paplist[cno].state = PAP_INACTIVE; +} + +/* + * get a pap socket - return the refnum + * +*/ +int +ppgetskt(addr) +AddrBlock *addr; +{ + int i; + AddrBlock useaddr; + PAPSOCKET *ps; + + for (i=0; i < NUMPAP; i++) + if (paplist[i].state == PAP_INACTIVE) + break; + if (i==NUMPAP) return(-1); /* no sockets left */ + ps = &paplist[i]; + ps->state = PAP_OPENING; + ps->connid = (byte)time(0); + ps->papseq = 1; /* start at one */ + ps->paprseq = 0; /* assume zero to start */ + ps->papuseq = 0; + ps->paprskt = 0; + ps->pwr.valid = ps->prr.valid = FALSE; /* no valid writes/reads */ + ps->pwr.active = FALSE; /* pwr is not in a send (overkill) */ + ps->sdc.valid = FALSE; /* no valid send data credit */ + ps->request_active = 0; + useaddr = *addr; + useaddr.skt = 0; + ATPOpenSocket(&useaddr,&ps->paprskt); + if (paplist[i].paprskt < 0) { + ppfreeskt(i); + return(-1); + } + return(i); /* return connection number */ +} + +/* + * shutdown the pap socket +*/ +private void +psshutdown(ps, which) +PAPSOCKET *ps; +int which; +{ + int cno = ppskttocno(ps); + + /* dequeue write elements - note, what about one in transit? (e.g. */ + /* one with a sndrsp active) */ + /* turns out to be a good question */ + if ((which & PAP_SHUT_WRITE) || (which & PAP_SHUT_ALL)) + if (ps->pwr.valid) { + *(ps->pwr.comp) = -1; /* just in case (what should it be?) */ + if (ps->pwr.active) + (void)ATPRspCancel(&ps->pwr.abr, FALSE); /* ignore error */ + ps->pwr.valid = FALSE; + /* else problem */ + } + /* dequeue read elements */ + if ((which & PAP_SHUT_READ) || (which & PAP_SHUT_ALL)) + if (ps->prr.valid) { + ATPReqCancel(&ps->prr.abr, FALSE); /* not async! */ + *(ps->prr.comp) = ps->prr.abr.abResult; + ps->prr.valid = FALSE; + } + if (which & PAP_SHUT_WRITE) + ps->sdc.valid = FALSE; /* mark as bad */ + + if (which & PAP_SHUT_ALL) + ppfreeskt(cno); /* close off socket here */ + else + ATPCloseSocket(paplist[cno].paprskt); /* close off req rec socket */ +} + + +/* + * pap read request management + * + * read request are placed on a list + * +*/ + +private boolean +setup_prr(cno,rbuf,rlen,eof,comp) +int cno; +char *rbuf; +int *rlen; +int *eof; +int *comp; +{ + PAPSOCKET *ps = cnotopapskt(cno); + PRR *prr = &ps->prr; + struct atpProto *ap; + PAPUserBytes *pub; + + if (prr->valid) + return(FALSE); + prr->numbds = setup_bds(prr->bds, sizeof(prr->bds), PAPSegSize, rbuf, + ps->flowq*PAPSegSize, (atpUserDataType)0); +/* prr->cno = ps->cno; */ /* is this needed? */ + prr->eof = eof; + prr->comp = comp; + *eof = FALSE; /* assume */ + *comp = 1; /* assume */ + prr->datasize = rlen; + ap = &prr->abr.proto.atp; + + ap->atpUserData = 0; + pub = (PAPUserBytes *)&ap->atpUserData; + /* lock ps */ + pub->connid = ps->connid; + pub->PAPtype = papSendData; + pub->other.seqno = htons(ps->papseq); + nextpapseq(ps->papseq); + ap->atpAddress = ps->addr; + ap->atpAddress.skt = ps->rrskt; + /* unlock ps */ + ap->atpSocket = 0; + ap->atpReqCount = 0; + ap->atpDataPtr = 0; + ap->atpRspBDSPtr = prr->bds; + ap->fatpXO = 1; + ap->atpNumBufs = prr->numbds; /* number of bds segments */ + ap->atpTimeOut = PAPREADTIMEOUT; + ap->atpRetries = PAPREADRETRIES; + prr->valid = TRUE; + + /* lock ps */ + if (dbug.db_pap) + fprintf(stderr,"pap: prr entry on cno %d, seq %d\n",cno, ps->papseq); + return(TRUE); +} + + + + +/* + * PAP write request management + * + * write request are organized into queue's + * +*/ +private boolean +setup_pwr(cno, sbuf,slen, eof, compstate) +int cno; +char *sbuf; +int slen; +int eof; +int *compstate; +{ + int cnt; + PAPSOCKET *ps = cnotopapskt(cno); + PWR *pwr = &ps->pwr; + atpProto *ap; + PAPUserBytes *pub; + atpUserDataType udata; + + if (pwr->valid) + return(FALSE); + /* setup reply */ + udata = 0; /* expect to expand to fit */ + pub = (PAPUserBytes *)&udata; + pub->connid = ps->connid; + pub->PAPtype = papData; + pub->other.std.eof = eof ? 1 : 0; + cnt = setup_bds(pwr->bds, sizeof(pwr->bds), PAPSegSize, sbuf, slen, + (atpUserDataType)udata); +#ifdef notdef + /* this is probably the right way */ + pub = (PAPUserBytes *)&pwr->bds[cnt-1].userData; + pub->other.std.eof = eof ? 1 : 0; +#endif + ap = &pwr->abr.proto.atp; + ap->atpSocket = ps->paprskt; /* socket we're coming from */ + ap->atpRspBDSPtr = pwr->bds; /* buffer data */ + ap->fatpEOM = 1; /* mark as last message in transaction */ + ap->atpNumBufs = cnt; /* count of buffer */ + ap->atpBDSSize = cnt; /* same */ + pwr->comp = compstate; + pwr->cno = cno; + pwr->valid = TRUE; /* valid pwr entry */ + pwr->active = FALSE; /* not in send response */ + *compstate = 1; + + if (dbug.db_pap) { + fprintf(stderr, "pap: pwr entry with %d bytes\n", slen); + if (eof) + fprintf(stderr, "pap: EOF SET\n"); + } + return(TRUE); +} + diff --git a/lib/cap/abpap.h b/lib/cap/abpap.h new file mode 100644 index 0000000..0c18f0a --- /dev/null +++ b/lib/cap/abpap.h @@ -0,0 +1,177 @@ +/* + * $Author: djh $ $Date: 1995/08/29 01:52:43 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abpap.h,v 2.4 1995/08/29 01:52:43 djh Rel djh $ + * $Revision: 2.4 $ + * + */ + +/* + * abpap.h - Printer Access protocol access definitions + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Edit History: + * + * June 22, 1986 CCKim Created + * + */ + +#define papOpenConn 1 +#define papOpenConnReply 2 +#define papSendData 3 +#define papData 4 +#define papTickle 5 +#define papCloseConn 6 +#define papCloseConnReply 7 +#define papSendStatus 8 +#define papStatusReply 9 + +#define min_PAPpkt_size (lapSize+ddpSize+atpSize) + +typedef struct { /* in ATP user bytes */ + byte connid; /* connection ID */ + byte PAPtype; /* type of PAP packet */ + union { + struct { + byte eof; /* eof in Data (TResp) */ + byte unused; /* unused */ + } std; + word seqno; /* send data sequence number */ + } other; +} PAPUserBytes; + +typedef union { /* in ATP data area */ + struct { /* OpenConn (TReq) */ + byte atprskt; /* ATP responding socket # */ + byte flowq; /* flow quantum */ + word wtime; /* wait time */ + } papO; + struct { /* OpenConnReply (TResp) */ + byte atprskt; /* ATP responding socket # */ + byte flowq; /* flow quantum */ + word result; /* result */ + byte status[256]; /* pascal string */ + } papOR; + struct { /* Status (TResp) */ + byte unused[4]; /* unused */ + byte status[256]; /* status string */ + } papS; +} PAP; + +typedef struct { + byte lapddp[lapSize+ddpSize]; + ATP atp; + PAP pap; +} PAPpkt; + + +typedef struct { + int valid; /* validity flag */ + u_short transID; + u_char skt; + int creditno; +} SDC; /* outstand send data credit (to us) */ + + +typedef struct { + int valid; + int active; + BDS bds[8]; + ABusRecord abr; + int *comp; + int cno; + int (*rspcallback)(); + u_long rspcbarg; +} PWR; /* pap read request */ + + +typedef struct { + int valid; + BDS bds[8]; + ABusRecord abr; + int *eof; + int *comp; + int *datasize; +/* int cno; */ + int numbds; + int (*callback)(); + u_long cbarg; +} PRR; /* pap read request */ + +/* PAP queue element - for deferred events */ +typedef struct { + QElem link; /* link to next for various things */ + int active; /* 1 if active, 0 ow. */ + int state; /* state of socket */ + + int rrskt; /* remote responding socket */ + int rflowq; /* remote flow quantum */ + int papuseq; /* remote pap data req. seq. used */ + int paprseq; /* remote pap data request sequence recevied */ + + int flowq; /* our flow quantum */ + int papseq; /* our pap data request sequence */ + int paprskt; /* our responding socket */ + + byte connid; /* PAP connection id */ + + QElemPtr rrhead; /* head of list of outstanding read reqs */ + QElemPtr wrhead; /* head of list of outstanding write reqs */ + PRR prr; /* active pap read request */ + PWR pwr; /* active pap write request */ + SDC sdc; /* outstanding sdc */ + + /* write only */ + ABusRecord request_abr; /* used for requests on writes */ + int request_active; /* indicates whether request is active */ + + /* Tickle and opne */ + ABusRecord abr; /* used for tickle and open */ + BDS bds[1]; /* same */ + + /* used by open only */ + struct timeval wtime; /* last tickle time (time packet read) */ + PAP po,por; /* not used except for open */ + PAPStatusRec *statusbuff; /* used by open and server */ + int *comp; + + /* used by getnextjob only */ + AddrBlock addr; + u_short transID; /* atp transid of papopen */ +} PAPSOCKET; + +/* It does not make sense for NUMSPAP to be less than NUMPAP */ +#define NUMPAP 12 /* 12 connections allowed at once */ +#define NUMSPAP 10 /* Number of servers allowed */ + +#define PAP_UNUSED -1 +#define PAP_BLOCKED 0 +#define PAP_UNBLOCKED 1 +#define PAP_WAITING 2 +#define PAP_ARBITRATE 3 + +/* pap socket states */ +#define PAP_CLOSED 0 /* socket is closed */ +#define PAP_OPENED 1 /* socket is open */ +#define PAP_INACTIVE 3 /* socket in inactive */ +#define PAP_OPENING 4 /* socket is opening */ +#define PAP_GNJOPENING 5 /* socket being opened by server code */ +#define PAP_GNJFOUND 6 /* job queued for socket */ + +#define nextpapseq(x) (x) = ((++(x) % 65536) == 0) ? 1 : (x) % 65536 + +/* + * callback functions for open reply and status + * + */ +extern byte *((*papopencallback)(/* int cno, AddrBlock *from, int result */)); +extern byte *((*papstatuscallback)(/* int cno, AddrBlock *from */)); + +PAPSOCKET *cnotopapskt(); +int ppskttocno(); +int abskttocno(); +void start_papc(); +int ppgetskt(); diff --git a/lib/cap/abpapc.c b/lib/cap/abpapc.c new file mode 100644 index 0000000..5fb7402 --- /dev/null +++ b/lib/cap/abpapc.c @@ -0,0 +1,279 @@ +/* + * $Author: djh $ $Date: 1996/06/18 10:48:17 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abpapc.c,v 2.4 1996/06/18 10:48:17 djh Rel djh $ + * $Revision: 2.4 $ +*/ + +/* + * abpapc.c - Printer Access Protocol access - client only routines. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * July 12, 1986 CCKim Created + * +*/ + +#include +#include +#include +#include +#include "abpap.h" +#include "cap_conf.h" + +OSErr PAPStatus(); /* OSErr PAPStatus(char *, PAPStatusRec *, */ + /* AddrBlock *) */ +OSErr PAPOpen(); /* OSErr PAPOpen(int *, char *, int, */ + /* PAPStatusRec *, int *) */ +private void opendone(); /* private void opendone(ABusRecord *, u_long) */ +private void res_open(); /* private void res_open(PRR *) */ +void pcinit(); +private boolean getpname(); + +/* + * PAPStatus + * + * As documented + * +*/ +OSErr +PAPStatus(printername, statusbuff, prtaddr) +char *printername; +PAPStatusRec *statusbuff; +AddrBlock *prtaddr; +{ + ABusRecord abr; + atpProto *ap; + PAPUserBytes *pub; + BDS bds[1]; /* one bds */ + PAP p; + int len; + AddrBlock addr; + EntityName en; + + if (prtaddr->net == 0 || prtaddr->node == 0 || prtaddr->skt == 0) { + create_entity(printername, &en); /* name to entity */ + if (!getpname(&addr,&en)) { + sprintf((char *)statusbuff->StatusStr+1,"?Can't find %s", + printername); + statusbuff->StatusStr[0] = (byte)(strlen(statusbuff->StatusStr+1)); + return(-1); + } + *prtaddr = addr; + } else addr = *prtaddr; + + ap = &abr.proto.atp; + ap->atpUserData = 0; + pub = (PAPUserBytes *)&ap->atpUserData; + pub->PAPtype = papSendStatus; + ap->atpAddress = addr; + ap->atpSocket = 0; + ap->atpReqCount = 0; + ap->atpDataPtr = NULL; + ap->atpRspBDSPtr = bds; + bds[0].buffSize = sizeof(PAPpkt); + bds[0].buffPtr = (char *)&p; + ap->fatpXO = 0; /* nyi */ + ap->atpTimeOut = 1; /* retry in seconds */ + ap->atpRetries = 3; /* 3 Retries */ + ap->atpNumBufs = 1; /* number of bds segments */ + len = ATPSndRequest(&abr, FALSE); + if (len == reqFailed) { + strcpy(statusbuff->StatusStr+1, "%Not responding"); + statusbuff->StatusStr[0] = (byte)(sizeof("%Not responding")-1); + return(reqFailed); + } + if (ap->atpNumRsp < 1 && (int)bds[0].dataSize < min_PAPpkt_size) + return(-1); + pub = (PAPUserBytes *) &bds[0].userData; + if (pub->PAPtype != papStatusReply) + return(-1); /* should never happen? */ + bcopy(p.papS.status, statusbuff->StatusStr, (int)p.papS.status[0]+1); + return(noErr); /* return okay */ +} + + +/* + * PAPOpen + * + * as documented + * +*/ +OSErr +PAPOpen(refnum, printername, flowquantum, statusbuff, compstate) +int *refnum; +char *printername; +int flowquantum; +PAPStatusRec *statusbuff; +int *compstate; +{ + atpProto *ap; + PAPUserBytes *pub; + int cno, err; + PAPSOCKET *ps; + AddrBlock addr; + EntityName en; + + create_entity(printername, &en); /* name to entity */ + + if (!getpname(&addr,&en)) { + sprintf((char *)statusbuff->StatusStr+1, + "%%Can't find %s",printername); + statusbuff->StatusStr[0] = (byte)(strlen(statusbuff->StatusStr+1)+1); + return(-1); + } + + cno = ppgetskt(&addr); + if (cno < 0) /* no slots left */ + return(cno); + if ((ps = cnotopapskt(cno)) == NULL) { + fprintf(stderr,"AWK\n"); + exit(9999); + } + + /* initialize status string */ + sprintf((char *)statusbuff->StatusStr+1, "%%no status"); + /* -2 instead of -1 to account for the eaten percent */ + statusbuff->StatusStr[0] = (byte)(sizeof("%%no status")-2); + *compstate = 1; /* start it out */ + ps->comp = compstate; + ps->statusbuff = statusbuff; + ps->addr = addr; + ps->flowq = flowquantum; + time((time_t *)&ps->wtime.tv_sec); /* get current time */ + ps->state = PAP_OPENING; + *refnum = cno; /* send back refnum */ + + ps->bds[0].buffPtr = (char *)&ps->por; /* establish bds here */ + ps->bds[0].buffSize = sizeof(PAP); + + ap = &ps->abr.proto.atp; + ap->atpUserData = 0; + pub = (PAPUserBytes *)&ap->atpUserData; + pub->PAPtype = papOpenConn; + pub->connid = ps->connid; + ps->po.papO.atprskt = ps->paprskt; + ps->po.papO.flowq = ps->flowq; /* until further notice */ + ps->po.papO.wtime = 1; + ap->atpAddress = ps->addr; + ap->atpSocket = 0; + ap->atpReqCount = 4; /* cheating */ + ap->atpDataPtr = (char *)&ps->po; + ap->atpRspBDSPtr = ps->bds; + ap->fatpXO = 1; /* set xo on */ + ap->atpNumBufs = 1; /* number of bds segments */ + + ap->atpTimeOut = PAPOPENTIMEOUT; + ap->atpRetries = PAPOPENRETRY; + err = cbATPSndRequest(&ps->abr, opendone, (u_long)ps); + return(err); +} + +/* + * Callback routine for open - called after sndrequest gets response + * or a timeout occurs and no response received + * +*/ +private void +opendone(abr, ps) +ABusRecord *abr; +PAPSOCKET *ps; +{ + PAPUserBytes *pub; + int sslen; + + if (ps->state != PAP_OPENING) { + if (dbug.db_pap) + fprintf(stderr,"papc: unexpected state change on session\n"); + /* sigh, what else to do ???? */ + *ps->comp = -1; + return; + } + pub = (PAPUserBytes *) &ps->bds[0].userData; + if (abr->abResult != reqFailed) { + if (abr->abResult == sktClosed) { + *ps->comp = sktClosed; /* only way to abort... */ + return; + } + if (abr->abResult == noErr && pub->PAPtype == papOpenConnReply) { + ps->rrskt = ps->por.papOR.atprskt; /* remote responding socket */ + sslen = (int)(ps->por.papS.status[0]); + bcopy(ps->por.papS.status, ps->statusbuff->StatusStr, sslen+1); + if (dbug.db_pap) + fprintf(stderr,"pap: open return from remote: %d\n", + ps->por.papOR.result); + if (ps->por.papOR.result == 0) { + ps->rflowq = ps->por.papOR.flowq; /* save remote flow quantum */ + start_papc(ps); + return; + } + } + /* wait two seconds */ + Timeout(res_open, ps, 4*2); /* will always.. */ + return; + } + res_open(ps); +} + +/* + * res_open(prr) - resume open call using prr. Really part of open done. + * +*/ +private void +res_open(ps) +PAPSOCKET *ps; +{ + long t; + int err; + atpProto *ap = &ps->abr.proto.atp; + + time(&t); + ps->po.papO.wtime = t - ps->wtime.tv_sec; + + ap->atpTimeOut = PAPOPENTIMEOUT; + ap->atpRetries = PAPOPENRETRY; + err = cbATPSndRequest(&ps->abr, opendone, (u_long)ps); + if (err < 0) { + *ps->comp = err; /* die */ + } +} + + +void +pcinit() +{ + /* nothing to do */ +} + + +/* + * Find the specified entity - return true and addr if found, false + * o.w. + * +*/ +private boolean +getpname(addr,ent) +AddrBlock *addr; +EntityName *ent; +{ + nbpProto nbpr; + NBPTEntry nbpt[1]; /* should be exact match */ + + nbpr.nbpRetransmitInfo.retransInterval = 8; + nbpr.nbpRetransmitInfo.retransCount = 3; + nbpr.nbpDataField = 1; + nbpr.nbpEntityPtr = ent; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + + NBPLookup(&nbpr, FALSE); + if (nbpr.nbpDataField != 1) + return(FALSE); + NBPExtract(nbpt, nbpr.nbpDataField, 1, ent, addr); /* get lw entry */ + return(TRUE); +} + diff --git a/lib/cap/abpaps.c b/lib/cap/abpaps.c new file mode 100644 index 0000000..2b31b1f --- /dev/null +++ b/lib/cap/abpaps.c @@ -0,0 +1,631 @@ +/* + * $Author: djh $ $Date: 1995/08/29 01:52:43 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abpaps.c,v 2.3 1995/08/29 01:52:43 djh Rel djh $ + * $Revision: 2.3 $ + * + */ + +/* + * abpaps.c - Printer Access Protocol access - server only routines. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University + * in the City of New York. + * + * Edit History: + * + * July 12, 1986 CCKim Created + * + */ + +#include +#include +#include +#include +#include +#include "abpap.h" +#include "cap_conf.h" + +/* Server functions */ +int SLInit(); /* int SLInit(int*, char*, int, PAPStatusRec*) */ +int GetNextJob(); /* int GetNextJob(int, int*, int*) */ +int PAPRegName(); /* int PAPRegName(int, char *) */ +int PAPPAPRemName(); /* int PAPPAPRemName(int, char *) */ +int HeresStatus(); /* int HeresStatus(int, PAPStatusRec*) */ +int SLClose(); /* int SLClose(int) */ + +/* internal functions */ +#ifndef CPLUSPLUSSTYPLE +void psinit(); /* init any server stuff */ +private void handle_server(); +private void pap_arb_done(); +private boolean isajob(); +private void trynewjob(); +private boolean findminjob(); +private void acceptnewjob(); +private void cancelalljobs(); +private void rb_freeup(); +private void papsendstatus(); +private void papopenreply(); +#else +/* private void handle_server(ABusRecord *, int) */ +/* private void pap_arb_done(PAPSSOCKET *) */ +/* private boolean isajob(PAPSOCKET *) */ +/* private void trynewjob(PAPSSOCKET *, ABusRecord *) */ +/* private boolean findminjob(PAPSOCKET *) */ +/* private void acceptnewjob(PAPSOCKET *, PAPSSOCKET *) */ +/* private void cancelalljobs(PAPSSOCKET*) */ +/* private void rb_freeup(ABusRecord*, ReplyBuf *) */ +/* private void papsendstatus(PAPSSOCKET*, ABusRecord*) */ +/* private void papopenreply(PAPSSOCKET*, unsigned char, AddrBlock*, */ +/* unsigned short, unsigned short, byte*, int, int) */ +#endif + + + +/* PAP Server Socket info - used only here - so not placed in abpap.h */ +typedef struct { + int active; + int state; /* current state */ + int resskt; /* our responding socket */ + int flowq; /* our flow quantum */ + PAPStatusRec *sb; /* status string */ + ABusRecord abr; /* for getrequest listens */ + QElemPtr gnj_q; /* get next job queue */ + PAP pap; /* pap packet */ +} PAPSSOCKET; + +PAPSSOCKET papslist[NUMSPAP]; + +/* + * Issued by PAP client in server to open service listening socket + * and to register the server's name on this service listening socket. + * The client can also provide an inital status string. + * + * Multiple SLInit call implies that the server can handle multiple + * printers - the maximum number is compiled in as NUMSPAP + * + * call parameters: entity name, flow quantum, status string + * returned: result code, server refnum + * +*/ +SLInit(refnum, printername, flowquantum, statusbuff) +int *refnum; +char *printername; +int flowquantum; +PAPStatusRec *statusbuff; +{ + AddrBlock useraddr; + int cno; + int err; + atpProto *ap; + + /* allocate server socket */ + for (cno = 0; cno < NUMSPAP; cno++) + if (!papslist[cno].active) + break; + if (cno==NUMSPAP) + return(-1); /* not enough server processes */ + + *refnum = cno; + papslist[cno].active = TRUE; + papslist[cno].state = PAP_BLOCKED; + papslist[cno].flowq = flowquantum; + papslist[cno].sb = statusbuff; + + useraddr.net = useraddr.node = useraddr.skt = 0; /* accept from anywhere */ + ATPOpenSocket(&useraddr, &papslist[cno].resskt); + + /* publish our name */ + if ((err = PAPRegName(cno, printername)) != noErr) + return(err); + + /* start listener */ + ap = &papslist[cno].abr.proto.atp; + ap->atpSocket = papslist[cno].resskt; + ap->atpReqCount = sizeof(papslist[cno].pap); + ap->atpDataPtr = (char *)&papslist[cno].pap; + ap->atpNumBufs = flowquantum; /* this is what ATP uses! */ + err = cbATPGetRequest(&papslist[cno].abr, handle_server, cno); + return(err); +} + + +/* + * getnextjob - call when ready to accept new job through specified + * listener socket (idenitified by server refnum) + * + * call parameters: server refnum + * returned parameters: result code, refnum to refer to connection in + * read, write, close calls + * +*/ +int +GetNextJob(srefnum, refnum, compstate) +int srefnum; +int *refnum; +int *compstate; +{ + PAPSOCKET *ps; + PAPSSOCKET *pss = &papslist[srefnum]; + AddrBlock useaddr; + int cno; + + useaddr.net = useaddr.node = useaddr.skt = 0; /* accept from anywhere */ + cno = ppgetskt(&useaddr); + *refnum = cno; + ps = cnotopapskt(cno); + if (cno < 0 || ps == NULL) + return(-1); + ps->flowq = pss->flowq; + ps->state = PAP_GNJOPENING; + ps->comp = compstate; + *compstate = 1; + + if (pss->state == PAP_BLOCKED) /* if blocked */ + pss->state = PAP_WAITING; /* now waiting */ + q_tail(&papslist[srefnum].gnj_q, &ps->link); /* here's the list */ + return(noErr); +} + + +/* + * papregname - allow a secondary name to be registered + * +*/ +int +PAPRegName(refnum, printername) +int refnum; +char *printername; +{ + EntityName en; + nbpProto nbpr; /* nbp proto */ + NBPTEntry nbpt[1]; /* table of entity names */ + int err; + + create_entity(printername, &en); + + nbpr.nbpAddress.skt = papslist[refnum].resskt; + nbpr.nbpRetransmitInfo.retransInterval = 4; + nbpr.nbpRetransmitInfo.retransCount = 3; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + nbpr.nbpDataField = 1; /* max entries */ + nbpr.nbpEntityPtr = &en; + + err = NBPRegister(&nbpr,FALSE); /* try synchronous */ + return(err); +} + +/* + * remove a server name + * +*/ +int +PAPRemName(refnum, printername) +int refnum; +char *printername; +{ + EntityName en; + int err; + + create_entity(printername, &en); + err = NBPRemove(&en); + return(err); +} + +/* + * hereisstatus - used to send new status string + * + * note: caller may modify status string at any time, but the status + * string MUST be a pascal style string. + * +*/ +int +HeresStatus(srefnum, statusbuff) +int srefnum; +PAPStatusRec *statusbuff; +{ + if (papslist[srefnum].active) + papslist[srefnum].sb = statusbuff; + else return(-1); + return(noErr); +} + +/* + * used to close down a server "process". Note: it does NOT deregister + * any names assocatied with that process. Furthermore, it does not close + * down any active pap connections. You are expected to do this + * before calling SLClose. The function of this call is to prevent + * further calls from being processed. + * +*/ +int +SLClose(srefnum) +int srefnum; +{ + int err; + + if (!papslist[srefnum].active) + return(-1); /* wasn't active */ + papslist[srefnum].active = FALSE; /* mark as unused now */ + err = ATPCloseSocket(papslist[srefnum].resskt); + cancelalljobs(&papslist[srefnum]); + /* possibly close down any active connections in the future */ + return(noErr); +} + + +/* + * psinit + * + * initialize server code + * +*/ +void +psinit() +{ +} + +/* + * Handle the requests coming to the server process + * +*/ +private void +handle_server(abr, cno) +ABusRecord *abr; +int cno; +{ + PAPSSOCKET *pss = &papslist[cno]; + PAPSOCKET *ps; + PAPUserBytes *pub; + atpProto *ap; + int err; + + if (abr->abResult != noErr) { + if (abr->abResult == sktClosed) + return; + fprintf(stderr, "pap: socket error for server socket %x\n",pss); + } + + ap = &abr->proto.atp; + pub = (PAPUserBytes *)&ap->atpUserData; + + if (dbug.db_pap) + fprintf(stderr, "[HS: PAPTYPE %d, STATE %d, PCID %x]\n", + pub->PAPtype, pss->state, pub->connid); + + switch (pub->PAPtype) { + default: + fprintf(stderr,"PAP: error: unexpected request received\n"); + fprintf(stderr,"PAP: conn %d,paptype %d,eof %d, unused %d\n", + pub->connid, pub->PAPtype, pub->other.std.eof, + pub->other.std.unused); + break; + case papOpenConn: + switch (pss->state) { + case PAP_WAITING: + if (dbug.db_pap) + fprintf(stderr, "Moving from wait to arbitrate\n"); + pss->state = PAP_ARBITRATE; + Timeout(pap_arb_done, pss, PAPARBITRATIONTIME); + trynewjob(pss, abr); /* check out new job */ + break; + case PAP_ARBITRATE: + if (dbug.db_pap) + fprintf(stderr, "Arbritrating\n"); + trynewjob(pss, abr); /* check out new job */ + break; + case PAP_UNBLOCKED: + if (dbug.db_pap) + fprintf(stderr, "Unblocked and have new job\n"); + ps = (PAPSOCKET *)dq_tail(&pss->gnj_q); + if (ps == NULL) { + fprintf(stderr, "pap: in unblocked state, but no gnj ready!!!\n"); + break; + } + ps->addr = ap->atpAddress; /* remote address */ + ps->transID = ap->atpTransID; + ps->connid = pub->connid; + ps->rrskt = pss->pap.papO.atprskt; + ps->rflowq = pss->pap.papO.flowq; + acceptnewjob(ps, pss); /* got new job */ + break; + default: + /* reply no good */ + break; + } + break; + case papSendStatus: + papsendstatus(pss, abr); + break; + } + ap = &pss->abr.proto.atp; + ap->atpSocket = pss->resskt; + ap->atpReqCount = sizeof(pss->pap); + ap->atpDataPtr = (char *)&pss->pap; + err = cbATPGetRequest(&pss->abr, handle_server, cno); + /* if erro ??? */ +} + +/* + * called when arbitration is completed + * +*/ +private void +pap_arb_done(pss) +PAPSSOCKET *pss; +{ + PAPSOCKET *ps; + + if (dbug.db_pap) + fprintf(stderr, "Arbritration done - accepting new jobs\n"); + ps = (PAPSOCKET *)q_mapf(pss->gnj_q, isajob, 0); + while (ps != NULL) { + acceptnewjob(ps, pss); + dq_elem(&pss->gnj_q, ps); + ps = (PAPSOCKET *)q_mapf(pss->gnj_q, isajob, 0); + } + pss->state = (pss->gnj_q == NULL) ? PAP_BLOCKED : PAP_UNBLOCKED; +} + +private boolean +isajob(ps) +PAPSOCKET *ps; +{ + return(ps->state == PAP_GNJFOUND); +} + + +/* + * Called during arbitration state. Trys out the new job and sees if it + * has priority over any extant ones - priority based on job wait time + * +*/ +private void +trynewjob(pss, abr) +PAPSSOCKET *pss; +ABusRecord *abr; +{ + atpProto *ap = &abr->proto.atp; + PAPUserBytes *pub; + int jobwaittime; + PAPSOCKET *ps; + + pub = (PAPUserBytes *)&ap->atpUserData; + jobwaittime = pss->pap.papO.wtime; + + ps = (PAPSOCKET *)q_map_min(pss->gnj_q, findminjob, 0); + if (ps->state == PAP_GNJOPENING || ps->wtime.tv_sec < jobwaittime) { + if (dbug.db_pap) + fprintf(stderr, "Arbritration - got better job\n"); + if (ps->state == PAP_GNJFOUND) + papopenreply(pss, (u_char)ps->connid, &ps->addr, ps->transID, + papPrinterBusy, pss->sb->StatusStr, 0, 0); + ps->wtime.tv_sec = jobwaittime; + ps->state = PAP_GNJFOUND; + ps->addr = ap->atpAddress; + ps->transID = ap->atpTransID; + ps->connid = pub->connid; + ps->rrskt = pss->pap.papO.atprskt; + ps->rflowq = pss->pap.papO.flowq; + } else { + papopenreply(pss, (u_char)pub->connid, &ap->atpAddress, ap->atpTransID, + papPrinterBusy, pss->sb->StatusStr, 0, 0); + } +} + +private boolean +findminjob(ps) +PAPSOCKET *ps; +{ + if (ps->state == PAP_GNJOPENING) + return(-1); + else return(ps->wtime.tv_sec); +} + +/* + * acceptnewjob - used to accept a job - after this is called, + * a GetNextJob call has completed + * +*/ +private void +acceptnewjob(ps, pss) +PAPSOCKET *ps; +PAPSSOCKET *pss; +{ + if (dbug.db_pap) + fprintf(stderr, "Accepting job: connid %d from [%d.%d]\n", ps->connid, + ntohs(ps->addr.net), ps->addr.node); + + papopenreply(pss, (u_char)ps->connid, &ps->addr, ps->transID, + papNoError, pss->sb->StatusStr, ps->flowq, ps->paprskt); + start_papc(ps); +} + +/* + * cancelalljobs - reject any pending jobs, remove all from the queue + * +*/ +private void +cancelalljobs(pss) +PAPSSOCKET *pss; +{ + PAPSOCKET *ps; + if (dbug.db_pap) + fprintf(stderr, "canceling all jobs\n"); + + while ((ps = (PAPSOCKET *)dq_tail(&pss->gnj_q)) != NULL) { + if (ps->state == PAP_GNJFOUND) + papopenreply(pss, (u_char)ps->connid, &ps->addr, ps->transID, + papPrinterBusy, pss->sb->StatusStr, ps->flowq, ps->paprskt); + *ps->comp = -1; + } +} + + + +/* + * The following functions require are used to send status messages and + * open reply codes back. Each requires an abr, bds, etc. to be allocated + * while they are running... + * +*/ + +typedef struct { + QElem link; + ABusRecord abr; /* apple bus record */ + BDS bds[1]; /* single bds should be good enuf */ + PAP pap; /* pap response packet */ +} replybuf; + +replybuf *free_replybuf; + +/* + * Free up reply buffer + * - would like to make a macro, but since it's the callback from + * the ATP SndResp routines, not possible + * +*/ +private void +rb_freeup(abr, rb) +ABusRecord *abr; +replybuf *rb; +{ + q_tail(&free_replybuf, &rb->link); +} + + +/* + * Send status back + * + * Note: we expect statusbuff to have a pascal string! + * + */ + +/* + * callback functions for open reply and status + * + */ +byte *((*papopencallback)()) = NULL; +byte *((*papstatuscallback)()) = NULL; + +private void +papsendstatus(pss, abr) +PAPSSOCKET *pss; +ABusRecord *abr; +{ + replybuf *rb; + atpProto *ap; + int err, sslen; + PAPUserBytes *pub; + byte *message; + + if ((rb = (replybuf *)dq_head(&free_replybuf)) == NULL && + (rb = (replybuf *)malloc(sizeof(replybuf))) == NULL) { + fprintf(stderr,"panic: replybuf: out of memory\n"); + exit(8); + } + + ap = &rb->abr.proto.atp; + /* find responding address and transid from request abr */ + ap->atpAddress = abr->proto.atp.atpAddress; + ap->atpTransID = abr->proto.atp.atpTransID; + ap->atpSocket = pss->resskt; + ap->atpRspBDSPtr = rb->bds; + message = pss->sb->StatusStr; + if (!papstatuscallback || + (message = (*papstatuscallback)((pss - papslist), &ap->atpAddress)) == NULL) + message = pss->sb->StatusStr; + sslen = (int)(message[0]); + rb->bds[0].buffSize = sslen + 4 +1 ; /* ugh */ + rb->bds[0].buffPtr = (char *)&rb->pap; + rb->bds[0].userData = 0L; + pub = (PAPUserBytes *)&rb->bds[0].userData; + pub->PAPtype = papStatusReply; + ap->atpNumBufs = 1; + ap->atpBDSSize = 1; + ap->fatpEOM = 1; /* eom */ + /* include string length byte */ + bcopy(message, rb->pap.papS.status, sslen + 1); + if (dbug.db_pap) { + message[(int)message[0]+1] = 0; /* null terminate */ + fprintf(stderr,"Sending %s to [%d,%d]\n", + message + 1, + ntohs(abr->proto.atp.atpAddress.net), + abr->proto.atp.atpAddress.node&0xff); + } + + err = cbATPSndRsp(&rb->abr, rb_freeup, rb); + if (err != noErr) { + rb_freeup(0, rb); + if (dbug.db_pap) + fprintf(stderr, "pap: sendstatus: ATPSndRsp returns %d\n",err); + } +} + + +/* + * Send a reply back to a PAPOpenConn request + * + * note: message is expected to be a "pascal" string (first byte is length) + * +*/ +private void +papopenreply(pss, connid, addr, transID, result, message, flowq, rskt) +PAPSSOCKET *pss; +u_char connid; +AddrBlock *addr; +u_short transID; +u_short result; +byte *message; +int flowq; +int rskt; +{ + replybuf *rb; + atpProto *ap; + int err, sslen; + PAPUserBytes *pub; + byte *message2; + + if ((rb = (replybuf *)dq_head(&free_replybuf)) == NULL && + (rb = (replybuf *)malloc(sizeof(replybuf))) == NULL) { + fprintf(stderr,"panic: replybuf: out of memory\n"); + exit(8); + } + ap = &rb->abr.proto.atp; + /* find responding address and transid from request abr */ + ap->atpAddress = *addr; + ap->atpSocket = pss->resskt; + ap->atpTransID = transID; + ap->atpRspBDSPtr = rb->bds; + if (papopencallback && + (message2 = (*papopencallback)((pss - papslist), &ap->atpAddress, result))) + message = message2; + sslen = (int)(message[0]); + rb->bds[0].buffSize = sslen + 4 + 1; /* ugh */ + rb->bds[0].buffPtr = (char *)&rb->pap; + rb->bds[0].userData = 0L; + pub = (PAPUserBytes *)&rb->bds[0].userData; + pub->connid = connid; + pub->PAPtype = papOpenConnReply; + rb->pap.papOR.result = result; + rb->pap.papOR.atprskt = rskt; + rb->pap.papOR.flowq = flowq; + bcopy(message, rb->pap.papOR.status, sslen+1); + if (dbug.db_pap) { + message[(int)message[0]+1] = 0; /* null terminate */ + fprintf(stderr,"Sending reply (%d) mesg %s to [%d,%d,%d,%x]\n", + result,message+1,ntohs(addr->net),addr->node,addr->skt,connid); + } + ap->atpNumBufs = 1; + ap->atpBDSSize = 1; + ap->fatpEOM = 1; /* eom */ + err = cbATPSndRsp(&rb->abr, rb_freeup, rb); + if (err != noErr) { + rb_freeup(0, rb); + if (dbug.db_pap) + fprintf(stderr, "pap: sendopenreply: ATPSndRsp returns %d\n",err); + } +} diff --git a/lib/cap/abpp.c b/lib/cap/abpp.c new file mode 100644 index 0000000..70e52a2 --- /dev/null +++ b/lib/cap/abpp.c @@ -0,0 +1,119 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:49:05 $ + * $Header: abpp.c,v 2.1 91/02/15 22:49:05 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * abpp - Unix Printer Access Protocol calls + * + * A set of routines which emulate the unix read/write calls. + * The calls are always synchronous. + * + * Note: these aren't much use unless you fork off since + * a blocking read will pretty much kill you + * + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * July 5, 1986 CCKim Created. + * + */ + +#include +#include +#include + + +/* + * ppopen + * + * open pap socket + * + * note: does not return until connection is opened. + * +*/ +int +ppopen(cno, lwname, flowq, pstatus) +int *cno; +char *lwname; +int flowq; +int (*pstatus)(); +{ + int err; + PAPStatusRec statusbuff; + int rcomp; + + while ((err = PAPOpen(cno, lwname, flowq, &statusbuff, &rcomp)) != noErr) { + (*pstatus)(&statusbuff.StatusStr[1]); + sleep(5); /* wait five seconds */ + } + do { + abSleep(4*5, TRUE); /* wait 2 seconds */ + (*pstatus)(&statusbuff.StatusStr[1]); + } while (rcomp > 0); + return(rcomp); +} + + +/* + * ppread + * + * Read from the LaserWriter + * + * Note: buf is assume to be PAPSegSize * flowquantum returned by the + * laserWriter. To be safe, use 512 (PAPSegSize) * 8 (the maximum flow + * quantum) + * + * returns number of bytes or zero to indicate eof + * +*/ +ppread(cno, buf) +int cno; +char *buf; +{ + int rcomp; + int rlen; + static int eof = 0; + int err; + + if (eof) /* check for eof on previous call */ + return(0); + do { + if ((err = PAPRead(cno, buf, &rlen, &eof, &rcomp)) != noErr) + return(err); + do { abSleep(4, TRUE); } while (rcomp > 0); + if (rcomp > 0) + return(rcomp); + } while (rlen == 0 && !eof); + + if (eof && rlen == 0) + return(0); /* zero length indicates eof */ + return(rlen); +} + +/* + * ppwrite - write to laserWriter + * + * eof - true if we are to send eof to the laserwriter + * +*/ +ppwrite(cno, buf, blen, eof) +int cno; +char *buf; +int blen; +{ + int wcomp; + int err; + + err = PAPWrite(cno, buf, blen, eof, &wcomp); /* send eof */ + if (err != noErr) + return(err); + do { abSleep(4, TRUE); } while (wcomp > 0); + return(err); +} diff --git a/lib/cap/abqueue.c b/lib/cap/abqueue.c new file mode 100644 index 0000000..558ce66 --- /dev/null +++ b/lib/cap/abqueue.c @@ -0,0 +1,307 @@ +/* + * $Author: djh $ $Date: 1993/09/28 08:24:19 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abqueue.c,v 2.2 1993/09/28 08:24:19 djh Rel djh $ + * $Revision: 2.2 $ +*/ + +/* + * abqueue - routines for queueing and dequeueing + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * July 1, 1986 Schilit Created + * + */ + +/* + * The following queuing code is structured such that the VAX + * queue instructions is used if "VAX" is defined. + * +*/ + +#include + +#ifndef VAX /* if not a VAX need queue routines */ + +/* + * remque(QElemPtr elem) + * + * Remove "elem" from queue. + * + * NB: libc routine of same name is used on VAX systems. + * +*/ + +remque(elem) +QElemPtr elem; +{ + (elem->q_back)->q_forw = elem->q_forw; + (elem->q_forw)->q_back = elem->q_back; +} + +/* + * insque(QElemPtr elem,pred) + * + * Insert "elem" before "pred" in queue. + * + * NB: libc routine of same name is used on VAX systems. + * +*/ + +insque(elem,pred) +QElemPtr elem,pred; +{ + elem->q_forw = pred->q_forw; + elem->q_back = pred; + pred->q_forw = elem; + (elem->q_forw)->q_back = elem; +} + +#endif /* not VAX */ + +/* + * q_tail(QElemPtr *head,ntail) + * + * q_tail adds "ntail" to the rear of the queue specified by "*head" + * + * If the queue does not exist (i.e. "head" points to a NIL) then one + * is created. + * +*/ + +void +q_tail(head,ntail) +QElemPtr *head; /* pointer to the head of the queue */ +QElemPtr ntail; +{ + if (*head == NILQ) { /* is this an empty queue? */ + *head = ntail; /* yes, first item is now head */ + ntail->q_back = /* set forward and back pointers */ + ntail->q_forw = ntail; /* list is circular! */ + } else + insque(ntail,(*head)->q_back); +} + +/* + * q_head(QElemPtr *head,nhead) + * + * q_head adds "nhead" to the front of the queue specified by "head." + * + * If the queue does not exist (i.e. "head" points to a NIL) then one + * is created. + * +*/ + +void +q_head(head,nhead) +QElemPtr *head; /* pointer to the head of the queue */ +QElemPtr nhead; +{ + + if (*head == NILQ) { /* is this an empty queue? */ + nhead->q_back = /* set forward and back pointers */ + nhead->q_forw = nhead; /* list is circular! */ + } else + insque(nhead,(*head)->q_back); + *head = nhead; /* yes, first item is now head */ +} + +/* + * q_elem inserts item after prev in the list. If the list is empty + * or no prev is specified, then the q_head is used to create a new + * list or insert at the head respectively + * +*/ +void +q_elem(head, prev, item) +QHead head; +QElemPtr prev; +QElemPtr item; +{ + if (*head == NILQ || prev == NILQ) + q_head(head, item); + else + insque(prev, item); +} + + +/* + * QElemPtr dq_tail(QElemPtr *head) + * + * dq_tail removes the last entry on the queue pointed to by "*head" + * and returns a pointer to the entry removed. + * + * If we remove the last entry, then "*head" is set to NIL. + * +*/ + +QElemPtr +dq_tail(head) +QElemPtr *head; +{ + QElemPtr tail; + + if (*head == NILQ) /* if no queue then */ + return(NILQ); /* return NIL */ + + tail = (*head)->q_back; /* pick up the tail */ + if (tail == *head) { /* is this the last element? */ + *head = NILQ; /* yes, set the head to NIL */ + } else + remque(tail); + return(tail); +} + + + +/* + * QElemPtr dq_head(QElemPtr *head) + * + * dq_head removes the first entry on the queue pointed to by "*head" + * and returns a pointer to the entry removed. + * + * If we remove the last entry, then "*head" is set to NIL. + * +*/ + +QElemPtr +dq_head(head) +QHead head; +{ + QElemPtr nhead,ohead; + + if (*head == NILQ) /* if no queue then */ + return(NILQ); /* return NIL */ + + ohead = *head; /* dereference and remember head */ + nhead = (*head)->q_forw; /* this is the new head */ + if (nhead == ohead) { /* is this the last element? */ + nhead = NILQ; /* yes, will set the head to NIL */ + } else + remque(ohead); /* remove head */ + *head = nhead; /* update head pointer */ + return(ohead); /* return head of list */ +} + + +/* + * remove element from list, setting head to null if list becomes empty + * +*/ + +void +dq_elem(head,item) +QHead head; +QElemPtr item; +{ + if (*head == NILQ) + return; /* can't remove from empty list! */ + + if (*head == item) /* removing head? */ + dq_head(head); /* get rid of it nicely then */ + else + remque(item); +} + + +/* + * QElemPtr q_mapf(QElemPtr head,int (*filter)()) + * + * q_mapf maps across all entries in the queue pointed to by "head" + * calling the "filter" routine with a pointer to each QElem. If the + * filter routine returns TRUE then q_mapf returns with the current + * QElemPtr. If filter never returns TRUE then NILQ is returned. + * + * q_mapf processes the list head to tail. + * +*/ + +QElemPtr +q_mapf(head,filter,arg) +QElemPtr head; +int (*filter)(); +void *arg; +{ + QElemPtr step; + + if (head == NILQ) + return(NILQ); + + step = head; + do { + if ((*filter)(step,arg)) + return(step); + step = step->q_forw; + } while (step != head); + return(NILQ); +} + +/* + * QElemPtr q_mapb(QElemPtr head,int (*filter)()) + * + * q_mapb is the same as q_mapf except the list is processed tail to + * front. + * +*/ + +QElemPtr +q_mapb(head,filter,arg) +QElemPtr head; +int (*filter)(); +void *arg; +{ + QElemPtr step; + + if (head == NILQ) + return(NILQ); + + step = head; + do { + step = step->q_back; + if ((*filter)(step,arg)) + return(step); + } while (step != head); + return(NILQ); +} + + +/* + * QElemPtr q_map_min(QElemPtr head,int (*filter)()) + * + * map over the elements, returning the element with who filter reports + * as having the smallest value + * + * bug: executes filter on head twice +*/ + +QElemPtr +q_map_min(head,filter,arg) +QElemPtr head; +int (*filter)(); +void *arg; +{ + QElemPtr step; + QElemPtr rv; + int minval, curval; + + if (head == NILQ) + return(NILQ); + + step = head; + minval = (*filter)(step, arg); /* assume */ + rv = step; + do { + step = step->q_back; + if ((curval = (*filter)(step,arg)) < minval) { + minval = curval; + rv = step; + } + } while (step != head); + return(rv); +} + diff --git a/lib/cap/absched.c b/lib/cap/absched.c new file mode 100644 index 0000000..9228793 --- /dev/null +++ b/lib/cap/absched.c @@ -0,0 +1,922 @@ +/* + * $Author: djh $ $Date: 1996/09/10 13:47:34 $ + * $Header: /mac/src/cap60/lib/cap/RCS/absched.c,v 2.8 1996/09/10 13:47:34 djh Rel djh $ + * $Revision: 2.8 $ +*/ + +/* + * absched.c - Simple Protocol Scheduling Facility + * + * Provides a very simple non-preemptive "scheduling" facility. + * It allows one to: + * (a) set timeouts with routines called back on timeouts + * (b) call listeners if input is available + * Base interface is through "abSleep" (bad name). abSleep + * will run for a the time passed returning only if the time has + * passed or if one of the above events has occurred + * and you asked it to return on any event occurring. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 14, 1986 Schilit Created. + * June 18, 1986 CCKim Chuck's handler runs protocol + * + */ + +/* needs to do printfs on other than lap_debug */ + +/* good place to stick the copyright so it shows up in object files */ +char Columbia_Copyright[] = "Copyright (c) 1986,1987,1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include /* to cover difference between bsd systems */ + +/* + * this is a hack that + * needs more elegance + * + */ + +#ifdef __bsdi__ +#define MULTI_BPF_PKT +#endif /* __bsdi__ */ + +#ifdef __NetBSD__ +#define MULTI_BPF_PKT +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +#define MULTI_BPF_PKT +#endif /* __FreeBSD__ */ + +#ifdef NeXT +#define MULTI_BPF_PKT +#endif /* NeXT */ + +/* + * NOTE: it is not important to get the TZ information. It is simply + * included because some systems complain if there isn't a valid pointer + * there. + * +*/ + +/* + * define localtime_gtod if your system's get time of day call + * calls _gettimeofday to get the system time and uses disk based + * information to get the time zone information EVERY TIME. + * + * (e.g. gettimeofday is based on pd_localtime, but does it incorrectly) + * + * For AUX version 1.0. + * +*/ +#ifdef LOCALTIME_GTOD +# define gettimeofday(tv, tz) _gettimeofday((tv)) +#endif + +/* + * Configuration defines + * NOFFS - no ffs function defined, use our own + * + * Note: need not be ffs as per vax. It may be any routine + * that returns bits assuming 1 == 1, 2==2, 4==3, 8==4, etc. + * and 0 is none +*/ + +/* + * structure defs + * +*/ + +/* timer support */ +typedef struct TimerEntry { + struct TimerEntry *t_next; /* pointer to next */ + struct timeval t_time; /* timeout internal form */ + ProcPtr t_fun; /* function to call */ + caddr_t t_arg; /* argument for function */ +} TimerEntry; + +#define NILTIMER (TimerEntry *) 0 /* a nil pointer */ + +/* select on fds */ + +typedef struct { + boolean fl_valid; /* is this item valid? (true/false) */ + int (*fl_listener)(); /* listener */ + caddr_t fl_addrarg; /* any arguments */ + int fl_intarg; +} FDLISTENER; + +/* local defines */ +#define MICRO 1000000 /* micro is one million'th, 1mil per sec*/ +#define UNITT 250000 /* 1/4 second is tick unit: 250k usec */ + +/* variables */ +private gfd_set fdbitmask; /* file descriptors open... */ +private gfd_set *fdbits; +private int fdnotinited = 1; /* 0 is not inited */ +private int fdmaxdesc = -1; /* mark not inited */ + + +/* list of fd and listeners */ +/* NOFILE is the number of file descriptors. If it isn't set */ +/* then we use FD_SETSIZE which must be set one way or the other */ +/* prefer NOFILE since it can be much smaller */ +#ifndef NOFILE +# define NOFILE FD_SETSIZE +#endif +FDLISTENER fdlist[NOFILE]; + +/* + * Tappan: + * optimization - holds the current gettimeofday() so lower level routines + * can avoid system call overhead - updated when abSleep() is entered and + * after every sleep + * + * cck: optimization for Timeout and other time related support + * +*/ +private struct timeval tv_now; +/* should be able to pass null for tz if no tz information wanted. */ +/* careful: some systems may want structure and may blow up */ +#define NO_TZ ((struct timezone *)0) + +/* routine declarations */ + +private TimerEntry *tm_alloc(); +private void tm_free(); +private int i_doTimeout(); +#ifdef NOFFS +private int ffs(); +#endif +private int FD_GETBIT(); +private void abstoreltime(); +private void reltoabstime(); +private void apptoabstime(); + +export int init_fdlistening(); +export int fdlistener(); +export int fdunlisten(); +export int fdlistenread(); +export int fdlistensuspend(); +export int fdlistenresume(); +export void Timeout(); +export void AppleTimeout(); +export void relTimeout(); +export void absTimeout(); +export int remTimeout(); +export int doTimeout(); +export boolean minTimeout(); +export int abSleep(); +int abSelect(); /* haven't decided whether this should */ + /* be exported or not */ +/* + * FD LISTENER SUPPORT + * +*/ + +int +init_fdlistening() +{ + int i; + + if (!fdnotinited) /* already inited */ + return(NOFILE); + fdbits = &fdbitmask; + FD_ZERO(fdbits); /* make sure these are cleared */ + fdmaxdesc = -1; /* mark init, but not in use */ + fdnotinited = FALSE; /* initted */ + for (i = 0; i < NOFILE; i++) + fdlist[i].fl_valid = FALSE; /* not in use */ + return(NOFILE); +} + +/* + * install a listener "listener" on file descriptor "fd" + * + * the listener is called with the file descriptor when INPUT is ready + * on the fd. + * + * returns: 0 okay, -1 problem (fd too large, too small, etc) +*/ +export int +fdlistener(fd, listener, addrarg, intarg) +int fd; +int (*listener)(); +caddr_t addrarg; +int intarg; +{ + if (fd < 0 || fd >= NOFILE || fdnotinited) + return(-1); + FD_SET(fd, fdbits); + fdmaxdesc = max(fd, fdmaxdesc); + fdlist[fd].fl_listener = listener; + fdlist[fd].fl_valid = TRUE; + fdlist[fd].fl_addrarg = addrarg; + fdlist[fd].fl_intarg = intarg; + return(0); +} + +/* + * close off a listener on file descriptor "fd" + * +*/ +export int +fdunlisten(fd) +int fd; +{ + FDLISTENER *fdi; + + if (fd < 0 || fd >= NOFILE || fdnotinited) + return(-1); + + fdi = &fdlist[fd]; /* mark end */ + if (fdi->fl_valid) { /* this was a valid item */ + FD_CLR(fd, fdbits); + fdi->fl_valid = FALSE; /* mark off list */ + if (fd >= fdmaxdesc) { /* was it at the tail of the list */ + /* use >= instead of = above is paranoia */ + /* select out boundary case and no fdmaxdesc case (paranoia) */ + if (fd == 0 || fdmaxdesc < 0) { + fdmaxdesc = -1; + return(0); + } + /* step back one and start from there */ + for ( fdmaxdesc--, fdi = &fdlist[fdmaxdesc]; fdmaxdesc >= 0; + fdmaxdesc--, fdi--) { + if (fdi->fl_valid) + break; + } + /* if none valid, then we come out with fdmaxdesc = -1 */ + } + } + return(0); +} + +/* + * run the listener on the file descriptor fd + * + * returns value returned by listener if there was one + * -1 if no listener or fd not valid + * +*/ +export int +fdlistenread(fd) +{ + FDLISTENER *fdi; + + if (dbug.db_skd) + fprintf(stderr,"fdlistenread call\n"); + /* paranoia */ + if (fd < 0 || fd >= NOFILE || fdnotinited) + return(-1); + + if (fdlist[fd].fl_valid) { + fdi = &fdlist[fd]; + if (fdi->fl_listener != NULL) + return((*(fdi->fl_listener))(fd, fdi->fl_addrarg, fdi->fl_intarg)); + } + return(-1); +} + +/* primarily useful outside libraries where we are not event based, but */ +/* are more poll based. for instance, you might want the listener for */ +/* a terminal to just return that data is available */ + +/* listensuspend and resume both return 0 on okay, -1 on error */ +/* error; bad fd or invalid fd */ +/* listensuspend temporarily supsends listening (selecting) on a fd */ +export int +fdlistensuspend(fd) +{ + if (fd < 0 || fd >= NOFILE || fdnotinited) + return(-1); + if (fdlist[fd].fl_valid) { + FD_CLR(fd, fdbits); /* don't bother updating max */ + return(0); + } + return(-1); +} + +/* listensupend resumes listening (selecting) on a fd */ +export int +fdlistenresume(fd) +{ + if (fd < 0 || fd >= NOFILE || fdnotinited) + return(-1); + if (fdlist[fd].fl_valid) { + FD_SET(fd, fdbits); /* don't bother updating max */ + return(0); + } + return(-1); +} + + +/* + * TIMER SUPPORT + * + * SUPPORTS a timer facility. The clock resolution is in 1/4 second + * units (TICKS). + * + * Timeouts are given as a + * triple. + * The combination should be unique or else remTimeout + * must be called for the "unknown" number of times the combination + * occurs. +*/ +private TimerEntry TimoutQ = {NILTIMER,{0,0},NILPROC,0}; +private TimerEntry FreeTimoutQ = {NILTIMER, {0,0}, NILPROC, 0}; +/* these are used primarily for debugging */ +int mcalloced = 0; +int mcfreesize = 0; + +/* allocate and dealloc functions for timer entries */ +/* keeps chain of allocated pointers */ + +/* allocate a timer entry with specified fun, arg */ +private TimerEntry * +tm_alloc(fun,arg) +ProcPtr fun; +caddr_t arg; +{ + TimerEntry *tu; + + if (FreeTimoutQ.t_next == NILTIMER) { + tu = (TimerEntry *) malloc(sizeof(TimerEntry)); /* new entry */ + mcalloced++; + } else { + mcfreesize--; + tu = FreeTimoutQ.t_next; /* get first free */ + FreeTimoutQ.t_next = tu->t_next; /* and unlink it */ + } + tu->t_fun = fun; /* set function to call */ + tu->t_arg = arg; /* and arg to pass... */ + tu->t_next = NILTIMER; /* make sure it doesn't go anywhere */ + return(tu); +} + +/* + * link timer entry into the free list + * +*/ +private void +tm_free(tu) +TimerEntry *tu; +{ + mcfreesize++; + tu->t_next = FreeTimoutQ.t_next; + FreeTimoutQ.t_next = tu; +} + +/* + * void + * Timeout(ProcPtr fun,caddr_t arg,int t) + * + * Call "fun" with "arg" after elapsed time "t" has expired. "t" + * is in internal tick units of 1/4 seconds. + * + * This unit conforms to LAP and PAP timeout units. + * + * FastTimeout is the same as Timeout except it is designed to be + * called from called "Timeout" routines. (Basic difference, it + * doesn't update the time of day). + * + * GENERAL WARNING: YOU MUST NOT DELAY FOR A LONG TIME IN THE TIMEOUT + * ROUTINES! DOING SO MAY CAUSE PROTOCOL ERRORS + * +*/ + +export void +Timeout(fun,arg,tim) +ProcPtr fun; +caddr_t arg; +int tim; +{ + AppleTimeout(fun, arg, tim, TRUE); +} + +export void +AppleTimeout(fun, arg, tim, doupdate) +ProcPtr fun; +caddr_t arg; +int tim; +boolean doupdate; +{ + struct timeval tv; + + apptoabstime(&tv, tim, doupdate); + absTimeout(fun, arg, &tv); +} + +export void +relTimeout(fun,arg,tv,doupdate) +ProcPtr fun; +caddr_t arg; +struct timeval *tv; +boolean doupdate; +{ + struct timeval tv_abs; + + reltoabstime(tv, &tv_abs, doupdate); + absTimeout(fun, arg, &tv_abs); /* send down timeout */ +} + +export void +absTimeout(fun,arg,tv) +ProcPtr fun; +caddr_t arg; +struct timeval *tv; +{ + TimerEntry *tu,*tn,*t; + + tu = tm_alloc(fun, arg); + tu->t_time = *tv; /* copy absolute time time */ +#ifdef DEBUG + if (dbug.db_skd) + fprintf(stderr,"TIMEOUT: %x at %d/%d %d\n", + fun, tim, tu->t_time.tv_sec, tu->t_time.tv_usec); +#endif + for (t=(&TimoutQ), tn=TimoutQ.t_next; + tn != NILTIMER && (cmptime(&(tu->t_time),&(tn->t_time)) > 0); + t = tn, tn=t->t_next) + /* NULL */; + tu->t_next = t->t_next; /* make sure new entry knows where it is */ + t->t_next = tu; /* and link it in the list */ +} + +/* + * int + * remTimeout(ProcPtr fun, caddr_t arg); + * + * Given the function and function arg of a pending timeout + * remove that timeout from the q. + * + * Question: should we remove all instances? Should timeout enforce + * a single (fun, arg) pair? + * + * Resolved above by allowing multiple instances. remTimeout will + * remove the first. remTimeout is now modified to return TRUE if it + * removed something, so just call until false if you need to worry about + * it. + * +*/ +export int +remTimeout(fun,arg) +ProcPtr fun; +caddr_t arg; +{ + TimerEntry *t,*tn; + + /* t acts as prev, tn is curr */ + for (t=(&TimoutQ), tn=TimoutQ.t_next; + (tn != NILTIMER) && /* if current is valid and */ + /* either fun or arg mismatches */ + ( (tn->t_fun != fun) || (tn->t_arg != arg)); + t = tn, tn = t->t_next) + /* NULL */; + if (tn == NILTIMER) /* find anything? */ + return(FALSE); /* no, missing entry! */ + t->t_next = tn->t_next; /* unlink ourselves */ + tm_free(tn); /* and release timer block */ + return(TRUE); +} + +/* + * void + * doTimeout() + * + * doTimeout expires entries on the timeout queue. The timeout + * function is called with the function argument, the timeout + * entry is unlinked from the q and memory is returned. + * + * doTimeout can be called at any time, if the q is empty, or + * no timers have expired then no action is taken. + * + * doTimeout updates the "current" time because it is designed + * be called by external parties. doUnforcedTimeout is the guts + * and is only meant be called from within this module. + * +*/ +export int +doTimeout() +{ + gettimeofday(&tv_now, NO_TZ); /* update, 'cause could be at any point */ + return(i_doTimeout()); +} + +/* update time before calling doTimeOut */ +private int +i_doTimeout() +{ + TimerEntry *t,*tn; + int ntimeout = 0; + + t = &TimoutQ, tn = TimoutQ.t_next; + while (tn != NILTIMER && (cmptime(&tn->t_time, &tv_now) <= 0)) { + t->t_next = tn->t_next; /* unlink tn */ + if (tn->t_fun != NULL) + (*tn->t_fun)(tn->t_arg); /* call timeout function */ + if (dbug.db_skd) + fprintf(stderr, " timeout occurred "); + ntimeout++; + tm_free(tn); + tn = t->t_next; + /* t should stay constant since head of list is min time! */ + } + return(ntimeout); +} + +/* + * boolean + * minTimeout(struct timeval *mt) + * + * minTimeout returns the minimum timeout of all entries on the + * timeout q. The timer records are ordered earliest first so + * this routine only needs to check on the first one. + * +*/ +export boolean +minTimeout(mt) +struct timeval *mt; +{ + TimerEntry *tt; + + if ((tt = TimoutQ.t_next) == NILTIMER) /* anything on queue? */ + return(FALSE); /* nothing... */ + *mt = tt->t_time; /* else pass along min time */ + return(TRUE); +} + + +/* + * + * external interface to "protocol scheduler" + * + * +*/ + +/* + * abSleep(int t, boolean nowait) + * + * this is the main "protocol event scheduler loop". It waits for + * incoming packets or timeout events. + * keeps running until timeout "t" if nowait is not set, returns after + * the first timeout or incoming packet if nowait is set. + * (e.g. returns after first "protocol event scheduled" if nowait is set) + * +*/ +export int +abSleep(appt,nowait) +int appt,nowait; +{ + struct timeval sleept; + int eventhappened = 0; /* no events happened */ + + apptoabstime(&sleept,appt,TRUE); /* find absolute time for sleep */ + do { + if (abSelect(&sleept, nowait) > 0) { + eventhappened++; + if (nowait) + break; + } + /* abSelect will have updated tv_now */ + } while (cmptime(&sleept,&tv_now) > 0); /* past wait time? */ + return(eventhappened); +} + +/* + * abSelect(struct timeval *awt, nowait) + * + * Call with absolute time. Might return before that... + * Returns > 0 if event occured. + * +*/ +int +abSelect(awt,nowait) +struct timeval *awt; +int nowait; +{ + struct timeval rwt,mt; + register int rdy; + int fd; + gfd_set rdybits; +#ifdef ABRPC + register int i, temp; + extern gfd_set svc_fdset; + gfd_set svcbits; + int svcrdy; +#endif ABRPC + + int nevent; + /* + * Tappan: + * optimization: if a timeout is less than the argument sleep time + * then we don't have to return after the select() times out. If + * a timeout is not less then we don't have to scan the timeout lists. + * Note: we must return if a timeout is less than the arg. sleep time + * because this may have been an event we were waiting for!!!!! + */ + /* cck: the above is correct, except we need to know if they are */ + /* waiting to drop out after the first event. */ + int timeoutless; /* one of the following states: */ +#define TL_FALSE FALSE /* timeout is not less than sleep */ +#define TL_TRUE 1 /* timeout is less than sleep time */ +#define TL_USED 2 /* timeout was run */ + + + if (dbug.db_skd) + fprintf(stderr,"abSelect enter... "); + + nevent = 0; + do { + timeoutless = TL_FALSE; + + /* if min timeout on functions and smaller than requested */ + if (minTimeout(&mt) && cmptime(&mt,awt) < 0) { + abstoreltime(&mt,&rwt, FALSE); /* yes use it */ + timeoutless = TL_TRUE; /* and mark it */ + } else + abstoreltime(awt,&rwt, FALSE); /* use requested */ +#ifdef DEBUG + if (dbug.db_skd) + fprintf(stderr, "%d %d ",rwt.tv_sec, rwt.tv_usec); +#endif + /* rwt.tv_sec less than 0 means (a) that we are past our welcome here */ + /* or that (b) a timeout event is in our past and we should take care */ + /* of it before any read calls */ + /* in case (a) timeoutless is zero and we drop out almost immediately */ + /* in case (b) timeoutless is non-zero and we call doTimeout */ + /* to special case (a) here would need about 5 extra lines of code */ + if ((long)rwt.tv_sec < 0) { + if (dbug.db_skd) + fprintf(stderr," negative "); + rdy = 0; + } else { +#ifndef ABRPC + /* should really sleep if fdmaxdesc == -1 or else we might */ + /* go into a tight loop here! Will comprimise by getting */ + /* 1 second accuracy with sleep (shouldn't be happening */ + /* anyway. If sleep turns out not be widespread, then */ + /* .... don't know what to do */ + if (fdmaxdesc == -1) { + rdy = rwt.tv_sec + (rwt.tv_usec/(2*UNITT))/2; + sleep(rdy == 0 ? 1 : rdy); + rdy = 0; + } else { + rdybits = *fdbits; /* update before call to select */ + rdy = select(fdmaxdesc+1,&rdybits,0,0,&rwt); /* perform wait... */ + } +#else ABRPC + rdybits = *fdbits; /* update before call to select */ + for (i = 0; i < howmany(FD_SETSIZE, NFDBITS); i++) + rdybits.fds_bits[i] |= svc_fdset.fds_bits[i]; + rdy = select(FD_SETSIZE,&rdybits,0,0,&rwt); /* perform wait... */ + if (rdy > 0) { + svcrdy = 0; + for (i = 0; i < howmany(FD_SETSIZE, NFDBITS); i++) + if (temp = rdybits.fds_bits[i] & svc_fdset.fds_bits[i]) { + svcbits.fds_bits[i] = temp; + rdybits.fds_bits[i] &= ~ svc_fdset.fds_bits[i]; + while (temp) { + if (temp & 1) { + svcrdy++; + rdy--; + } + temp >>= 1; + } + } + else svcbits.fds_bits[i] = 0; + if (svcrdy > 0) + svc_getreqset(&svcbits); + } +#endif ABRPC + } + if (dbug.db_skd) + fprintf(stderr,"%d ", rdy); + if (rdy < 0) + return(rdy); + if (rdy > 0) { + /* rdy should be # of set file descriptors in the masks */ + /* since we only pass it the "read" bits, this loop */ + /* should take care of all */ + while (rdy--) { + if ((fd = FD_GETBIT(&rdybits, fdmaxdesc)) < 0) + break; /* paranoia */ + FD_CLR(fd, &rdybits); +#ifdef MULTI_BPF_PKT + /* + * BPF can return multiple packets + * + */ + { extern short lap_proto; + if (lap_proto == LAP_ETALK) { + extern int read_buf_len; + do { + fdlistenread(fd); + } while (read_buf_len > 0); + } else + fdlistenread(fd); + } +#else /* MULTI_BPF_PKT */ + fdlistenread(fd); +#endif /* MULTI_BPF_PKT */ + nevent++; + } + } else { + /* + * Tappan: + * Optimization: If a packet was waiting don't check the timeout lists - + * odds are that the timeout hasn't expired yet, and we'll catch it the + * next time through anyway + * cck: this should be okay though and things have been + * running fine with this, but there are a lot of "nowait" + * calls to abSleep... If we modify, take out the "else" on rdy>0 + * and check for: timeoutless && (rdy <= 0 || nowait) + */ + if (timeoutless) { + if (dbug.db_skd) + fprintf(stderr, "doTimeout..."); + /* An internal timeout occured before the user timeout, */ + /* and there were no packets available */ + gettimeofday(&tv_now, NO_TZ); /* do this to reduce subroutine */ + i_doTimeout(); /* call overhead */ + nevent++; + timeoutless = TL_USED; /* mark we ran the timeout */ + } + } + if (nevent && nowait) { + if (dbug.db_skd) + fprintf(stderr, "exit to abSleep\n"); + return(nevent); + } + /* cck: we don't want to update the time of day if */ + /* the minimum time out was less than the sleep time and doTimeout */ + /* was run because the time of day was updated by running doTimeout */ + /* timeout routines are supposed to be fairly quick and often update */ + /* the time of day themselves */ + if (timeoutless != TL_USED) + gettimeofday(&tv_now, NO_TZ); + } while (timeoutless); + if (dbug.db_skd) + fprintf(stderr,"exit to abSleep\n"); + return(nevent); /* return "event" count */ +} + +#ifdef NOFFS + +/* Find the First Set bit and return its number. Numbering starts */ +/* at one */ +private int +ffs(pat) +register int pat; +{ + register int j; + register int i; + + for (i = 0; i < 8*sizeof(int); i+=8) { /* each byte */ + if (pat & 0xff) { /* if byte has bits */ + /* do a linear scan */ + for (j = 0; j < 8; j++) { + if (pat & 0x1) + return(i+j+1); + pat >>= 1; + } + } + pat >>= 8; /* go to the next byte */ + } + return(0); /* none */ +} + +#endif + +/* + * Find the first active file descriptor + * + * really doesn't belong here + * +*/ +private int +FD_GETBIT(p, maxfd) +gfd_set *p; +int maxfd; +{ + register int i; + register int w; + register gfd_mask *fm; + int top; + + top = howmany(maxfd, NFDBITS); /* find length of array */ + fm = (gfd_mask *)p->fds_bits; /* point to start of array */ + for (w=0,i=0; i < top; i++,fm++) { + if ((w=ffs(*fm)) > 0) + break; + } + if (w < 1) + return(-1); + w += (i*NFDBITS)-1; /* find bit no */ + return((w > FD_SETSIZE) ? -1 : w); +} + +/* + * abstoreltime(struct timeval *t) + * + * convert absolute time in t to relative time by subtracting the + * current time as returned by gettimeofday. + * + * novalidtod means that we can't be sure the tod clock is set properly + * so update it + * +*/ +private void +abstoreltime(at,rt, novalidtod) +register struct timeval *at; +register struct timeval *rt; +boolean novalidtod; +{ + if (novalidtod) + gettimeofday(&tv_now, NO_TZ); /* update current time */ + if (rt != at) + *rt = *at; /* copy relative times */ + rt->tv_sec -= tv_now.tv_sec; /* add in user elapsed time */ + if ((rt->tv_usec -= tv_now.tv_usec) < 0) { /* both seconds and usecs */ + --rt->tv_sec; /* yes, so one less second */ + rt->tv_usec += MICRO; /* and fix up usecs */ + } +} + +/* + * reltoabstime(struct timeval *rt, *at) + * +*/ +private void +reltoabstime(rt, at, novalidtod) +register struct timeval *at; +register struct timeval *rt; +boolean novalidtod; +{ + if (novalidtod) + gettimeofday(&tv_now, NO_TZ); /* update current time */ + + if (at != rt) + *at = *rt; /* copy relative to absolute */ + at->tv_sec += tv_now.tv_sec; /* add in user elapsed time */ + /* do micro seconds */ + if ((at->tv_usec += tv_now.tv_usec) >= MICRO) { + ++at->tv_sec; /* yes, so one more second */ + at->tv_usec -= MICRO; /* and fix up usecs */ + } +} + + +/* + * void + * apptoabstime(struct timval *tv,int t) + * + * Construct actual time of timeout given unit tick 1/4 of a second. + * + * novalidtod means that we can't be sure the tod clock is set properly + * so update it +*/ +private void +apptoabstime(tv,t,novalidtod) +register struct timeval *tv; +register int t; +int novalidtod; +{ + if (novalidtod) + gettimeofday(&tv_now, NO_TZ); /* update current time */ + *tv = tv_now; + tv->tv_sec += ticktosec(t); /* seconds till timeout */ + tv->tv_usec += (t%4)*UNITT; /* micro seconds... */ + if (tv->tv_usec >= MICRO) { /* second or more? */ + tv->tv_sec++; + tv->tv_usec -= MICRO; /* adjust */ + } +} + +/* + * user access to scheduler clock + * +*/ + +getschedulerclock(tv) +struct timeval **tv; +{ + *tv = &tv_now; /* return clock */ +} + +updateschedulerclock() +{ + gettimeofday(&tv_now, NO_TZ); +} diff --git a/lib/cap/abversion.c b/lib/cap/abversion.c new file mode 100644 index 0000000..d28cb18 --- /dev/null +++ b/lib/cap/abversion.c @@ -0,0 +1,57 @@ +/* + * $Author: djh $ $Date: 1996/05/18 10:51:21 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abversion.c,v 2.98 1996/05/18 10:51:21 djh Rel djh $ + * $Revision: 2.98 $ + * + */ + +/* + * abversion.c - Return version information + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * March 1988 CCKim Created. + * + */ + +#include + +private struct cap_version myversion; + +struct cap_version * +what_cap_version() +{ + extern char Columbia_Copyright[]; + extern short lap_proto; + + myversion.cv_copyright = Columbia_Copyright; + myversion.cv_name = "CAP"; + myversion.cv_version = 6; + myversion.cv_subversion = 0; + myversion.cv_patchlevel = 198; + myversion.cv_rmonth = "June"; + myversion.cv_ryear = "1996"; + switch (lap_proto) { + case LAP_KIP: + myversion.cv_type = "UDP encapsulation"; + break; + case LAP_MKIP: + myversion.cv_type = "Modified UDP encapsulation"; + break; + case LAP_ETALK: +#ifdef PHASE2 + myversion.cv_type = "EtherTalk Phase 2 encapsulation"; +#else PHASE2 + myversion.cv_type = "EtherTalk encapsulation"; +#endif PHASE2 + break; + case LAP_KERNEL: + myversion.cv_type = "Kernel Based EtherTalk encapsulation"; + break; + } + return(&myversion); +} diff --git a/lib/cap/abzip.c b/lib/cap/abzip.c new file mode 100644 index 0000000..0370ced --- /dev/null +++ b/lib/cap/abzip.c @@ -0,0 +1,211 @@ +/* + * $Author: djh $ $Date: 1994/01/31 23:51:14 $ + * $Header: /mac/src/cap60/lib/cap/RCS/abzip.c,v 2.4 1994/01/31 23:51:14 djh Rel djh $ + * $Revision: 2.4 $ + * + */ + +/* + * abzip.c - AppleTalk Zone Information Protocol + * + * Only support for GetZoneList & GetLocalZones. GetMyZone is faked. + * + * MUST BE RUNNING KIP 1/88 or later to work properly (KIP 9/87 was the first + * revision with zones, but the code didn't return properly) + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March 1988, CCKim Create + * + */ + +#include +#include +#include /* so htons() works for non-vax */ +#include /* include appletalk definitions */ + +/* move these defines to appletalk.h */ +#ifndef zip_GetMyZone /* in case defined */ +/* define atp zip commands */ +#define zip_GetMyZone 7 /* get my zone command */ +#define zip_GetZoneList 8 /* get zone list command */ +#define zip_GetLocZones 9 /* get local zone list */ + +/* atp user bytes for zip commands */ +/* no reply struct since return is packed array of pascal strings */ + +typedef struct zipUserBytes { + byte zip_cmd; /* zip command (LastFlag on return) */ + byte zip_zero; /* always zero */ + word zip_index; /* zip index (count on return) */ +} zipUserBytes; + +#endif zip_GetMyZOne + +extern u_char this_zone[]; +extern u_char async_zone[]; + +/* + * send a get zone list command to our bridge + * arguments: + * start_index: zone start index (first is 1) + * buffer to return zone list reply in - must be atpMaxData + * zipuserbytes: on noErr return: set to user bytes returned by bridge + * function: zip_GetZoneList or zip_GetLocZones + * + */ + +OSErr +atpgetzonelist(start_index, buf, zipuserbytes, function) +int start_index; +char *buf; +zipUserBytes *zipuserbytes; +byte function; +{ + AddrBlock addr; + BDS bds[1]; /* 1 for zip */ + zipUserBytes *zub; + atpProto *ap; + ABusRecord abr; + OSErr err, GetBridgeAddress(); + +#ifndef PHASE2 + if (function == zip_GetLocZones) { + fprintf(stderr, "ZIP: local zones request undefined for Phase 1\n"); + exit(1); + } +#endif PHASE2 + + if (function != zip_GetZoneList && function != zip_GetLocZones) + return(-1); + + ap = &abr.proto.atp; + + ap->atpUserData = 0L; + zub = (zipUserBytes *)&ap->atpUserData; + zub->zip_cmd = function; + zub->zip_zero = 0; + zub->zip_index = htons(start_index); /* start at 1 */ + + GetBridgeAddress(&addr); + addr.skt = ddpZIP; + + if (addr.node == 0) + fprintf(stderr, "warning: no appletalk router found on network\n"); + + ap->atpAddress.net = addr.net; + ap->atpAddress.node = addr.node; + ap->atpAddress.skt = ddpZIP; + + ap->atpSocket = 0; + ap->atpReqCount = 0; + ap->atpDataPtr = 0; + ap->atpNumBufs = 1; + bds[0].buffPtr = buf; /* expect single reply from kip */ + bds[0].buffSize = atpMaxData; + bds[0].userData = 0L; + ap->atpRspBDSPtr = bds; + ap->fatpXO = 0; + ap->atpTimeOut = 4; + ap->atpRetries = 3; + + /* send off request */ + err = ATPSndRequest(&abr, FALSE); + + if (err == noErr) + bcopy(&bds[0].userData, zipuserbytes, sizeof(bds[0].userData)); + + return(err); +} + +/* + * Given an array of char pointers + * return: array filled with pointers to zone names (malloc'ed) + * up the the size of the array + * return: the real count in realcnt + * return: any errors + * + */ + +OSErr +GetZoneList(function, zones, numzones, realcnt) +byte function; +byte *zones[]; +int numzones; +int *realcnt; +{ + OSErr err; + byte *bp; + byte *p; + byte buf[atpMaxData+1]; + int gotcnt, zc, zi, i; + zipUserBytes zub; + + /* setup GetZoneList command */ + + zc = 0; + zi = 1; + do { + if ((err=atpgetzonelist(zi, buf, &zub, function)) != noErr) + return(err); + gotcnt = ntohs(zub.zip_index); /* get count */ + if (gotcnt && numzones) { + for (bp = buf, i = 0; i < gotcnt; i++) { + if (numzones) { + p = (byte *)malloc(*bp+1); /* room for string */ + cpyp2cstr(p, bp); /* copy out */ + zones[zc++] = p; /* mark it */ + numzones--; + } + bp += (*bp + 1); + } + } + zi += gotcnt; /* new index */ + } while (zub.zip_cmd == 0); + *realcnt = zi - 1; + + return(noErr); +} + +/* + * Don't ask bridge because it may be in a different zone + * + */ + +u_char * +GetMyZone() +{ + return((u_char *)this_zone); +} + +/* + * return zone for Async Appletalk + * + */ + +u_char * +GetAsyncZone() +{ + return((u_char *)async_zone); +} + +/* + * Get rid of memory malloc'ed by GetZoneList + * + */ + +FreeZoneList(zones, cnt) +char **zones; +int cnt; +{ + while (cnt--) { + if (zones[cnt]) + free(zones[cnt]); + zones[cnt] = NULL; + } +} diff --git a/lib/cap/atalkdbm.c b/lib/cap/atalkdbm.c new file mode 100644 index 0000000..28b71a2 --- /dev/null +++ b/lib/cap/atalkdbm.c @@ -0,0 +1,619 @@ +/* + * $Date: 1996/06/18 10:48:17 $ + * $Header: /mac/src/cap60/lib/cap/RCS/atalkdbm.c,v 2.13 1996/06/18 10:48:17 djh Rel djh $ + * $Revision: 2.13 $ + * + * mods for async appletalk support, /etc/etalk.local for EtherTalk and + * changes for quoted zone names: djh@munnari.OZ.AU, 27/11/90 + * Phase 2 support: djh@munnari.OZ.AU, 28/04/91 + * + */ + +#include +#include +#if (defined(SOLARIS) || defined(linux)) +#include +#endif SOLARIS || linux +#include +#include +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH +#include +#include "atalkdbm.h" + +#ifndef TAB +# define TAB "/etc/atalk.local" +#endif TAB +#ifndef ETAB +# define ETAB "/etc/etalk.local" +#endif ETAB +#ifndef CONFIGDIR +# define CONFIGDIR "/etc" +#endif CONFIGDIR + +#include + +static int opened = 0; +#define HAVE_ZONE -1 /* our zone was set */ +#define CONFIGURED 1 /* set when configured */ + +static int name_toipaddr(); + +char *linep; +int linecnt = 0; + +/* export these to the rest of CAP */ + +u_short this_net=0, bridge_net=0, nis_net=0, async_net=0; +u_char this_node=0, bridge_node=0, nis_node=0; +u_char this_zone[34], async_zone[34], interface[50]; + +#ifdef PHASE2 +u_short net_range_start = 0, net_range_end = 0xfffe; +#endif PHASE2 + +struct in_addr bridge_addr; + +char enet_device[50]; +char enet_zone[34]; +char netnumber[64]; +int argsread; + +/* + * GetMyAddr(AddrBlock) + * set up AddrBlock to my address. + * Use rather than directly referring to my_node and my_net. + * + */ + +GetMyAddr(addr) +AddrBlock *addr; +{ + addr->net = this_net; + addr->node = this_node; +} + +SetMyAddr(addr) +AddrBlock *addr; +{ + this_net = nis_net = addr->net; + this_node = nis_node = addr->node; +} + +/* similarly for nis */ + +GetNisAddr(addr) +AddrBlock *addr; +{ + addr->net = nis_net; + addr->node = nis_node; +} + +SetNisAddr(addr) +AddrBlock *addr; +{ + nis_net = addr->net; + nis_node = addr->node; +} + +/* + * Set zone name - sets alternate atalk configuration file: atalk. + * + * + */ + +zoneset(zonename) +u_char *zonename; +{ + strncpy((char *)this_zone, (char *)zonename, 32); + opened = HAVE_ZONE; +} + +/* + * get base configuration from our config file (default "/etc/atalk.local"). + * Use this info as a "hint" for Phase I EtherTalk networks with no router. + * Use to select "default zone" for Phase II EtherTalk (maybe eventually). + * This file need not exist for use with EtherTalk but is mandatory for KIP. + * + * Fix the format of this file as follows ... + * + * mynet mynode myzone + * bridgenet bridgenode bridgeIP + * nisnet nisnode + * asyncnet asynczone + * + * If the optional line 4 async info isn't required, line 3 is also optional + * (a default NIS line has nisnet=mynet and nisnode=mynode). Comment lines + * start with a '#', blank lines are ignored. Zone names can now be quoted + * (with " or ') to contain spaces, the '_' -> ' ' mapping garbage is gone. + * + */ + +openatalkdb(name) +char *name; +{ + FILE *fp; + int a, c; + char fn[255]; + char line[256], st[64]; + char bridge_name[64]; + char *p; + + if (opened == CONFIGURED) + return; + + if (name == NULL) { + if (opened == HAVE_ZONE) { + strcpy(fn, CONFIGDIR); + strcat(fn,"/atalk."); + a = strlen(fn); /* find where we are */ + p = fn+a; /* move to end */ + a = 0; + while ((c=(int)this_zone[a++]) != '\0') + *p++ = (isascii(c) && isalpha(c) && isupper(c)) ? tolower(c) : c; + *p = '\0'; /* tie string */ + } else + strcpy(fn, TAB); + } else + strcpy(fn, name); + + if ((fp = fopen(fn, "r")) == NULL) { + perror(fn); + exit(1); + } + + this_node = bridge_node = nis_node = 0; + this_net = bridge_net = nis_net = async_net = 0; + *this_zone = *async_zone = *interface = 0; + bzero(&bridge_addr, sizeof(struct in_addr)); + + while (fgets(line, sizeof(line), fp) != NULL) { + linecnt++; /* remember which line */ + if (line[0] == '#' || line[0] == '\n') + continue; + if ((p = (char *)index(line, '\n')) != NULL) + *p = '\0'; + linep = line; + + /* backward compatability with rutgers native ethertalk */ + if ((argsread = sscanf(line, "%s %s %s", enet_device, + enet_zone, netnumber)) >= 2 && strncmp(enet_device, "enet", 4) == 0) { + if (argsread == 3) { + this_net = htons(atnetshort(netnumber)); + if ( this_net <= 0 || this_net >= 65280 ) { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr, "openatalkdb: invalid network number: %d\n",this_net); + exit(1); + } + } + strncpy((char *)interface, enet_device,50); + strncpy((char *)this_zone, enet_zone, 34); + opened = CONFIGURED; + fclose(fp); + return; + } + + if (this_net == 0) { + getfield(st, sizeof(st), 0); + this_net = htons(atnetshort(st)); + getfield(st, sizeof(st), 0); + this_node = atoi(st) & 0xff; + getfield((char *)this_zone, sizeof(this_zone), 0); + + /* + * let node "0" stand for last byte of IP address + * Lennart Lovstrand 890430 + * + */ + if (this_node == 0) { + struct hostent *he, *gethostbyname(); + char name[100]; + + (void) gethostname(name, sizeof(name)); + if ((he = gethostbyname(name)) == NULL) { + perror(name); + exit(2); + } + /* if only one interface */ + if (he->h_addr_list[1] == NULL) + /* get the last byte for node number */ + this_node = (unsigned char) he->h_addr[3]; + } + + if (this_net == 0 || this_node == 0 || *this_zone == '\0') { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr, "openatalkdb: bad format/missing information\n"); + exit(1); + } + continue; + } + + if (bridge_net == 0) { + getfield(st, sizeof(st), 0); + bridge_net = htons(atnetshort(st)); + getfield(st, sizeof(st), 0); + bridge_node = atoi(st); + getfield(bridge_name, sizeof(bridge_name), 0); + if (bridge_net == 0 || bridge_node == 0 || *bridge_name == '\0') { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr, "openatalkdb: bad format/missing information\n"); + exit(1); + } + if (name_toipaddr(bridge_name, &bridge_addr) < 0) { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr,"openatalkdb: bridge \"%s\" is unknown: %s\n", + bridge_name,line); + exit(1); + } + continue; + } + if (nis_net == 0) { + getfield(st, sizeof(st), 0); + nis_net = htons(atnetshort(st)); + getfield(st, sizeof(st), 0); + nis_node = atoi(st); + if (nis_net == 0 || nis_node == 0) { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr, "openatalkdb: bad format/missing information\n"); + exit(1); + } + continue; + } + if (async_net == 0) { + getfield(st, sizeof(st), 0); + async_net = htons(atnetshort(st)); + getfield((char *)async_zone, sizeof(async_zone), 0); + if (async_net == 0 || *async_zone == '\0') { + fprintf(stderr, "openatalkdb: in %s, error at line %d\n",fn,linecnt); + fprintf(stderr, "openatalkdb: bad format/missing information\n"); + exit(1); + } + continue; + } + } + if (nis_net == 0) { /* usual case */ + nis_net = this_net; + nis_node = this_node; + } + opened++; + fclose(fp); +} + +/* + * Manage EtherNet network information file (default "/etc/etalk.local"). + * If the file exists, it is read and the information it contains + * is copied to the relevant variables (this_net, this_node etc.). + * If the file does not exist, it is created and "/etc/atalk.local" is + * used to set default values. Use editetalkdb() to alter values once set. + * + */ + +openetalkdb(name) +char *name; +{ + FILE *fp, *fopen(); + char line[256], st[64]; + char fn[256], bridge_name[64]; + char *p, c; + + if (name == NULL) + strncpy(fn, ETAB, sizeof(fn)); + else + strncpy(fn, name, sizeof(fn)); + + if ((fp = fopen(fn, "r")) == NULL) { /* etalk file doesn't exist */ + if (access(TAB, R_OK) == 0) { + openatalkdb(TAB); /* set some defaults */ + } else { + this_node = bridge_node = nis_node = 0; + this_net = bridge_net = nis_net = async_net = 0; + *this_zone = *async_zone = *interface = 0; + bzero(&bridge_addr, sizeof(struct in_addr)); + } + etalkdbupdate(fn); /* create it ... */ + } else { + linecnt = 0; + while (fgets(line, sizeof(line), fp) != NULL) { + linecnt++; /* remember which line */ + if (line[0] == '#' || line[0] == '\n') /* ignore comments */ + continue; + if ((p = (char *)index(line, '\n')) != NULL) + *p = '\0'; + matchvariable(line); + } + fclose(fp); + } +} + +/* + * alter the variable ... + */ + +editetalkdb(str) +char *str; +{ + matchvariable(str); +} + +/* + * write out the values that we have + * + */ + +extern short lap_proto; + +etalkdbupdate(name) +char *name; +{ + long now; + FILE *fp, *fopen(); + char fn[256], *ctime(), *inet_ntoa(), *prZone(); +#ifdef ISO_TRANSLATE + void cMac2ISO(), cISO2Mac(); +#endif ISO_TRANSLATE + + if (name == NULL) + strncpy(fn, ETAB, sizeof(fn)); + else + strncpy(fn, name, sizeof(fn)); + + if ((fp = fopen(fn, "w")) != NULL) { + time(&now); + fprintf(fp, "#\n# EtherTalk dynamic configuration data\n#\n"); +#ifdef ISO_TRANSLATE + fprintf(fp, "# Using ISO character translation\n#\n"); +#endif ISO_TRANSLATE + fprintf(fp, "# Last update:\t%s#\n", ctime(&now)); + if (lap_proto == LAP_MKIP) + fprintf(fp, "# Generated by UAB\n#\n"); + if (lap_proto == LAP_ETALK) +#ifdef PHASE2 + fprintf(fp, "# Generated by Native EtherTalk (Phase 2)\n#\n"); +#else PHASE2 + fprintf(fp, "# Generated by Native EtherTalk (Phase 1)\n#\n"); +#endif PHASE2 + if (lap_proto == LAP_KERNEL) + fprintf(fp, "# Generated by Kernel AppleTalk\n#\n"); + if (*interface != '\0') + fprintf(fp, "%-12s\t\"%s\"\n", ETH_INTERFACE, interface); +#ifdef PHASE2 + fprintf(fp, "%-12s\t%2d.%02d\n", ETH_NET_START, + ((ntohs(net_range_start) >> 8) & 0xff), (ntohs(net_range_start) & 0xff)); + fprintf(fp, "%-12s\t%2d.%02d\n", ETH_NET_END, + ((ntohs(net_range_end) >> 8) & 0xff), (ntohs(net_range_end) & 0xff)); +#endif PHASE2 + fprintf(fp, "%-12s\t%2d.%02d\n", ETH_THIS_NET, + ((ntohs(this_net) >> 8) & 0xff), (ntohs(this_net) & 0xff)); + fprintf(fp, "%-12s\t%d\n", ETH_THIS_NODE, (this_node & 0xff)); +#ifdef ISO_TRANSLATE + cMac2ISO(this_zone); + fprintf(fp, "%-12s\t\"%s\"\n", ETH_THIS_ZONE, (char *)this_zone); + cISO2Mac(this_zone); +#else ISO_TRANSLATE + fprintf(fp, "%-12s\t\"%s\"\n", ETH_THIS_ZONE, prZone((char *)this_zone)); +#endif ISO_TRANSLATE + fprintf(fp, "%-12s\t%2d.%02d\n", ETH_BRIDGE_NET, + ((ntohs(bridge_net) >> 8) & 0xff), (ntohs(bridge_net) & 0xff)); + fprintf(fp, "%-12s\t%d\n", ETH_BRIDGE_NODE, (bridge_node & 0xff)); + fprintf(fp, "%-12s\t%s\n", ETH_BRIDGE_IP, inet_ntoa(bridge_addr)); + fprintf(fp, "%-12s\t%2d.%02d\n", ETH_NIS_NET, + ((ntohs(nis_net) >> 8) & 0xff), (ntohs(nis_net) & 0xff)); + fprintf(fp, "%-12s\t%d\n", ETH_NIS_NODE, (nis_node & 0xff)); + fprintf(fp, "%-12s\t%2d.%02d\n", ETH_ASYNC_NET, + ((ntohs(async_net) >> 8) & 0xff), (ntohs(async_net) & 0xff)); + fprintf(fp, "%-12s\t\"%s\"\n", ETH_ASYNC_ZONE, prZone((char *)async_zone)); + fclose(fp); + } else { + fprintf(stderr, "openetalkdb: cannot open %s for writing\n", fn); + exit(1); + } +} + +/* + * identify and set the variable in str (identifier value) + * + */ + +matchvariable(str) +char *str; +{ + char name[64], value[64]; +#ifdef ISO_TRANSLATE + void cISO2Mac(); +#endif ISO_TRANSLATE + + linep = str; + getfield(name, sizeof(name), 0); + getfield(value, sizeof(value), 0); + + if (strcmp(name, ETH_INTERFACE) == 0) + strncpy((char *)interface, value, sizeof(interface)); +#ifdef PHASE2 + else if (strcmp(name, ETH_NET_START) == 0) + net_range_start = htons(atnetshort(value)); + else if (strcmp(name, ETH_NET_END) == 0) + net_range_end = htons(atnetshort(value)); +#endif PHASE2 + else if (strcmp(name, ETH_THIS_NET) == 0) + this_net = htons(atnetshort(value)); + else if (strcmp(name, ETH_THIS_NODE) == 0) + this_node = atoi(value) & 0xff; + else if (strcmp(name, ETH_THIS_ZONE) == 0) +#ifdef ISO_TRANSLATE + { + cISO2Mac(value); + strncpy((char *)this_zone, value, sizeof(this_zone)); + } +#else ISO_TRANSLATE + strncpy((char *)this_zone, value, sizeof(this_zone)); +#endif ISO_TRANSLATE + else if (strcmp(name, ETH_BRIDGE_NET) == 0) + bridge_net = htons(atnetshort(value)); + else if (strcmp(name, ETH_BRIDGE_NODE) == 0) + bridge_node = atoi(value) & 0xff; + else if (strcmp(name, ETH_BRIDGE_IP) == 0) + name_toipaddr(value, &bridge_addr); + else if (strcmp(name, ETH_NIS_NET) == 0) + nis_net = htons(atnetshort(value)); + else if (strcmp(name, ETH_NIS_NODE) == 0) + nis_node = atoi(value) & 0xff; + else if (strcmp(name, ETH_ASYNC_NET) == 0) + async_net = htons(atnetshort(value)); + else if (strcmp(name, ETH_ASYNC_ZONE) == 0) + strncpy((char *)async_zone, value, sizeof(async_zone)); + else { + fprintf(stderr, "bogus line in file: %s\n", str); + exit(1); + } +} + +/* + * Get a short number or address. + */ +atnetshort(st) +register char *st; +{ + register char *cp; + + if (*st == '\0') + return (0); + if ((cp = (char *)index(st, '.')) == 0) + return (atoi(st)); + *cp++ = 0; + return ((atoi(st)<<8) | atoi(cp)); +} + + +static int +name_toipaddr(name, ipaddr) +char *name; +struct in_addr *ipaddr; +{ + struct hostent *host; + + if (isdigit(name[0])) { + if ((ipaddr->s_addr = inet_addr(name)) == -1) + return(-1); + return(0); + } + if ((host = gethostbyname(name)) == 0) + return(-1); + bcopy(host->h_addr, (caddr_t)&ipaddr->s_addr, sizeof(ipaddr->s_addr)); + return(0); +} + +/* + * Get next field from 'line' buffer into 'str'. 'linep' is the + * pointer to current position. + * + * Fields are white space separated, except within quoted strings. + * If 'quote' is true the quotes of such a string are retained, otherwise + * they are stripped. Quotes are included in strings by doubling them or + * escaping with '\'. + */ + +getfield(str, len, quote) + char *str; +{ + register char *lp = linep; + register char *cp = str; + + while (*lp == ' ' || *lp == '\t') + lp++; /* skip spaces/tabs */ + if (*lp == 0 || *lp == '#') { + *cp = 0; + return; + } + len--; /* save a spot for a null */ + + if (*lp == '"' || *lp == '\'') { /* quoted string */ + register term = *lp; + + if (quote) { + *cp++ = term; + len -= 2; /* one for now, one for later */ + } + lp++; + while (*lp) { + if (*lp == term) { + if (lp[1] == term) + lp++; + else + break; + } + /* check for \', \", \\ only */ + if (*lp == '\\' + && (lp[1] == '\'' || lp[1] == '"' || lp[1] == '\\')) + lp++; + *cp++ = *lp++; + if (--len <= 0) { + fprintf(stderr, + "string truncated: %s, linenum %d", + str, linecnt); + if (quote) + *cp++ = term; + *cp = 0; + linep = lp; + return; + } + } + if (!*lp) + fprintf(stderr,"unterminated string: %s, linenum %d", + str, linecnt); + else { + lp++; /* skip the terminator */ + + if (*lp && *lp != ' ' && *lp != '\t' && *lp != '#') { + fprintf(stderr, + "garbage after string: %s, linenum %d", + str, linecnt); + while (*lp && *lp != ' ' && + *lp != '\t' && *lp != '#') + lp++; + } + } + if (quote) + *cp++ = term; + } else { + while (*lp && *lp != ' ' && *lp != '\t' && *lp != '#') { + *cp++ = *lp++; + if (--len <= 0) { + fprintf(stderr, + "string truncated: %s, linenum %d", + str, linecnt); + break; + } + } + } + *cp = 0; + linep = lp; +} + +/* + * if the zone name contains a double or single forward quote, + * escape it with '\' so that it is retained when read back in. + * + */ + +char * +prZone(zone) +char *zone; +{ + int i; + static char azone[48]; + + i = 0; + while(*zone != '\0' && i < sizeof(azone)) { + if(*zone == '"' || *zone == '\'' || *zone == '\\') + azone[i++] = '\\'; + azone[i++] = *zone++; + } + azone[i] = '\0'; + return(azone); +} diff --git a/lib/cap/atalkdbm.h b/lib/cap/atalkdbm.h new file mode 100644 index 0000000..b382984 --- /dev/null +++ b/lib/cap/atalkdbm.h @@ -0,0 +1,24 @@ +/* + * atalkdbm.h + * + * fixed strings for "free format" etalk.local file + * + * djh@munnari.OZ.AU, 27/11/90 + * + */ + +#define ETH_THIS_NET "thisNet" +#define ETH_THIS_NODE "thisNode" +#define ETH_THIS_ZONE "thisZone" +#define ETH_BRIDGE_NET "bridgeNet" +#define ETH_BRIDGE_NODE "bridgeNode" +#define ETH_BRIDGE_IP "bridgeIP" +#define ETH_NIS_NET "nisNet" +#define ETH_NIS_NODE "nisNode" +#define ETH_ASYNC_NET "asyncNet" +#define ETH_ASYNC_ZONE "asyncZone" +#define ETH_INTERFACE "interface" +#ifdef PHASE2 +#define ETH_NET_START "netRangeStart" +#define ETH_NET_END "netRangeEnd" +#endif PHASE2 diff --git a/lib/cap/authenticate.c b/lib/cap/authenticate.c new file mode 100644 index 0000000..f93d0b0 --- /dev/null +++ b/lib/cap/authenticate.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 1990, The Regents of the University of California. + * Edward Moy, Workstation Software Support Group, Workstation Support Services, + * Information Systems and Technology. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software so long as it is not sold for profit, + * provided that this notice and the original copyright notices are + * retained. The University of California makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifdef AUTHENTICATE +#include +#include +#include +#include +#include /* include appletalk definitions */ + +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#define ANYNBPNAME (1 << 1) +#define ANYPROGNAME (1 << 0) +#ifndef AUTHCONFIG +#define AUTHCONFIG "/etc/cap.auth" +#endif AUTHCONFIG +#define CLEARSYMBOL '*' +#define DOINNET (1 << 0) +#define DONETWORK (1 << 2) +#define DOOUTNET (1 << 1) +#define MAXAUTHNBP 64 +#define NNETS 16 + +#define issep(x) (isspace(x) || (x) == ',') + +typedef unsigned short Net; + +typedef struct { + Net *list; + Net *ptr; + int count; + int size; +} NetList; + +typedef struct { + char *progname; + char *nbpname; + char *type; + unsigned short flags; +} Ident; + +static void innet(); +/* static void kerberos(); */ +static void network(); +static void outnet(); + +static char anystr[] = "*"; +static char *authconfig = AUTHCONFIG; +static char authtype[32]; +static int doauthenticate = 0; +static int donetwork = FALSE; +static NetList inlist; +static Ident myident; +static unsigned short mynode; +static int ntypetab; +static NetList outlist; +static struct { + char *type; + void (*func)(); +} typetab[] = { + {"innet", innet}, + /* {"kerberos", kerberos}, */ + {"network", network}, + {"outnet", outnet}, + {0, 0}, +}; + +char *malloc(); +char *realloc(); + +static +lowercase(str) +register char *str; +{ + while(*str) { + if(isupper(*str)) + *str = tolower(*str); + str++; + } +} + +static +comparenet(n1, n2) +register Net *n1, *n2; +{ + return((*n1 > *n2) ? 1 : ((*n1 < *n2) ? -1 : 0)); +} + +static +compareident(n1, n2) +register Ident *n1, *n2; +{ + register int comp; + + if(!(n1->flags & ANYPROGNAME) && !(n1->flags & ANYPROGNAME) && + (comp = strcmp(n1->progname, n2->progname)) != 0) + return(comp); + if(!(n1->flags & ANYNBPNAME) && !(n1->flags & ANYNBPNAME) && + (comp = strcmp(n1->nbpname, n2->nbpname)) != 0) + return(comp); + return(0); +} + +static +findtype(ident) +register Ident *ident; +{ + register int i, low, high, com; + + low = 0; + high = ntypetab - 1; + while(low <= high) { + i = (low + high) / 2; + if ((com = strcmp(ident->type, typetab[i].type)) == 0) + return(i); + if(com > 0) + low = i + 1; + else + high = i - 1; + } + return(-1); +} + +static +addnet(net, list) +register Net net; +register NetList *list; +{ + register int i; + + if(list->list == NULL) { + if((list->list = (Net *)malloc(NNETS * sizeof(Net))) == NULL) { + fprintf(stderr, "%s: malloc failed\n", + myident.progname); + exit(1); + } + list->ptr = list->list; + list->count = 0; + list->size = NNETS; + } + if(list->count >= list->size) { + if((list->list = (Net *)realloc(list->list, (list->size += + NNETS) * sizeof(Net))) == NULL) { + fprintf(stderr, "%s: realloc failed\n", + myident.progname); + exit(1); + } + list->ptr = list->list + list->count; + } + *list->ptr++ = net; + list->count++; +} + +static +findnet(net, list) +register Net net; +register NetList *list; +{ + register int i, low, high; + + low = 0; + high = list->count - 1; + while(low <= high) { + i = (low + high) / 2; + if (net == list->list[i]) + return(TRUE); + if(net > list->list[i]) + low = i + 1; + else + high = i - 1; + } + return(FALSE); +} + +static char * +parse(cp, ident) +register char *cp; +register Ident *ident; +{ + register char *p, *q; + + while(isspace(*cp)) + cp++; + ident->progname = cp; + if(!(cp = index(cp, '.'))) + return(NULL); + *cp++ = 0; + ident->nbpname = cp; + if(!(cp = index(cp, '.'))) + return(NULL); + *cp++ = 0; + ident->type = cp; + while(*cp && !isspace(*cp)) + cp++; + if(*cp) + *cp++ = 0; + ident->flags = 0; + if(*ident->progname == 0 || strcmp(ident->progname, anystr) == 0) + ident->flags |= ANYPROGNAME; + else + lowercase(ident->progname); + if(*ident->nbpname == 0 || strcmp(ident->nbpname, anystr) == 0) + ident->flags |= ANYNBPNAME; + else + lowercase(ident->nbpname); + return(cp); +} + +initauthenticate(progname, nbpname) +char *progname, *nbpname; +{ + register FILE *fp; + register int i, l; + register char *cp; + char buf[BUFSIZ]; + Ident ident; + + if(cp = rindex(progname, '/')) + cp++; + else + cp = progname; + i = strlen(cp); + if((myident.progname = malloc(i + strlen(nbpname) + 2)) == NULL) { + fprintf(stderr, "%s: malloc failed\n", cp); + exit(1); + } + strcpy(myident.progname, cp); + lowercase(myident.progname); + myident.nbpname = myident.progname + i + 1; + strcpy(myident.nbpname, nbpname); + lowercase(myident.nbpname); + myident.flags = 0; + if((fp = fopen(authconfig, "r")) == NULL) + return; + ntypetab = 0; + while(typetab[ntypetab].type) + ntypetab++; + l = 0; + while(fgets(buf, BUFSIZ, fp)) { + l++; + if(*buf == '#') + continue; + if((cp = parse(buf, &ident)) == NULL) { + fprintf(stderr, "%s: %s: Syntax error, line %d\n", + myident.progname, authconfig, l); + continue; + } + if(compareident(&ident, &myident) != 0) + continue; + if((i = findtype(&ident)) < 0) { + fprintf(stderr, "%s: %s: Unknown type, line %d\n", + myident.progname, authconfig, l); + continue; + } + (*typetab[i].func)(cp, myident.progname, authconfig, l); + } + fclose(fp); + if(inlist.list) + qsort((char *)inlist.list, inlist.count, sizeof(Net), + comparenet); + if(outlist.list) + qsort((char *)outlist.list, outlist.count, sizeof(Net), + comparenet); +} + +static void +innet(cp, p, a, l) +register char *cp; +char *p, *a; +int l; +{ + register char *ep; + register int save; + unsigned n1, n2; + + while(isspace(*cp)) + cp++; + if(*cp == CLEARSYMBOL) { + cp++; + if(inlist.list) { + free(inlist.list); + inlist.list = NULL; + doauthenticate &= ~DOINNET; + } + } + while(isspace(*cp)) + cp++; + if(*cp == 0) + return; + while(*cp) { + ep = cp; + while(isdigit(*ep) || *ep == '.') + ep++; + if(*ep && !issep(*ep)) { +errinzone: + fprintf(stderr, + "%s: %s: innet: Syntax error, line %d\n", + p, a, l); + exit(1); + } + save = *ep; + *ep = 0; + if(sscanf(cp, "%u.%u", &n1, &n2) != 2 || n1 > 255 || n2 > 255) + goto errinzone; + addnet((n1 << 8) + n2, &inlist); + cp = ep; + if(save) + cp++; + while(issep(*cp)) + cp++; + } + doauthenticate |= DOINNET; +} + +static void +outnet(cp, p, a, l) +register char *cp; +char *p, *a; +int l; +{ + register char *ep; + register int save; + unsigned n1, n2; + + while(isspace(*cp)) + cp++; + if(*cp == CLEARSYMBOL) { + cp++; + if(outlist.list) { + free(outlist.list); + outlist.list = NULL; + doauthenticate &= ~DOOUTNET; + } + } + while(isspace(*cp)) + cp++; + if(*cp == 0) + return; + while(*cp) { + ep = cp; + while(isdigit(*ep) || *ep == '.') + ep++; + if(*ep && !issep(*ep)) { +erroutzone: + fprintf(stderr, + "%s: %s: outnet: Syntax error, line %d\n", + p, a, l); + exit(1); + } + save = *ep; + *ep = 0; + if(sscanf(cp, "%u.%u", &n1, &n2) != 2 || n1 > 255 || n2 > 255) + goto erroutzone; + addnet((n1 << 8) + n2, &outlist); + cp = ep; + if(save) + cp++; + while(issep(*cp)) + cp++; + } + doauthenticate |= DOOUTNET; +} + +static void +network(cp, p, a, l) +register char *cp; +char *p, *a; +int l; +{ + register char *ep; + + while(isspace(*cp)) + cp++; + ep = cp + strlen(cp); + while(--ep >= cp && isspace(*ep)) + *ep = 0; + if(ep < cp) { + fprintf(stderr, + "%s: %s: network: Not type specified, line %d\n", p, a, l); + exit(1); + } + strcpy(authtype, cp); + donetwork = TRUE; + doauthenticate |= DONETWORK; +} + +static +netauth(net, node, authtype) +unsigned net, node; +char *authtype; +{ + register int i, nbpactive; + register OSErr err; + nbpProto abr; + EntityName ent; + AddrBlock addr; + NBPTEntry buf; + + if(!(nbpactive = isNBPInited())) + nbpInit(); + bzero((char *)&ent, sizeof(ent)); + sprintf((char *)ent.objStr.s, "%u.%u.%u", net >> 8, net & 0xff, node); + strcpy((char *)ent.typeStr.s, authtype); + strcpy((char *)ent.zoneStr.s, "*"); + abr.nbpEntityPtr = &ent; + abr.nbpAddress.net = htons(net); + abr.nbpAddress.node = node; + abr.nbpAddress.skt = 0; + abr.nbpRetransmitInfo.retransInterval = sectotick(1); + abr.nbpRetransmitInfo.retransCount = 2; + err = NBPConfirm(&abr, FALSE); + if(!nbpactive) + nbpShutdown(); + return(err == noErr || err == nbpConfDiff); +} + +authenticate(net, node) +unsigned net, node; +{ + if(!doauthenticate) + return(TRUE); + if(inlist.list && !findnet(net, &inlist)) + return(FALSE); + if(outlist.list && findnet(net, &outlist)) + return(FALSE); + if(donetwork) + return(netauth(net, node, authtype)); + return(TRUE); +} +#else AUTHENTICATE +int auth_dummy_for_ld; /* keep the loader and ranlib happy */ +#endif AUTHENTICATE diff --git a/lib/cap/cap_conf.h b/lib/cap/cap_conf.h new file mode 100644 index 0000000..2ae4e16 --- /dev/null +++ b/lib/cap/cap_conf.h @@ -0,0 +1,44 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:49:45 $ + * $Header: cap_conf.h,v 2.1 91/02/15 22:49:45 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * cap_conf.h - site configuration file + * + * This file is to contain parameters individual sites might like to + * change + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986 by The Trustees of Columbia University in the + * City of New York. + * + * + * Edit History: + * + * July 6, 1986 CCKim Created. + * July 9, 1986 CCKim Convert to "bridge" philosophy + * Add some params + * + */ + +/* timeout parameters for PAP */ +#define PAPOPENTIMEOUT sectotick(2) /* spec says 2 seconds, use */ + /* 3 seconds- o.w. see */ + /* anomalous behavior */ +#define PAPOPENRETRY 5 /* as spec'ed */ + +#define PAPCLOSETIMEOUT sectotick(5) +#define PAPCLOSERETRY 3 + +#define PAPCONNECTIONTIMEOUT sectotick(120) /* two minutes */ +#define PAPTICKLETIMEOUT sectotick(60) /* one minute - half connection */ + /* timeout */ + +#define PAPREADTIMEOUT sectotick(15) /* 15 second retry (a little long */ + /* by my standards) */ +#define PAPREADRETRIES 255 /* retry count of infinity */ + +#define PAPARBITRATIONTIME sectotick(2) /* two seconds */ diff --git a/lib/cap/makefile b/lib/cap/makefile new file mode 100644 index 0000000..1490f8a --- /dev/null +++ b/lib/cap/makefile @@ -0,0 +1,94 @@ +# Makefile autoconfigured for ... +# SunOS system on Mon Mar 4 19:08:43 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O +NBPFLAGS= +I=/usr/include +LIBCAP=libcap.a +DESTDIR=/usr/local/lib +AUTHCONFIG=\"/etc/cap.auth\" + +LIBABSRCS=abatp.c abddp.c abmisc.c abnbp.c abauxddp.c abauxnbp.c \ + abpap.c abpapc.c abpaps.c abpp.c abqueue.c abasp.c \ + abzip.c abversion.c atalkdbm.c absched.c abkip.c \ + authenticate.c ablog.c +LIBABOBJS=abatp.o abmisc.o abzip.o abversion.o absched.o \ + abpap.o abpapc.o abpaps.o abpp.o abqueue.o abasp.o \ + authenticate.o ablog.o + +# LABOBJ defines the various low level delivery mechanisms +# default: abkip.o abddp.o abnbp.o atalkdbm.o +# with UAB: abmkip.o abddp.o abnbp.o atalkdbm.o +# for A/UX: abauxddp.o abauxnbp.o +# for EtherTalk: abetalk.o abddp.o abnbp.o atalkdbm.o +LAPOBJ=abkip.o abddp.o abnbp.o atalkdbm.o + +# USEVPRINTF - use vprintf in logging +LOGDEFS=-DUSEVPRINTF + +DEPENDS=$I/netat/appletalk.h $I/netat/aberrors.h $I/netat/abqueue.h + +all: $(LIBCAP) + +$(LIBCAP): $(LIBABOBJS) $(LAPOBJ) + ar rv $(LIBCAP) $(LIBABOBJS) $(LAPOBJ) + +clean: + -rm -f *.o *.a core + +install: $(LIBCAP) + ${INSTALLER} $(LIBCAP) $(DESTDIR) + (cd $(DESTDIR);ranlib $(LIBCAP)) + +dist: + @cat todist + +lint: $(LIBABSRCS) + lint $(LIBABSRCS) + +abetalk.o: + (cd ../../support/ethertalk; make abetalk.o) + cp ../../support/ethertalk/abetalk.o abetalk.o + +abmkip.o: abkip.c ${DEPENDS} $I/netat/abnbp.h $I/netat/compat.h + cp abkip.c abmkip.c + ${CC} ${CFLAGS} -DUAB_MKIP -c abmkip.c + /bin/rm abmkip.c + +atalkdbm.o: atalkdbm.c ${DEPENDS} + ${CC} ${CFLAGS} -DTAB=\"/etc/atalk.local\" -DCONFIGDIR=\"/etc\" -c atalkdbm.c + +authenticate.o: authenticate.c ${DEPENDS} + ${CC} ${CFLAGS} -DAUTHCONFIG=${AUTHCONFIG} -c authenticate.c + +ablog.o: ablog.c ${DEPENDS} + ${CC} ${CFLAGS} ${LOGDEFS} -c ablog.c + +abnbp.o: abnbp.c ${DEPENDS} $I/netat/abnbp.h + ${CC} ${CFLAGS} ${NBPFLAGS} -c abnbp.c + +abkip.o: abkip.c ${DEPENDS} $I/netat/abnbp.h $I/netat/compat.h +abddp.o: abddp.c ${DEPENDS} cap_conf.h +abatp.o: abatp.c ${DEPENDS} abatp.h +abatpaux.o: abatpaux.c ${DEPENDS} abatp.h +abasp.o: abasp.c ${DEPENDS} abasp.h +abpap.o: abpap.c ${DEPENDS} abpap.h cap_conf.h +abpapc.o: abpapc.c ${DEPENDS} abpap.h cap_conf.h +abpaps.o: abpaps.c ${DEPENDS} abpap.h cap_conf.h +abzip.o: abzip.c ${DEPENDS} +abmisc.o: abmisc.c ${DEPENDS} +abpp.o: abpp.c ${DEPENDS} +abversion.o: abversion.c ${DEPENDS} +abauxddp.o: abauxddp.c ${DEPENDS} cap_conf.h +abauxnbp.o: abauxnbp.c ${DEPENDS} $I/netat/abnbp.h +absched.o: absched.c ${DEPENDS} $I/netat/compat.h +atalkdbm.o: atalkdbm.c ${DEPENDS} $I/netat/compat.h atalkdbm.h +abqueue.o: abqueue.c $I/netat/abqueue.h diff --git a/lib/cap/scandir.c b/lib/cap/scandir.c new file mode 100644 index 0000000..a4750ee --- /dev/null +++ b/lib/cap/scandir.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1983 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. + */ + +#ifdef SOLARIS +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)scandir.c 5.3 (Berkeley) 6/18/88"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Scan the directory dirname calling select to make a list of selected + * directory entries then sort using qsort and compare routine dcomp. + * Returns the number of entries and a pointer to a list of pointers to + * struct direct (through namelist). Returns -1 if there were any errors. + */ +#include +#include +#include +#include + +struct dirent *GetNextDir(buf,nbytes) +char buf[]; +int *nbytes; +{ + struct dirent *p,*d; + + p = (struct dirent *) buf; + + *nbytes = *nbytes - p->d_reclen; + if(*nbytes == 0) + return(NULL); + + d = (struct dirent *) &buf[p->d_reclen]; + return(d); +} + +scandir(dirname, namelist, select, dcomp) + char *dirname; + struct dirent *(*namelist[]); + int (*select)(), (*dcomp)(); +{ + int dir_fd; + int nbytes; + int nitems,done; + char buf[8192]; + char *cp1, *cp2; + struct stat stb; + struct dirent *d, *p, **names,*bp; + long arraysz; + + if ((dir_fd = open(dirname,O_RDONLY))== -1) + return(-1); + if (fstat(dir_fd, &stb) < 0) + return(-1); + + /* + * estimate the array size by taking the size of the directory file + * and dividing it by a multiple of the minimum size entry. + */ + arraysz = (stb.st_size / 24); + names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *)); + if (names == NULL) + return(-1); + + nitems = 0; + d = (struct dirent *) buf; + while ((nbytes = getdents(dir_fd, d, 8192)) > 0) + { + + while(d != NULL) + { + if (select != NULL && !(*select)(d)) + { + d = GetNextDir(d,&nbytes); + continue; /* just selected names */ + } + /* + * Make a minimum size copy of the data + */ + p = (struct dirent *)malloc(d->d_reclen + 1); + if (p == NULL) + return(-1); + p->d_ino = d->d_ino; + p->d_off = d->d_off; + p->d_reclen = d->d_reclen; + for (cp1 = p->d_name, cp2 = d->d_name; + *cp1++ = *cp2++; ); + /* + * Check to make sure the array has space left and + * realloc the maximum size. + */ + if (++nitems >= arraysz) + { + + /* just might have grown */ + if (fstat(dir_fd, &stb) < 0) + return(-1); + arraysz = stb.st_size / 12; + names = (struct dirent **)realloc((char *)names, + arraysz * sizeof(struct dirent *)); + if (names == NULL) + return(-1); + } + names[nitems-1] = p; + d = GetNextDir(d,&nbytes); + } + d = (struct dirent *) buf; + } + close(dir_fd); + if (nitems && dcomp != NULL) + qsort(names, nitems, sizeof(struct dirent *), dcomp); + *namelist = names; + return(nitems); +} + +/* + * Alphabetic order comparison routine for those who want it. + */ +alphasort(d1, d2) + struct dirent **d1, **d2; +{ + return(strcmp((*d1)->d_name, (*d2)->d_name)); +} +#else SOLARIS +int scan_dummy_for_ld; /* keep the loader and ranlib happy */ +#endif SOLARIS diff --git a/lib/cap/todist b/lib/cap/todist new file mode 100644 index 0000000..34769f9 --- /dev/null +++ b/lib/cap/todist @@ -0,0 +1,23 @@ +lib/cap/Makefile +lib/cap/Makefile.m4 +lib/cap/abasp.c +lib/cap/abasp.h +lib/cap/abatp.c +lib/cap/abatp.h +lib/cap/abddp.c +lib/cap/ablap.c +lib/cap/abmisc.c +lib/cap/absched.c +lib/cap/abnbp.c +lib/cap/abkip.c +lib/cap/abpap.c +lib/cap/abpap.h +lib/cap/abpapc.c +lib/cap/abpaps.c +lib/cap/abpp.c +lib/cap/abqueue.c +lib/cap/abversion.c +lib/cap/abzip.c +lib/cap/atalkdbm.c +lib/cap/cap_conf.h +lib/cap/whatiswhat diff --git a/lib/cap/whatiswhat b/lib/cap/whatiswhat new file mode 100644 index 0000000..9a38cdf --- /dev/null +++ b/lib/cap/whatiswhat @@ -0,0 +1,18 @@ + absched.c - Protocol Scheduling (see doc/sched.notes) + abmisc.c - Various routines + abqueue.c - General queue handling + ablap.c - Link Access Protocol (LAP) routines (NOT USED) + abddp.c - Datagram Delivery Protocol (DDP) routines + abatp.c - AppleTalk Transaction Protocol (ATP) routines + abnbp.c - Name Binding Protocol (NBP) routines. See + doc/cap.notes for more information. + abpap.c - Printer Access Protocol (PAP) routines + abpapc.c - PAP client only routines (PAPOpen, PAPStatus) + abpaps.c - PAP server only routines (SLInit, GetNextJob, etc.) + abpp.c - Some unix style read and write routines for pap + abasp.c - appletalk session protocol + abversion.c - returns version information about CAP + abzip.c - Zone Information Protocol routines (ATP ONLY) + atalkdbm.c - Appletalk routing table handler (SUMEX) + [SHOULD BE SPECIFIC TO KIP(UDP) ENCAPSULATION] + abkip.c - Glue to KIP (UDP) encapsulation diff --git a/lib/makefile b/lib/makefile new file mode 100644 index 0000000..9195d1e --- /dev/null +++ b/lib/makefile @@ -0,0 +1,41 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 14:00:01 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp + + +all: + (cd cap; make) + (cd afp; make) + (cd afpc; make) + + +install: normal + +normal: + (cd cap; make install) + (cd afp; make install) + (cd afpc; make install) + +xenix5: + (cd xenix; make install) + +clean: + -(cd cap; make clean) + -(cd afp; make clean) + -(cd afpc; make clean) + + +dist: + @cat todist + @(cd cap; make dist) + @(cd afp; make dist) + @(cd afpc; make dist) + diff --git a/lib/xenix/Makefile b/lib/xenix/Makefile new file mode 100644 index 0000000..5e64693 --- /dev/null +++ b/lib/xenix/Makefile @@ -0,0 +1,41 @@ +# +# $Author: djh $ $Date: 91/02/15 22:50:33 $ +# $Header: Makefile,v 2.1 91/02/15 22:50:33 djh Rel $ +# $Revision: 2.1 $ +# + +# +# Makefile for BSD library for xenix. +# + +SHELL = /bin/sh +CC = cc +CFLAGS = -O +OBJS = fsync.o ftruncate.o groups.o rename.o scandir.o +SRCS = fsync.c ftruncate.c groups.c rename.c scandir.c +OTHERS = Makefile +SHAR = libbsd.sh +LIB = Slibbsd.a + +all: $(LIB) + +install: all + copy -m $(LIB) /usr/lib/386/$(LIB) + copy -m ../afp/libafp.a /usr/lib/386/Slibafp.a + copy -m ../afpc/libafpc.a /usr/lib/386/Slibafpc.a + copy -m ../cap/libcap.a /usr/lib/386/Slibcap.a + +clean: + rm -f *.o + +clobber: clean + rm -f $(LIB) $(SHAR) + +shar: $(SHAR) + +$(SHAR): $(OTHERS) $(SRCS) + shar $(OTHERS) $(SRCS) >$@ + +$(LIB): $(OBJS) + ar rc $@ $(OBJS) + ranlib $@ diff --git a/lib/xenix/fsync.c b/lib/xenix/fsync.c new file mode 100644 index 0000000..1e2aff6 --- /dev/null +++ b/lib/xenix/fsync.c @@ -0,0 +1,19 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:50:34 $ + * $Header: fsync.c,v 2.1 91/02/15 22:50:34 djh Rel $ + * $Revision: 2.1 $ + */ + +/* + * Fsync() for non-BSD systems. + * + * For now, this is a no-op, since we can't force writes without + * three system calls. + */ + +int +fsync(fd) + int fd; +{ + return 0; +} diff --git a/lib/xenix/ftruncate.c b/lib/xenix/ftruncate.c new file mode 100644 index 0000000..9dba28d --- /dev/null +++ b/lib/xenix/ftruncate.c @@ -0,0 +1,50 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:50:35 $ + * $Header: ftruncate.c,v 2.1 91/02/15 22:50:35 djh Rel $ + * $Revision: 2.1 $ + */ + +/* + * Ftruncate() for non-BSD systems. + * + * This module gives the basic functionality for ftruncate() which + * truncates the given file descriptor to the given length. + * ftruncate() is a Berkeley system call, but does not exist in any + * form on many other versions of UNIX such as SysV. Note that Xenix + * has chsize() which changes the size of a given file descriptor, + * so that is used if M_XENIX is defined. + * + * Since there is not a known way to support this under generic SysV, + * there is no code generated for those systems. + * + * SPECIAL NOTE: On Xenix, using this call in the BSD library + * will REQUIRE the use of -lx for the extended library since chsize() + * is not in the standard C library. + * + * By Marc Frajola, 3/27/87 + */ + +#ifdef M_XENIX + +#include +#include +#include + +ftruncate(fd,length) + int fd; /* File descriptor to truncate */ + off_t length; /* Length to truncate file to */ +{ + int status; /* Status returned from truncate proc */ + +#if defined(M_XENIX) + status = chsize(fd,length); +#else + /* Stuff for Microport here */ + status = -1; + NON-XENIX SYSTEMS CURRENTLY NOT SUPPORTED +#endif + + return(status); +} + +#endif /* M_XENIX */ diff --git a/lib/xenix/groups.c b/lib/xenix/groups.c new file mode 100644 index 0000000..b74af42 --- /dev/null +++ b/lib/xenix/groups.c @@ -0,0 +1,36 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:50:36 $ + * $Header: groups.c,v 2.1 91/02/15 22:50:36 djh Rel $ + * $Revision: 2.1 $ + */ + +/* + * BSD groups functions for non-BSD systems. + * + * For now, we're always part of one group, our egid. + */ + +int +initgroups(username, usergid) + char *username; + int usergid; +{ + /* + * When this function is called, setgid() has already been called. + * Therefore, we don't have to do anything. + */ + + return 0; +} + +int +getgroups(gv, gmax) + int *gv; + int gmax; +{ + if (gmax < 1) + return 0; + + gv[0] = getegid(); + return 1; +} diff --git a/lib/xenix/rename.c b/lib/xenix/rename.c new file mode 100644 index 0000000..5bb70e3 --- /dev/null +++ b/lib/xenix/rename.c @@ -0,0 +1,82 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:50:37 $ + * $Header: rename.c,v 2.1 91/02/15 22:50:37 djh Rel $ + * $Revision: 2.1 $ + */ + +/* + * Rename system call -- Replacement for Berzerkeley 4.2 rename system + * call that is missing in Xenix. + * + * By Marc Frajola and Chris Paris. + * Directory hack by Chip Salzenberg. + */ + +#include +#include +#include +#include +#include + +rename(src,dest) + char *src; /* Source file to rename */ + char *dest; /* Name for renamed file */ +{ + int status; /* Status returned from link system call */ + struct stat stbuf; /* Buffer for statting destination file */ + + /* Find out what the destination is: */ + status = stat(dest,&stbuf); + if (status >= 0) { + /* See if the file is a regular file; if not, return error: */ + if ((stbuf.st_mode & S_IFMT) != S_IFREG) { + return(-1); + } + } + + /* Unlink destination since it is a file: */ + unlink(dest); + + /* Find out what the source is: */ + status = stat(src,&stbuf); + if (status < 0) + return -1; + if ((stbuf.st_mode & S_IFMT) == S_IFDIR) + { + /* Directory hack for SCO Xenix */ + + static char mvdir[] = "/usr/lib/mv_dir"; + void (*oldsigcld)(); + int pid; + + oldsigcld = signal(SIGCLD, SIG_DFL); + while ((pid = fork()) == -1) + { + if (errno != EAGAIN) + return -1; + sleep(5); + } + if (pid == 0) + { + execl(mvdir, mvdir, src, dest, (char *) 0); + perror(mvdir); + exit(1); + } + if (wait(&status) != pid) + { + fprintf(stderr, "rename: wait failure\n"); + status = -1; + } + (void) signal(SIGCLD, oldsigcld); + } + else + { + /* Link source to destination file: */ + status = link(src,dest); + if (status != 0) { + return(-1); + } + status = unlink(src); + } + return((status == 0) ? 0 : (-1)); +} diff --git a/lib/xenix/scandir.c b/lib/xenix/scandir.c new file mode 100644 index 0000000..e682740 --- /dev/null +++ b/lib/xenix/scandir.c @@ -0,0 +1,108 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:50:38 $ + * $Header: scandir.c,v 2.1 91/02/15 22:50:38 djh Rel $ + * $Revision: 2.1 $ + */ + +/* + * Copyright (c) 1983 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)scandir.c 5.3 (Berkeley) 6/18/88"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Scan the directory dirname calling select to make a list of selected + * directory entries then sort using qsort and compare routine dcomp. + * Returns the number of entries and a pointer to a list of pointers to + * struct direct (through namelist). Returns -1 if there were any errors. + */ + +#include +#include +#include + +scandir(dirname, namelist, select, dcomp) + char *dirname; + struct direct *(*namelist[]); + int (*select)(), (*dcomp)(); +{ + register struct direct *d, *p, **names; + register int nitems; + register char *cp1, *cp2; + struct stat stb; + long arraysz; + DIR *dirp; + + if ((dirp = opendir(dirname)) == NULL) + return(-1); + if (fstat(dirp->dd_fd, &stb) < 0) + return(-1); + + /* + * estimate the array size by taking the size of the directory file + * and dividing it by a multiple of the minimum size entry. + */ + arraysz = (stb.st_size / 24); + names = (struct direct **)malloc(arraysz * sizeof(struct direct *)); + if (names == NULL) + return(-1); + + nitems = 0; + while ((d = readdir(dirp)) != NULL) { + if (select != NULL && !(*select)(d)) + continue; /* just selected names */ + /* + * Make a minimum size copy of the data + */ + p = (struct direct *)malloc(DIRSIZ(d)); + if (p == NULL) + return(-1); + p->d_ino = d->d_ino; + p->d_reclen = d->d_reclen; + p->d_namlen = d->d_namlen; + for (cp1 = p->d_name, cp2 = d->d_name; *cp1++ = *cp2++; ); + /* + * Check to make sure the array has space left and + * realloc the maximum size. + */ + if (++nitems >= arraysz) { + if (fstat(dirp->dd_fd, &stb) < 0) + return(-1); /* just might have grown */ + arraysz = stb.st_size / 12; + names = (struct direct **)realloc((char *)names, + arraysz * sizeof(struct direct *)); + if (names == NULL) + return(-1); + } + names[nitems-1] = p; + } + closedir(dirp); + if (nitems && dcomp != NULL) + qsort(names, nitems, sizeof(struct direct *), dcomp); + *namelist = names; + return(nitems); +} + +/* + * Alphabetic order comparison routine for those who want it. + */ +alphasort(d1, d2) + struct direct **d1, **d2; +{ + return(strcmp((*d1)->d_name, (*d2)->d_name)); +} diff --git a/m4.setup b/m4.setup new file mode 100644 index 0000000..e67633e --- /dev/null +++ b/m4.setup @@ -0,0 +1,383 @@ +define(`concat',$1$2$3$4$5$6$7$8$9) +changequote([,]) + +# os - one of: +# "bsd" - bsd 4.2, bsd 4.3, ultrix 1.1, acis 4.2,4.3 other +# "standard" bsd systems without nfs +# "ultrix12" - Ultrix 1.2 +# "ultrix20" - Ultrix 2.0 or greater +# "ultrix40" - Ultrix 4.0 or greater +# "hpux" - HP/UX +# "aux" - A/UX +# "xenix5" - SCO Xenix System V +# "aix" - IBM AIX System V +# "pyr" - pyramid (in BSD universe) +# "sunos" - SunOS +# "encore" - Encore MultiMax +# "next" - NeXT/MACH +# "dynix" - Sequent Balance +# "irix" - Silicon Graphics IRIS-4D/IRIX +# Warning: hpux, pyr are hardcoded in some of the makefiles (sorry) + +# MAJOR CONFIGURATION +# set to one of the above (or configure your own below) +define([os],[sunos]) + +# System call configuration (not for system v compatibilty) +# known: X_GETOPT, X_VPRINTF, X_GETMNT, X_STATFS, X_QUOTA, +# X_SUNQUOTA, X_FLOCK, X_LOCKF, X_FCNTLLOCKF +# getopt - "cap: argument processing" +define([X_GETOPT],1) +# getmnt - "aufs: info on file systems (dec)" +# define([X_GETMNT],1) +# statfs - "aufs: info on file systems (sun nfs)" +define([X_STATFS],1) +# quota - "aufs: info on user quota" +# define([X_QUOTA],1) +# getmntent - "aufs: used by sunquota" +define([X_SUNQUOTA],1) +# flock - "afp: file locking" +define([X_FLOCK],1) +# lockf - "afp: byte range locking using unistd.h" +define([X_LOCKF],1) +# lockf - "afp: byte range locking using fcntl.h" +# define([X_FCNTLLOCKF],1) +# vfprintf - "cap: variable arg fprintf" +define([X_VPRINTF],1) +# recvmsg - "cap: lib: scatter gather recv" +# define([X_NORECVMSG],1) +# sendmsg - "cap: lib: scatter gather send" +# define([X_NOSENDMSG],1) +# ffs - "cap: lib: ffs - find first set bit" +# define([X_NOFFS],1) + +# GETOPT support +ifdef([X_GETOPT],[],[define([needgetopt],[att_getopt.o])]) +# VPRINTF support +ifdef([X_VPRINTF],[define([usevprintf], 1)],[]) + +# Path to cap sources: useful for testing +define([cwd],[/home/munnari/staff/djh/src/cap60.beta]) +# turn on if your system sends packets very quickly +# (see applications/aufs/INSTALLATION) +# define([fastether],1) # For papif and samples + +# The following selects the correct lap delivery objects +define([lapobj],[abkip.o abddp.o abnbp.o atalkdbm.o]) + +# This determines what happens to UAB +define([uabprogs],[]) +define([uabplibs],[]) +define([uabpobjs],[]) + +# This sets up Native EtherTalk support +define([etherprogs],[]) +define([etherpobjs],[]) + +# The following selects byteswapping or otherwise +# define([usebyteswap],1) + +# uncomment if your param.h includes types.h and types.h doesn't +# prevent itself from being included twice +# define _TYPES +# define([selfdefinetypes],1) +# MINOR CONFIGURATION: configure various programs + +#define([columbia],1) # so columbia can do things quickly +#define([debug],1) # testing things (without disrupting) + +# location of include files +define([includedir],[[/usr/include]]) +# location of des subroutine source (see lib/afp/README) +define([desloc],[[../../extras]]) +# location of atalk.local, etc. +define([etcdest],[[/etc]]) +ifdef([columbia],[define([etcdest],[[/usr/local/lib/cap]])]) +# location of user cap programs +define([capdestdir],[[/usr/local/cap]]) +# location of cap "server" programs (aufs, lwsrv, papif, uab etc.) +define([capsrvrdestdir],[[/usr/local/cap]]) +# location of some cap data files +define([caplibdestdir],[[/usr/local/lib/cap]]) +# location of cap libraries +define([libdestdir],[[/usr/local/lib]]) +# override for aux (doesn't search /usr/local/lib) +ifelse(os,[aux],[define([libdestdir],[[/usr/lib]])]) +# cap library names +define([caplib],[[libcap.a]]) +define([afplib],[[libafp.a]]) +define([afpclib],[[libafpc.a]]) +# names to load cap libraries with +define([libcap],[[-lcap]]) +define([libafp],[[-lafp]]) +define([libafpc],[[-lafpc]]) +ifelse(os,[encore], + [define([libcap],concat([-L],libdestdir,[ ],libcap))]) +ifelse(os,[encore], + [define([libafp],concat([-L],libdestdir,[ ],libafp))]) +ifelse(os,[encore], + [define([libafpc],concat([-L],libdestdir,[ ],libafpc))]) +ifelse(os,[aix], + [define([libcap],concat([-L],libdestdir,[ ],libcap))]) +ifelse(os,[aix], + [define([libafp],concat([-L],libdestdir,[ ],libafp))]) +ifelse(os,[aix], + [define([libafpc],concat([-L],libdestdir,[ ],libafpc))]) +# any special libraries +define([libspecial],[]) +ifelse(os,[xenix5],[ + define([libspecial],concat(libspecial,[ -lsocket]))]) +ifelse(os,[dynix],[ + define([libspecial],concat(libspecial,[ -lseq]))]) +ifelse(os,[irix],[ + define([libspecial],concat(libspecial,[ -lbsd]))]) +ifelse(os,[aix],[ + define([libspecial],concat(libspecial,[ -lbsd]))]) +ifelse(os,[aux],[ + define([libspecial],concat(libspecial,[ -lat]))]) + +# +# special configurations for individual source files +# + +# +# Aufs: see applications/aufs/INSTALLATION +# +# WARNING: OS DEPENDENT +# define([smartunixfinderinfo],1) +# +# Valid here are -DNONXLATE, -DFULL_NCS_SUPPORT +# or GGTYPE="gid_t" +# Others: USESTATFS, USEGETMNT, USECHOWN, USEQUOTA, USESUNQUOTA +# are autoconfigured +define([aufsosdefs],[]) + +# +# lib/cap/authenticate.c: configuration file +# +define([authconfig],[[\"/etc/cap.auth\"]]) + +# +# lwsrv: see applications/lwsrv/README +# +# Valid is: -DADOBE_DSC2_CONFORMANT +define([simpleflags],[]) +# +# Valid are: -DDOCNAME, -DPAGECOUNT=buffer_size +define([lwflags],[-DDOCNAME -DPAGECOUNT=512]) +# and more later ... + +# +# lwrename: set name of file containing list of printers to be renamed +# +define([lwrenamefile],[[\"/usr/local/lib/cap/lwrename.list\"]]) + +# +# papif: see applications/papif/README +# +# uncomment and set to right location to turn on printing "plain text files" +# define([pstextloc],[[\"/usr/local/lib/ps/pstext\"]]) +# +# uncomment and set to right location to turn on page reversal +# define([psrevloc],[[\"/usr/local/lib/ps/psrev\"]]) +# +# Valid are: +# -DVERBOSELOG - default (set =0 to turn off) +# -DNO_STRUCT - default is on (structured) +# -DNOACCT - default is on (accounting) +# -DIDLESTUFF - default is off +# -DSFLOWQ - default is 8 (min 1, max 8) +# -DRFLOWQ - default is 8 (min 1, max 8) +# -DATPRESPONSETIMEOUT - default is 120 (2 minutes) - in seconds +# -DWATCHTIME - default is 10 seconds (in seconds) +# -DDEBUG - default is off +# -DSTRIPCONTROLD - default is off +# -DMAPCRTOLF - default is off +# -DMACUSER - default is off (need LPD_JOB environment variable in lpd) +# see papif README file for explanations of the above +# The following defines are recommended for System V lp printers (vs. bsd lpd) +# -DWATCHTIME=0 (no status) -DNOACCT +define([papflags],[-DMACUSER]) +define([papflags],concat(papflags,[ -DIDLESTUFF])) + +# Set -DBANNERFIRST if you want the banner page to come out as the +# first page instead of the last page +# Set -DBANNERLAST if want it last +# Set -DPSBANNER if you want a custom PostScipt Banner (must specify +# short banners in printcap). This still defaults to regular banners +# Add -DCHARGEBANNER if you want to charge the banner page to the user +# on system V - there is no accounting, so leave blank +define([papbanner],[-DCHARGEBANNER]) + +# for cap.printers - uncomment and change the following line to point +# papif, et. al. to a location other than /etc/cap.printers. (Note: +# line below would set it to $etcdir/cap.printers) +# define([capprinters],concat([\"],etcdest,[/],[cap.printers],[\"])) +ifdef([columbia], + [define([capprinters],concat([\"],etcdest,[/],[cap.printers],[\"]))]) +# for atalkdbm - allows change following line(s) to modify atalk.local +# (probably shouldn't). Remember that atalk.local is expected to +# be in etcdest +define([atalklocal],concat([\"],etcdest,[/],[atalk.local],[\"])) +define([configdir],concat([\"],etcdest,[\"])) + +# in case you want to globally change the c compiler +define([thecompiler],[cc]) +define([theloader],[ld]) +define([theinstaller],[cp]) + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# END OF CONFIGABLE OPTIONS # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +# You should only edit past here if you are "porting" +# Automatics +define([osname], + ifelse( os,[ultrix12],[Ultrix 1.2], + os,[ultrix20],[Ultrix 2.0], + os,[ultrix40],[Ultrix 4.0], + os,[aux],[A/UX], + os,[sunos],[SunOS], + os,[encore],[Encore Umax], + os,[hpux],[HP-UX (for 9000 series)], + os,[bsd],[Standard BSD], + os,[pyr],[Pyramid in BSD universe], + os,[xenix5],[SCO Xenix System V], + os,[aix],[IBM AIX System V], + os,[next],[NeXT/MACH], + os,[dynix],[Sequent Balance], + os,[irix],[Silicon Graphics IRIS/IRIX], + [Unknown])) +define([cflags],ifdef([selfdefinetypes],[-D_TYPES],[])) +define([cflags],concat(cflags,ifdef([usebyteswap],[ -DBYTESWAPPED],[]))) +define([bigcflags],ifelse(os,[hpux],[+Nd2000 +Ns2000])) +# The encore optimiser is slightly over zealous +ifelse(os,[encore],[],[ + define([cflags],concat(cflags,[ -O]))]) +ifelse(os,[pyr],[ + define([cflags],concat(cflags,[ -q]))]) +ifelse(os,[next],[ + define([cflags],concat(cflags,[ -DNeXT -DADDRINPACK]))]) +ifelse(os,[xenix5],[ + define([cflags],concat(cflags,[ -Dxenix5 -I$I -DLAI_TCP -Di386]))]) +ifelse(os,[aix],[ + define([cflags],concat(cflags,[ -DAIX -DUSETIMES -DNOWAIT3 -DUSEDIRENT]))]) +ifelse(os,[irix],[ + define([cflags],concat(cflags,[ -D_BSD_COMPAT]))]) + +# was used for nbp, but found we needed more... leave in case +define([nbpflags],[]) +define([lflags],[]) +define([mflags],[]) + +# aux's c compiler isn't nice - it doesn't have a preprocessor +# definition for aux. So, let's invent one. Also turn on -n +# for shared code. +ifelse(os,[aux],[ + define([cflags],concat(cflags,[ -n -Daux])) + define([lflags],concat(lflags,[ -n]))]) + +# check to see if we need sysvinstall usage +ifelse(os,[aux], [define([sysvinstall],[yes])], + os,[hpux],[define([sysvinstall],[yes])], + os,[irix],[define([sysvinstall],[yes])]) + +# or if we really want to use install +# ifelse(os,[someOS], [define([theinstaller],[install])]) + +# for cap library +# Valid are: +# NOFFS - no ffs function defined, fake it out +# LOCALTIME_GTOD - uses pd localtime, but gettimeofday always reads +# disk based time of day. Always defined for AUX for now. Probably +# needs to be changed for versions of Aux after 1.0 +# NORECVMSG - no recvmsg in system (fake it out) +# NOSENDMSG - no recvmsg in system (fake it out) +define([caposdefs], + concat( ifdef([X_NOFFS],[ -DNOFFS],[]), + ifelse(os,[aux],[ -DLOCALTIME_GTOD],[]), + ifdef([X_NORECVMSG], [ -DNORECVMSG],[]), + ifdef([X_NOSENDMSG], [ -DNOSENDMSG],[]))) +# for afp library +# two cases for X_LOCKF - if lockf isn't defined +# first: X_FCNTLLOCKF is defined, so just note that lockf uses fcntl.h +# second: " " isn't defined so define no lockf +define([afposdefs], + concat( ifdef([X_FLOCK],[],[ -DNOFLOCK]), + ifdef([X_LOCKF],[], + [ifdef([X_FCNTLLOCKF],[-DLOCKFUSESFCNTL],[-DNOLOCKF])]))) +# for aufs +define([aufsosdefs], + concat(aufsosdefs, + ifdef([X_STATFS],[ -DUSESTATFS],[]), + ifdef([X_GETMNT],[ -DUSEGETMNT],[]), + ifdef([X_QUOTA],[ -DUSEQUOTA],[]), + ifdef([X_SUNQUOTA],[ -DUSESUNQUOTA],[]), + ifelse(os,[aix],[ -DNOVFORK -DUSECHOWN],[]))) + +# if no ranlib (or fakeout like hpux) and need to order libaries +ifelse(os,[dummy],[define(uselordertsort,[1])], + os,[aux], [define(uselordertsort,[1])], + os,[irix], [define(uselordertsort,[1])]) + +# lw config +define([lwflags], + concat(lwflags, + ifdef([fastether],[-DSFLOWQ=1],[]), + ifelse(os,[aix],[],[]))) + +# more papif config +define([papflags], + concat(papflags, + ifelse(os,[aix],[ -DNOVFORK],[]))) + +#NBPFLAGS = nbpflags() +#SPECCFLAGS = specialcflags() +#BIGCFLAGS = bigcflags() +#CFLAGS = cflags() +#LFLAGS = lflags() +#AFPOSDEFS = afposdefs() +#AUFSOSDEFS = aufsosdefs() + +ifdef([debug],[ +# location of include files + define([includedir],[cwd]) +# location of des subroutine source (see lib/afp/README) + define([desloc],[[../../extras]]) +# location of atalk.local, etc. + define([etcdest],concat(cwd,[[[/etc]]])) +# location of user cap programs + define([capdestdir],concat(cwd,[[[/bin]]])) +# location of cap "server" programs (aufs, lwsrv, papif, uab etc.) + define([capsrvrdestdir],concat(cwd,[[[/bin]]])) +# location of some cap data files + define([caplibdestdir],concat(cwd,[[[/bin]]])) +# location of cap libraries + define([libdestdir],concat(cwd,[[[/lib]]])) +# cap library names + define([caplib],[[libcap.a]]) + define([afplib],[[libafp.a]]) + define([afpclib],[[libafpc.a]]) +# names to load cap libraries with + define([libcap],concat(cwd,[[[/lib/libcap.a]]])) + define([libafp],concat(cwd,[[[/lib/libafp.a]]])) + define([libafpc],concat(cwd,[[[/lib/libafpc.a]]])) + define([capprinters],concat([\"],etcdest,[/],[cap.printers],[\"])) + define([cflags],concat(cflags,[ -I],includedir)) + define([atalklocal],concat([\"],etcdest,[/],[atalk.local],[\"])) + define([configdir],concat([\"],etcdest,[\"])) +]) + +define([datestring],maketemp(/tmp/capcXXXXXX)) +syscmd(date > datestring()) +##########MARKER########## +# Makefile autoconfigured for ... +[#] osname() system on include(datestring()) +syscmd(rm datestring()) + +MFLAGS=mflags() +LFLAGS=lflags() +CC=thecompiler() +LD=theloader() +SHELL=/bin/sh +INSTALLER=theinstaller() diff --git a/man/AUFS.1 b/man/AUFS.1 new file mode 100644 index 0000000..f58f593 --- /dev/null +++ b/man/AUFS.1 @@ -0,0 +1,481 @@ +.TH AUFS 1 "20 Jun 1990" "Columbia University" +.SH NAME +aufs +\- AppleTalk file protocol UNIX File Server +.SH SYNOPSIS +AUFS is an implementation of a file server on a UNIX host connected +to an AppleTalk network, for client computers on AppleTalk that support AFP. +Specifically, it works as a file server for Macintosh computers with +the AppleShare client code. +.PP +Any specified UNIX directory can be accessed as an AUFS volume. +An AUFS volume can be used to store and use Macintosh files and applications; +or to access UNIX files from the Macintosh in text-only mode. +This manual entry explains the design of the AUFS system from the user +point of view and the steps needed to set up your UNIX account for +Macintosh file service. +See AUFS(8) for information on how to set up the +.I aufs +daemon that establishes the server on the UNIX system. +To use AUFS, you must have a valid login account on the UNIX system and +write access to at least one directory. In some circumstances, it is +possible to set up an AUFS server with "guest" access to allow read-only +access to supplied files. +.PP +Here are the basics for using AUFS. +More detailed descriptions of the implementation follow. +.PP +.B +UNIX setup +.RS 5 +.IP 1) 5 +Make sure your UNIX password is no more than eight characters long. +.IP 2) 5 +Make a subdirectory of your UNIX home directory or work area +to be your Macintosh volume, say 'mac'. +Create two +additional subdirectories named +.I .resource +and +.I .finderinfo +at the same level, then change your working directory to the mac +subdirectory and make the same subdirectories again. +For example: +.br +.sp +.I + % mkdir mac +.br +.I + % mkdir .finderinfo .resource +.br +.I + % cd mac +.br +.I + % mkdir .finderinfo .resource +.br +.sp +The top level .finderinfo directory is used to store information about +the volume window size, position and layout. You can do without the top +level .finderinfo and .resource +directories but the window will always open with default settings, changes +to which will not be saved. +.IP 3) 5 +Make a file named +.I afpvols +or +.I .afpvols +in your home directory to designate your Macintosh directory, +with a line like: +.br +.sp +.I + ~/mac:UNIX_mac_files: +.br +.sp +where the part before the colon is the UNIX pathname (here relative +to my home directory) and the part after is the volume name that the +Macintosh will use and display. +.RE +.PP +.B +Macintosh setup and use +.RS 5 +.IP 1) 5 +Make sure you have installed AppleShare client code (version 1.1 or later) +and Chooser (version 3.0 or later) on your Macintosh startup disk. +Your Macintosh must be connected to an AppleTalk net that is bridged +to the Ethernet. +.IP 2) 5 +Follow these steps to mount your AUFS volume: +.RS 5 +.IP a) 5 +Select +.I Chooser +from the Apple menu. +When the Chooser menu window comes up, click on the AppleShare icon in +the upper left corner (if you do not have this icon, you have not +properly installed AppleShare). +Activate AppleTalk if it is not already activated. +.IP b) 5 +Chooser will query the net for available file servers and list their +names. +Double click on the file server you wish to use. +You may need to select another AppleTalk zone to find the server. +.IP c) 5 +A connection request menu window will now appear. +Click the button for +.I registered user +if it is not already clicked. +Type your UNIX login account name in the +.I Name: +field (Chooser will supply the name from its name field as the default). +Click in the +.I Password: +field to activate text entry there and type in either your UNIX password +("Clear text" is specified in the login dialog box) or your AUFS password +(asterisks will show instead of the characters you typed for security). +Press the RETURN key or click on OK. +.I guest +connections may also be allowed at your site (see LOCAL CONFIGURATION, below). +.IP d) 5 +The AUFS server logs you in and a final menu window appears, listing +the available "volumes" (directories) that you may mount. +This list consists of the entries in your +.I afpvols +or +.I .afpvols +file, plus any volumes that the UNIX system manager has made available +for general use. +Click on the desired volume name and then click OK to mount it +(you may shift-click to select several volumes). +The AUFS volume icon (a UNIX "daemon") should appear on your desktop. +.IP e) 5 +You are returned to the Chooser main menu. +Click its close box to return to your application. +.RE +.IP 3) 5 +You work with this AUFS volume as if it were an attached Macintosh hard disk. +Note that the figures for space used and available refer to the entire +UNIX disk partition, not just your Macintosh volume directory. +.IP 4) 5 +To dismount an AUFS volume, simply drag its icon to the trash can. +AUFS volumes will also be dismounted if you select +.I Shutdown +from the Finder's +.I Special +menu. +.RE +.SH DESCRIPTION +.B +How Files Are Stored +.PP +The Macintosh and UNIX operating systems use very different structures +for storing files. +This section describes how the Macintosh structure is mapped into the +UNIX structure. +.PP +Macintosh files consist of two separate parts known as the +"data" and "resource" forks. +In addition, there is "finder" information kept for the file. +Roughly speaking, the resource fork stores programs, the data fork stores +text or data, and the finder information stores file creator, +file type, etc. +Under UNIX, all files are simply single streams of bytes whose meaning is +interpreted according to how they are used (the file system does have +additional structures equivalent to the finder information, but they +are not separately accessible). +.PP +To store a three-part Macintosh file in the UNIX file system, +AUFS adopts the following scheme. +Each UNIX directory that will store Macintosh files must contain two +subdirectories named +.I .finderinfo +and +.I .resource. +The Macintosh data fork is stored directly in the main directory with +the same name as seen on the Macintosh (see below for name translation +exceptions). +The Macintosh resource fork and finder information are stored (with the same +name as the data fork) in the +.I .resource +and +.I .finderinfo +subdirectories, respectively. +For example, if the Macintosh user stores the application "MacWrite" on the +AUFS volume "mac", it could be seen in the UNIX file system as the three +files: +.br +.sp + ~/mac/MacWrite +.br + ~/mac/.resource/MacWrite +.br + ~/mac/.finderinfo/MacWrite +.br +.sp +.PP +Macintosh folders are simply mapped to UNIX subdirectories. +For example, if the AUFS volume "mac" contained the folder "paintings", +the UNIX directory ~/mac would contain additional subdirectories +.br +.sp + ~/mac/paintings +.br + ~/mac/paintings/.resource +.br + ~/mac/paintings/.finderinfo +.br +.sp +Finder information for the folder itself +is stored in the parent UNIX directory .finderinfo +subdirectory, but folders have no resource fork. +.PP +The top-level UNIX directory that serves as the root of the AUFS +volume (e.g., "~/mac") also contains two files ".ADeskTop" and ".IDeskTop" +that are the equivalent of the Macintosh DeskTop file. .IDeskTop contains +information about icons and .ADeskTop contains information for +"applications mappings". Applications mappings allow you to double click +on a document and have the correct application mapped. +In general, you should keep these files around to maintain the highest +level of performance; +however, they do grow without bounds, so it might be worthwhile to +periodically delete them with the UNIX +.I rm +command (when the volume is not mounted) and rebuild your desktop +with the 'builddt' command in the contrib/DeskTop directory. +.PP +The AFP protocol does not handle file protections. +Instead, it implements a limited set of directory protections. +Unfortunately, these protections do not map directly into UNIX +protections. +In AUFS, both of the AppleShare protections "See Folder" and +"See Files" are mapped to the UNIX directory protections "read" +and "search"; and the AppleShare protection "Make changes" maps +to UNIX write access. +Individual UNIX file protections are also honored, so you don't have +access to files from the Macintosh unless you also have access from UNIX. +Changing directory protections with AppleShare results in modifying +the protections of the files in the directory to match, if possible. +.PP +The names of control files and subdirectories purposely begin with +a period (.) character, because then they are not shown by the standard +UNIX +.I ls +command, +thus presenting a less cluttered view from the UNIX side of your +Macintosh files. +Use the +.I -a +option to the +.I ls +command to see them. +.PP +.B +How AUFS Volumes Are Set Up on UNIX +.PP +Real Macintosh volumes are normally separate disk drives. +Under AUFS, any UNIX directory tree (e.g., some directory plus all its +subdirectories) may be used as a volume. +You must specify which of your directories may be mounted by AUFS as +Macintosh volumes. +This is done by creating a file named +.I afpvols +or +.I .afpvols +in your UNIX home directory (the first form overrides the second). +Each directory that is to be mountable by AUFS is represented in this file +by a single line with the following format: +.br +.sp + UNIX_path_name:Macintosh_volume_name[:optional_password] +.br +.sp +For example, I could create the subdirectory "mac" in my UNIX home +directory and then include this line in my "afpvols" file: +.br +.sp + ~/mac:UNIX_mac_files +.br +.sp +If you do not have an afpvols file in your home directory, your home +directory will be made available for mounting by default. +.PP +The UNIX system manager may also specify a system-wide afpvols file +when he installs AUFS, to describe volumes that may +be mounted by any Macintosh client. +.PP +It is possible to have AUFS volumes in your afpvols file that overlap. +That is, a subdirectory of one AUFS volume directory could be separately +mounted as its own volume. +It is dangerous to have overlapping volumes mounted simultaneously on +the Macintosh. +.PP +.B +AUFS Color Volume Icons +.PP +Color icons for AppleShare volumes (and, in fact, any directory) are stored +in an invisible Macintosh file named "Icon^M". The ^M is a carriage return +character. Under AUFS this file is renamed to the UNIX file "Icon:0d". When +the AUFS volume owner (or any user with write permission) first mounts the +volume, AUFS creates an approriate color icon file - if none already exists. +When configuring CAP, you can define USE_HOST_ICON to have the volume icon +associate with the underlying UNIX hardware or operating system. +.PP +There are two methods for creating a new color icon file. Using the +Macintosh utility 'ResEdit' (make sure that the file contains resources +'icl4', 'icl8', ICN#', 'ics#', 'ics4' and 'ics8' and that all of the +resource IDs are set to -16455. The 'Invisible' bit should also be set). +You can also paste a new icon into the 'Get Info' window of an AUFS +directory and then move the three forks of the "Icon:0d" file into the +root of the AUFS volume. +.PP +.B +Macintosh Volumes vs. UNIX volumes under AUFS +.PP +AUFS maintains a distinction between "Macintosh" volumes and "UNIX" +volumes. +The first are intended to store and use standard Macintosh files and +applications. +The second provide a mechanism for seeing standard UNIX files from +the Macintosh. +The presence of the +.I .resource +and +.I .finderinfo +subdirectories in the UNIX root directory of the volume is used by AUFS to +distinguish the two types of volumes. +.I Both +subdirectories must be present to make this directory a "Macintosh" +volume. +AUFS will +.I not +create these subdirectories for you in the volume root directory; you must do +that if you want to use it as a Macintosh volume. +Subdirectories (folders) created by AUFS (with the Macintosh "New Folder" +command) will be made with the +.I .resource +and +.I .finderinfo +subdirectories only if the root directory has them. +.PP +UNIX directories that you create to serve as Macintosh volumes +under AUFS should be managed solely from the Macintosh client. +Using UNIX file utilities to move, rename, etc., files or subdirectories +is dangerous. +The only exception to this is occasionally removing the desktop files +(.ADeskTop and .IDeskTop in the volume root directory) which otherwise +grow without limit; they will be rebuilt when the directory is next +mounted by AUFS. +.PP +If either of these subdirectories is missing from the UNIX root directory +of the volume, then AUFS treats it as a "UNIX" volume, containing +normal UNIX files that may be accessed as "text-only" by the Macintosh. +AUFS shows files in such volumes as having creator "unix" and file +type "TEXT" and uses a special gothic U icon to represent them on the +desktop. +.PP +For "UNIX" volumes, +AUFS does automatic conversion between the UNIX line terminator LF (the +line feed character) and the Macintosh line terminator CR (the carriage +return character) when reading or writing UNIX files from the Macintosh. +In addition, any time that "line at a time" reads with CR as the end of +line terminator are done by the Macintosh, then both CR and LF are +recognized as end of line terminators by AUFS regardless of the file creator +and type. +BinHex is an example of a program that does this. +.PP +You can turn off automatic CR/LF translation for a UNIX file by setting +the file type/creator to other than TEXT/unix (with a Macintosh utility +like DiskTop, for example). +"Line at a time" translation can only be turned off by the system +administrator when installing AUFS. +.PP +The system administrator can also configure AUFS to look in UNIX files +to determine file type and provide either alternative finder information +or text translations. +See the LOCAL CONFIGURATION section (below) to see if this has been done. +.PP +Although a directory intended as a UNIX volume under AUFS cannot have +both the +.I .resource +and +.I .finderinfo +subdirectories, it can have a +.I .finderinfo +subdirectory alone, which speeds up access to UNIX files by allowing +AUFS to store finder information, rather than having to construct it +every time the volume is accessed. +In this case, Macintosh documents that have no resource fork can also +be saved into the UNIX volume, although their contents may not be +intelligible to any UNIX program. +.PP +.B +Macintosh -- UNIX File Name Translations +.PP +File naming rules differ slightly between the UNIX (BSD version) +and Macintosh operating systems, therefore these translation rules are used. +The Macintosh system does not distinguish between upper and lower case +letters in names; UNIX does. +AUFS only does case translation if configured to do so. +Macintosh file names may not exceed 31 characters in length; Berkeley +UNIX names may be up to 255 characters long. +No truncation of Macintosh names is necessary when stored on UNIX, but +if AUFS encounters a UNIX file in the mounted directory with a name longer +than 31 characters, it simply skips it (not visible to the Macintosh). +Macintosh file names may contain any character, including special 8 bit +character codes (like those for the trademark symbol), except for a colon; +UNIX names may contain any 7 bit character except for the slash. +Thus, AUFS translates colons found in UNIX file names into slashes as viewed +by the Macintosh; and 8 bit special characters or the slash found in +Macintosh names into a colon followed by the two digit hex code for the +character when stored on UNIX. +In general, if you expect to use a file on both the UNIX and Macintosh +systems, stick to file names of 31 characters or less, using only letters, +numbers, period, underscore, and hyphen. +.SH LOCAL CONFIGURATION +.br +.sp +.SH KNOWN BUGS +DeskTop files grow without bounds. +The only way to prune them is to delete them from the UNIX side +and rebuild the desktop using the 'builddt' command in the contrib/DeskTop +directory. +.PP +Applications mappings in the DeskTop files can quickly get out of sync +with reality. +Not enough information is stored to keep everything in sync, and it +would be costly to recover anyway if available. +Problems may occur when you move around directories holding applications. +A work-around is to delete and rebuild the DeskTop files as described +above. +.PP +The file creator "unix" and the file type "TEXT" are not registered +with Apple. +.PP +You cannot change the owner of a file; thus drop folders do not work +well. +.PP +AUFS will follow symbolic links for directories and files. +However, for directories, no more than about four symbolic links can +be followed in any path. +.PP +Read/writes and many other operations are blocking. +.PP +AUFS uses a complete path name specification when handling files. +There is no checking that path names are within system length limits. +.PP +File dating is different under UNIX and the Macintosh OS. +In the distributed vanilla CAP code, +Macintosh modification time is mapped to the later of the UNIX +"last status change time" and "last modification time"; +creation time is unknown on UNIX and is approximated by the earliest of +"last status change time", "last modification time", and "last access time". +By setting the USE_MAC_DATES flag in the features file at Configure time, +code is included that maintains the file create time correctly. +.PP +Specifications for the Macintosh Hierarchical Filing System and AppleShare +require that directory ids be fixed across the lifetime of a volume, and +not be reused. Code to implement fixed directory ids is only included by +setting the FIXED_DIRIDS flag in the features file at Configure time. +.PP +In a directory used as an AUFS volume, path names can get very long. +Some implementations of the UNIX +.I tar +program may have problems with these names when used to archive the directory. +.PP +Dumping a AUFS directory on one UNIX machine and restoring on another +may not work correctly if the machines have different byte orderings. +.PP +See the source directory for more design and bug notes. +.SH AUTHOR +Bill Schilit and Charlie C. Kim, Columbia University. +.br +This manual entry by Phil Farrell, Stanford University School of Earth Sciences. +.br +This manual entry additionally updated by Rakesh Patel, Rutgers University. +.br +This manual entry additionally updated by David Hornsby, Melbourne University. +.SH SEE ALSO +AUFS(8), CAP(3), CAP(8), atis(8) diff --git a/man/AUFS.8 b/man/AUFS.8 new file mode 100644 index 0000000..c0d24b9 --- /dev/null +++ b/man/AUFS.8 @@ -0,0 +1,381 @@ +.TH aufs 8 "Jan 31 1994" "Columbia University" +.SH NAME +aufs +\- daemon program to establish AppleTalk filing protocol UNIX File Server +.SH SYNOPSIS +.B aufs +[ +.BI \-n " " +] [ +.BI \-V " " +] [ +.BI \-U " " +] [ +.BI \-P " " +] [ +.BI \-G " " +] [ +.BI \-X " " +] [ +.BI \-A " " +] [ +.BI \-F " " +] [ +.BI \-B " " +] [ +.BI \-f " " +] [ +.BI \-[i|I] " " +] [ +.BI \-c " " +] [ +.BI \-l " " +] [ +.BI \-m " " +] [ +.BI \-M " " +] [ +.BI \-S " " +] [ +.BI \-R " " +] [ +.BI \-r " " +] [ +.BI \-k +] [ +.BI \-p +] [ +.BI \-s +] [ +.BI \-u +] [ +.BI \-T +] [ +.BI \-d " " +] [ +.BI \-a " " +] [ +.BI \-t " <" "Input | Output | Both" > +] [ +.BI \-L " " +] [ +.BI \-Z " " +] +.SH DESCRIPTION +.I aufs +implements a file server on a UNIX host for client computers on AppleTalk +that support AFP, +or Macintoshes on the internet that support AFP via TCP/IP using AppleShare +client 3.7 or later. +This manual entry describes how to run the UNIX server daemon process. +See AUFS(1) for information about how to use the server. +.PP +.I aufs +is normally started at boot time via a command in start-cap-servers (which +is usually run from /etc/rc.local). +The CAP name information server daemon +.I atis +must be running when +.I aufs +is started. +AUFS must be run from the root account. +If debugging options are specified (see \-d or \-a, below), +.I aufs +runs in the foreground to log messages to standard output. +Otherwise, it automatically puts itself into the background to run as +a daemon process. +The master daemon forks a new child process to handle each client connection +request. +.SH OPTIONS +There are no arguments needed for normal operation. Optional arguments +allow control of configuration and debugging. +.TP 10 +.BI \-n " " +is used to specify the server name. By default the server name is +" Aufs". +.TP 10 +.BI \-V " " +is used to specify a server-wide volumes file. +The volumes listed in the file will be available to every AFP client. +Individual users may also have their own volumes file in their home directory. +See AUFS(1) for a description of the volumes file format. +.TP 10 +.BI \-U " " +is used to modify the maximum number of sessions allowed. The default +is around 10 or so (depends on the ASP implementation). The maximum +value is limited solely by the number of DDP sockets available. +One UNIX process is created for each open session. +.TP 10 +.BI \-P " " +specifies the absolute pathname of a "lookaside" password file containing +cleartext usernames and passwords or to an optional administrative file +that lists valid usernames for use with the DISTRIB_PASSWDS feature (must +be specified at compile time by enabling the option at Configure time). +This option requires special configuration when installing aufs -- +see the installation instructions in the source directory. +.TP 10 +.BI \-G " " +is used to allow "guest" or "Anonymous" AppleShare sessions. This is +not enabled by default, as it is a security violation unless it is +done in a very controlled fashion. In particular, should be +the username of a UNIX account with very limited privilege and volume +access. A common account used for this purpose is "nobody". For guest +sessions, no user volumes file is allowed or read. +.TP 10 +.BI \-X " " +is used to allow access control for +.I lwsrv. +.I Lwsrv +requires the same option in order to enable the access control +(this option must be specified at compile time by enabling the +LWSRV_AUFS_SECURITY option at Configure time). +The directory specified will be used to store temporary information +used to authenticate the user. It is not uncommon to use /tmp as +the directory, although it is probably much better to use a separate +directory. +.I aufs +will normally remove the temporary files, but if the directory used +is not /tmp, something should be run that will remove all the files +within that directory when the machine is starting up. +.TP 10 +.BI \-A " " +allows the maximum number of times an application may be opened to be +controlled (this option must be specified at compile time by enabling +the APPLICATION_MANAGER option at Configure time). The control file +lists the full path to each Application data fork followed by a colon ':' +and a number. An optional trailing 'P' may be added to protect the Application +from Finder copying. For more details, see contrib/AppManager/README. +.TP 10 +.BI \-F " " +specifies a global file which maps a UNIX file suffix into a Mac Type and +Creator (this option must be specified at compile time by enabling the +USR_FILE_TYPES option at Configure time). The mapping file also indicates +the type of data translation to be used and a specific comment string. A +user may over-ride these mappings by having a .afpfile (or afpfile) file +in their home directory. +.TP 10 +.BI \-T +enables AppleShareIP (AFP via TCP/IP) support in +.I aufs. +Note that the first +.I aufs +process defaults to the well-know port number 548. Subsequent incarnations +of +.I aufs +will require that the TCP/IP port number be specified using the \-B option. +Clients needing to connect to these servers, that are not also connected to +the same network via AppleTalk, must specify the port number in +the dialog box obtained by clicking on the Chooser "Server IP Address..." +button. The format is the same as for the \-B option, ie: 128.250.1.21:2169 +to use port number 2169 on host 128.250.1.21. +.TP 10 +.BI \-B " " +tells +.I aufs +to listen for TCP/IP connections at the specified IP address and optional +port (defaults to any available interface address and port number 548). +This option implies \-T. +.TP 10 +.BI \-f " " +specifies the pathname of a file containing yes/no permissions for client +IP numbers or subnets wishing to connect using AFP via TCP/IP. See the file +cap60/etc/aufsIPFilter for details. +.TP 10 +.BI \-c " " +specifies a directory where +.I aufs +can put coredumps. Hopefully, you won't see any coredumps. +.TP 10 +.BI \-l " " +can be used to specify the path name of a file for logging messages. +The default log file is a file with the name .log +(see -n option) in the current working directory where +.I aufs +is started. There is no way to turn off logging. +.TP 10 +.BI \-m " " +specifies the path name of a file which contains a "message of the day" to +be displayed when an AFP 2.1 compatible client connects to the server. +.TP 10 +.BI \-M " " +specifies the path name of a file which contains a message to be sent to +all connected (and AFP 2.1 compatible) clients when the parent AUFS process +is sent an URG signal. Typically used for "the server will be unavailable" +messages. +.TP 10 +.BI \-S " " +is used to specify the number of packets the server is allowed to send in +each ATP response to the client, +where can vary from 1 to 8. +This controls the flow rate for data sent from the server to the client. +It may be required when the UNIX host system sends back to back +packets at a faster rate than the target system or intervening gateways +can accept. +The default value is installation dependent (see LOCAL CONFIGURATION, below). +.TP 10 +.BI \-R " " +is used to specify to the client the number of packets he is allowed to +send in each ATP response to the server, where can vary from 1 to 8. +This controls the flow rate for data sent from the client to the server. +It may be required when the UNIX host system cannot process received +back to back packets (due to speed or buffer space limitations) +as fast as the remote system or intervening gateways can send them. +The default value is installation dependent (see LOCAL CONFIGURATION, below). +.TP 10 +.BI \-r " " +is used to specify a README file (full path name) to be linked into the top +level directory of a new AUFS user. For example: to explain the purpose of +.finderinfo and .resource directories and/or any local configuration +settings (this option must be specified at compile time by enabling the +AUFS_README option at Configure time). +.TP 10 +.BI \-[i|I] " " +sets an AUFS idle timeout, after which the AUFS session will begin to +close down, sending warning messages at the 5, 3 and 1 minute marks. +Any access to the server volume from the 5 minute mark onward will +reset the timeout and send a "no longer shutting down" message to the +Macintosh. The \-i flag specifies that timeouts are for GUEST +connections only, \-I specifies everyone. The field is +measured in minutes (this option must be specified at compile time by +enabling the AUFS_IDLE_TIMEOUT option at Configure time). +.TP 10 +.BI \-u +tells the AUFS server not offer volumes specified in the afpvols file +of the user's home directory. For use when the directories are NFS mounted +or the server has a specific/special function. +.TP 10 +.BI \-k +specifies that DDP checksums are not to be used, the field is set to zero. +.TP 10 +.BI \-p +is used to tell AFP 2.1 compatible Macintosh clients to not save the user's +password in long term storage. +.TP 10 +.BI \-L "" +is used to specify a full path name to an external authorization program. +The program is passed the AppleTalk network number, node number and name of +the client and the AUFS server name, in that order. The program should +return 0 to authorize the user and non-zero to deny access. An +unsuccessful attempt is treated in the same way as "user unknown" or +"login disabled". This option may also be used to log server connections +(this option must be specified at compile time by enabling the +LOGIN_AUTH_PROG option at Configure time). +.SH DEBUGGING OPTIONS +.TP 10 +.BI \-Z "" +is used to specify the name of the output file to use for detailed debugging +of AFP commands (this option must be specified at compile time by enabling +the DEBUG_AFP_CMD option at Configure time). +.TP 10 +.B \-s +tells +.I aufs +to report usage statistics such as system time use and +number of times encountered for the various AFP commands. +These statistics are recorded in the log file at the end of a run. +.TP 10 +.BI \-d " " +specifies debugging flags for the cap libraries. See cap(3) for a +list of valid flags. +.TP 10 +.BI \-a " " +specifies debugging flags for +.I aufs. +Valid values (case independent) include +.I DeskTop +for desktop management, +.I Directory +for directory calls, +.I Enumerate +for enumerate calls, +.I File +for file calls, +.I Fork +for fork calls, +.I OS +for os +dependent debugging, +.I Server +for a trace of calls, +.I Unix +for unix level debugging, +.I Volume +for volume debugging, +.I debug +to mark as debugging (keeps +.I aufs +from backgrounding if no other debug flags are set), and +.I All +for all of the above. +A list of multiple options should be separated by blanks and enclosed in quotes. +You can also set the environment variable AUFSDEBUG to hold these values. +.TP 10 +.BI \-t " <" "Input | Output | Both" > +specifies that packets traces (partial dumps) of the specified +AFP commands should be done, for input, output, or both (can be +abbreviated to first character). +For example, to trace all Enumerate packets received by +.I aufs +you would specify +.I \-t IEnumerate +A list of multiple options should be enclosed in quotes. +You can also set the environment variable AUFSTRACE to hold these values. +.SH SIGNALS +.I aufs +operates by forking off a child process to deal with each session. +Child processes will take the SIGHUP signal to mean that the process +should quit after sending a termination notice to the remote client, +SIGTERM to mean that it should initiate a shutdown in 5 minutes, with +termination messages to the remote client at odd minute intervals and +SIGURG to mean that a message is to be read from the specified file (the -M +option) and sent to the remote client. +WARNING: it is possible to catch +.I aufs +in a state where it is in a critical section that should not have been +interrupted and the actions taken in the signal handlers are not legal. +.PP +If your system has process groups implemented, then signals to the parent +(master) +.I aufs +daemon have these effects: +.TP 15 +.B SIGHUP +If the parent process receives SIGHUP, it will send a SIGHUP to all children +and terminate immediately. +.TP 15 +.B SIGTERM +If the parent process receives SIGTERM, it will send SIGTERM to all children +and shutdown after a little over 5 minutes. +.TP 15 +.B SIGURG +If the parent process receives a SIGURG, it will send SIGURG to all children +who will then collect and display an advisory message from the specified file. +.TP 15 +.B SIGUSR1 +If the parent process receives SIGUSR1, it will re-read the global afpvols +volume configuration file (this option requires that REREAD_AFPVOLS be +defined at configuration time). +.TP 15 +.B SIGUSR2 +Sending a SIGUSR2 signal to the AUFS parent process causes it to close and +then reopen the specified log file. This allows log files to be truncated +at intervals (this option requires that CLOSE_LOG_SIG be used to define +the signal name - default SIGUSR2 - at configuration time). +.SH LOCAL CONFIGURATION +.br +.sp +.SH BUGS AND NOTES +There are no known bugs in the code, but it is recognized that the DeskTop +management is less than optimial. +.PP +If the client does not execute the correct unmounting or shutdown sequence, +the aufs child process can be left running and will need to be +removed by the system administrator. +.PP +Notes and warnings pertaining to client use and file system implementation +are documented in AUFS(1). +.SH AUTHOR +AUFS was written by Bill Schilit, Computer Science Deparment and +Charlie C. Kim, User Services, Columbia University. +.SH SEE ALSO +AUFS(1), CAP(3), CAP(8), atis(8) diff --git a/man/CAP.3 b/man/CAP.3 new file mode 100644 index 0000000..b02b964 --- /dev/null +++ b/man/CAP.3 @@ -0,0 +1,74 @@ +.TH CAP 3 "24 July 1990" "Columbia University" +.SH NAME +libcap \- base CAP networking library +.br +libafpc \- afp client protocol library +.br +libafp \- afp support routines +.SH DESCRIPTION +.I libcap +is the library of routines that implement a large subset of the +AppleTalk protocols. There are two different +LAP delivery mechanisms for CAP: IPTalk and Ethertalk (possibly using +UAB). For a discussion +of the differences, and details on configuring the CAP system, +see CAP(8). +.PP +The architecture of +the CAP AppleTalk protocol implementation is fairly simple. At the +lowest layer, the various LAP mechanisms provide DDP support. +On top of the DDP layer, ATP and NBP provide the basic transport and +name binding functionality. ASP, PAP, and the parts of ZIP that are +implemented are layered on top of ATP. +.PP +The protocols are not run automatically. Programs are responsible for +giving up the cpu at regular intervals by a call to a "sleep" routine +(abSleep) to allow the +protocols to run. The various protocols will schedule two kinds of +events that can be run. The first is a "packet ready" event that +indicates input is ready on a particular socket. The second is a +"timeout" event. Note: a "packet ready" event takes precendent over a +"timeout" event. These events are handled by calling handler +routines that run fairly quickly and without blocking. See +doc/sched.notes in the source directory for futher information. +.PP +The afp client protocols are in +.I libafpc. +The library +.I libafp +provides a +set of support routines for AFP client and server programs. +.SH DEBUG FLAGS +The following standard CAP debugging flags are provided by the abmisc +.I "dbugarg" +routine. +.nf + d - DDP + a - ATP + n - NBP + p - PAP + s - ASP + k - Scheduler + v - Version +.fi +The following flags are also accepted, but are not used. +.nf + i - Initialization code + l - LAP +.fi +The version flag prints details about the CAP release and current patch +level, together with a string describing the low level delivery method. +.SH SEE ALSO +CAP(8), AUFS(8), atis(8), atprint(1), atlook(1) +.PP +See the following files in the source code directory for CAP: +.nf +.ta \w'doc/sched.notes 'u +doc/abmisc.doc miscellaneous routines +doc/asp.notes asp notes +doc/atp.notes atp notes +doc/cap.notes general notes +doc/nbp.ext nbp extensions for unix +doc/pap.notes pap notes +doc/sched.notes protocol scheduler notes +.fi diff --git a/man/CAP.8 b/man/CAP.8 new file mode 100644 index 0000000..7744a3b --- /dev/null +++ b/man/CAP.8 @@ -0,0 +1,136 @@ +.TH CAP 8 "24 July 1990" "Columbia University" +.SH NAME +CAP \- Columbia AppleTalk Package for Unix +.SH DESCRIPTION +.I CAP +implements a portion of Apple +Computer's AppleTalk protocols on a variety of UNIX hosts. There are two +distinct link access delivery mechanisms for CAP: IPTalk and Ethertalk. +.PP +IPTalk is the traditional CAP configuration. It encapsulates +Appletalk in UDP/IP packets, thus allowing you to +install CAP on any system that supports IP. It requires +special Appletalk bridges that understand the IPTalk encapsulation. +Suitable candidates are the Kinetics/Shiva FastPath, Webster MultiPort +Gateway, +Cayman GatorBox or Stanford University's Seagate box. +.PP +Ethertalk is the standard Apple way of using Appletalk on Ethernet. +The three methods of producing EtherTalk packets with CAP are: +.br +"Native EtherTalk" +using packet filters built on the Stanford ENET model. Currently available +for NIT or ENET on SunOS and the ULTRIX Packet Filter only. Native +EtherTalk is both Phase 1 and Phase 2 compatible. +.sp +.br +"Kernel AppleTalk" for systems that provide appropriate kernel AppleTalk +support at the DDP level or where the 'netatalk-1.2' package from +the University of Michingan is installed. Currently supports Phase 1 only. +.sp +.br +"UAB" is the Unix Appletalk Bridge. The main use for UAB is as a bridge +between two or more ethernet interfaces but it can also be used to provide +access to CAP services. With UAB, EtherTalk packets pass through a single +gateway process which places fewer restrictions on the network interface. +UAB currently runs under SunOS, ULTRIX, SGI IRIX, SONY NEWS and (soon) HP-UX. +UAB is currently Phase 1 compatible only. +.PP +CAP routines are structured, for the most part, the same as the Apple +routines described in "Inside AppleTalk" and "Inside LaserWriter." +Refer to the Apple documents and the procedure comments for a complete +description of the routines and how to call them. +.PP +Currently the AppleTalk protocols supported by CAP are: +.br +.I AppleTalk Transaction Protocol (ATP) +.br +.I Name Binding Protocol (NBP) +.br +.I Printer Access Protocol (PAP) +.br +.I AppleTalk Session Protocol (ASP) +.br +.I AppleTalk Filing Protocol (AFP) client side +.br +In addition, the +.I Datagram Delivery Protocol (DDP) +and +.I Zone Information Protocol (ZIP) +are partially available. +The structure of the Internet Appletalk Bridge software makes it +impossible to provide full DDP service for IPTalk. Only the Get Zone List ATP +ZIP command is implemented for ZIP. +.PP +IPTalk CAP, is of course, highly dependent upon the availability of a +Ethernet to Appletalk gateway which we term an Internet Appletalk +Bridge. Currently, CAP is based upon the KIP revision of the UDP +code as done by Bill Croft of SUMEX at Stanford University. +The K-STAR code, version 7.0 or higher, for the Shiva FastPath is +compatible as is any version of 'Megan', the gateway code for the Webster +MultiPort Gateway. +.PP +Three main CAP applications are available: +.TP +.I papif +acts as a line printer input filter spooling from a unix host to a +LaserWriter. See +.I papif(8) +for more information. +.TP +.I lwsrv +offloads spooling to the LaserWriter from the MacIntosh to a Unix host. +See +.I lwsrv(8) +for more information. +.TP +.I aufs +is an AppleTalk Filing Protocol unix file server (AppleShare +compatible). See +.I AUFS(1) +and +.I AUFS(8) +for more information. +.PP +In addition, there are a number of sample programs. These are included +strictly to point out some of the functionality made available by CAP. +.TP +.I lwpr +is a simple program which allows printing from a CAP host to a +LaserWriter. See +.I atprint(1) +for more information. +.TP +.I tlw +accesses the interactive mode of the LaserWriter, known as the +executive. +See +.I atprint(1) +for more information. +.TP +.I isrv, iwpr +spool from and print to an AppleTalk ImageWriter. See +.I atprint(1) for more information. +.TP +.I atlook, atlooklws, atpinger +lookup Appletalk names. See +.I atlook(1) +for more information. +.SH AUTHOR +Charlie C. Kim, User Services, Columbia University Center for +Computing Activites and Bill Schilit, Computer Science Department, +Columbia University. Contributions from Bill Croft, Stanford +University and Dan Tappan of Bolt, Bernak, and Newman. +.SH NOTES +Bill Croft's original work in this area provided the inspiration for +CAP. +.PP +Unfortunately, the interfaces for CAP have diverged some from the Apple +specifications (additional features, some code was written before Apple +released their specifications, etc). Most of the differences are +documented in the "doc" directory that comes with CAP. +.PP +The ZIP functions work only with revision 1/88 or later of KIP. +.SH "SEE ALSO" +aarpd(8), atis(8), AUFS(1), AUFS(8), papif(8), +lwsrv(8), atprint(1), atlook(1), CAP(3) diff --git a/man/UAB.8 b/man/UAB.8 new file mode 100644 index 0000000..0d6daed --- /dev/null +++ b/man/UAB.8 @@ -0,0 +1,161 @@ +.TH uab 8 +.SH NAME +uab +\- Unix AppleTalk Bridge +.SH SYNTAX +.I uab +-D +-f +-l +-d +tdump debug nodebug statistics (stat) exit +.SH DESCRIPTION +The Unix AppleTalk +Bridge ( +.I uab) +program allows certain unix systems to act as AppleTalk +Bridges. +.I uab +can be functionally divided into two parts. The +first is the actual AppleTalk Bridge implementation and the second are +the routines that define particular "Link Access Protocols" (aka +"hardware" delivery methods e.g. EtherTalk). +.I uab +also supports an +internal demultiplexing that allows +packets delivered to the +.I uab +node to be delivered to other processes +within that system. +.PP +Currently, +.I uab +runs on Ultrix 1.2 (and beyond) and SunOS 4.0 and +supports EtherTalk (Phase 1). Unfortunately, with the current definition of +KIP's UDP encapsulation and delivery rules, it is not feasible to +implement KIP. +The only internal packet +delivery mechanism defined is a modified version of KIP's UDP +encapsulation (modified-KIP) that uses a different UDP port range over +the internal +loopback; thus CAP programs must be relinked with a different low +level delivery mechanism to work with +.I uab. +Note that all packets for +these programs are sent and received through the +.I uab process. +.PP +Since +.I uab +does not understand KIP, +it is necessary to have an AppleTalk Bridge that +understands both KIP encapsulation and EtherTalk before KIP based +"systems" (e.g. programs compiled with CAP, bridges that only speak +KIP on the ethernet interface--revisions of KIP before 2/88, etc) can +work with +.I uab's +modified-KIP based programs. +.PP +uab is configured by use of a "bridge description" file described in +detail later. +.PP +The flags are: D to set the debugging level to a particular value, d +to increment the debug level by one, f to specify a bridge description +file (default bridge_desc), and l to specify a logging file (default none). +.I uab +acts in response to certain signals. +.I uab +invoked as: +.nf + uab +.fi +where name is one of tdump (SIGUSR1), debug (SIGIOT), nodebug +(SIGEMT), statistics (SIGUSR2), or exit (SIGTERM) will +send these signals. +tdump causes the running uab to dump its internal tables (rtmp, etc.) to +/usr/tmp/uab.dump. (dump is +reserved for nbp if uab ever support nbp). debug tells the running +uab to increment the debug level: if not log file was currently +active, the log will go to /usr/tmp/uab.run. +nodebug turns off all debugging. +stat or statisitics dumps statistics to /usr/tmp/uab.stats. exit +causes the running uab to stop. +.PP +The bridge description file is a list of the valid ports for a +particular Unix AppleTalk Bridge (UAB). +In order to minimize the maintaince headache, one may use the host +name as a selector. +Each port description is entered on a single line. +.PP +The first item in a line is the host selector field. It can be the +host name (as returned by gethostname). In addition, you can use % to +match any character. "*" can be used to match any host. Finally, you +can use "*" at the end of a string to complete a match. (Allowing "*" +at both the beginning and end or at an arbritrary location is a pain +because it is an unanchored search -- would have to use a regular +expression matcher to do this -- ugh). +.PP +The second field contains a tuple that specifies the interface's +link access protocol (LAP) and any device specific data required. +Valid LAP method specifications: +.nf + ELAP - EtherTalk Link Access Protocol + EtherTalk - same as above + ASYNC - Asynchronous AppleTalk + ASYNCATALK - same as above +.fi +The device specific data consists of a "device name" followed by an +colon and a "device number". If the colon is omitted, the device +number is assumed to be zero. +For Ethertalk, this should be interpreted as a ethernet "tap" device +(SunOS, Ultrix). For example, "ie:1" for ethertalk on interface ie1. +For Asynchronous AppleTalk, this is just a label. Suggested use as:0 +.PP +The third field specifies the local demultiplexing delivery +mechanism for delivery of DDP packets not destined for the bridge +process. Currently defined mechanisms are: "none" which says there +will be no other client processes; "mkip" - modified version of kip +style udp encapsulation using a different udp port range. +Specify 'none' for Asynchronous AppleTalk. +(Hopefully, there will be a way to do direct kip etc. in the future) +.PP +The fourth and last field specifies two items paired in a +[,] format. The first is the DDP network that should +be associated with this port. If you specify zero, the ddp network +number will be acquired via RTMP if there are other bridges that +know the network number. Note that only a single network is allowed +at this point. The network number may be specified as or +.. In both cases, the number may be decimal +(no leading zero), octal (leading zero), or hexadecimal (leading 0x +or 0X). If this field is not specified, +.I uab +will assume that this port is for multiplexing only. It will not send +out any rtmp data packets or respond to rtmp network request packets, etc. +.PP +The second item specifies the zone name associated with +the ddp network associated with this port. If it is not specified, +it will be acquired via ZIP if there are other bridges on the +network that know the zone name. Note: you may omit the comma if +you do not wish to specify a zone. +Note, \ can be used to quote a character (like a space in the zone +name :-) A "\" at the end of a line continues the line. Carriage +return and line feed both terminate a line. +.PP +You MUST SPECIFY a network number and Zone for Asynchronous AppleTalk. +.PP +You should order the file from more specific to less specific (in +terms of host name matches. Once a match has been found, then only +matches with the exactly same pattern will succeed! +.SH BUGS +None known. +.SH NOTES +Better method for internal demuxing is needed. +.SH AUTHOR +Charlie C. Kim, Academic Computing and Communications Group, Center +for Computing Activities, Columbia University +.SH FILES +.nf +/usr/tmp/uab.stats +/usr/tmp/uab.run +/usr/tmp/uab.dump +.fi diff --git a/man/aarpd.8 b/man/aarpd.8 new file mode 100644 index 0000000..a65d31c --- /dev/null +++ b/man/aarpd.8 @@ -0,0 +1,69 @@ +.TH aarpd 8 "20 August 1991" "Melbourne University" +.SH NAME +aarpd \- AppleTalk Address Resolution Protocol Daemon +.SH SYNTAX +.B aarpd +[ +.BI \-d " " +] [ +.BI \-D " " +] [ +.BI \-l " " +] [ +.B +] +.SH DESCRIPTION +.PP +.I aarpd +implements the AppleTalk Address Resolution Protocol for CAP programs using +Native EtherTalk (Phase 1 or Phase 2). It communicates with CAP processes +using RPC (Remote Procedure Calls) which interrogate a central AARP table. +Addresses that are not currently available provoke AARP lookups on the +network. These are added to the AARP table whose contents are aged and +deleted at intervals. +.PP +.I aarpd +maintains the address of the current default router. This is obtained +from +.I atis +which is listening to RTMP packets. The default router is chosen by +counting the RTMP tuples, favouring routers that perform "split horizon". +.PP +At startup, +.I aarpd +probes for a node number, determines the network number and +maintains a copy of the network information in /etc/etalk.local which it +updates as necessary. +.PP +The options +.I aarpd +accepts are: +.TP +.BI -d " " + are standard debugging flags for the +.I CAP +library. See CAP(3) for a +description of the valid flags. +.TP +.BI -l " " +specifies a file that messages should be logged to. +.TP +.BI -D " " +defines the debugging level (an integer from 1 to 20). +The higher the level is set, the more verbose +.I aarpd +is when logging. +.TP +.BI " " +defines the network interface (as listed by netstat(8)) such as ie0, le0, +ln0 or pf0. The zoneName argument is a valid zonename for the EtherTalk +connected to the interface. These values may be seeded in /etc/etalk.local +but it is recommended that both arguments be supplied. +.SH FILES +/etc/etalk.local - refer man/etalk.local.5 +.SH NOTES +.I aarpd +be run as "root" (uid 0) and should be started before any other CAP +programs. +.SH "SEE ALSO" +CAP(3), CAP(8), AUFS(1), AUFS(8) diff --git a/man/ash.1 b/man/ash.1 new file mode 100644 index 0000000..4c90c54 --- /dev/null +++ b/man/ash.1 @@ -0,0 +1,108 @@ +.TH ash 1 "26 July 1990" "Columbia University" +.SH NAME +ash \- utility program to make AppleTalk afp client calls to test CAP access to AppleShare servers +.SH SYNOPSIS +.B ash +[ +.BI \-d " " +] [ +.I nbpentity +] +.SH DESCRIPTION +.I ash +acts as an afp client "shell" to allow the user to issue a few simple commands +to an AppleShare (afp) server. +This can be used to test the Columbia AppleTalk Package (CAP) afp client +routines, or to test a CAP afp server ( +.I aufs +program). +.PP +Basically, the user invokes +.I ash +with the AppleTalk NBP name of a server which it will attempt to contact. +If no server name is specified, it will attempt to contact a default server +name compiled into the program and probably non-existent at your site. +After connecting to the server, you are prompted to log in with your +server name and password, and then offered a list of volumes to select. +Finally, you enter a loop which prompts for simple actions that can be +attempted on the server, such as listing a directory (folder) or changing +directories (folders). +You must terminate the program to end the loop (for example, with CTRL-C). +If you are testing an +.I aufs +server, this may leave the +.I aufs +child process running on the UNIX host for you to kill separately. +.SH OPTIONS +.TP 10 +.BI \-d " " +specify standard CAP debugging flags. See CAP(3) for a list of valid flags. +.TP 10 +.I nbpentity +specify the fully-qualified NBP name of the afp server to be contacted, +for example, +.I "UNIX Aufs:AFPServer@*". +If no NBP name is specified, a default server name compiled into the +program will be used. +This will almost certainly not work at your site. +Don't forget to enclose this argument in quotes if it contains any +embedded blanks. +.SH SAMPLE SESSION +This is a typescript of a sample session initiated on a UNIX host named +"pangea" which is running a standard +.I aufs +server named "Pangea Aufs". +My responses to prompts are in italics. +.nf +.RI pangea> " ash 'Pangea Aufs:AFPServer@*'" +abInit: [ddp: 4.51, 31] starting +Looking for Pangea Aufs:AFPServer@* ... +Unix server name Pangea Aufs +There are 3 AVO strings +Entries in indexed string: 3 +1: 'AFPVersion 1.0' +2: 'AFPVersion 1.1' +3: 'AFPVersion 2.0' +There is 1 UAM strings +Entries in indexed string: 1 +1: 'Cleartxt passwrd' +comp = 0 +User name? +.I farrell +.BI Password: " [not echoed for security]" +Found 3 volumes +Volume Earth_Sciences has no password +Volume Pangea-Mac files has no password +Volume Pangea-Unix files has no password +Returned server: 649031553 +Server time Thu Jul 26 15:32:33 1990 +What volume? +.I Earth_Sciences +VOLID = 1 +.RI > ? +Valid commands are: +d[irectory] - long file listing +l[s] - short file listing +c[d] - connect to directory +g[et] - get a file +p[ut] - put a file +.RI > ls +7 items +Count = 7 +README [file 12289, 2], res 0 + dat 592 = 592 +Virus Prevention [dir - 320, 2, 4 entries] +MacIP3.03 [dir - 321, 2, 4 entries] +System 6.0.5 [dir - 322, 2, 4 entries] +MacIP 4.0 [dir - 323, 2, 9 entries] +Downloading Tools [dir - 324, 2, 4 entries] +Hypercard v1.2.2 [dir - 325, 2, 6 entries] +okay +.RI > ^C +.fi +.SH SEE ALSO +CAP(8), CAP(3), aufs(8), AUFS(1) +.SH AUTHOR +Manual entry by Phil Farrell, Stanford University School of Earth Sciences +.SH BUGS +Should provide a graceful way to exit that will properly close the server +connection. diff --git a/man/atalk.local.5 b/man/atalk.local.5 new file mode 100644 index 0000000..39dd3a6 --- /dev/null +++ b/man/atalk.local.5 @@ -0,0 +1,141 @@ +.TH atalk.local 5 "9 December 1990" "Melbourne University" +.SH NAME +atalk.local \- configuration file for CAP. +.SH DESCRIPTION +atalk.local contains the static configuration information needed to run +the CAP system with IPTalk (See CAP(8)). This document defines the +permissible contents of atalk.local for this distribution. +.PP +The version of CAP that supports Ethertalk uses another file +called etalk.local. The contents of etalk.local are "free format" with +keyword identifiers for each piece of information. The file is written by +both UAB and the Native Ethertalk AARP daemon aarpd +after they have identified the network, node and zone name. Refer to +etalk.local(5) and UAB(8) for more information. If atalk.local exists, it +is used to "seed" values that are not otherwise obtained from the network. +.PP +Unlike LocalTalk and EtherTalk hosts, IPTalk based CAP hosts do not +dynamically establish their AppleTalk addresses, zones, or the network's +AppleTalk bridge. +The primary function of +.I atalk.local +is to define these values. +.PP +The format of the file for IPTalk is quite simple. +There are two significant lines in the file, and two extra lines which are +optional (the third line is mandatory if the fourth line is used). +There may also be any number of comment lines, which are identified +by an initial pound sign (#) character. +The first significant line defines the AppleTalk address of the host. +The second line defines which AppleTalk Bridge to use. +This bridge must be running software that supports the IPTalk +(KIP) encapsulation. +For example: +.nf + # host's AppleTalk network, node, zone + 55.1 6 "MY ZONE" + # bridge's AppleTalk network, node, and IP address + 55.1 5 128.254.1.5 +.fi +A network number can be specified as a single number or as two bytes +separated by a "." (as above). +.sp +.BI IMPORTANT NOTE: +An underscore in the zone name is +no longer treated as a space as in previous versions. +If a space in a zone name is desired, then use double or single forward +(" or ') quotes to surround the name, as in the example shown above. +This is the same convention used by later versions of +.I atalkad. +To have a quote in the zone name, escape it with a backslash (\\). +.SH OPTIONAL LINES +atalk.local may have up to two more (non comment) lines. The third line +describes the network and node number for the NIS information server. That +is, the network and node numbers for the host that is running atis, see +atis(8). If this line is not present, the network and node numbers are +taken from the first (host) line. +.PP +The optional fourth line (meaning you MUST HAVE a NIS entry) specifies the +network and zone name for Asynchronous AppleTalk running on this host. +Async AppleTalk allows connection to AppleTalk networks via serial lines +connected to UNIX hosts. The entry specifies a Network number and Zone name +that belong to this host only and must match the 'A' entries in atalkatab +(using atalkad 1.23 or later available via FTP from munnari.OZ.AU). This +service is currently only available with a Webster MultiPort Gateway, Shiva +FastPath (early 1991) and when CAP is used with UAB. +.PP +A fully qualified atalk.local would therefore look like this. +.sp +.nf + # host's AppleTalk network, node, zone + 55.1 6 "MY ZONE" + # bridge's AppleTalk network, node, and IP address + 55.1 5 128.254.1.5 + # name information server network, node + 55.1 6 + # async appletalk network and zone + 170.32 "Async Zone" +.fi +.PP +The contents of +.I atalk.local +for IPtalk follow rigidly from what is defined in atalkad's +.I atalkatab +file. Remember that +.I atalkatab +defines mappings for IP subnets to AppleTalk networks. The host's +appletalk network number must set as one of those mappings for +the subnet that the host is on. In addition, the zone name +specified in +.I atalk.local +must be the zone name of the appletalk network specified. +The host's node number must be the last byte of the host's ip network +number. For more information on why this is necessary, see the +section on IPTalk routing +below. +.PP +The AppleTalk bridge information comes from the anete network number in +atalkatab. +The anete network number is the one specified in the bridge's configuration +for the UDP encapsulated "interface". +When using the anete network number for the bridge, +its AppleTalk node number MUST EQUAL the last byte of its IP address. +.PP +The overall combination of the Async AppleTalk UNIX code and the specified +bridge form the "pseudo" Async AppleTalk bridge. +.SH IPTalk ROUTING +Appletalk bridges that implement IPtalk route to UDP encapsulated +networks by using the AppleTalk to IP mappings (N[0123] and "H" flagged lines) +defined in +.I atalkatab +to go from AppleTalk network numbers to IP network +numbers and vice versa. Of +particular interest is that it assumes +that the IP address of a host can be found by replacing the last byte +of the IP address defined in the mapping with the DDP node number. +(Remember, KIP encapsulates DDP packets not LAP packets). +This has +two major consequences. First, +the node numbers of KIP/CAP hosts +must be the same as the last byte of their IP network numbers. +.PP +Second, Appletalk to IP network mappings +are expected to cover subnets (8 bit subnets for Class B networks, 16 +bit for Class A networks). For the purposes of routing, it does not +matter how your IP networks are really subnetted (or not subnetted). +However, the mapping also defines a second piece of information. It +tells how to broadcast NBP lookup requests to all hosts on a +particular UDP capsulated Appletalk network and the +underlying IP network structure is of importance here. Unfortunately, +the details of this issue are beyond the scope of this document. +.SH FILES +atalk.local usually resides in /etc +.SH BUGS +The node numbers should be automatically determined since they are +fixed. In addition, it would be nice if the zone name were picked up +automatically as well. +.PP +.SH SEE ALSO +KIP documentation +.br +CAP(3), CAP(8), atalkatab(5), atalkad(8), atis(8) diff --git a/man/atis.8 b/man/atis.8 new file mode 100644 index 0000000..f96d6fa --- /dev/null +++ b/man/atis.8 @@ -0,0 +1,156 @@ +.TH atis 8 "20 June 1990" "Columbia University" +.SH NAME +atis \- AppleTalk information server and name registry for UNIX +.SH SYNTAX +.B atis +[ +.BI \-d " " +] [ +.BI \-D " " +] [ +.B \-E +] [ +.B \-N +] [ +.B \-R +] [ +.BI \-l " " +] [ +.BI \-k +] [ +.B reload +] [ +.B dump +] [ +.B debug +] [ +.B nodebug +] [ +.B exit +] +.SH DESCRIPTION +.I atis +acts as a name information server and an echo server +for a CAP Unix host. +In the traditional (IPTalk) version of CAP, you need not +run +.I atis +if no program to be run will register an NBP entity. Examples of +programs which do register NBP entities are: lwsrv and aufs. +In the Ethertalk version of CAP, you must run atis in order to +discover the network number of your host and keep the default +router up to date. In any case, +it is recommended that you run +.I atis +in the event you do start to add programs which will register names. +The echo service, while optional, is highly recommended. (Note: The +AppleShare client requires use of the echo service). +.PP +The Name Information Service function is to act as a name registry +for the particular host atis is running on. +.PP +The options +.I atis +accepts are: +.TP +.BI \-d " " + are standard debugging flags for the +.I CAP +library. The important one for atis is "n" for NBP. See CAP(3) for a +description of the valid flags. +.TP +.BI \-l " " +specifies a file that messages should be logged to. +.TP +.BI \-k +specifies that DDP checksums are not to be used, the field is set to zero. +.TP +.BI \-D " " +defines the debugging level (an integer from 1 to 3). +The higher the level is set, the more verbose +.I atis +is when logging. +.TP +.B \-N +tells +.I atis +to run without installing the NIS listener. +.TP +.B \-E +says that +.I atis +should run without the ECHO listener. +.TP +.B \-R +says that +.I atis +should run without the RTMP (Routing) listener. RTMP is started +only for the Ethertalk configuration. It is used to discover the +local network number, and to keep the default router up to date. +.PP +.I atis +automatically disassociates itself from the controlling terminal +unless debug options are set. To allow some control over the +disassocated process, the pid is recorded in /etc/atis.pid and the +following commands options have been added. +.TP +.B dump +sends a SIGQUIT signal to the running atis. This causes the running +atis to dump +its name table into /usr/tmp/nis.db. +.TP +.B reload +sends a SIGHUP signal to the running atis. This +causes the running +.I atis +to reload its name tables from /usr/tmp/nis.db. +.TP +.B exit +will stop the currently running copy of +.I atis +by sending the signal SIGTERM. +.TP +.B debug +increments the debug level of the running server by sending it a +SIGIOT. If no log file was defined, /usr/tmp/atis.run is created and used. +.TP +.B nodebug +turns off debugging in the running server by sending it a SIGEMT. If +/usr/tmp/atis.run was created by +.I debug +then the file is closed. +.PP +Examples of valid commands are +.nf + atis debug debug debug +.fi +to set the debug level to 3 or +.nf + atis dump exit +.fi +to dump the database and exit. +.PP +.SH FILES +/etc/atis.pid records the pid of the currently running server. +.br +/etc/atalk.local is used to find the closest Ethernet/Appletalk bridge +(directory location alterable at compile time). +.br +/usr/tmp/nis.db is used to hold the name data base when dumped +.br +/usr/tmp/atis.run is used for logging if debug is turned on without a +log file via "atis debug". +.SH NOTES +The current copy of atis runs with Revisions 10/86, 9/87, 1/88 or 6/88 of the +KIP UDP software for the Kinetics FastPath gateway as done by Bill Croft of +Stanford University. This is compatible with K-STAR 7.0 from Kinetics +or later from Shiva and the gateway code 'Megan' for the Webster MultiPort +Gateway. +.br +.I atis +.B must +be run as "root" (uid 0). +.SH AUTHOR +atis was written by Charlie C. Kim of Columbia University. +.SH "SEE ALSO" +CAP(3), CAP(8), AUFS(1), AUFS(8), lwsrv(8), atprint(1), atlook(1) diff --git a/man/atlook.1 b/man/atlook.1 new file mode 100644 index 0000000..df1c831 --- /dev/null +++ b/man/atlook.1 @@ -0,0 +1,164 @@ +.TH atlook 1 "20 June 1990" "Columbia University" +.SH NAME +atlook, atlooklws, atpinger \- look up appletalk devices accessible from UNIX +.SH SYNTAX +.B atlook, +.B atlooklws, +.B atpinger +[ +.B \-dn +] [ +.B \-n +] [ +.B \-s +] [ +.BI \-r " " +] [ +.BI \-t " [" p ] +] [ +.BI \-l " " +] [ +.BI \-k +] [ +.B \-P +] [ +.B \-S +] +.B nbp-entity-name +.SH DESCRIPTION +.I atlook, +.I atlooklws, +and +.I atpinger +allow you to look for various nbp entities on the AppleTalk internetwork. +.I atlook +and +.I atpinger +do a lookup on the wildcard nbp entity name "=:=@*" (any object, any +type, my zone) by default while +.I atlooklws +(which stands for look LaserWriter status) +only tries to find entities +of type "LaserWriter". +.PP +.I atpinger +also attempts to evoke a "echo reply" from the remote by sending an +"echo request". (c.f. Apple Echo Protocol). +.I atpinger +actually sends a special packet that contains information that allows +it to report the round trip time (in seconds). In addition, +.I atpinger +will report if the sent packet length is not the same as the returned +packet length. +.PP +.I atlooklws +tries to find the PAP status of the remote object and confirms +the nbp address. +.PP +You can override the zone easily by simply specifying it. +For example, +.I atlook myzone +would do a lookup on "=:=@myzone" and +.I atlooklws myzone +would look for "=:LaserWriter@myzone". +To override +the type or object, you should give a fully specified NBP entity name, +for example, +.I atlook =:Macintosh@myzone. +Normally, the output is sorted with a primary key of "type" and a +secondary key of "object". +.PP +The NBP entity object or type fields may contain 3-digit octal numbers +to represent characters not in the 7-bit ascii character set. For +example, \\252 is the \s-4\v'-0.4m'TM\v'0.4m'\s+4 trademark symbol in +the Macintosh Character Codes set (refer +.I Inside AppleTalk, 2nd Edition, Appendix D). +Such octal numbers +start with a leading backslash which, when used on the UNIX +command line, must be escaped: +.eo +.ul 1 +atlook =:UNIX\\252 +.ec \ +.PP +To lookup an NBP entity containing the backslash character itself ... +.eo +.ul 1 +atlook =:back\\\\slash +.ec \ +.PP +In AppleTalk Phase 2 environments, +.I atlook +and friends can use the partial matching +ability of NBP by specifying the wildcard symbol \\305 in the command line. +.eo +.ul 1 +atlook mu\\305:= +.ec \ +will find all entities with object names that start with "mu" and have +any NBP type. +Only one partial match character may be used per object or type field. +.PP +.I atlook, +.I atlooklws, +and +.I atpinger +accept the following arguments: +.TP 10 +.B \-P +says to ping the entities after lookup. The timeout for response is 5 +seconds. The default length of the packet is based on an internal +structure whose size is system dependent. +.I atpinger +is +.I atlook +with +.I \-P +set as default. +.TP 10 +.B \-S +says to get the LaserWriter status after lookup. +.I atlooklws +is +.I atlook +with +.I \-S +set as default. +.TP 10 +.B \-n +says to sort by the network numbers for output. +.TP 10 +.B \-s +says to sort the output by socket numbers. +.TP 10 +.B \-k +specifies that DDP checksums are not to be used, the field is set to zero. +.TP 10 +.BI \-t " [" p ] +can be used to specifiy the NBP timeout as ticks (1/4 second units) on +lookups. The default is +3 ticks. With 'p' preceeding the number, it specifies the +ping timeout in ticks. Using +.I \-t p +turns on pinging. +.TP 10 +.BI \-r " " +can be used to specify the number of NBP retries (default is 3). +.TP 10 +.BI \-l " " +can be used to specify the ping packet length (lower bound: internal +data structure, upper bound: ddpMaxData (currently 586). Using this +option turns on pinging. +.TP 10 +.BI \-d " " +can be used to specify standard CAP library debugging flags. See +CAP(3) for valid flags. +.SH NOTES +.I atpinger +figures out the round trip delay by inserting the time the packet was +sent into the echo request packet. +.SH AUTHOR +atlook, atlooklws, and atpinger were written by Charlie C. Kim of Columbia +University and share a common source. +.SH "SEE ALSO" +atis(8), CAP(3), CAP(8) diff --git a/man/atprint.1 b/man/atprint.1 new file mode 100644 index 0000000..8929b5f --- /dev/null +++ b/man/atprint.1 @@ -0,0 +1,105 @@ +.TH AppleTalkPrinting 1 "20 June 1990" "Columbia University" +.SH NAME +lwpr, iwpr, tlw, papif, lwsrv, isrv +\- UNIX access to AppleTalk printers and UNIX print spoolers for AppleTalk +.SH DESCRIPTION +The Columbia AppleTalk Package (CAP) allows UNIX programs to print to +printers connected to the AppleTalk network, or to act as print spoolers +for Macintosh computers on the AppleTalk network. +The four sample programs +.I lwpr, +.I iwpr, +.I tlw, +.I isrv, +are distributed as part of CAP to illustrate this printer access and +are documented here. +The two production applications +.I papif +and +.I lwsrv +distributed in CAP for LaserWriter printing and spooling are documented +in their own manual entries. +.PP +.I lwpr +is the simplest program of the bunch. +It allows one to send a single PostScript file directly to a +LaserWriter, as a networked Macintosh would. +The syntax is simply +.I +lwpr \-p lwname filename +The +.I \-p lwname +option specifies the name of the LaserWriter, which should be the fully +qualified network name, e.g. "LaserWriter:LaserWriter@MYZONE". +If you have your PRINTER environment +variable set to a printer that maps to a LaserWriter (via +cap.printers), then you may omit the "-p" option. +.I lwpr +runs in the foreground, establishing a connection to the LaserWriter and +then sending the file. +.PP +.I iwpr +functions exactly the same way as +.I lwpr, +except that it expects to send a properly formatted file +to an Appletalk ImageWriter II, e.g. "A-TALK:ImageWriter@MYZONE". +.PP +.I tlw +allows you to communicate with the interactive executive on a networked +PostScript device. +You can specify the device in one of three ways (-a is the default). +"tlw -u " maps a Unix short printer name to an Appletalk +entity name via the cap.printers file (often found in /etc). +"tlw -a " specifies a device of type "LaserWriter" in the current +zone "*". +"tlw -a " lets you choose an arbitrary object, type and zone, +e.g. "tlw -a frobozz:LaserShare@zork". +You can terminate the conversation with an EOF signal (normally CTRL-D). +.PP +.I isrv +is designed to run as a background daemon process that advertises itself +on the AppleTalk network as an ImageWriter II printer and accepts print +jobs from Macintosh computers, which it then sends to a normal UNIX +printer queue connected to a real ImageWriter II. +.I isrv +is invoked as +.I +isrv -P -T [-d ] +where is the name of the UNIX printer queue connected +to the real ImageWriter; +the must be "ImageWriter"; +and the optional +.I -d +argument can be used to specify standard CAP debugging flags (see +CAP(3)). +If debug flags are specified, +.I isrv +will stay in the foreground to log debug messages to standard output. +Otherwise, it will put itself into the background to run as a daemon. +.SH NOTES +.I isrv +is incredibly slow. We do not recommend the use of AppleTalk +ImageWriters outside of an AppleTalk network (e.g. use it on a single +cable only). It is included because it was written before we realized +how bad the overhead was. +.PP +.I lsrv, lwsrv, +and +.I papif +were previously documented here. +.I lsrv +has been removed from the distribution. +.I lwsrv +and +.I papif +are now documented separately. +.SH BUGS +There are bound to be several. +.SH FILES +/etc/cap.printers \- papif configuation file to associate UNIX printer +queues with LaserWriter names. Location may vary according to local option. +.SH AUTHOR +lwpr, iwpr, tlw and isrv were written by Charlie C. Kim of +Columbia University. +.SH "SEE ALSO" +CAP(3), CAP(8), atis(8), lwsrv(8), papif(8) diff --git a/man/aufsmkkey.8 b/man/aufsmkkey.8 new file mode 100644 index 0000000..601dd04 --- /dev/null +++ b/man/aufsmkkey.8 @@ -0,0 +1,65 @@ +.\" troff -man +.TH AUFSMKKEY 8L "Jun 20 1995" "AUFS Distributed Passwords" +.SH NAME +aufsmkkey \- AUFS distributed password global key tool +.SH SYNOPSIS +.B aufsmkkey +.SH DESCRIPTION +.I aufsmkkey +is the administrative tool used to create or edit the global key file +used for AUFS Randnum or 2-Way Randnum user authentication (this replaces +the current AUFS authentication code which uses cleartext passwords). This +feature must be enabled in CAP AUFS by defining DISTRIB_PASSWDS at CAP +configuration time. +.sp +.I aufsmkkey +must be run by the UNIX superuser. +.sp +The global key is kept in the file /usr/local/lib/cap/afppass +(or an alternate file defined by AFP_DISTPW_FILE) and is used to encrypt +the contents of each user password file. The global file also stores default +values for password expiry (either an expiry period up to 10 years +or a global cutoff date), minimum AUFS password length and maximum failed +login attempts. This file is also encrypted and is expected to be owned by +user root and set to mode 0600. +.sp +User password files are created or edited by the +.I aufsmkusr +tool and are normally kept in ~user/.afppass, set to mode 0600 and owned by +the user. The location and mode of the user password file may be customised +at compile time using the defines AFP_DISTPW_PATH and AFP_DISTPW_MODE +(useful, for example, if user home directories are mounted via NFS from +another machine). The user password files contain the current password +expiry date, minimum password length, maximum failed login attempts (all +can be set to zero to disable the feature), number of failed login attempts +and the user's AUFS password. +.sp +AUFS passwords can only be altered by the user using the AppleShare +Workstation software (using the 'Set Password' button in the AppleShare +login dialog box). The software will not permit the new password to be +identical to the old password or to be the same as the user's UNIX +password. +.sp +The minimum password length may be set to values between 0 (disabled) and 8. +Maximum failed login attempts to between 0 (disabled) and 255. +.sp +The expiry date may be set to a period measured in days or months, for +example: 60d, 60, 2m are equivalent input values or to a specific date +using a string of the form YY/MM/DD and an optional HH:MM:SS. EG: +95/06/20 16:44:55 is Tuesday June 20, 1995 at 4:44:55pm. +.sp +When the password has expired, the AppleShare user may still connect, but +the only command available is 'Set Password'. If the maximum number of login +failures have occurred, the user is advised that the account has been +disabled and to contact the server administrator. +.sp +.SH FILES +~/.afppass - user password file. +.br +/usr/local/lib/cap/afppass - global key file. +.SH SEE ALSO +aufsmkusr(8), CAP (Columbia AppleTalk Package) +.SH AUTHOR +djh\@munnari.OZ.AU, June 1995. +.SH NOTICE +Copyright (c) 1995, The University of Melbourne. diff --git a/man/aufsmkusr.8 b/man/aufsmkusr.8 new file mode 100644 index 0000000..d883d91 --- /dev/null +++ b/man/aufsmkusr.8 @@ -0,0 +1,107 @@ +.\" troff -man +.TH AUFSMKUSR 8L "Jun 20 1995" "AUFS Distributed Passwords" +.SH NAME +aufsmkusr \- AUFS distributed password tool +.SH SYNOPSIS +.B aufsmkusr +[ +.BI \-f " " +] [ +.BI user\ ... +] +.SH DESCRIPTION +.I aufsmkusr +is the administrative tool used to create or edit distributed user password +files for AUFS Randnum or 2-Way Randnum user authentication (this replaces +the current AUFS authentication code which uses cleartext passwords). This +feature must be enabled in CAP AUFS by defining DISTRIB_PASSWDS at CAP +configuration time. +.sp +.I aufsmkusr +must be run by the UNIX superuser. +.sp +User password files are normally kept in ~user/.afppass, set to mode 0600 +and owned by the user. The location and mode of the user password file may +be customised at compile time using the defines AFP_DISTPW_PATH +and AFP_DISTPW_MODE (useful, for example, if user home directories are +mounted via NFS from another machine). The user password files contain the +current password expiry date, minimum password length, maximum failed login +attempts (all can be set to zero to disable the feature), number of failed +login attempts and the user's AUFS password. +.sp +The ~user/.afppass files are encrypted with a global key created with the +.I aufsmkkey +tool. The global key is kept in the file /usr/local/lib/cap/afppass (or an +alternate file defined by AFP_DISTPW_FILE). The global file also stores +default values for password expiry (either an expiry period up to 10 years +or a global cutoff date), minimum AUFS password length and maximum failed +login attempts. This file is also encrypted and is expected to be owned by +user root and set to mode 0600. +.sp +AUFS passwords can only be altered by the user using the AppleShare +Workstation software (using the 'Set Password' button in the AppleShare +login dialog box). The software will not permit the new password to be +identical to the old password or to be the same as the user's UNIX +password. +.sp +.I aufsmkusr +may be used in batch or interactive modes. +.PP +The arguments that +.I aufsmkusr +accepts are: +.TP +.BI \-f " " +.sp +specifies that +.I aufsmkusr +creates AUFS user password files for the users listed in the "batch file". +The format is expected to be 'username password' with one entry per line +and the user name and password separated by white space. Comment lines may +begin with the # character, blank lines are ignored. Passwords containing +spaces may be quoted with double quotes. Passwords are limited to a maximum +of 8 characters and will be truncated if longer. +.sp +If the batch file is not set to mode 0600, the program will exit +(since this is considered to be a security breach). +.sp +When created from a batch file, the default values for minimum password +length and maximum failed login attempts are read from the global key +file. The expiry date of the password is set to the current time. This +forces the users to change their passwords when they first connect to AUFS. +.TP +.BI user\ ... +.sp +If used in interactive mode, +.I aufsmkusr +may be used to edit or create a +password file for users listed on the command line. If no user name is +provided, it will be prompted for. +.sp +The minimum password length may be set to values between 0 (disabled) and 8. +Maximum failed login attempts to between 0 (disabled) and 255. If non-zero, +the current number of failed login attempts may also be edited (ie: reset). +.sp +The expiry date may be set to a period measured in days or months, for +example: 60d, 60, 2m are equivalent input values or to a specific date +using a string of the form YY/MM/DD and an optional HH:MM:SS. EG: +95/06/20 16:44:55 is Tuesday June 20, 1995 at 4:44:55pm. +.sp +If the user expiry date is later than the global expiry date, a warning +message is printed. +.sp +When the password has expired, the AppleShare user may still connect, but +the only command available is 'Set Password'. If the maximum number of login +failures have occurred, the user is advised that the account is disabled +and to contact the server administrator. +.sp +.SH FILES +~/.afppass - user password file. +.br +/usr/local/lib/cap/afppass - global key file. +.SH SEE ALSO +aufsmkkey(8), CAP (Columbia AppleTalk Package) +.SH AUTHOR +djh\@munnari.OZ.AU, June 1995. +.SH NOTICE +Copyright (c) 1995, The University of Melbourne. diff --git a/man/cvt2apple.1 b/man/cvt2apple.1 new file mode 100644 index 0000000..ff75a61 --- /dev/null +++ b/man/cvt2apple.1 @@ -0,0 +1,58 @@ +.TH cvt2apple 1 "24 July 1990" "Columbia University" +.SH NAME +cvt2apple, cvt2cap \- convert CAP AUFS files to and from Apple standard file formats. +.SH SYNTAX +.BR cvt2apple " [" \-d ] +.I cap-file apple-file +.PP +.B cvt2cap +.I apple-file cap-file +.SH DESCRIPTION +.I cvt2apple +converts CAP AUFS format files to +Apple-Single or Apple-Double format files suitable +for launching by the A/UX +.I launch +program. +.I cvt2cap +converts Apple single and/or Apple-Double files to CAP AUFS format files. +.PP +A CAP AUFS file consists of three parts: the data fork, which is +the file which has same name as the whole CAP file; the resource fork +which is a file with the same name as the data fork but located +in a subdirectory called ".resource" of the directory in which the +data fork is located; and the finder information, located in a subdirectory +called ".finderinfo". +.PP +An Apple-Single format file contains all this information in a single file +using a standard format (see the A/UX Release Notes 1.0 page 5-2). +An Apple-Double +format file is similar to an Apple-Single file except that it consists +of two separate parts: the data fork is +stored with the base name; and the resource/finder part is stored in +a file with the base name prefixed by the '%' character. +.PP +.I cvt2apple +by default makes Apple-Single format files. +If the +.I \-d +option is specified then an Apple-Double format file is created. This should +normally be used for data files or for applications that write to their +data forks. +.PP +To copy a Macintosh file so it can be used under A/UX, first mount an AUFS +volume from the destination A/UX system on the source Macintosh; +next drag the application you want to run into that filesystem; +and finally, on the destination A/UX machine run +.I cvt2apple +to convert the CAP file to a launchable Apple format file. +.SH NOTES +.I cvt2apple +does not create Icon, real-name, or file-info records in Apple format +files. +As most Mac files contain their own icon information, this +is probably not a problem. +.SH AUTHOR +Paul Campbell +.SH "SEE ALSO" +AUFS(1), AUFS(8), CAP(8) diff --git a/man/etalk.local.5 b/man/etalk.local.5 new file mode 100644 index 0000000..fb1a5d3 --- /dev/null +++ b/man/etalk.local.5 @@ -0,0 +1,47 @@ +.TH etalk.local 5 "27 February 1991" "Melbourne University" +.SH NAME +etalk.local \- configuration file for EtherTalk based CAP. +.SH DESCRIPTION +The contents of etalk.local are "free format" with +keyword identifiers for each piece of information. The file is written by +both UAB and the Native Ethertalk AARP daemon aarpd +after they have identified the network, node and zone name. +If the file atalk.local exists, it +is used to "seed" values that are not otherwise obtained from the network. +.PP +The format of the file is quite simple. Each piece of information is +presented on a single line in the form "identifier data". Comments +are identified with a '#' character at the beginning of the line. Note that +since the file is (re)written whenever UAB starts or when the Native +EtherTalk version of CAP identifies a new default router, comments added by +the user will not persist. +.PP +An example of etalk.local: +.nf + # + # EtherTalk dynamic configuration data + # + # Last update: Sat Dec 29 14:46:45 1990 + # + # Generated by UAB + # + interface "ie0" + thisNet 99.116 + thisNode 69 + thisZone "unimelb-CompSci" + bridgeNet 99.116 + bridgeNode 69 + bridgeIP 127.0.0.1 + nisNet 99.116 + nisNode 69 + asyncNet 170.170 + asyncZone "unimelb-Async" +.fi +.sp +.BI IMPORTANT\ NOTE: +An underscore in the zone name is +no longer treated as a space as in previous versions of atalk.local. +.SH FILES +etalk.local usually resides in /etc +.SH SEE ALSO +CAP(3), CAP(8), atalkatab(5), atalkad(8), atis(8) diff --git a/man/getzones.1 b/man/getzones.1 new file mode 100644 index 0000000..82bbee9 --- /dev/null +++ b/man/getzones.1 @@ -0,0 +1,51 @@ +.TH getzones 1 "26 July 1990" "Columbia University" +.SH NAME +getzones \- display the AppleTalk zones visible to this host. +.SH SYNOPSIS +.B getzones +[ +.BI \-d " " +] [ +.BI \-l +] [ +.BI \-m +] [ +.BI \-s +] [ +.BI \-v +] +.SH DESCRIPTION +.I getzones +queries the AppleTalk to Ethernet gateway listed in +.I atalk.local +or +.I etalk.local +for the list of acccessible AppleTalk zones and then displays that list +on standard output. +.PP +The options +.I getzones +accepts are: +.TP +.BI \-d " " + are standard debugging flags for the +.I CAP +library. See CAP(3) for a description of the valid flags. +.TP +.BI \-l +lists the local zones for this network (Phase 2 networks only). +.TP +.BI \-m +print the zone name for this host (myZone). +.TP +.BI \-s +sort the zones list. +.TP +.BI \-v +produces a more verbose output including a zone count and +the zone name for this host marked with an asterisk. +.SH SEE ALSO +CAP(8), CAP(3), atalk.local(5), etalk.local(5) +.SH AUTHOR +Manual entry by Phil Farrell, Stanford University School of Earth Sciences. +Updated by David Hornsby, The University of Melbourne. diff --git a/man/instappl.1 b/man/instappl.1 new file mode 100644 index 0000000..b599387 --- /dev/null +++ b/man/instappl.1 @@ -0,0 +1,97 @@ +.TH instappl 1 "26 July 1990" "Columbia University" +.SH NAME +instappl \- install a UNIX generated file consisting of a Macintosh resource fork into an AUFS volume. +.SH SYNOPSIS +.B instappl +[ +.BI \-c " " +] [ +.BI \-t " " +] [ +.B \-l +.B \-m +] [ +.B \-i +] +.I +.RI [ ] +.SH DESCRIPTION +.I instappl +is a UNIX program to install a file consisting of a +Macintosh resource fork (e.g., a downloaded Macintosh application) into an +.I AUFS +AppleShare volume on the same UNIX host. +The +.I AUFS +volume must be one that has been configured to store +Macintosh files and applications (see AUFS(1)). +.I AUFS +stores the three "forks" of a Macintosh file into separate UNIX files. +A file consisting of only a data fork resembles a UNIX stream file +and can be copied directly into the +.I AUFS +directory. +A file that consists of a Macintosh resource fork +cannot be simply copied into the +.I AUFS +volume directory. +Finder information must also be set so that this file will be recognized +as an application when the +.I AUFS +volume is accessed by a Macintosh. +.I instappl +correctly installs the file consisting of a resource fork, creating +a null data fork and correct finder information. +It allows you to set the Macintosh creator and filetype with the +.I \-c +and +.I \-t +options. +Additional file attributes can be set with the +.IR "\-l, \-m," " and " \-i +options. +.SH REQUIRED ARGUMENTS +.TP +.I +specifies the path name of the input resource fork file that is to be +installed in +the +.I AUFS +volume. +.TP +.I +specifies the path name of the +.I AUFS +volume directory where the file will be installed. +.SH OPTIONS +.TP +.I +By default, the name to be used by +.I AUFS +when it serves this file to a Macintosh client is +simply the last component of the input file name. +If you wish it be different than that, +then specify the desired name as an optional argument. +.TP +.BI -c " " +specify a one to four character string to be used to set the Macintosh +creator value for this file. +The default value is "????" (unknown). +.TP +.BI -t " " +specify a one to four character string to be used to set the Macintosh +file type value for this file. +The default value is "APPL" (application). +.TP +.B \-l +sets the read-only (or locked) attribute for the file. +.TP +.B \-m +sets the multi-user attribute for the file. +.TP +.B \-i +sets the invisibile attribute for the file. +.SH SEE ALSO +AUFS(1), AUFS(8), CAP(3), CAP(8) +.SH AUTHOR +Manual entry by Phil Farrell, Stanford University School of Earth Sciences diff --git a/man/lwrename.8 b/man/lwrename.8 new file mode 100644 index 0000000..9b1fa0a --- /dev/null +++ b/man/lwrename.8 @@ -0,0 +1,168 @@ +.TH lwrename 8L "17 Dec 1993" "Stanford Earth Sciences" +.SH NAME +lwrename \- daemon to hide printers on AppleTalk network by resetting + AppleTalk type value. +.SH SYNOPSIS +.B lwrename +.RI "[\-t " minutes ] +[\-s] [\-r] +.RI [ lwrenamefile ] +.SH OPTIONS +.TP +.BI "\-t " minutes +Specify how many integer minutes to sleep in between each +sweep of the network looking for the monitored printers. +Default=2 minutes. +.TP +.B \-s +Do not run as a background daemon. +Only make a single sweep looking for and changing type on monitored +printers; wait the sleep time; reset printers to original types if +"\-r" option also specified; and exit. +Use for testing. +.TP +.B \-r +If running a single sweep with \-s option, first reset printers to +original types before exiting. +No effect unless \-s also specified. +Use for testing. +To make a normal +.I lwrename +background daemon reset printers and exit, send it the HUP signal. +.TP +.I lwrenamefile +Pathname of the file containing the list of printers to be monitored +(see format below). +Default=LWRENAMEFILE compile-time flag or "/etc/lwrename.list" if no +compile-time flag. +.SH DESCRIPTION +.I lwrename +is part of the CAP software for AppleTalk network access from +UNIX systems. +It is used by a UNIX host to "capture" LaserWriter (or equivalent +PostScript) printers on the network so that they are not directly +visible to Macintosh or other computers on the network. +Then an +.I lwsrv +daemon on the UNIX host can advertise the printer and create a spooler +for it. +This is particularly useful if you are using one of the authentication +options in +.I lwsrv +to restrict access to the printer or provide accounting by user name. +To capture the printer, you must send the appropriate PostScript code +to it to change its network type value to something other than +"LaserWriter". +Then it cannot be seen by the Macintosh, at least not with standard +printer drivers. +This is no protection against hackers who know how to modify the +Macintosh driver with ResEdit, etc. +.PP +Unfortunately, many printers store changes to their network type +in ordinary RAM that gets reset back to the default "LaserWriter" after +every power cycle. +For these printers, sending the appropriate PostScript code once is +not good enough. +You have to keep checking to see if the printer has reverted to its +default type and then rename it again. +.PP +.I lwrename +runs as a daemon process to automate this checking and renaming for you. +It periodically queries the AppleTalk network looking for any of a set +of PostScript printers listed in a file. +Whenever it finds one of these printers under its original +AppleTalk type of "LaserWriter" (or other type specified in the +file), it sends the appropriate PostScript code to rename the printer +to a new type that you specify in the file (or a default type +compiled into the program -- see below). +This way, after a printer power cycle resets the AppleTalk type +back to the default, +.I lwrename +will quickly find it and reset it to the "captured" value. +.PP +The list of printers to be monitored is stored in a file. +The pathname of this file can be given as an optional command line +argument; if no argument is given, the compile-time option LWRENAMEFILE +will be used as the pathname, or the name "/etc/lwrename.list" if that +option was not specified at compile time. +Comment lines are allowed in this file; start them with the # character. +One line defines each printer; tab characters are used to parse the +line into three fields. The third field is optional; if you omit it, +be sure also to omit the tab character before it. The format for +each printer description line is: +.br + passwordprinter_NBP_namenewtype +.br +Password is an integer value, either the factory default of 0, or a +value you have previously set by another means. +Printer_NBP_name is the full NBP name needed to find the printer on the +network, with its original (default) type, in this format: +.br + name:type@zone +.br +Use * for the current or default zone. +Newtype is an optional third field specifying the new AppleTalk type to +be used when renaming the printer. If you omit this field, be sure +to omit the tab before it. +WARNING: any trailing blanks on the line after "newtype" +will be interpreted as part of the type name! +If newtype is not specified for a particular printer, it reverts to the +compile-time option LWRENAMETYPE, or to "LaserShared" if that option +was not specified at compile time. +.PP +When first started, +.I lwrename +will automatically fork itself into the background, unless the +"\-s" flag is specified (for testing). +It will sleep between each network "sweep" for the number of +minutes specified in the optional "\-t" argument, or for two minutes +if no argument is given. +It runs forever until killed, unless you specify the "\-s" flag +to run one sweep only for testing. +If you send it the HUP signal (or if you specify the "\-r" flag with +the "\-s" flag), it will first restore the original type to all of the +printers it is monitoring before exiting. +Any other kill signal just kills it without resetting any printer types. +.PP +.I lwrename +does not need to run from the root account, although you may +want to so restrict it to prevent private users from battling to +control printers on the network. +If your AppleTalk access method requires special privilege (e.g., the +packetfilter under Ultrix), make sure +.I lwrename +executes under the appropriate user or group. +Also, if you have set printer passwords, you probably want to change +permissions on the printer list file to limit read access to the +root or manager account. +Generally, you would start +.I lwrename +from your +.I start\-cap\-servers +file. +.SH FILES +/etc/lwrename.list default location for list of monitored printers +.SH SEE ALSO +CAP(8), atlook(1), lwsrv(8) +.SH AUTHOR +Edward Moy, Workstation Software Support Group, Workstation Support Services, +Information Systems and Technology, University of California. +.PP +Revisions and manual page by Phil Farrell, Stanford University +School of Earth Sciences. +.PP +See source code for copyright and distribution restrictions. +.SH BUGS +There is no error checking for correct syntax in the list of printers, +except to verify that at least one tab is present to separate the +required first and second fields. +Be careful or you may get strange results. +.PP +There is no way to dynamically update the list of printers to be +monitored by +.I lwrename. +To change the printer list, kill +.I lwrename +with the HUP signal (to restore the current list of printers to +original type); edit the list; and then restart +.I lwrename. diff --git a/man/lwsrv.8 b/man/lwsrv.8 new file mode 100644 index 0000000..fac3999 --- /dev/null +++ b/man/lwsrv.8 @@ -0,0 +1,276 @@ +.TH lwsrv 8 "24 July 1990" "Columbia University" +.SH NAME +lwsrv \- simple LaserWriter spooling agent +.SH SYNOPSIS +.B lwsrv +.BI \-n " " +.BI \-p " " +.BI \-a " " +.BI \-f " " +[ +.BI \-e +] [ +.BI \-h +] [ +.BI \-k +] [ +.BI \-A " < on | off >" +] [ +.BI \-S +] [ +.BI \-T " < crtolf | quote8bit | makenondscconformant >" +] [ +.BI \-X " < directory name >" +] [ +.BI \-L +] [ +.BI \-N +] [ +.BI \-P +] +.SH DESCRIPTION +.I lwsrv +is a multi-threaded LaserWriter spooler (e.g. multiple incoming jobs +are allowed) that advertises itself on the AppleTalk internetwork and +accepts print jobs from Macintosh computers as if it were a real +LaserWriter. +.I lwsrv +allows these jobs to be queued for printing via the standard Berkeley +lpd spooling software (it is easily modified for other systems). +.I lwsrv +assumes that it is spooling for a particular type of LaserWriter printer as +defined by an input file that specifies the font coordination list. +.PP +A generic problem with +LaserWriter spoolers is that the proper Apple dictionary must be +downloaded to the LaserWriter. An Apple dictionary is the "prologue" +code termed a "Procedure Set" or "ProcSet", sent by the LaserWriter +driver on a Macintosh (contained in +LaserPrep), that defines a "set" of routines for use by Macintosh +applications. +.PP +.I lwsrv +attempts to resolve this problem by inserting the proper dictionary in the +incoming jobs before spooling them. +Unfortunately, +.I lwsrv +does not know about these ProcSets a priori. They must be supplied +to it, though it does attempt to retrieve them from the client. +.PP +.I lwsrv +will be able to record an unknown ProcSet if it follows Version 2 of the +.I +Adobe Systems PostScript Document Structuring Conventions (DSC). +Any Apple LaserPrep of +version 4.0 or higher should do this. (Warning: the ProcSet as +uploaded is not usable unless the "-e" flag is used, cf. Installation +instructions). +.PP +With System 7.0, the handling of ProcSets changes somewhat. If you wish to +use lwsrv with System 7.0 Printer Drivers, you should run lwsrv with the -N +option. This causes lwsrv to not capture new ProcSets but to pass them +through unedited. If edited ProcSets for earlier versions of the Printer Drivers +are found, they will used where appropriate (edited System 7.0 Procsets from +lwsrv sessions without -N should be removed). The result is that lwsrv can +be used successfully in a mixed driver environment without the necessity of +having the printer reinitialized each time. +.PP +.I lwsrv +is normally started automatically at boot time from /etc/rc.local. +Unless debug flags are set, it will automatically put itself into +the background to run as a daemon. +.SH REQUIRED ARGUMENTS +.TP 10 +.BI \-n " " +is used to specify the printer name that will be registered (e.g. that +will show up in Chooser). It may be up to 31 characters long. +.TP 10 +.BI \-p " " +specifies the name of the Unix printer queue to which lwsrv will send the +spooled print requests. should exist in +/etc/printcap. Multiple pairs of \-n and \-p flags can be used to provide +multiple printer queues per lwsrv process. +.TP 10 +.BI \-a " " +specifies the name of the directory that will hold the various +Procedure Sets (of which an AppleDict in a LaserPrep is a particular +instance). The first line of a ProcSet must be +"%%BeginProcSet: ". In addition, +unknown ProcSets will be recorded in this directory if possible. +Received ProcSets will probably require some editing. +.TP 10 +.BI \-f " " +specifies the file that contains a Font Coordination List. It +essentially tells the Macintosh or other devices spooling to +.I lwsrv +what fonts exist on the actual print device. +Sample FontFiles are included with the source distribution. +.SH OPTIONS +.TP 10 +.B \-e +tells lwsrv that it should allow an "eexec" to occur in a +ProcSet. This option may cause problems! See the lwsrv +source distribution README file +for more information. +.TP 10 +.B \-h +means to suppress the printing of the burst or banner page +by passing the "-h" option to +.I lpr +when printing the spooled file. +.TP 10 +.BI \-k +specifies that DDP checksums are not to be used, the field is set to zero. +.TP 10 +.BI \-C " " +specifies an alternate print spooler, the default is normally "/usr/ucb/lpr" +or "/usr/bin/lp". +.TP 10 +.BI \-A " <" "on | off" > +defines whether lwsrv should suppose that all client programs speaking +to it will properly follow the Adobe Document Structuring Convention +version 2.0. For now, all it really does is define where the +procedure set definitions are loaded. Turning this on, when there is +a client program that doesn't quite follow the conventions, may cause +the procedure set not to be loaded or to be loaded in the wrong place. +(Specifically, it looks for an IncludeProcSet instruction). +The default value is set at installation time (see LOCAL CONFIGURATION +section, below). +.TP 10 +.B \-S +tells lwsrv that it should run in a single fork. Normally, lwsrv +forks off a child to deal with each print request to minimize +contention. Unfortunately, status information returned to the client +programs is not as detailed when lwsrv is running multiforking. +.TP 10 +.BI \-N +tells lwsrv to not collect new ProcSets. This is required for use with +System 7.0 (see above). +.TP 10 +.BI \-P +specifies that no Adobe pre-processing be carried out. Use this option on a +print spooler dedicated to PC printing. +.TP 10 +.BI \-T " <" "crtolf | quote8bit | makenondscconformant" > +are TransScript compatibility options. Since the names are so long, +spaces, hyphens (-), underscores (_), and tabs are allowed to +enhance readability, for example: +.nf + -T "cr to lf" -T "quote 8-bit" +.fi +.RS 10 +.TP 10 +.B quote8bit +is used to quote 8 +bit characters since TransScript masks characters to 7 bits. Warning: +this could potentially cause problems if the PostScript interpreter +does not decode the quoted characters in some circumstances. +.TP 10 +.B crtolf +is used to translate the Macintosh end of line terminator CR (carriage +return) to the UNIX end of line terminator LF (line feed). This is +necessary if the spooled file will be run through TranScript filters +like +.I psrv +or +.I psrev +that assume the UNIX end of line terminator. Warning: this can +potentially cause problems with binary data streams. +.TP 10 +.B makenondscconformant +should be used when +.I lwsrv +is spooling to a UNIX printer queue that filters files through +.I psrv +(a page reversal filter). +This is standard for serial +TranScript printers and may have been configured that way for +.I papif. +This option causes the printed file to be prepended with a line +containing "%!" so that it looks like it does not conform to the DSC. +The problem is that in version 2.0 of TranScript +.I psrv +leaves out part of the file when handling DSC version 2.0 documents. +.RE +.TP 10 +.BI \-X " " +(must be specifically included with -DLWSRV_AUFS_SECURITY at compile time) +is used to allow access control such that only those users who have mounted an +.I aufs +volume will be able to print via the spooler. +The directory specified will be used to access temporary information +stored there by +.I aufs. +That information is used by lwsrv to control who has permission to access +the printer being spooled for. The Berkeley +.I lpr +program is invoked as the user that was specified when logging in through +.I aufs. +.I Aufs +must also be started with the +.BI \-X +option, and the same directory, otherwise no one will have access to the spooler. +Generally, /tmp is the directory used. See the +.I aufs (8) +man page for the setup requirements for the directory if you are not using /tmp. +.TP 10 +.BI \-L +passes directly to lpr. Typically used to specify the \-l option +to allow gray scale PostScript to be printed. Note: there is no space +between the -L and the lpr argument. +.SH DEBUGGING OPTIONS +.TP 10 +.B \-r +keeps the file spooled from the Macintosh for inspection +instead of removing it after printing. +.TP 10 +.BI \-t " " +is used to record all data received from the remote side in +. The remote side is also forced to send any ProcSets +used. The result is not printed. +.TP 10 +.BI \-l " " +is used to specify an activity log file. +.TP 10 +.BI \-d " " +can be used to specify standard cap library debugging flags. See +CAP(3) for valid flags. +.SH EXAMPLE +For example, to publish a LaserWriter spooler "Laser" that prints on the UNIX +printer "ps" with Procedure sets recorded in /usr/lib/adicts and using +the font coordination list /usr/lib/LW+Fonts, you would start +.I lwsrv +as follows: +.nf +.I + lwsrv -n Laser -p ps -a /usr/lib/adicts -f /usr/lib/LW+Fonts +.fi +.SH LOCAL CONFIGURATION + +.SH NOTES +The Apple LaserPrep ProcSets (dictionaries) are not supplied +in the CAP distribution +as they are copyrighted property of Apple Computer. +.PP +There are a number of "font list files" that are used in the dialog +with the spooling client. These tell the client (Macintosh) what +fonts are available on the "supposed" printer. +The laser font files supplied may not work for all LaserPreps. They +are known to work for LaserPreps 3.0 through 4.0. +.SH BUGS +.I lwsrv +cannot properly update the status message. +When printing from a +Macintosh, you will see the message "Starting job" until it completes. +.SH FILES +Sample font list files in the CAP distribution: +.nf +.ta \w'extras/LWPlusFonts 'u +extras/LWFonts fontfile list for LaserWriter +extras/LWPlusFonts fontfile list for LaserWriter Plus +.fi +.SH AUTHOR +lwsrv was written by Bill Schilit, Computer Science Department, +and Charlie C. Kim, User Services, Columbia University +.SH SEE ALSO +atis(8), CAP(3), CAP(8), lpr(1), papif(8) diff --git a/man/makefile b/man/makefile new file mode 100644 index 0000000..1fac125 --- /dev/null +++ b/man/makefile @@ -0,0 +1,49 @@ +# +# CAP manual entries +# + +all: links txt + +links: + ln papif.8 papof.8 + ln atprint.1 tlw.1 + ln atprint.1 lwpr.1 + ln atprint.1 isrv.8 + ln atprint.1 iwpr.1 + ln CAP.3 libcap.3 + ln CAP.3 libafpc.3 + ln CAP.3 libafp.3 + ln cvt2apple.1 cvt2cap.1 + ln atlook.1 atpinger.1 + ln atlook.1 atlooklws.1 + +# +# make only one text copy of the source files +# +txt: + nroff -man CAP.3 > text/CAP.3 + nroff -man CAP.8 > text/CAP.8 + nroff -man UAB.8 > text/UAB.8 + nroff -man AUFS.1 > text/AUFS.1 + nroff -man AUFS.8 > text/AUFS.8 + nroff -man ash.1 > text/ash.1 + nroff -man atalk.local.5 > text/atalk.local.5 + nroff -man etalk.local.5 > text/etalk.local.5 + nroff -man atis.8 > text/atis.8 + nroff -man atlook.1 > text/atlook.1 + nroff -man atprint.1 > text/atprint.1 + nroff -man cvt2apple.1 > text/cvt2apple.1 + nroff -man getzones.1 > text/getzones.1 + nroff -man instappl.1 > text/instappl.1 + nroff -man lwsrv.8 > text/lwsrv.8 + nroff -man papif.8 > text/papif.8 + nroff -man snitch.1 > text/snitch.1 + nroff -man aarpd.8 > text/aarpd.8 + +clean: + rm -f papof.8 tlw.1 lwpr.1 isrv.8 iwpr.1 + rm -f libcap.3 libafpc.3 libafp.3 + rm -f cvt2cap.1 atpinger.1 atlooklws.1 text/* + +spotless: clean + rm -f *.orig diff --git a/man/papif.8 b/man/papif.8 new file mode 100644 index 0000000..be04ed1 --- /dev/null +++ b/man/papif.8 @@ -0,0 +1,429 @@ +.TH papif 8 "24 July 1990" "Columbia University" +.SH NAME +papif, papof \- printer system input and output filters for printing to AppleTalk LaserWriters +.SH DESCRIPTION +.I papif +and +.I papof +are input and output filters, respectively, for the Berkeley line +printer spooling system (see the section "Line Printer Spooling +System" below for definition of these terms and some explanation), +that print to a LaserWriter (or other PostScript printer) attached to +an AppleTalk network (see CAP(8)). +.I papif +is actually the "formatted text" input filter, but may also be used to +"backend" other input filters. +.I papof +simply creates a PostScript format banner page for use by +.I papif. +.PP +.I papif +has functionality similar to that of +.I pscomm +in Adobe's TranScript package. TranScript provides support for +spooling to a LaserWriter via a serial line. +.I papif +can be used in most +cases as a replacement for +.I pscomm +when a printer is attached to AppleTalk instead of a serial line. +.PP +When +.I papif +is used in conjunction with TranScript, two special TranScript filters +.I psrv +and +.I pstext +may be used to reverse the order in which pages are printed +and convert plain text files to PostScript, respectively. +If +.I pstext +is used +.I papif +decides to invoke pstext if the first two characters in the file are +not the PostScript magic sequence "%!". +If +.I psrev +is used, then the first 11 characters must be "%!PS-Adobe-". All +files conforming to the Adobe Document Structuring Convention +(required for +.I psrv +to work properly) must start with that sequence. +.I papif +will work properly even if the input is a pipe. +.PP +A particular printer is established as usable under the Berkeley line +printer spooling system by installing an entry in /etc/printcap (see +printcap(8)). Unfortunately, there is no printcap entry field that allows +one to easily specify the NBP entity name of the AppleTalk printer +(the name that shows up in Chooser). To bypass this problem, +.I papif +translates the UNIX printer name to the NBP entity name by the use of +a lookaside file called "cap.printers". The format of cap.printers is +described in the section "cap.printers format", below. +.PP +The Berkeley line printer daemon does not pass the UNIX printer queue +name as an argument to printing filters. +.I papif +needs to know +which UNIX printer queue it is servicing, so it can map to the NBP +entity name via cap.printers. +.I papif +will determine the printer queue name from any of these methods: +.RS 5 +.TP 5 +1) +It will use the value passed by a "-P" or "-p" command line option. +This can be arranged by creating a shell script to be specified in +the /etc/printcap file as the "if" filter. +This script would in turn just call +.I papif, +passing all the arguments it received from lpd, plus the "-P printer-name" +argument. +The shell script can either have a fixed value +for the "-P" switch or try to figure it out. +For example, lpd always +forks off the input filter in the spool directory of the printer, so +if the spool directory has a name matching the UNIX printer name, then +this value could be used - this is the method used by TranScript. +If the system already has TranScript or is licensed for TranScript, then +the shell scripts provided with TranScript are very good. +.TP 5 +2) +If argv[0] (the name it is invoked by on the command line) is neither +.I papif +nor +.I psif, +then that name will be used as the printer queue +name. +This can be arranged, for example, by renaming +.I papif +to the queue name, or making a hard or symbolic link with the queue name, +and then specifying that name as the "if" filter in the /etc/printcap file. +.TP 5 +3) +The PRINTER environment variable can be set to the queue name, for example, +by an "if" shell script filter. +.RE +If none of these methods is used to set the printer queue name, then +.I papif +will assume that the NBP entity name is "LaserWriter +Plus:LaserWriter@*" (LaserWriter named "LaserWriter Plus" in the local +zone). +See the section "Example printcap entries" for an example printcap entry. +.PP +If the printer name ends in -dup* (e.g. has a name such as lw-dup or +lj-duplex), and papif was compiled with the DUPLEXMODE option, papif will +assume that this printer has the capability to print on both sides of a +page. The printer is then directed (by the addition of some PostScript +code) to use this ability. +.SH ARGUMENTS +.PP +.I papif +accepts the following arguments, most of which are supplied by the +printer system daemon, lpd: +.TP 10 +.BI \-n " " +is a standard lpd argument specifying the user name. +.TP 10 +.BI \-h " " +is a standard lpd argument specifying the name of the host from which +the print job was submitted. +.TP 10 +.B \-c, \-x, \-y, \-i, \-w +are standard lpd arguments which are ignored. +.TP 10 +.B \-L +is a Rutgers added option to specify landscape mode printing. +.TP 10 +.BI \-P " " +is a TranScript compatible option that specifies the UNIX printer name. +.TP 10 +.BI \-2 +directs the printer to print this job in 2-sided mode. Only use this if +your printer has the hardware to support this. This switch only exists +if papif has been compiled with the DUPLEXMODE feature. +.TP 10 +.BI \-p " " +is a TranScript compatible option that specifies the program name. +.TP 10 +.B \-r +is a TranScript compatible option that says never to invoke the page +reversal filter. +.TP 10 +.BI \-k +specifies that DDP checksums are not to be used, the field is set to zero. +.TP 10 +.I +An argument without a leading hyphen (-) is assumed to be the +accounting file name. +.TP 10 +.BI \-d " " +is used to specify the +.B CAP +library debugging arguments. (No spaces allowed between the +.I \-d +and the flags). +See CAP(3) for valid flags. +.PP +In addition, a number of environment variables may be set to modify +.I papif +operating parameters. +Most of these can and should be customized when compiling +.I papif +(cf. installation instructions) rather than at runtime. +The LOCAL CONFIGURATION section, below, should specify the values +that have been compiled into +.I papif +at this site. +The first group are TranScript compatibility options (e.g. used by +TranScript). Most of these options are "binary" in that setting them +to 0 means off and non-zero means on. +.TP +.I BANNERFIRST +if set, says to print the banner first. +.TP +.I BANNERLAST +If set, says to printer the banner last. BANNERFIRST overrides +BANNERLAST. +.TP +.I CHARGEBANNER +is a papif specific binary option. If set, banner pages +will be charged to the user if accounting is being done. +.PP +Note if you wish to define banner pages as on, you must have an +"of" output filter defined in /etc/printcap that presents a ".banner" +file in PostScript format. +CAP includes a filter +.I papof +that accomplishes this. You should also +be able to use the standard TranScript filter for this as well. +.TP +.I VERBOSELOG +If set, tells papif to be a little more verbose in its logging. This +is the default. +.TP +.I REVERSE +a string containing the location of the page reversal filter. This is a +standard part of TranScript. +.TP +.I PSTEXT +a string containing the location of the text filter that translates plain +text to PostScript. This is a standard part of TranScript. +.PP +Here are a number of non-TranScript options settable in the environment. +.TP +.I ADOBE_STRUCTURED +PAPIF has a minimal understanding of the Adobe Systems Document +Structuring Conventions (to wit, it understands the "%%EOF" marker). +Setting ADOBE_STRUCTURED to be true implies that it should try to +interpret the incoming document. +.TP +.I IDLESTUFF +Some sites have reported seeing an "idle" bug where papif doesn't do +anything while the LaserWriter is idle. You can define "IDLESTUFF" +when compiling papif to enable some code to get around this problem. +Note: with the new release of papif, this problem shouldn't be there +anymore, but it is included just in case. +.TP +.I SFLOWQ, RFLOWQ +.I papif +has a settable "Send Flow Quantum multiplier" called SFLOWQ that +tells how many packets it should be sending in a single write. This +should be set to 1 (maximum 8) if there are problems with the FastPath or +LaserWriter dropping packets. +RFLOWQ is the same thing but in the other direction. You can probably +leave it set to 8 (the maximum). +.TP +.I ATPRESPONSETIMEOUT +specifies the ATP response cache timeout in +ticks (1/4 second intervals). (Note: zero is infinity: it never times +out). This is used to extend the time that a response from the +LaserWriter may take. +.TP +.I WATCHTIME +specifies the minimum interval in seconds that +will take place +before +.I papif +polls the LaserWriter for its status (and updates the +status file in the spool directory). Zero implies that no status +watching should be done. +.TP +.I DEBUG +if set, tells +.I papif +that is should print out debugging information giving the state of the +environment variables, etc. +.TP +.I STRIPCONTROLD +if set, tells +.I papif +that it should map control-d's to a line feed. Danger: this may cause +problems with binary files. +.TP +.I MAPCRTOLF +if set, tells +.I papif +that it should map carriage returns to line feeds. This option is +useful when printing Macintosh files. (It is preferrable to make +.I lwsrv +do this mapping if possible). Danger: this may cause +problems with binary files. +.TP +.I CAPPRINTERS +a string containing the name of +an alternate cap.printers file for mapping printer queue name +to LaserWriter NBP network name. +.TP +.I JOBOUTPUT +a string containing the name of a file to get a copy of the +.I papif +logging messages. This is useful when using +.I lp +and the TranScript interfaces under a System V style machine. +.TP +.I BANNER +a string containing the name of a banner file other than ".banner". +This is useful when using +.I lp +and the TranScript interfaces under a System V style machine. +.TP +.I DOACCT +if set, turns on page accounting. No accounting will be +done even if set unless an accounting file is specified as an +argument to papif (e.g., the "af" field is set in /etc/printcap). +.TP +.I PSJOBHEADER +if set, specifies a file that contains PostScript code to be sent to +the printer before the print job. Uses include manipulating printer +features such as duplex mode, paper tray control and so on (this +option must be specified at compile time by enabling the PSJOBHEADER +option at Configure time). +.SH Line Printer Spooling System +Technically, an output filter is used to massage output from a filter. +However, +.I papof +only accepts the banner from lpd and converts it to PostScript for use +by the input filter. +.PP +An input filter takes certain types of input and has the +responsibility for actually sending it to the printer. Common +types of input are formatted text ("if"), graphics (plot(3x)) input ("gf"), +etc. +.I papif +was originally designed as the "if" or formatted text +input filter and hence its name. It can also be used in combination +with other programs, such as those provided with Adobe's TranScript +package that take a particular type of input and convert them into +PostScript format by simply piping the output of those "filters" into +.I papif. +In other words, +.I papif +can be used as the "active" backend +for various filters. Its role is equivalent to that of +.I pscomm +in the TranScript package. +.SH "Example printcap entries" +A printcap entry for a system without TranScript would look something +like: +.sp +.nf + ps:LaserWriter:A sample LaserWriter printer:\\ # printer name + :lp=/dev/ps:\\ # spool device + :sd=/usr/spool/lpd/ps:\\ # spool directory + :pl#72:pw#85:\\ # page length and width + :sf:\\ # suppress form feeds + :lf=/usr/adm/ps-errs:\\ # log file + :af=/usr/adm/ps.acct:\\ # accounting file + :if=/usr/local/lib/cap/ps:\\ # input filter + :of=/usr/local/lib/cap/papof: # output filter +.fi +.sp +The spool device (:lp=) is not used, but +some systems may do locking on it, so it may need to be +unique. Just create a unique null file. +"/usr/local/lib/cap/ps" could be a copy of papif +compiled with the defaults required or it could be a shell script as such: +.sp +.nf + #!/bin/sh + BANNERLAST=1 + PSTEXT=/usr/local/lib/ps/pstext + export BANNERLAST PSTEXT + # pass the printer name and the arguments lpd passed us + /usr/local/cap/papif -P ps $* +.fi +.sp +.I papof +is the supplied output filter. +The accounting file and log file must be created by hand if +accounting or logging is desired. The page length and width are +probably only +required if pstext is used. +.PP +A TranScript printer entry should be installed with the aid of +TranScript. One point to be careful of is that TranScript's "psof" +filter assumes that "sb" (or short banner) has been defined. +.I papof +works with "sb" on, but is designed for use without "sb" defined. +.SH "cap.printers format" +Cap.printers is used to map UNIX printer queue names to LaserWriter +NBP network names. It consists of a series of lines, each specifying +a single mapping. Multiple lines may map the various aliases of a +UNIX printer queue to the same NBP name. Lines beginning with the +pound sign (#) are comment lines. +.PP +Each mapping line contains the "UNIX printer name" without spaces followed +by an "=" character and the exact NBP entity name. There can be no +extraneous spaces! Please note that due to a bug in the Apple "Namer" +program, under certain conditions, a blank space may be added to the +end of the name you set for the LaserWriter with that program. +If that happens, this extra blank space must be included in the +cap.printers entry (just before the ":"). It is best to use the CAP +.I +atlook +program to confirm the exact printer NBP entity name. +For example: +.nf + ps=ALaserWriter:LaserWriter@SomeZone +.fi + +See the sample cap.printers file +in the +.I papif +distribution directory for more examples. +.SH LP +.I papif +should work with the appropriate shell script surrounding it under the +UNIX System V "lp" spooling system. In particular, it +is known to work under +Release 1 of +.I A/UX +from Apple Computer. See the README file in the +.I papif +source directory for more information. +.SH LOCAL CONFIGURATION + +.SH NOTES +.I papif +TranScript is available under license from Adobe Systems Incorporated. +.SH BUGS +.I papif +has changed considerably since the idle code was added and the idle code +may no longer work properly. +.SH FILES +.nf +.ta \w'/etc/cap.printers 'u +/etc/printcap +/etc/cap.printers (location is settable) +\&.status lpd status file +newstatus papif status temporary file +\&.banner lpd banner file +.fi +.SH AUTHOR +Charlie C. Kim, User Services Group, Center for Computing Activities, +Columbia University +.SH "SEE ALSO" +CAP(3), CAP(8), printcap(8), lpd(8), lwsrv(8), atlook(1), transcript(8) + diff --git a/man/snitch.1 b/man/snitch.1 new file mode 100644 index 0000000..4539869 --- /dev/null +++ b/man/snitch.1 @@ -0,0 +1,117 @@ +.TH SNITCH 1 "21 Mar 1988" "Carnegie-Mellon University" +.SH NAME +snitch \- respond to AppleTalk Inter\(buPoll requests +.SH SYNOPSIS +.B snitch +[ +.BI \-d " " +] [ +.BI \-n " " +] [ +.BI \-t " " +] [ +.BI \-f " " +] [ +.BI \-l " " +] [ +.BI \-D n +] [ +.B \-S +] +.SH DESCRIPTION +.I snitch +is a CAP program designed to run as a daemon to respond to Inter\(buPoll +requests. +It performs the same function as the "Responder" program on a Macintosh. +It registers itself as "name:type@*" via NBP, where 'name' defaults +to the host name (without the domain) and 'type' defaults to "UNIX/CAP". +You can override the default NBP name values with the +.I \-n +and +.I \-t +options. +The response packets that are sent back to Inter\(buPoll try to have useful +information about the machine you are running on and the version of CAP. +You can override this information with the +.I \-f +and +.I \-l +options. +.PP +The kinds of packets that +.I snitch +sends back are only meaningful to +Inter\(buPoll, at least for now. Also, the packet format is not documented by +Apple, so it currently is an educated guess. +.PP +To kill a +.I snitch +process cleanly, send the process a QUIT or TERM signal. +It will nbp_delete itself (as all nbp services should). +.PP +To really make this work with Inter\(buPoll, you want to modify your copy +of Inter\(buPoll on the Macintosh (with ResEdit or similar program) to +add the string "UNIX/CAP" (or your snitchtype) to the STR# resource +that specifies the kinds of expected machine types. +The first item in that STR# is a number that +indicates the number of following valid entries - add one to it and then +add "UNIX/CAP" at the end. The string list is STR# "NIP Devices". +.SH OPTIONS +.TP 10 +.BI \-d " " +specifies standard CAP debugging flags. See cap(3) for a list of valid +flag values. +.TP 10 +.BI \-n " " +specifies the NBP entity name to be used when registering +.I snitch +on the AppleTalk network. The default value is the UNIX machine host +name without the domain qualifiers. +.TP 10 +.BI \-t " " +specifies the NBP entity type to be used when registering +.I snitch +on the AppleTalk network. The default value is "UNIX/CAP". +.TP 10 +.BI \-f " " + is a character string +that will be passed to Inter\(buPoll +to be displayed as the "Finder" version for the UNIX host. +You might use this to specify UNIX and CAP versions, for example. +The string must be no more than 100 characters long (actually, Inter\(buPoll +will only display about 60 characters) and enclosed in quotes if it contains +embedded blanks or special characters. +.TP 10 +.BI \-l " " +like the +.I \-f +option, but this string will be displayed by Inter\(buPoll as the +"LaserWriter" driver version. +.TP 10 +.BI \-D n +Set +.I snitch +debugging level to +.I n. +Valid values are 1 for basic debugging messages and 2 for additional +dumps of user data. Writes to stderr. +.TP 10 +.BI \-S +Tells +.I snitch +to disassociate from the terminal (like starting +.I snitch +in the background). +.SH FILES +Uses the cap file "atalk.local", usually in /etc. +.SH "SEE ALSO" +CAP(8), atis(8), cap(3) +.SH EXAMPLE +snitch -n "myCapMachine" -S +.SH DIAGNOSTICS +Are written to stderr if any debugging switches are on. +If you use the "-S" switch, you won't see anything. +.SH BUGS +Only works with Inter\(buPoll by chance, might not work with future versions. +.SH AUTHOR +Ravinder Chandhok, Carnegie-Mellon University diff --git a/netat/Makefile b/netat/Makefile new file mode 100644 index 0000000..636085c --- /dev/null +++ b/netat/Makefile @@ -0,0 +1,11 @@ +HFILES = aberrors.h abqueue.h appletalk.h afp.h afpcmd.h afpc.h \ + compat.h sysvcompat.h macfile.h abnbp.h fcntldomv.h + +install: $(HFILES) + -mkdir /usr/include/netat + cp $(HFILES) /usr/include/netat + +dist: + @cat todist + +clean: diff --git a/netat/Makefile.m4 b/netat/Makefile.m4 new file mode 100644 index 0000000..ccb39a0 --- /dev/null +++ b/netat/Makefile.m4 @@ -0,0 +1,15 @@ +I=includedir() +HFILES = aberrors.h abqueue.h appletalk.h afp.h afpcmd.h afpc.h \ + compat.h sysvcompat.h macfile.h abnbp.h fcntldomv.h afppass.h + +install: $(HFILES) + -mkdir $I/netat + cp $(HFILES) $I/netat + +dist: + @cat todist + +clean: + +spotless: + -rm -f *.orig Makefile makefile diff --git a/netat/aberrors.h b/netat/aberrors.h new file mode 100644 index 0000000..a03de6d --- /dev/null +++ b/netat/aberrors.h @@ -0,0 +1,83 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:58:28 $ + * $Header: aberrors.h,v 2.1 91/02/15 22:58:28 djh Rel $ + * $Revision: 2.1 $ + * +*/ + +/* + * aberrors.h - AppleTalk Errors + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 30, 1986 Schilit Created. + * + */ + +/* Error codes */ + +#define noErr 0 + +#define ddpSktErr -91 /* error in socket number */ +#define ddpLenErr -92 /* data length too big */ +#define noBridgeErr -93 /* no net bridge for non-local send */ +#define lapProtErr -94 /* error in attach/detach */ +#define excessCollsns -95 /* excessive collisions on write */ +#define portInUse -97 /* driver Open error */ +#define portNotCf -98 /* driver Open error */ + +/* ATP error codes */ + +#define reqFailed -1096 /* SendRequest failed: retrys exceeded */ +#define tooManyReqs -1097 /* Too many concurrent requests */ +#define tooManySkts -1098 /* Socket table full */ +#define badATPSkt -1099 /* bad ATP responding socket */ +#define badBuffNum -1100 /* Bad sequence number */ +#define noRelErr -1101 /* no release received */ +#define cbNotFound -1102 /* Control block not found */ +#define noSendResp -1103 /* atpaddrsp issued before sndresp */ +#define noDataArea -1104 /* no data area for MPP request */ +#define reqAborted -1105 /* SendRequest aborted by RelTCB */ + +/* NBP error codes */ + +#define nbpBuffOvr -1024 /* buffer overflow in LookupName */ +#define nbpNoConfirm -1025 /* name not confirmed on ConfirmName */ +#define nbpConfDiff -1026 /* name confirmed at different socket */ +#define nbpDuplicate -1027 /* duplicate name already exists */ +#define nbpNotFound -1028 /* name not found on remove */ +#define nbpNISErr -1029 /* error trying to open the NIS */ + +#define buf2SmallErr -3101 +#define noMPPErr -3102 +#define ckSumErr -3103 +#define extractErr -3104 +#define readQErr -3105 +#define atpLenErr -3106 +#define atpBadRsp -3107 +#define recNotFnd -3108 +#define sktClosed -3109 + +/* ASP Errors */ +/* errors, don't really belong here, but... */ +/* -1060 thru -1065 belong to US!!! yeah! */ +#define BadReqRcvd -1060 /* improper request recv'ed */ +#define noATPResource -1061 /* problems with atp resource */ +#define aspFault -1062 /* internal error */ +#define BadVersNum -1066 +#define BufTooSmall -1067 +#define NoMoreSessions -1068 +#define NoServers -1069 +#define ParamErr -1070 +#define ServerBusy -1071 +#define SessClosed -1072 +#define SizeErr -1073 +#define TooManyClients -1074 +#define NoAck -1075 + diff --git a/netat/abnbp.h b/netat/abnbp.h new file mode 100644 index 0000000..1b3b1f7 --- /dev/null +++ b/netat/abnbp.h @@ -0,0 +1,131 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:58:34 $ + * $Header: abnbp.h,v 2.1 91/02/15 22:58:34 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * abnbp.h - Name Binding Protocol definitions + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * April 1, 1976 CCKIM Created + * +*/ + +/* PATCH: mips.ultrix.byteswap, djh@munnari.OZ.AU, 12/11/90 */ +/* PATCH: XENIX/file.3, djh@munnari.OZ.AU, 20/11/90 */ + +/* NBP Information Socket - for NBP server */ +#define nbpNIS 2 + +#define nbpBrRq 1 +#define nbpLkUp 2 +#define nbpLkUpReply 3 +#define nbpRegister 0x7 /* register a name */ +#define nbpDelete 0x8 /* delete a name */ +#define nbpTickle 0x9 /* let them know socket is alive and well */ +#define nbpStatusReply 0xa /* status on register/delete */ +#define nbpLookAside 0xb /* Lookup via NIS */ +/* maximum is 0xf */ + + +/* status reply codes - all max out at 0xf - zero is reserved for noErr */ + +#define nbpSR_noErr 0x0 + +/* status reply codes for register */ +#define nbpSR_access 0x1 /* permission denied */ +#define nbpSR_overflow 0x2 /* name server overflow */ + +/* status reply codes for delete */ +/* - uses nbpSR_access */ +#define nbpSR_nsn 0x2 /* no such name */ + + +#define nbpTupleMax 15 +/* 3 for field lengths + 3*32 for the three names */ +#define MAX_TUPLE_SIZE 99 + +/* control + id + Addrblock + enumerator */ +#define nbpBaseSize (1+1+1+sizeof(AddrBlock)) +/* nbpBaseSize + 3 for the three object lengths */ +#define nbpMinSize (nbpBaseSize + 3) + +typedef struct { + AddrBlock addr; /* address */ + byte enume; /* enumerator */ + byte name[MAX_TUPLE_SIZE]; /* the entity name */ +} NBPTuple; + +/* following would be really simple if we could use #if, but vms */ +/* c doesn't like and hopefully someday... */ +#ifndef BYTESWAPPED +#ifdef ultrix +# ifdef mips +# define BYTESWAPPED +# endif +#endif +# ifdef vax +# define BYTESWAPPED +# endif +#endif +#ifndef BYTESWAPPED +# ifdef ns16000 +# define BYTESWAPPED +# endif +#endif +#ifndef BYTESWAPPED +# ifdef ns32000 +# define BYTESWAPPED +# endif +#endif +/* add this in case sequent doesn't define nsxxxxx */ +#ifndef BYTESWAPPED +# ifdef sequent +# define BYTESWAPPED +# endif +#endif +#ifndef BYTESWAPPED +# ifdef MIPSEL +# define BYTESWAPPED +# endif +#endif +#ifndef BYTESWAPPED +# ifdef i386 +# define BYTESWAPPED +# endif +#endif + + +typedef struct { +#ifdef BYTESWAPPED + /* then these structure elements need to be reversed */ + /* because the machines are byteswapped */ +#ifdef AIX + unsigned tcnt : 4, /* tuple count */ +#else AIX + byte tcnt : 4, /* tuple count */ +#endif AIX + control : 4; /* control */ +#else BYTESWAPPED +#ifdef AIX + unsigned control : 4, /* control */ +#else AIX + byte control : 4, /* control */ +#endif AIX + tcnt : 4; /* tuple count */ +#endif BYTESWAPPED + byte id; /* NBP identifier */ + NBPTuple tuple[nbpTupleMax]; /* start of first tuple, */ +} NBP; /* space for rest */ + +typedef struct { + byte lapddp[lapSize+ddpSize]; + NBP nbp; +} NBPpkt; diff --git a/netat/abqueue.h b/netat/abqueue.h new file mode 100644 index 0000000..de4cd13 --- /dev/null +++ b/netat/abqueue.h @@ -0,0 +1,33 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:58:37 $ + * $Header: abqueue.h,v 2.1 91/02/15 22:58:37 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * abqueue.h - header file for abqueue and abqueue users + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * July 1, 1986 Schilit Created + * + */ + +typedef struct qelem { + struct qelem *q_forw; + struct qelem *q_back; +} QElem, *QElemPtr, **QHead; + +#define NILQ (QElemPtr) 0 +#define NILQHEAD (QHead) 0 + +void q_head(); +void q_tail(); +QElemPtr dq_head(),dq_tail(); +QElemPtr q_next(),q_prev(); + diff --git a/netat/afp.h b/netat/afp.h new file mode 100644 index 0000000..a92ed54 --- /dev/null +++ b/netat/afp.h @@ -0,0 +1,287 @@ +/* + * $Author: djh $ $Date: 1996/04/25 01:06:27 $ + * $Header: /mac/src/cap60/netat/RCS/afp.h,v 2.5 1996/04/25 01:06:27 djh Rel djh $ + * $Revision: 2.5 $ + * + */ + +/* + * afp.h - header file for AppleTalk Filing Protocol + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March, 1987 Schilit Created + * + */ + +#ifndef _MACFILE +#include +#endif _MACFILE + +/* AFP Errors. The little "ae" prevents conflicts with other codes */ + +#define aeAccessDenied -5000 +#define aeAuthContinue -5001 /* Authorization not yet complete */ +#define aeBadUAM -5002 /* Unknown User Auth Method */ +#define aeBadVersNum -5003 /* Server cannot speak AFP version */ +#define aeBitMapErr -5004 +#define aeCantMove -5005 +#define aeDenyConflict -5006 +#define aeDirNotEmpty -5007 +#define aeDiskFull -5008 +#define aeEOFErr -5009 +#define aeFileBusy -5010 +#define aeFlatVol -5011 +#define aeItemNotFound -5012 +#define aeLockErr -5013 +#define aeMiscErr -5014 +#define aeNoMoreLocks -5015 +#define aeNoServer -5016 /* Server not responding */ +#define aeObjectExists -5017 +#define aeObjectNotFound -5018 +#define aeParamErr -5019 +#define aeRangeNotLocked -5020 +#define aeRangeOverlap -5021 +#define aeSessClosed -5022 /* Sessions was closed, no response */ +#define aeUserNotAuth -5023 /* User authorization failure */ +#define aeCallNotSupported -5024 +#define aeObjectTypeErr -5025 +#define aeTooManyFilesOpen -5026 +#define aeServerGoingDown -5027 +#define aeCantRename -5028 +#define aeDirNotFound -5029 +#define aeIconTypeError -5030 +#define aeVolumeLocked -5031 /* AFP2.0 */ +#define aeObjectLocked -5032 /* AFP2.0 */ +#define aeIDNotFound -5034 /* AFP2.1 */ +#define aeIDExists -5035 /* AFP2.1 */ +#define aeCatalogChanged -5037 /* AFP2.1 */ +#define aeSameObjectErr -5038 /* AFP2.1 */ +#define aeBadIDErr -5039 /* AFP2.1 */ +#define aePwdSameErr -5040 /* AFP2.1 */ +#define aePwdTooShort -5041 /* AFP2.1 */ +#define aePwdExpired -5042 /* AFP2.1 */ +#define aeInsideSharedErr -5043 /* AFP2.1 */ +#define aeInsideTrashErr -5044 /* AFP2.1 */ + +/* AFP Commands Definitions */ + +#define AFPByteRangeLock 1 /* Lock a range of bytes in a file */ +#define AFPCloseVol 2 /* Close a volume */ +#define AFPCloseDir 3 /* Close a directory */ +#define AFPCloseFork 4 /* Close a fork */ +#define AFPCopyFile 5 /* Copy a file */ +#define AFPCreateDir 6 /* Create a directory */ +#define AFPCreateFile 7 /* Create a file */ +#define AFPDelete 8 /* Delete a file or directory */ +#define AFPEnumerate 9 /* Enumerate directory entries */ +#define AFPFlush 10 /* Flush a volume */ +#define AFPFlushFork 11 /* Flush a fork */ +#define AFPGetForkParms 14 /* Get fork parameters */ +#define AFPGetSrvrInfo 15 /* Get server info */ +#define AFPGetSrvrParms 16 /* Get server parameters */ +#define AFPGetVolParms 17 /* Get volume parameters */ +#define AFPLogin 18 /* Login to the server */ +#define AFPLoginCont 19 /* Continue a login sequence */ +#define AFPLogout 20 /* Logout (FPLogout) */ +#define AFPMapID 21 /* Map a protection ID to: */ +#define MapID_C 1 /* creater name */ +#define MapID_G 2 /* group name */ +#define AFPMapName 22 /* Map a protection name to: */ +#define MapName_C 3 /* creator ID (uid) */ +#define MapName_G 4 /* group ID (gid) */ +#define AFPMove 23 /* Move a file */ +#define AFPOpenVol 24 /* Open a volume */ +#define AFPOpenDir 25 /* Open a directory */ +#define AFPOpenFork 26 /* Open a fork */ +#define AFPRead 27 /* Read from a fork */ +#define AFPRename 28 /* Rename a file or directory */ +#define AFPSetDirParms 29 /* Set directory parameters */ +#define AFPSetFileParms 30 /* Set file parameters */ +#define AFPSetForkParms 31 /* Set fork parameters */ +#define AFPSetVolParms 32 /* Set volume parameters */ +#define AFPWrite 33 /* Write to a fork */ +#define AFPGetFileDirParms 34 /* Get params for a file or directory */ +#define AFPSetFileDirParms 35 /* Set params for a file or directory */ +#define AFPChgPasswd 36 /* AFP2.0: Change Password */ +#define AFPGetUserInfo 37 /* AFP2.0: Get User Information */ +#define AFPGetSrvrMsg 38 /* AFP2.1: Get Server Message */ +#define AFPCreateID 39 /* AFP2.1: Create a File ID */ +#define AFPDeleteID 40 /* AFP2.1: Invalidate a File ID */ +#define AFPResolveID 41 /* AFP2.1: Get params for a file by File ID */ +#define AFPExchangeFiles 42 /* AFP2.1: Exchange 2 files Data/Resource */ +#define AFPCatSearch 43 /* AFP2.1: Search volume for files */ +#define AFPOpenDT 48 /* Open the volume's desktop database */ +#define AFPCloseDT 49 /* Close the volume's desktop database */ +#define AFPGetIcon 51 /* Get an icon from the dt database */ +#define AFPGetIconInfo 52 /* Get icon info from the dt database */ +#define AFPAddAPPL 53 /* Find an application from the dt database */ +#define AFPRmvAPPL 54 /* Remove an application from the dt ... */ +#define AFPGetAPPL 55 /* Get an application ... */ +#define AFPAddComment 56 /* Add a comment to the dt */ +#define AFPRmvComment 57 /* Remove a comment from the dt */ +#define AFPGetComment 58 /* Get a comment from the dt */ +#define AFPAddIcon 192 /* Add an icon to the dt */ +#define AFPMaxCmd AFPAddIcon +#define AFPShutDown 0xffff /* Shutdown server - unlikely command? */ + +#define MAXSNAM 31 /* max server name string */ +#define MAXUAME 16 /* max size for each UAM string */ +#define MAXVERE 16 /* max size for each version string */ + +/* some base definitions */ +#define MAXPSTR 255 /* max size of a pascal string */ +#define MAXVLEN 27 /* max size of volume name */ +#define MAXVNAME MAXVLEN +#define MAXPLEN 8 /* max size of volume password */ +#define MAXPASSWD 8 +#define MAXDLEN 1024 /* max length of a path */ +#ifndef MAXPATH +#define MAXPATH MAXDLEN +#endif MAXPATH +#ifdef AIX +#undef MAXPATH +#define MAXPATH MAXDLEN +#endif AIX +#define MAXLFLEN 31 /* max length of long file name */ +#define MAXSFLEN 12 /* max length of short file name */ +#define MAXUFLEN ((3*MAXLFLEN)+1) /* max length of unix expanded name */ + +typedef struct { /* Directory Only Parms */ + sdword dp_dirid; /* directory id */ + word dp_nchild; /* number of offspring */ + sdword dp_ownerid; /* owner id */ + sdword dp_groupid; /* group id */ + dword dp_accright; /* access rights */ +} DirParm; + +typedef struct { /* File Only Parms */ + sdword fp_fileno; /* file number */ + sdword fp_rflen; /* resource fork length */ + sdword fp_dflen; /* data fork length */ +} FileParm; + +#define FDP_DIRFLG 0x80 /* directory flag */ +#define FDP_ISDIR(flg) (((flg) & FDP_DIRFLG) != 0) + +typedef struct { /* FileDirParms */ + byte fdp_flg; /* Directory flag */ + byte fdp_zero; /* zero byte for packing */ + word fdp_attr; /* attribute flags */ + sdword fdp_pdirid; /* parent directory ID */ + sdword fdp_cdate; /* creation date */ + sdword fdp_mdate; /* modification date */ + sdword fdp_bdate; /* backup date */ + byte fdp_finfo[FINFOLEN]; /* Finder info */ + char fdp_lname[MAXLFLEN]; /* long name */ + char fdp_sname[MAXSFLEN]; /* short name */ + word fdp_fbitmap; /* file bitmap for packing */ + word fdp_dbitmap; /* directory bitmap for packing */ + union { /* union for file/directory only parms */ + DirParm dp_parms; /* directory only parms */ + FileParm fp_parms; /* file only parms */ + } fdp_parms; /* these are called fdp_parms */ + word fdp_prodos_ft; /* prodos file type information */ + dword fdp_prodos_aux; /* prodos aux file type info */ +} FileDirParm, *FDParmPtr; + +/* Volume Params */ + +#define VP_ATTR 00001 /* attributes */ +#define VP_SIG 00002 /* signature byte */ +#define VP_CDATE 00004 /* creation date */ +#define VP_MDATE 00010 /* modification date */ +#define VP_BDATE 00020 /* backup date */ +#define VP_VOLID 00040 /* volume id */ +#define VP_FREE 00100 /* free bytes */ +#define VP_SIZE 00200 /* size in bytes */ +#define VP_NAME 00400 /* volume name */ +#define VP_EFREE 01000 /* AFP2.2: extended free bytes */ +#define VP_ESIZE 02000 /* AFP2.2: extended total bytes */ +#define VP_ALLOC 04000 /* AFP2.2: allocation block size */ +#define VP_ALL (07777) + +#define VOL_VAR_DIRID 0x03 /* volume has variable dirids */ +#define VOL_FIXED_DIRID 0x02 /* volume has fixed dirids */ +#define VOL_FLAT 0x01 /* volume is flat file systems */ + +/* DirParms - Directory Parameters Bitmap */ +/* Bit on signifies item is present in packed parameters block */ + +#define DP_ATTR 0x0001 /* (LSB) attributes */ +#define DP_PDIR 0x0002 /* parent directory id */ +#define DP_CDATE 0x0004 /* creation date */ +#define DP_MDATE 0x0008 /* modify date */ +#define DP_BDATE 0x0010 /* backup date */ +#define DP_FINFO 0x0020 /* finder info */ +#define DP_LNAME 0x0040 /* long name flag */ +#define DP_SNAME 0x0080 /* short name flag */ +#define DP_DIRID 0x0100 /* directory id */ +#define DP_CHILD 0x0200 /* number of directory offspring */ +#define DP_CRTID 0x0400 /* creator id */ +#define DP_GRPID 0x0800 /* group id */ +#define DP_ACCES 0x1000 /* access bits */ +#define DP_PDOS 0x2000 /* AFP2.0: prodos file type */ + +/* list of all bitmap items aufs can fill in or set */ + +#ifdef SHORT_NAMES +#define DP_AUFS_VALID (DP_ATTR|DP_PDIR|DP_CDATE|DP_MDATE|DP_BDATE|DP_FINFO|\ + DP_SNAME|DP_LNAME|DP_DIRID|DP_CHILD|DP_CRTID|DP_GRPID|\ + DP_ACCES|DP_PDOS) +#else SHORT_NAMES +#define DP_AUFS_VALID (DP_ATTR|DP_PDIR|DP_CDATE|DP_MDATE|DP_BDATE|DP_FINFO|\ + DP_LNAME|DP_DIRID|DP_CHILD|DP_CRTID|DP_GRPID|DP_ACCES|DP_PDOS) +#endif SHORT_NAMES + +#define DP_ALL (0x3777) /* all bits */ + +/* File Params */ + +#define FP_ATTR 0x0001 /* attributes: */ +#define FPA_INV 0x001 /* invisible */ +#define FPA_MUS 0x002 /* multi-user */ +#define FPA_SYS 0x004 /* AFP2.0: System */ +#define FPA_DAO 0x008 /* DAlreadyOpen */ +#define FPA_RAO 0x010 /* RAlreadyOpen */ +#define FPA_WRI 0x020 /* Write Inhibit */ +#define FPA_BKUP 0x040 /* AFP2.0: backup needed */ +#define FPA_RNI 0x080 /* AFP2.0: rename inhibit */ +#define FPA_DEI 0x100 /* AFP2.0: delete inhibit */ +#define FPA_CPR 0x400 /* AFP2.0: copy protect */ +#define FPA_SCL 0x8000 /* set/clear */ +#define FPA_MASK1 (FPA_INV|FPA_MUS|FPA_DAO|FPA_RAO|FPA_WRI) /* AFP 1.1 */ +#define FP_PDIR 0x0002 /* parent directory id */ +#define FP_CDATE 0x0004 /* creation date */ +#define FP_MDATE 0x0008 /* modification date */ +#define FP_BDATE 0x0010 /* backup date */ +#define FP_FINFO 0x0020 /* finder info */ +#define FP_LNAME 0x0040 /* long name */ +#define FP_SNAME 0x0080 /* short name */ +#define FP_FILNO 0x0100 /* file number */ +#define FP_DFLEN 0x0200 /* data fork length */ +#define FP_RFLEN 0x0400 /* resource fork length */ +#define FP_PDOS 0x2000 /* AFP2.0: prodos file type */ +#ifdef SHORT_NAMES +#define FP_AUFS_VALID (FP_ATTR|FP_PDIR|FP_CDATE|FP_MDATE|FP_BDATE|FP_FINFO|\ + FP_SNAME|FP_LNAME|FP_FILNO|FP_DFLEN|FP_RFLEN|FP_PDOS) +#else SHORT_NAMES +#define FP_AUFS_VALID (FP_ATTR|FP_PDIR|FP_CDATE|FP_MDATE|FP_BDATE|FP_FINFO|\ + FP_LNAME|FP_FILNO|FP_DFLEN|FP_RFLEN|FP_PDOS) +#endif SHORT_NAMES + +/* Get User Info bitmap items */ + +#define UIP_USERID 0x1 /* user id (dword) */ +#define UIP_PRIMARY_GID 0x2 /* primary group (dword) */ + +#define AFSTYPE "AFPServer" /* NBP type for AFS */ + +#define ASIP_PORT 548 /* AppleShare over TCP/IP well-known port */ + +char *afperr(); /* in afperr.c */ diff --git a/netat/afpc.h b/netat/afpc.h new file mode 100644 index 0000000..d04e232 --- /dev/null +++ b/netat/afpc.h @@ -0,0 +1,24 @@ +/* + * $Author: djh $ $Date: 1995/06/19 01:33:09 $ + * $Header: /mac/src/cap60/netat/RCS/afpc.h,v 2.2 1995/06/19 01:33:09 djh Rel djh $ + * $Revision: 2.2 $ +*/ + +/* + * abafpcmd.h - header file for AppleTalk Filing Protocol Client routines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March, 1987 CCKim Created + * + */ + +#define USE_UAM_ANON 0 +#define USE_UAM_CLEAR 1 +#define USE_UAM_RANDNUM 2 +#define USE_UAM_2WAYRAND 3 diff --git a/netat/afpcmd.h b/netat/afpcmd.h new file mode 100644 index 0000000..171d025 --- /dev/null +++ b/netat/afpcmd.h @@ -0,0 +1,723 @@ +/* + * $Author: djh $ $Date: 1996/04/25 01:24:27 $ + * $Header: /mac/src/cap60/netat/RCS/afpcmd.h,v 2.6 1996/04/25 01:24:27 djh Rel djh $ + * $Revision: 2.6 $ + * + */ + +/* + * abafpcmd.h - header file for AppleTalk Filing Protocol Command Packets + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * March, 1987 Schilit Created + * + */ + +typedef struct { /* FPAddAPPL */ + byte aap_cmd; /* Command */ + byte aap_zero; /* Always zero */ + word aap_dtrefnum; /* desk top refnum */ + sdword aap_dirid; /* directory id */ + byte aap_fcreator[4]; /* file creator */ + dword aap_apptag; /* application tag */ + byte aap_ptype; /* path type */ + byte aap_path[MAXPATH]; /* path */ +} AddAPPLPkt, *AAPPtr; + +typedef struct { /* AddComment */ + byte adc_cmd; /* Command */ + byte adc_zero; /* always zero */ + word adc_dtrefnum; /* desk top refnum */ + sdword adc_dirid; /* directory id */ + byte adc_ptype; /* path type */ + byte adc_path[MAXPATH]; /* path */ + byte adc_clen; /* comment length */ + byte adc_comment[MAXCLEN]; /* comment string (PASCAL) */ +} AddCommentPkt, *ACPPtr; + +typedef struct { /* AddIcon */ + byte adi_cmd; /* Command */ + byte adi_zero; + word adi_dtref; /* Desktop refnum */ + byte adi_fcreator[4]; /* file creator */ + byte adi_ftype[4]; /* file type */ + byte adi_icontype; /* icon type */ + byte adi_zero2; + dword adi_icontag; /* user icon tag */ + word adi_iconsize; /* icon size */ +} AddIconPkt, *AIPPtr; + +typedef struct { /* ByteRangeLock */ + byte brl_cmd; /* command */ + byte brl_flg; /* flags */ +#define BRL_START 0x100 /* high bit */ +#define BRL_UNLOCK 0x001 /* low bit */ + word brl_refnum; /* file refnum */ + dword brl_offset; /* offset to start lock */ + dword brl_length; /* number of bytes to lock */ +} ByteRangeLockPkt, *BRLPPtr; + +typedef struct { /* ByteRangeLock Reply */ + dword brlr_rangestart; /* range locked */ +} ByteRangeLockReplyPkt, *BRLRPPtr; + +typedef struct { /* FPCloseDir */ + byte cdr_cmd; /* command */ + byte cdr_zero; /* always zero */ + word cdr_volid; /* volume id */ + sdword cdr_dirid; /* directory id */ +} CloseDirPkt, *CDPPtr; + +typedef struct { /* FPCloseDT */ + byte cdt_cmd; /* command */ + byte cdt_zero; /* zero byte */ + word cdt_dtrefnum; /* desktop database refnum */ +} CloseDTPkt, *CDTPPtr; + +typedef struct { /* FPCloseFork */ + byte cfk_cmd; /* command */ + byte cfk_zero; /* zero byte */ + word cfk_refnum; /* open fork reference number */ +} CloseForkPkt, *CFkPPtr; + +typedef struct { /* FPCloseVol */ + byte cv_cmd; /* command */ + byte cv_zero; /* always zero */ + word cv_volid; /* volume ID */ +} CloseVolPkt, *CVPPtr; + +typedef struct { /* FPCopyFile (optional) */ + byte cpf_cmd; /* command */ + byte cpf_zero; /* always zero */ + word cpf_svolid; /* source volume id */ + sdword cpf_sdirid; /* source directory id */ + word cpf_dvolid; /* destination volume id */ + sdword cpf_ddirid; /* destination directory id */ + byte cpf_sptype; /* source path type */ + byte cpf_spath[MAXPATH]; /* source path */ + byte cpf_dptype; /* destination path type */ + byte cpf_dpath[MAXPATH]; /* destination path */ + byte cpf_newtype; /* new path type */ + byte cpf_newname[MAXPATH]; /* new name */ +} CopyFilePkt, *CpFPPtr; + +typedef struct { /* FPCreateDir */ + byte crd_cmd; /* command */ + byte crd_zero; /* always zero */ + word crd_volid; /* volume id */ + sdword crd_dirid; /* directory id */ + byte crd_ptype; /* path type */ + byte crd_path[MAXPATH]; /* path */ +} CreateDirPkt, *CRDPPtr; + +typedef struct { /* FPCreateFile */ + byte crf_cmd; /* command */ + byte crf_flg; /* flags */ +#define CRF_HARD 0x80 /* hard create */ + word crf_volid; /* volume id */ + sdword crf_dirid; /* directory id */ + byte crf_ptype; /* path name type */ + byte crf_path[MAXPATH]; /* path name */ +} CreateFilePkt, *CFPPtr; + +typedef struct { /* FPDelete */ + byte del_cmd; /* command */ + byte del_zero; /* always zero */ + word del_volid; /* volume id */ + sdword del_dirid; /* directory id */ + byte del_ptype; /* path type */ + byte del_path[MAXPATH]; /* path */ +} DeletePkt, *DPPtr; + +typedef struct { /* FPEnumerate */ + byte enu_cmd; /* command */ + byte enu_zero; /* always zero */ + word enu_volid; /* volume id */ + sdword enu_dirid; /* directory id */ + word enu_fbitmap; /* file bitmap */ + word enu_dbitmap; /* directory bitmap */ + word enu_reqcnt; /* request count */ + word enu_stidx; /* start index */ + word enu_maxreply; /* max reply size */ + byte enu_ptype; /* path type */ + byte enu_path[MAXPATH]; /* path */ +} EnumeratePkt, *EPPtr; + +typedef struct { + byte enurfdp_len; + byte enurfdp_flag; + byte enurfdp_parms[1]; +} EnuReplyParms; + +typedef struct { /* FPEnumerate Reply */ + word enur_fbitmap; + word enur_dbitmap; + word enur_actcnt; + EnuReplyParms enurfdp[1]; +} EnumerateReplyPkt, *ERPPtr; + +#define ENUR_ACTCNT_OFF 4 /* offset to enur_actcnt */ + +typedef struct { /* FPFlush */ + byte fls_cmd; /* command */ + byte fls_zero; /* always zero */ + word fls_volid; /* volume ID */ +} FlushPkt, *FPPtr; + +typedef struct { /* FPFlushFork */ + byte flf_cmd; /* command */ + byte flf_zero; /* always zero */ + word flf_refnum; /* open fork reference number */ +} FlushForkPkt, *FFkPPtr; + +typedef struct { /* FPGetAPPL */ + byte gap_cmd; + byte gap_zero; + word gap_dtrefnum; /* desk top reference number */ + byte gap_fcreator[4]; /* creator type of the appl to be returned */ + word gap_applidx; /* index of the APPL entry to be retrieved */ + word gap_bitmap; /* bitmap of parms to return */ +} GetAPPLPkt, *GAPPtr; + +typedef struct { /* FPGetAPPL Reply */ + word gapr_bitmap; /* returned bitmap */ + dword gapr_appltag; /* appl tag */ + FileDirParm fdp; /* file parms */ +} GetAPPLReplyPkt, *GARPPtr; + +typedef struct { /* FPGetComment */ + byte gcm_cmd; /* command */ + byte gcm_zero; + word gcm_dtrefnum; /* desktop reference number */ + sdword gcm_dirid; /* directory id */ + byte gcm_ptype; /* path type */ + byte gcm_path[MAXPATH]; /* path */ +} GetCommentPkt, *GCPPtr; + +typedef struct { /* FPGetComment Reply */ + byte gcmr_clen; /* comment length */ + byte gcmr_ctxt[MAXCLEN]; /* comment text */ +} GetCommentReplyPkt, *GCRPPtr; + +typedef struct { /* FPGetFileDirParms */ + byte gdp_cmd; /* command */ + byte gdp_zero; /* always zero */ + word gdp_volid; /* volume ID */ + sdword gdp_dirid; /* directory id */ + word gdp_fbitmap; /* file bitmap */ + word gdp_dbitmap; /* directory bitmap */ + byte gdp_ptype; /* path type */ + byte gdp_path[MAXPATH]; /* path */ +} GetFileDirParmsPkt, *GFDPPPtr; + +typedef struct { /* FPGetFileDirParms Reply */ + word gdpr_fbitmap; /* file bitmap */ + word gdpr_dbitmap; /* directory bitmap */ + word gdpr_flags; + byte gdpr_zero; + /* followed by packed parms */ +} GetFileDirParmsReplyPkt, *GFDPRPPtr; + +typedef struct { /* FPGetForkParms */ + byte gfp_cmd; /* command */ + byte gfp_zero; /* zero word */ + word gfp_refnum; /* open fork reference number */ + word gfp_bitmap; /* bitmap */ +} GetForkParmsPkt, *GFkPPPtr; + +typedef struct { /* FPGetForkParms Reply */ + byte gfpr_bitmap; /* bitmap */ + /* followed by packed fork parms */ +} GetForkParmsReplyPkt, *GFkPRPPtr; + + +typedef struct { /* FPGetIcon */ + byte gic_cmd; + byte gic_zero; + word gic_dtrefnum; /* desktop ref num */ + byte gic_fcreator[4]; /* file creator */ + byte gic_ftype[4]; /* file type */ + byte gic_itype; /* icon type */ + byte gic_zero2; + word gic_length; +} GetIconPkt, *GIPPtr; + +typedef struct { /* FPGetIconInfo */ + byte gii_cmd; + byte gii_zero; + word gii_dtrefnum; + byte gii_fcreator[4]; + word gii_iidx; /* icon index */ +} GetIconInfoPkt, *GIIPPtr; + +typedef struct { /* FPGetIconInfo Reply */ + dword giir_itag; /* icon tag */ + byte giir_ftype[4]; /* file type */ + byte giir_itype; /* icon type */ + byte giir_zero; + word giir_size; /* size of icon */ +} GetIconInfoReplyPkt, *GIIRPPtr; + + +typedef struct { /* FPGetSrvrInfo */ + byte gsi_cmd; /* GetSrvrInfo command */ +} GetSrvrInfoPkt, *GSIPPtr; + +typedef struct { + char sr_machtype[17]; /* machine name */ + byte *sr_avo; /* offset to afp versions */ + byte *sr_uamo; /* user access methods offset (ISTR) */ + char *sr_vicono; /* offset to volume icon */ + word sr_flags; /* flags */ +#define SupportsFPCopyFile 0x01 /* can do a local server copyfile */ +#define SupportsChgPwd 0x02 /* AFP2.0: can do change password */ +#define DontAllowSavePwd 0x04 /* AFP2.1: user can't save password */ +#define SupportsServerMsgs 0x08 /* AFP2.1: can send server messages */ +#define SupportsServerSig 0x10 /* AFP2.2: can supply unique signature */ +#define SupportsTCPIP 0x20 /* AFP2.2: AFP commands via TCP/IP stream */ +#define SupportsSrvrNotif 0x40 /* AFP2.2: server to client messages */ + byte sr_servername[33]; /* server name */ + byte *sr_sigo; /* AFP2.2: offset to signature */ + byte *sr_naddro; /* AFP2.2: offset to network address count */ +} GetSrvrInfoReplyPkt, *GSIRPPtr; + +typedef struct { /* FPGetSrvrParms */ + byte gsp_cmd; /* command */ +} GetSrvrParmsPkt, *GSPPPtr; + +typedef struct { /* SrvrParm */ + byte volp_flag; /* flags */ +#define SRVRP_PASSWD 0x80 /* password is present */ +#define SRVRP_CONFIG 0x01 /* user configuration for prodos */ + byte volp_name[MAXVLEN]; /* volume name */ +} VolParm; + + +typedef struct { /* FPGetSrvrParms Reply */ +/* word gspr_volid; /* volume id */ + dword gspr_time; /* server time */ + byte gspr_nvols; /* number of volume parms returned */ + VolParm gspr_volp[1]; /* one VolParm for each volume */ +} GetSrvrParmsReplyPkt, *GSPRPPtr; + +typedef struct { /* FPGetVolParms */ + byte gvp_cmd; /* command */ + byte gvp_zero; /* always zero */ + word gvp_volid; /* volume id */ + word gvp_bitmap; /* request bitmap */ +} GetVolParmsPkt, *GVPPPtr; + +typedef struct { /* FPGetVolParms */ + word gvpr_bitmap; /* return bitmap */ + word gvpr_attr; /* attributes */ + word gvpr_sig; /* volume signature */ + sdword gvpr_cdate; /* volume creation date */ + sdword gvpr_mdate; /* volume modification date */ + sdword gvpr_bdate; /* volume backup date */ + word gvpr_volid; /* volume id */ + sdword gvpr_size; /* size of volume in bytes */ + sdword gvpr_free; /* free bytes on volume */ + byte gvpr_name[MAXVLEN]; /* advertised name */ + byte gvpr_esize[8]; /* extended volume size */ + byte gvpr_efree[8]; /* extended bytes free */ +} GetVolParmsReplyPkt, *GVPRPPtr; + + +#define UAM_UNKNOWN -1 /* internal code */ +#define UAM_ANON 0 +#define UAM_CLEAR 1 +#define UAM_RANDNUM 2 +#define UAM_2WAYRAND 3 + +#define UAMP_USER 1 /* need user name */ +#define UAMP_PASS 2 /* need password */ +#define UAMP_RAND 4 /* need randnum */ +#define UAMP_ENCR 8 /* need encrypted passwd */ +#define UAMP_ZERO 0x10 /* need zero between user and passwd */ +#define UAMP_INUM 0x20 /* id number for fplogin exchange */ +#define UAMP_TWAY 0x40 /* need second random dword */ + +typedef struct { /* FPLogin */ + byte log_cmd; /* Login command */ + byte log_ver[MAXPSTR]; /* version string */ + byte log_uam[MAXPSTR]; /* UAM method */ + byte log_auth[MAXPSTR]; /* authorization info (optional) */ + byte log_passwd[MAXPLEN]; /* 8 bytes for password (plaintext) */ + byte log_user[MAXPSTR]; /* user name */ + byte log_flag; /* flags for authinfo */ + byte log_zero; /* used for zero's */ +} LoginPkt, *LPPtr; + +typedef struct { /* FPLogin Reply */ + byte logr_flag; /* flags... */ + word logr_idnum; /* id number (under some uams) */ + byte logr_randnum[8]; /* 64bit random number for RANDNUM uam */ +} LoginReplyPkt, *LRPPtr; + +typedef struct { /* FPLoginCont */ + byte lgc_cmd; /* command */ + byte lgc_zero; /* is this here? */ + word lgc_idno; /* ID number */ + byte lgc_encrypted[8]; /* encrypted version of random number */ + /* for randnum exchange uam (64 bits) */ + byte lgc_wsencrypt[8]; /* encrypted version of workstation id */ + /* for 2-Way rand exchange uam (64 bits) */ + byte lgc_flags; /* really just the uam */ +} LoginContPkt, *LCPPtr; + +typedef struct { /* FPLogout */ + byte lgo_cmd; +} LogoutPkt, *LOPPtr; + +typedef struct { /* FPMapID */ + byte mpi_cmd; /* MapID command */ + byte mpi_fcn; /* function */ + sdword mpi_id; /* ID to map */ +} MapIDPkt, *MIPPtr; + +typedef struct { /* FPMapID Reply */ + byte mpir_name[MAXPSTR]; +} MapIDReplyPkt, *MIRPPtr; + +typedef struct { /* FPMapName */ + byte mpn_cmd; /* command */ + byte mpn_fcn; /* function */ + byte mpn_name[MAXPSTR]; /* name */ +} MapNamePkt, *MNPPtr; + +typedef struct { /* FPMapName Reply */ + word mpnr_id; /* returned id */ +} MapNameReplyPkt, *MNRPPtr; + +typedef struct { /* FPMove */ + byte mov_cmd; /* command */ + byte mov_zero; /* always zero */ + word mov_volid; /* volume id */ + sdword mov_sdirid; /* source directory id */ + sdword mov_ddirid; /* destination directory id */ + byte mov_sptype; /* source path type */ + byte mov_spath[MAXPATH]; /* source path */ + byte mov_dptype; /* destination path type */ + byte mov_dpath[MAXPATH]; /* destination path */ + byte mov_newtype; /* new type */ + byte mov_newname[MAXPATH]; /* new name */ +} MovePkt, *MPPtr; + +typedef struct { /* FPOpenDir */ + byte odr_cmd; /* command */ + byte odr_zero; /* always zero */ + word odr_volid; /* volume ID */ + sdword odr_dirid; /* directory ID */ + byte odr_ptype; /* path type */ + byte odr_path[MAXPATH]; /* path */ +} OpenDirPkt, *ODPPtr; + +typedef struct { /* FPOpenDT */ + byte odt_cmd; /* command */ + byte odt_zero; + word odt_volid; /* desktop volume id */ +} OpenDTPkt, *ODTPPtr; + +typedef struct { /* FPOpenDT Reply */ + word odtr_dtrefnum; /* desktop reference number */ +} OpenDTReplyPkt, *ODTRPPtr; + + +typedef struct { /* FPOpenFork */ + byte ofk_cmd; /* command */ + byte ofk_rdflg; /* resource/data flag */ +#define OFK_RSRC 0x80 /* resource fork */ + word ofk_volid; /* volume id */ + sdword ofk_dirid; /* directory id */ + word ofk_bitmap; /* bitmap */ + word ofk_mode; /* access mode */ +#define OFK_MRD 0x01 /* read mode */ +#define OFK_MWR 0x02 /* write mode */ +#define OFK_MRDX 0x10 /* exclusive read mode */ +#define OFK_MWRX 0x20 /* exclusive write mode */ + byte ofk_ptype; /* path type */ + byte ofk_path[MAXPATH]; /* path name */ +} OpenForkPkt, *OFkPPtr; + +typedef struct { /* FPOpenFork Reply */ + word ofkr_bitmap; /* bitmap */ + word ofkr_refnum; /* opened fork reference number */ + /* File parameters follow */ +} OpenForkReplyPkt, *OFkRPPtr; + +typedef struct { /* FPOpenVol */ + byte ovl_cmd; /* command */ + byte ovl_zero; /* always zero */ + word ovl_bitmap; /* request bitmap */ + byte ovl_name[MAXVNAME]; /* volume name packed... */ + /* possible null byte */ + byte ovl_pass[MAXPASSWD]; /* password (optional) */ +} OpenVolPkt, *OVPPtr; + +typedef struct { /* FPOpenVol Reply */ + word ovlr_bitmap; /* request bitmap */ + /* volume parameters follow */ +} OpenVolReplyPkt, *OVRPPtr; + +typedef struct { /* FPRead */ + byte rdf_cmd; + byte rdf_zero; + word rdf_refnum; /* fork reference number */ + sdword rdf_offset; /* offset for read */ + sdword rdf_reqcnt; /* request count */ + byte rdf_flag; +#define RDF_NEWLINE 0xFF + byte rdf_nlchar; /* newline char */ +} ReadPkt, *ReadPPtr; + +typedef struct { /* FPRead Reply */ + byte rdfr_data[1]; /* data */ +} ReadReplyPkt, *RRPPtr; + +typedef struct { /* FPRemoveAPPL */ + byte rma_cmd; + byte rma_zero; + word rma_refnum; + sdword rma_dirid; + byte rma_fcreator[4]; + byte rma_ptype; + byte rma_path[MAXPATH]; +} RemoveAPPLPkt, *RAPPtr; + +typedef struct { /* FPRemoveComment */ + byte rmc_cmd; + byte rmc_zero; + word rmc_dtrefnum; /* dest top ref num */ + sdword rmc_dirid; + byte rmc_ptype; + byte rmc_path[MAXPATH]; +} RemoveCommentPkt, *RCPPtr; + +typedef struct { /* FPRename */ + byte ren_cmd; /* command */ + byte ren_zero; /* always zero */ + word ren_volid; /* volume id */ + sdword ren_dirid; /* directory id */ + byte ren_ptype; /* path type */ + byte ren_path[MAXPATH]; /* path name */ + byte ren_ntype; /* new type */ + byte ren_npath[MAXPATH]; /* new path */ +} RenamePkt, *RPPtr; + + +typedef struct { /* FPSetDirParms */ + byte sdp_cmd; /* command */ + byte sdp_zero; /* always zero */ + word sdp_volid; /* volume ID */ + sdword sdp_dirid; /* parent directory id */ + word sdp_bitmap; /* bitmap */ + byte sdp_ptype; /* path type */ + byte sdp_path[MAXPATH]; /* path */ + /* Possible null byte */ + /* Directory Parameters: */ + FileDirParm fdp; +} SetDirParmsPkt, *SDPPPtr; + +typedef struct { /* FPSetFileParms */ + byte sfp_cmd; /* command */ + byte sfp_zero; /* always zero */ + word sfp_volid; /* volume id */ + sdword sfp_dirid; /* directory id */ + word sfp_bitmap; /* set bitmap */ + byte sfp_ptype; /* path type */ + byte sfp_path[MAXPATH]; /* path + file parameters to set */ + /* possible null byte */ + /* File Parameters: */ + FileDirParm fdp; +} SetFileParmsPkt, *SFPPPtr; + +typedef struct { /* FPSetFileDirParms */ + byte scp_cmd; /* set common parms command */ + byte scp_zero; + word scp_volid; + sdword scp_dirid; + word scp_bitmap; + byte scp_ptype; + byte scp_path[MAXPATH]; + /* possible null byte */ + /* Common Parameters follow: */ + FileDirParm fdp; +} SetFileDirParmsPkt, *SFDPPPtr; + +typedef struct { /* FPSetForkParms */ + byte sfkp_cmd; /* command */ + byte sfkp_zero; /* zero word */ + word sfkp_refnum; /* reference number */ + word sfkp_bitmap; /* bitmap */ + sdword sfkp_rflen; /* resource fork length */ + sdword sfkp_dflen; /* data fork length */ +} SetForkParmsPkt, *SFkPPPtr; + +typedef struct { /* FPSetVolParms */ + byte svp_cmd; /* command */ + byte svp_zero; /* always zero */ + word svp_volid; /* volume id */ + word svp_bitmap; /* set bitmap */ + dword svp_backdata; /* backup data to set */ +} SetVolParmsPkt, *SVPPPtr; + +typedef struct { /* FPWrite */ + byte wrt_cmd; + byte wrt_flag; +#define WRT_START 0x01 + word wrt_refnum; + dword wrt_offset; + dword wrt_reqcnt; +} WritePkt, *WPPtr; + +typedef struct { /* FPWrite Reply */ + dword wrtr_lwrt; /* last written */ +} WriteReplyPkt, *WRPPtr; + +/* New as of AFP 2.0 */ +typedef struct { + byte cp_cmd; /* command */ + byte cp_zero; /* always zero */ + byte cp_uam[MAXPSTR]; /* authentication method */ + byte cp_user[MAXPSTR]; /* user name */ + byte cp_oldpass[MAXPLEN]; /* 8 bytes for old password */ + byte cp_newpass[MAXPLEN]; /* 8 bytes for new password */ + byte cp_pad; /* dummy for padding */ +} ChgPasswdPkt, *CPPtr; + +typedef struct { + byte cpr_cmd; + byte cpr_zero; + byte cpr_uam[MAXPSTR]; /* authentication method */ + byte cpr_user[MAXPSTR]; /* user name */ + byte cpr_newpass[MAXPLEN]; /* 8 bytes for new password */ + byte cpr_pad; /* dummy */ +} ChgPasswdReplyPkt, *CPRPtr; + +typedef struct { + byte gui_cmd; /* command */ + byte gui_flag; /* flag word */ +#define GUI_THIS_USER 0x1 /* set implies current user, o.w. use userid */ + dword gui_userid; /* user id */ + word gui_bitmap; /* bitmap of info to return */ +} GetUserInfoPkt, *GUIPtr; + +typedef struct { + word guir_bitmap; /* bitmap to return */ + dword guir_userid; /* user id */ + dword guir_pgroup; /* primary group */ +} GetUserInfoReplyPkt, *GUIRPtr; + +typedef struct { /* FPExchangeFiles */ + byte exc_cmd; /* command */ + byte exc_zero; /* always zero */ + word exc_volid; /* volume id */ + sdword exc_adirid; /* first directory id */ + sdword exc_bdirid; /* second directory id */ + byte exc_aptype; /* first path type */ + byte exc_apath[MAXPATH]; /* first path */ + byte exc_bptype; /* second path type */ + byte exc_bpath[MAXPATH]; /* second path */ +} ExchPkt, *EXPtr; + +#define SRVRMSGLEN 200 + +typedef struct { /* FPGetSrvrMsg */ + byte msg_cmd; /* command */ + byte msg_zero; /* always zero */ + word msg_typ; /* message type */ + word msg_bitmap; /* bitmap */ +} SrvrMsgPkt, *SrvrMsgPtr; + +typedef struct { + word msgr_typ; /* message type */ + word msgr_bitmap; /* bitmap */ + byte msgr_data[SRVRMSGLEN]; /* message string */ +} SrvrMsgReplyPkt, *SrvrMsgReplyPtr; + +typedef struct { /* FPCreateID */ + byte crid_cmd; /* command */ + byte crid_zero; /* always zero */ + word crid_volid; /* volume ID */ + sdword crid_dirid; /* directory ID */ + byte crid_ptype; /* path type */ + byte crid_path[MAXPATH]; /* path */ +} CreateIDPkt, *CreateIDPtr; + +typedef struct { /* FPDeleteID */ + byte did_cmd; /* command */ + byte did_zero; /* always zero */ + word did_volid; /* volume ID */ + sdword did_fileid; /* file ID */ +} DeleteIDPkt, *DeleteIDPtr; + +typedef struct { /* FPResolveID */ + byte rid_cmd; /* command */ + byte rid_zero; /* always zero */ + word rid_volid; /* volume ID */ + sdword rid_fileid; /* file id */ + word rid_fbitmap; /* result bitmap */ +} ResolveIDPkt, *ResolveIDPtr; + +typedef struct { /* FPResolveID Reply */ + word ridr_fbitmap; /* result bitmap */ + /* followed by packed parms */ +} ResolveIDReplyPkt, *ResolveIDRPtr; + + +typedef struct { + int pe_typ; /* type of data */ + int pe_off; /* offset in host structure */ + int pe_siz; /* size or max size */ + int pe_bit; /* bit if specified by bitmap */ +} PackEntry; + +#define P_END -1 /* end of structure */ +#define P_WORD 0 /* 2 byte word */ +#define P_BYTE 1 /* 1 byte */ +#define P_DWRD 2 /* 4 byte double word */ +#define P_BYTS 3 /* arbitrary length bytes set by struct len */ +#define P_PSTR 4 /* pascal string/c string */ +#define P_BMAP 5 /* bitmap (not sent to net) */ +#define P_OSTR 6 /* offset c/pascal string */ +#define P_PATH 7 /* preserved pascal string */ +#define P_OPTR 8 /* offset to object pointer */ +#define P_OPTH 9 /* offset to preserved pascal string */ +#define P_EVEN 10 /* move to even boundary (+0,+1) */ +#define P_ZERO 11 /* mark entry as zero */ +#define P_TIME 12 /* entry points to time (dword) */ + +/* Pack(pointer, type of data, scalar result field) */ +#define PACK(pt,t,s) {t,(int) &((pt) 0)->s,sizeof(((pt) 0)->s),0} +/* Paks(pointer, type of data, array result field) */ +#ifdef ADDRINPACK +#define PAKS(pt,t,s) {t,(int) &((pt) 0)->s,sizeof(((pt) 0)->s),0} +#else ADDRINPACK +#define PAKS(pt,t,s) {t,(int) ((pt) 0)->s,sizeof(((pt) 0)->s),0} +#endif ADDRINPACK +/* Both require that a "type of data" field type P_BMAP be set first */ +/* pakb(pointer, type of data, scalar result field, bit of bitmap */ +#define PAKB(pt,t,s,b) {t,(int) &((pt) 0)->s,sizeof(((pt) 0)->s),b} +/* pksb(pointer, type of data, array result field, bit of bitmap */ +#ifdef ADDRINPACK +#define PKSB(pt,t,s,b) {t,(int) &((pt) 0)->s,sizeof(((pt) 0)->s),b} +#else ADDRINPACK +#define PKSB(pt,t,s,b) {t,(int) ((pt) 0)->s,sizeof(((pt) 0)->s),b} +#endif ADDRINPACK + +/* Pack even - to mark that we should be on even boundary */ +#define PACKEVEN() {P_EVEN, 0, 0, 0} + +/* PACKEND() for ending the packet */ +#define PACKEND() {P_END,0,0,0} + +typedef struct { /* type for packing offset objects */ + int optr_len; + byte *optr_loc; +} OPTRType; diff --git a/netat/afppass.h b/netat/afppass.h new file mode 100644 index 0000000..914a26b --- /dev/null +++ b/netat/afppass.h @@ -0,0 +1,101 @@ +/* + * $Author: djh $ $Date: 1995/06/20 03:27:33 $ + * $Header: /mac/src/cap60/netat/RCS/afppass.h,v 2.1 1995/06/20 03:27:33 djh Rel djh $ + * $Revision: 2.1 $ + * + */ + +/* + * AUFS Distributed Passwords + * + * Copyright 1995 - The University of Melbourne. All rights reserved. + * May be used only for CAP/AUFS authentication. Any other use + * requires prior permission in writing from the copyright owner. + * + * djh@munnari.OZ.AU + * June 1995 + * + * afppass.h - AUFS Distributed Password defines. + * + * User passwords are normally stored in ~user/.afppass in DES encrypted + * form. This file also contains values for password expiry date, minimum + * password length, maximum failed login attempts and number of failed + * login attempts. + * + * For greater security, the file must be owned by the user and be set to + * mode AFP_DISTPW_MODE (usually 0600 or -rw-------), if this is not the + * case, the file is deleted. + * + * The decryption key is stored in a global afppass (defaults to the + * file /usr/local/lib/cap/afppass) which also contains default values + * for expiry date, minimum password length and maximum failed attempts. + * If this file is not owned by root and mode 0600 it will be removed. + * + * Notes: + * 1. In the case of user home directories mounted via NFS, the files must + * be set to mode 0644 (since root cannot read mode 0600 files on remote + * filesystems). You can change the mode using the define + * -DAFP_DISTPW_MODE=0644 + * + * 2. If you prefer to keep the .afppass files centrally, you can define + * the path using the define -DAFP_DISTPW_PATH=\"/usr/local/lib/cap/upw\" + * + * 3. The decryption key for the global afppass is defined by AFP_DIST_PASS + * Should be localized for each site, using -DAFP_DIST_PASS=\"password\". + * + * 4. AFP passwords can only be changed by the user with the AppleShare + * workstation client or by the UNIX superuser using aufsmkusr. + * + * 5. User AFP passwords MUST NOT be identical to UNIX login passwords, + * this restriction is enforced by the library routines. + * + */ + +#define KEYSIZE 8 +#define MINKEYSIZE 6 +#define AFPPDSIZE 34 + +struct afppass { + u_char afp_magic; /* magic for sanity checking */ +#define AFPDP_MAGIC 79 + u_char afp_minmpwlen; /* mininum password length */ + u_char afp_maxattempt; /* maximum failed login attempts */ + u_char afp_numattempt; /* count of attempts to date */ + time_t afp_expires; /* date password expires */ + u_char afp_password[10]; /* current user password */ +}; + +/* + * Gobal key. Should be localized by each site. + * + */ + +#ifndef AFP_DISTPW_PASS +#define AFP_DISTPW_PASS "%-[&'*!~" +#endif AFP_DISTPW_PASS + +/* + * Misc files & permission. + * + */ + +#ifndef AFP_DISTPW_FILE +#define AFP_DISTPW_FILE "/usr/local/lib/cap/afppass" +#endif AFP_DISTPW_FILE + +#ifndef AFP_DISTPW_USER +#define AFP_DISTPW_USER ".afppass" +#endif AFP_DISTPW_USER + +#ifndef AFP_DISTPW_MODE +#define AFP_DISTPW_MODE 0600 +#endif AFP_DISTPW_MODE + +/* + * Misc numbers. + * + */ + +#define SECS_IN_DAY 24*60*60 +#define SECS_IN_MON 30*24*60*60 +#define SECS_10_YRS 10*365*24*60*60 diff --git a/netat/appletalk.h b/netat/appletalk.h new file mode 100644 index 0000000..c1aa624 --- /dev/null +++ b/netat/appletalk.h @@ -0,0 +1,544 @@ +/* + * $Author: djh $ $Date: 1994/10/24 06:42:08 $ + * $Header: /mac/src/cap60/netat/RCS/appletalk.h,v 2.8 1994/10/24 06:42:08 djh Rel djh $ + * $Revision: 2.8 $ +*/ + +/* + * appletalk.h - Appletalk definitions + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in + * the City of New York. + * + * Portions Copyright (C) 1985, Stanford Univ. SUMEX project. + * C language version (C) 1984, Stanford Univ. SUMEX project. + * May be used but not sold without permission. + * + * Portions Copyright (C) 1984, Apple Computer Inc. + * Gene Tyacke, Alan Oppenheimer, G. Sidhu, Rich Andrews. + * + * Edit History: + * + * June 13, 1986 Schilit Created. + * + */ + +#include +#include +#include /* this definitely doesn't belong here!! */ +/* but there's no way that I'm going through 50 files to fix it */ + +/* misc structures */ + +/* use these when protocol depends on actual number of bits somehow */ +/* use ifdefs for different machines */ +typedef unsigned char byte; +typedef char sbyte; +typedef unsigned short word; +typedef short sword; + +#ifdef __alpha +/* The alpha has 64bit longs */ +typedef unsigned int dword; +typedef int sdword; +#else __alpha +typedef unsigned long dword; +typedef long sdword; +#endif __alpha + +typedef int OSErr; + +typedef struct { /* BDS, buffer data structure */ + word buffSize; + char *buffPtr; + word dataSize; + dword userData; +} BDS; +typedef BDS * BDSPtr; + +typedef struct { /* BDSType */ + BDS a[8]; +} BDSType; + +typedef struct { /* Str32 */ + byte s[33]; /* make 33 in case we want a null byte */ +} Str32; + +#define userBytes userData + +typedef struct { /* AddrBlock */ + word net; + byte node; + byte skt; +} AddrBlock; + + +/* + * For each protocol, its header structure, definitions, + * and control / status parameter block. + */ + +/* + * LAP protocol + * +*/ +typedef struct { /* LAP */ + byte dst; + byte src; + byte type; +} LAP, LAPAdrBlock; + +#define dstNodeID dst +#define srcNodeID src +#define lapProtType type + +/* LAP definitions */ +#define lapShortDDP 1 /* short DDP type */ +#define lapDDP 2 /* DDP type */ +#define lapMB 10 /* Masterbridge packet */ + +#define lapSize 3 /* size of lap header */ + +/* LAP delivery mechanisms */ + +#define LAP_KIP 1 /* "standard" KIP */ +#define LAP_MKIP 2 /* modified KIP for UAB */ +#define LAP_ETALK 3 /* EtherTalk phase 1/2 */ +#define LAP_KERNEL 4 /* kernel EtherTalk */ + +/* + * Masterbridge routing protocol + * +*/ +#define mbMBS 70 /* socket for masterbridge requests */ +typedef struct { + byte type; /* type of packet */ + byte node; /* destination node */ + word net; /* destination net */ + dword bridge; /* bridge address */ + dword src; /* packet source (IP) */ + /* Maybe that should be a Appletalk address? */ + /* more general but less efficent */ +} MBPACKET; + +#define mbRRQ 50 /* route request packet */ +#define mbRRP 51 /* route reply packet */ +#define mbPING 52 /* ping packet */ +#define mbPINGR 53 /* ping reply packet */ + + +/* + * DDP protocol + * +*/ + +typedef struct { /* DDP */ + word length; + word checksum; + word dstNet; + word srcNet; + byte dstNode; + byte srcNode; + byte dstSkt; + byte srcSkt; + byte type; +} DDP; + +typedef struct { /* ShortDDP */ + word length; + byte dstSkt; + byte srcSkt; + byte type; +} ShortDDP; + +/* DDP definitions */ +#define ddpMaxSkt 0xFF /* max socket number */ +#define ddpMaxWKS 0x7F +#define ddpMaxData 586 +#define ddpLengthMask 0x3FF +#define ddpHopShift 10 +#define ddpSize 13 /* size of DDP header */ +#define ddpSSize 5 +#define ddpWKS 128 /* boundary of DDP well known sockets */ +#define ddpEWKS 64 /* start of experimental socket range */ +#define ddpRTMP 1 /* RTMP type */ +#define ddpNBP 2 /* NBP type */ +#define ddpATP 3 /* ATP type */ +#define ddpECHO 4 /* ECHO type */ +#define ddpRTMP_REQ 5 /* RTMP request */ +#define ddpZIP 6 /* ZIP packet */ +#define ddpADSP 7 /* ADSP packet */ +#define ddpIP 22 /* IP type */ +#define ddpARP 23 /* ARP type */ + +#define ddpWKSUnix 768 /* start of unofficial WKS range on UNIX */ +#define ddpOWKSUnix 200 /* start of official WKS range on UNIX */ +#define ddpNWKSUnix 16384 /* start of non-WKS .. */ +#define ddpIPSkt 72 /* socket used by Dartmouth encapsulation */ + + +/* + * Start of the DDP Encapsulated protocols + * +*/ + + +/* + * RTMP protocol defintions + * +*/ + +typedef struct { /* RTMP */ + word net; + byte idLen; + byte id; /* start of ID field */ +} RTMP; + +typedef struct { + word net; + byte hops; +} RTMPtuple; + +#define rtmpSkt 1 /* number of RTMP socket */ +#define rtmpSize 4 /* minimum size */ +#define rtmpTupleSize 3 + +/* + * NBP/NIS protocol definitions + * +*/ + +#define nbpEquals '=' +#define nbpWild 0xc5 +#define nbpStar '*' + +#define ENTITYSIZE 32 /* use up to 32 for data */ +/* note str32 is really 33 bytes now */ +typedef struct { /* EntityName */ + Str32 objStr; + Str32 typeStr; + Str32 zoneStr; +} EntityName; + +typedef struct { /* NBP Table entry */ + AddrBlock addr; + byte enume; /* add enumerator - cck */ + EntityName ent; +} NBPTEntry; + +typedef struct { /* RetransType */ + int retransInterval; + int retransCount; +} RetransType; + + +/* + * Echo protocol definitions + * +*/ +#define echoRequest 1 /* echo Request cmd */ +#define echoReply 2 /* echo reply cmd */ +#define echoSkt 4 /* echo socket */ + + +/* + * Zone Information Protocol protocol definitions + * + */ +#define zipZIS 0x6 /* Zone information socket */ + +/* define atp zip commands */ +#define zip_GetMyZone 7 /* get my zone command */ +#define zip_GetZoneList 8 /* get zone list command */ +#define zip_GetLocZones 9 /* get local zone list */ + +/* atp user bytes for zip commands */ +/* no reply struct since return is packed array of pascal strings */ + +typedef struct zipUserBytes { + byte zip_cmd; /* zip command (LastFlag on return) */ + byte zip_zero; /* always zero */ + word zip_index; /* zip index (count on return) */ +} zipUserBytes; + +/* + * Appletalk Transport Protocol protocol definitions + * +*/ + +#define atpSize 8 /* sizeof struct ATP */ + +typedef byte BitMapType; +typedef dword atpUserDataType; + +typedef union { + byte bytes[4]; + word words[2]; +} inadword; + +typedef struct { /* ATP */ + byte control; + BitMapType bitmap; + word transID; + atpUserDataType userData; +} ATP; + +#define atpMaxNum 8 +#define atpMaxData 578 +#define atpNData 512+16 /* normal amount of data */ + +/* + * ATP Client protocols + * +*/ + +/* + * Printer Access Protocol protocol definitions + * +*/ +/* PAPType Values: */ +#define PAPSegSize 512 + +typedef struct { + dword SystemStuff; /* PAP internal use (not used) */ + byte StatusStr[256]; /* status string + 1 byte for length */ +} PAPStatusRec; + +#define papNoError 0 /* no error - connection open */ +#define papPrinterBusy 0xffff /* printer busy */ + +/* + * Appletalk Session Protocol (ASP) definitions + * +*/ + +/* FOR ASP VERSION 1.0 */ +#define ASP_PROTOCOL_VERSION 0x0100 + +/* ASP Packet types - needed since spgetrequest returns */ +#define aspCloseSession 1 +#define aspCommand 2 +#define aspGetStat 3 +#define aspOpenSess 4 +#define aspTickle 5 +#define aspWrite 6 +#define aspWriteData 7 +#define aspAttention 8 + +/* ReqRefNum returned by SPGetRequest and used by SPWriteContinue, */ +/* SPWrtReply and SPCmdReply is a pointer to a special command block */ +/* Use ReqRefNumType to define a reqRefNum argument... */ +typedef char * ReqRefNumType; + +/* + * ABusRecord definitions + * + */ + + +/* abopcode defintions */ +#define tNBPConfirm 20 +#define tNBPLookUp 21 +#define tNBPRegister 100 +#define tNBPDelete 101 +#define tNBPTickle 102 +#define tNBPSLookUp 103 + +typedef struct atpProto { + byte atpSocket; /* listening/responding socket number */ + AddrBlock atpAddress; /* destination or source socket address */ + int atpReqCount; /* request buffer size in bytes */ + char *atpDataPtr; /* Pointer to request data */ + BDSPtr atpRspBDSPtr; /* pointer to response buffer list */ + BitMapType atpBitMap; /* transaction bitmap */ + int atpTransID; /* transaction id */ + int atpActCount; /* count of bytes received */ + atpUserDataType atpUserData; /* user bytes */ +#ifdef AIX + unsigned fatpXO : 1, /* exactly once boolean */ +#else AIX + byte fatpXO : 1, /* exactly once boolean */ +#endif AIX + fatpEOM : 1; /* end of message boolean */ + byte atpTimeOut; /* retry timeout interval in seconds */ + byte atpRetries; /* number of retries - 255 max */ + byte atpNumBufs; /* number of elements in response BDS */ + /* or number of response pkts sent */ + byte atpNumRsp; /* number of of response pkts received */ + /* or sequence number (on send) */ + byte atpBDSSize; /* number of elements in response bds */ + atpUserDataType atpRspUData; /* user bytes sent or received in */ + /* transaction response */ + char *atpRspBuf; /* Pointer to response buffer */ + int atpRspSize; /* size of response message buffer */ +} atpProto; + + +typedef struct lapProto { /* LAP record for LAP calls */ + LAPAdrBlock lapAddress; /* address for lap */ + word lapReqCount; /* bytes to read/write */ + word lapActCount; /* actual quantity from above operation */ + byte *lapDataPtr; /* pointer to data */ +} lapProto; + +typedef struct ddpProto { /* DDP record for DDP calls */ + word ddpType; /* type field */ + word ddpSocket; /* source socket */ + AddrBlock ddpAddress; /* destination address */ + word ddpReqCount; /* bytes to read/write */ + word ddpActCount; /* actual quantity from above operation */ + byte *ddpDataPtr; /* pointer to data buffer */ + word ddpNodeID; /* beats me */ +} ddpProto; + +typedef struct nbpProto { + QElem abQElem; /* internal: queue links */ + int abOpcode; /* type of call */ + int abResult; /* result code */ + dword abUserReference; /* for programmer use */ + EntityName *nbpEntityPtr; /* pointer to entity name */ + NBPTEntry *nbpBufPtr; /* user storage for NBP entities */ + word nbpBufSize; /* count of entries that fit in buffer */ + word nbpDataField; + AddrBlock nbpAddress; + RetransType nbpRetransmitInfo; + int nbpMaxEnt; /* internal: max entries */ + byte nbpID; /* internal: transaction ID */ + int retranscount; /* internal: modifiable retranscount */ +} nbpProto; + + + +typedef struct ABusRecord { +#ifdef notdefined /* not needed */ + ABCallType abOpcode; /* type of call */ +#endif + struct ABusRecord *abNext; /* INTERNAL USE ONLY: used to link read queue*/ + int abResult; /* result code */ + dword abUserReference; /* for programmer use */ + union { /* for various protocols */ + lapProto lap; + ddpProto ddp; + atpProto atp; + } proto; +} ABusRecord; + +typedef struct ABusRecord *abRecPtr; + + +/* IO Vector protocol levels for scatter gather processing */ + +#define IOV_LAP_LVL 0 /* lap index */ +#define IOV_DDP_LVL 1 /* ddp index */ +#define IOV_ATP_LVL 2 /* atp index */ +#define IOV_ATPU_LVL 3 /* user data from atp index */ + +/* size of iov's */ +#define IOV_LAP_SIZE 1 /* lap size */ +#define IOV_DDP_SIZE 2 /* ddp size */ +#define IOV_ATP_SIZE 3 /* atp size */ +#define IOV_ATPU_SIZE 4 /* user data from atp size */ + +/* maximum IOV level expected by DDP read routine */ +#define IOV_READ_MAX 10 /* no more than this allowed */ + + +/* Debug flags structure */ + +typedef union { + struct { + unsigned dbg_lap : 1; /* debug LAP */ + unsigned dbg_ddp : 1; /* debug DDP */ + unsigned dbg_atp : 1; /* debug ATP */ + unsigned dbg_nbp : 1; /* debug NBP */ + unsigned dbg_pap : 1; /* debug PAP */ + unsigned dbg_ini : 1; /* debug init code */ + unsigned dbg_asp : 1; /* debug asp */ + unsigned dbg_skd : 1; /* debug scheduler */ + } U_dbg; + unsigned db_flgs; +} DBUG; + +#define db_lap U_dbg.dbg_lap +#define db_ddp U_dbg.dbg_ddp +#define db_atp U_dbg.dbg_atp +#define db_nbp U_dbg.dbg_nbp +#define db_pap U_dbg.dbg_pap +#define db_ini U_dbg.dbg_ini +#define db_asp U_dbg.dbg_asp +#define db_skd U_dbg.dbg_skd + +extern DBUG dbug; + +#define ticktosec(sec) ((sec)/4) /* converts from internal tick units */ + /* to seconds */ +#define sectotick(sec) ((sec)*4) /* converts from seconds to internal ticks */ + + +/* define some things for the c compiler - CCK: I think some if not all */ +/* of these should be in the compiler, thus I use all lowercased as they */ +/* would be (as I normally use c compiler stuff). */ +#define private static +#define export +#define import extern +typedef int boolean; + +/* levels of registers - we declare importance of different register */ +/* vars this way */ +#define REGISTER1 register +#define REGISTER2 register +#define REGISTER3 register + +typedef int (*ProcPtr)(); /* procedure pointer */ +#define NILPROC (ProcPtr) 0 /* null procedure */ +#define NILPTR (u_char *) 0 + +/* Some general definitions - probably don't belong here */ + +#define TRUE 1 +#define FALSE 0 + +#define min(x,y) (((int)(x) > (int)(y)) ? (y) : (x)) +#define max(x,y) (((int)(x) > (int)(y)) ? (x) : (y)) + +#define tokipnet(x,y) ((y)|((x)<<8)) + +#ifdef pyr +#define ntohs(x) (x) +#endif + +/* high and low part of a "kip" network number - assume number in network */ +/* format */ +#define nkipnetnumber(x) ntohs((x))>>8 +#define nkipsubnetnumber(x) ntohs((x))&0xff +/* same but assumes in host form */ +#define kipnetnumber(x) (x)>>8 +#define kipsubnetnumber(x) (x)&0xff + +/* version information */ +struct cap_version { + int cv_version; /* e.g. 3 */ + int cv_subversion; /* e.g. 0 - if original was subverted :-) */ + int cv_patchlevel; /* e.g. 100, probably more */ + char *cv_copyright; /* pointer to copyright notice */ + char *cv_name; /* "CAP" */ + char *cv_type; /* "UDP", "EtherTalk", etc. */ + char *cv_rmonth; /* release month */ + char *cv_ryear; /* release year */ +}; + +/* routine declarations (only if not OSErr or int) */ +unsigned char *GetMyZone(); +struct cap_version *what_cap_version(); + +#define CAP_6_DBM 1 /* new atalk.local management */ + +/* logging flags */ + +#define L_UERR 0x20 /* want unix error message */ +#define L_EXIT 0x10 /* exit after logging */ +#define L_LVL 0xf /* debug levels */ +#define L_LVLMAX 15 /* maximum level */ + diff --git a/netat/compat.h b/netat/compat.h new file mode 100644 index 0000000..2433d68 --- /dev/null +++ b/netat/compat.h @@ -0,0 +1,71 @@ +/* + * $Author: djh $ $Date: 91/02/15 22:59:15 $ + * $Header: compat.h,v 2.1 91/02/15 22:59:15 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * compat.h - tries to cover up differences between various BSD based + * machines + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1987 by The Trustees of Columbia University in the + * City of New York. + * + * + * Edit History: + * + * August 6, 1987 CCKim Created. + * + */ + + +/* for those who have straight 4.2 or ultrix 1.1/1.2/2.0 systems */ +/* many of these systems don't include macros to handle select args */ +/* correctly */ +#ifndef FD_SETSIZE +# ifndef NOFILE +NO GUESSING - set this to the number of files allowed on your machine +# endif +# define NFDBITS (sizeof(int)*8) +# define FD_SETSIZE NOFILE +# ifndef howmany +# define howmany(x, y) (((x)+((y)-1))/(y)) +# endif +# define FD_SET(n, p) (p)->fds_bits[(n)/NFDBITS] |= (1<<((n)%NFDBITS)) +# define FD_CLR(n, p) (p)->fds_bits[(n)/NFDBITS] &= ~(1<<((n)%NFDBITS)) +# define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1<<((n)%NFDBITS)) +# define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) +typedef int gfd_mask; +typedef struct gfd_set { + gfd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; +} gfd_set; +#else +#define gfd_mask fd_mask +#define gfd_set fd_set +#endif + +#ifndef sigmask +/* mask for sigblock, etc */ +#define sigmask(sig) (1<<((sig)-1)) +#endif + + +/* some useful usr group standardizations */ + +#ifndef S_ISBLK +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif +#ifndef S_ISCHR +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#ifndef S_ISFIFO +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif diff --git a/netat/fcntldomv.h b/netat/fcntldomv.h new file mode 100644 index 0000000..f65be1a --- /dev/null +++ b/netat/fcntldomv.h @@ -0,0 +1,35 @@ + +/* + * fcntldomv.h - Definitions for hidden fcntl calls in HP/Apollo Domain BSD 4.3. + * + * The F_SETLK and F_GETLK fcntl calls are not documented for HP/Apollo Domain + * BSD 4.3 environment, but apparently exist and work. Below are the + * necessary definitions as defined in the SysV environment. + * + * These lock calls are only needed if the APPLICATION_MANAGER and/or + * DENYREADWRITE options are enabled in m4.features. The APPLICATION_MANAGER's + * features (inihibiting finder copying and limiting the number of simultaneous + * executions of applications) appear to work with these hidden calls. + * Interestingly enough, these features appear to work even when the managed + * applications reside on another node's disk, even though the Domain Sys V + * documentation for fcntl says that record locking works only on the + * station local to the call. + * + * Darrell Skinner Oct 1993 + * + */ + +#define F_GETLK 7 /* Get file lock */ +#define F_SETLK 8 /* Set file lock */ +#define F_RDLCK 01 /* Shared or Read lock. */ +#define F_WRLCK 02 /* Exclusive or Write lock. */ +#define F_UNLCK 03 /* Unlock. */ + +struct flock { + short l_type; /* Type of lock. */ + short l_whence; /* Flag for starting offset. */ + off_t l_start; /* Relative offset in bytes. */ + off_t l_len; /* Size; if 0 then until EOF. */ + short l_sysid; + short l_pid; /* Process ID of the lock owner. */ +}; diff --git a/netat/macfile.h b/netat/macfile.h new file mode 100644 index 0000000..d1402b2 --- /dev/null +++ b/netat/macfile.h @@ -0,0 +1,122 @@ +/* + * $Author: djh $ $Date: 1991/06/13 11:14:52 $ + * $Header: /mac/src/cap60/netat/RCS/macfile.h,v 2.2 1991/06/13 11:14:52 djh Rel djh $ + * $Revision: 2.2 $ +*/ + +/* + * macfile.h - header file with Macintosh file definitions + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Sept 1987 Created by Charlie + * + */ + +#ifndef _MACFILE + +#define MAXCLEN 199 /* max size of a comment string */ +#define FINFOLEN 32 /* Finder info is 32 bytes */ +#define MAXMACFLEN 31 /* max Mac file name length */ + +/* finder info defintions */ +typedef struct { + byte fi_fndr[FINFOLEN]; /* finder info */ + word fi_attr; /* attributes */ + byte fi_comln; /* length of comment */ + byte fi_comnt[MAXCLEN+1]; /* comment string */ +} OldFileInfo; +#define FI_BASELENGTH (FINFOLEN+3) + +typedef struct { + byte fi_fndr[FINFOLEN]; /* finder info */ + word fi_attr; /* attributes */ +#define FI_MAGIC1 255 + byte fi_magic1; /* was: length of comment */ +#define FI_VERSION 0x10 /* version major 1, minor 0 */ + /* if we have more than 8 versions wer're */ + /* doiong something wrong anyway */ + byte fi_version; /* version number */ +#define FI_MAGIC 0xda + byte fi_magic; /* magic word check */ + byte fi_bitmap; /* bitmap of included info */ +#define FI_BM_SHORTFILENAME 0x1 /* is this included? */ +#define FI_BM_MACINTOSHFILENAME 0x2 /* is this included? */ + byte fi_shortfilename[12+1]; /* possible short file name */ + byte fi_macfilename[32+1]; /* possible macintosh file name */ + byte fi_comln; /* comment length */ + byte fi_comnt[MAXCLEN+1]; /* comment string */ +#ifdef USE_MAC_DATES + byte fi_datemagic; /* sanity check */ +#define FI_MDATE 0x01 /* mtime & utime are valid */ +#define FI_CDATE 0x02 /* ctime is valid */ + byte fi_datevalid; /* validity flags */ + byte fi_ctime[4]; /* mac file create time */ + byte fi_mtime[4]; /* mac file modify time */ + byte fi_utime[4]; /* (real) time mtime was set */ +#endif USE_MAC_DATES +} FileInfo; + +/* Atribute flags */ +#define FI_ATTR_SETCLEAR 0x8000 /* set-clear attributes */ +#define FI_ATTR_READONLY 0x20 /* file is read-only */ +#define FI_ATTR_ROPEN 0x10 /* resource fork in use */ +#define FI_ATTR_DOPEN 0x80 /* data fork in use */ +#define FI_ATTR_MUSER 0x2 /* multi-user */ +#define FI_ATTR_INVISIBLE 0x1 /* invisible */ + +/**** MAC STUFF *****/ + +/* Flags */ +#define FNDR_fOnDesk 0x1 +#define FNDR_fHasCustomIcon 0x0400 +#define FNDR_fHasBundle 0x2000 +#define FNDR_fInvisible 0x4000 +/* locations */ +#define FNDR_fTrash -3 /* File in Trash */ +#define FNDR_fDesktop -2 /* File on desktop */ +#define FNDR_fDisk 0 /* File in disk window */ + +/* finder info structure */ +/* Note: technically, the fileFinderInfo and the dirFinderInfo should be */ +/* seperated into FileInfo, XFileInfo, and DirINfo, XDirInfo */ +typedef struct { + /* base finder information */ + byte fdType[4]; /* File type [4]*/ + byte fdCreator[4]; /* File creator [8]*/ + word fdFlags; /* Finder flags [10]*/ + word fdLocation[2]; /* File's location [14] */ + word fdFldr; /* File's window [16] */ + /* extended finder information */ + word fdIconID; /* Icon ID [18] */ + word fdUnused[4]; /* Unused [26] */ + word fdComment; /* Comment ID [28] */ + dword fdPutAway; /* Home directory ID [32] */ +} fileFinderInfo; + +typedef struct { + word frRect[4]; /* [rect] Folder'r rectange [8] */ + word frFlags; /* Folder's flags [10] */ + word frLocation[2]; /* Folder's location [14] */ + word frView; /* Folder's view [16] */ + /* extended finder information */ + word frScroll[2]; /* (Point) Scroll position [20] */ + dword frOpenChain; /* dir id chain of open folders [24] */ + byte frScript; /* script flag and code [26] */ + byte frXFlags; /* reserved [27] */ + word frComment; /* Comment id [28] */ + dword frPutAway; /* home directory id [32] */ +} dirFinderInfo; + +typedef union { + dirFinderInfo dir; + fileFinderInfo file; +} FinderInfo; + +#define _MACFILE +#endif /* MACFILE */ diff --git a/netat/sysvcompat.h b/netat/sysvcompat.h new file mode 100644 index 0000000..edb74c6 --- /dev/null +++ b/netat/sysvcompat.h @@ -0,0 +1,237 @@ +/* + * $Author: djh $ $Date: 1996/09/10 13:51:34 $ + * $Header: /mac/src/cap60/netat/RCS/sysvcompat.h,v 2.12 1996/09/10 13:51:34 djh Rel djh $ + * $Revision: 2.12 $ + * + */ + +#ifndef _sysvcompat_h_ +#define _sysvcompat_h_ + +/* + * sysvcompat.h - header file to allow us to port to sys v system machines + * without building a library of "compatible function" for functions that + * have slightly different name, etc. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * June, 1987 CCKim Created + * + */ + +/* + * Mappings from bsd to sysv string and bytestring function + * + */ + +#ifdef SOLARIS +# define B2S_STRING_MAPON /* map strings */ +# define B2S_BSTRING_MAPON /* map byte string instructions */ +# define USETIMES /* use times instead of getrusage */ +# define NOWAIT3 /* no wait3 */ +# define NOSIGMASK /* sigblock() doesn't exist */ +# define USERAND /* use srand, rand */ +# define USEGETCWD /* use getcwd instead of bsd getwd */ +# define NOPGRP /* no process groups (setpgrp, killpg) */ +# define NOVFORK /* no vfork in system */ +# define NEEDFCNTLDOTH /* if need fcntl.h for O_... */ +# define USESYSVLP /* use system V lp command instead of lpr */ +# define USEDIRENT /* use struct dirent */ +# define SHADOW_PASSWD +/* Hopefully, this is safe */ +# define L_SET 0 /* absolute offset */ +# define L_INCR 1 /* relative to current offset */ +# define L_XTND 2 /* relative to end of file */ +# define LB_SET 3 /* abs. block offset */ +# define LB_INCR 4 /* rel. block offset */ +# define LB_XTND 5 /* block offset rel. to eof */ +#endif SOLARIS + +#ifdef hpux +# define B2S_STRING_MAPON /* map strings */ +# define B2S_BSTRING_MAPON /* map byte string instructions */ +# define USECHOWN /* sysv allows us */ +# define NEEDFCNTLDOTH /* if need fcntl.h for O_... */ +# define USETIMES /* use times instead of getrusage */ +/* some versions may have this */ +#ifndef __hpux +# define NOWAIT3 /* no wait3 */ +# define NODUP2 /* no dup2 */ +# define NOLSTAT /* no symbolic links */ +# define NOPGRP /* no process groups (setpgrp, killpg) */ +#else __hpux +# define _BSD +# define POSIX +# define NOWAIT3 /* no rusage support for AUFS under HP-UX */ +# define WSTATUS union wait /* at least for HP-UX 9.01 if _BSD ... */ +/* # define ADDRINPACK */ +#endif __hpux +# define USERAND /* use srand, rand */ +# define USEGETCWD /* use getcwd instead of bsd getwd */ +# define NOUTIMES /* no utimes - use utime */ +#endif hpux + +#ifdef aux +# define B2S_STRING_MAPON /* map strings */ +# define B2S_BSTRING_MAPON /* map byte string instructions */ +# define USECHOWN /* sysv allows us */ +# define USETIMES /* use times instead of getrusage */ +# define USERAND /* use srand, rand */ +# define NOVFORK /* no vfork in system */ +#endif aux + +#ifdef uts +# define USETIMES /* getrusage - "use times not rusage" */ +# define NOWAIT3 /* wait3 - "no wait3, use wait" */ +# define USERAND /* random - "use rand,srand not random" */ +# define USEGETCWD /* getwd - "use getcwd not getwd" */ +# define NOUTIMES /* utimes - "use utime not utimes" */ +# define NOPGRP /* killpg - "missing setpgrp or killpg" */ +# define NOVFORK /* vfork - "novfork, use fork" */ +# define NEEDFCNTLDOTH /* if need fcntl.h for O_... */ +# define USECHOWN /* sysv allows us */ +# define NORUSAGE /* getrusage() is in resource.h, but not supported */ +# define NOSIGMASK /* sigblock() doesn't exist */ +# define USEDIRENT /* use struct dirent */ +# define USESTRINGDOTH /* use system V string.h */ +# define USESYSVLP /* use system V lp command instead of lpr */ +#endif uts + +#ifdef xenix5 +# define B2S_STRING_MAPON /* map strings */ +# define B2S_BSTRING_MAPON /* map byte string instructions */ +# define USETIMES /* use times instead of getrusage */ +# define NOWAIT3 /* no wait3 */ +# define NODUP2 /* no dup2 */ +# define NOLSTAT /* no symbolic links */ +# define USERAND /* use srand, rand */ +# define USEGETCWD /* use getcwd instead of bsd getwd */ +# define NOUTIMES /* no utimes - use utime */ +# define NOPGRP /* no process groups (setpgrp, killpg) */ +# define NOVFORK /* no vfork in system */ +# define NEEDFCNTLDOTH /* if need fcntl.h for O_... */ +# define USECHOWN /* sysv allows us */ +/* added by hand: */ +# define NEEDMSGHDR /* the one defined for us is broken */ +# define MAXPATHLEN 256 /* a guess! */ +# define NGROUPS 1 /* max number of groups per process */ +# define NOSIGMASK /* setsigmask() et al not available */ +# define L_SET 0 +# define L_INCR 1 +# define L_XTND 2 +#endif xenix5 + +#ifdef drsnx +#define USESYSVLP +#define USEDIRENT +#endif drsnx + +#ifdef EPIX +#define WSTATUS union wait +#endif EPIX + +#if defined (hp300) && !defined(__BSD_4_4__) +#define WSTATUS union wait +#endif /* hp300 && __BSD_4_4__) */ + +#ifdef NeXT +#define WSTATUS union wait +/* WIFSTOPPED and WIFSIGNALED are defined in "sys/wait.h". */ +#define W_TERMSIG(status) ((status).w_termsig) +#define W_COREDUMP(status) ((status).w_coredump) +#define W_RETCODE(status) ((status).w_retcode) +#endif /* NeXT */ + +/* FIXED CONFIGURATION -- ALL NEW CONFIGURATIONS MUST PRECEED */ + +/* map sigcld to sigchld if sigchld isn't there */ +#ifndef SIGCHLD +# ifdef SIGCLD +# define SIGCHLD SIGCLD +# endif SIGCLD +#endif SIGCHLD + +#ifdef B2S_STRING_MAPON +# ifndef USESTRINGDOTH +# define USESTRINGDOTH /* must use string.h */ +# endif USESTRINGDOTH +# define index(s,c) strchr((char *)(s),(c)) +# define rindex(s,c) strrchr((char *)(s),(c)) +#endif B2S_STRING_MAPON + +#ifdef B2S_BSTRING_MAPON +# ifdef SOLARIS +# define bcopy(s,d,l) memmove((char *)(d),(char *)(s),(l)) +# else SOLARIS +# define bcopy(s,d,l) memcpy((char *)(d),(char *)(s),(l)) +# endif SOLARIS +# define bcmp(b1,b2,l) memcmp((char *)(b1),(char *)(b2),(l)) +# define bzero(b,l) memset((char *)(b),0,(l)) +#endif B2S_BSTRING_MAPON + +/* + * WAIT/WAIT3 Compatibility Section + * + * We should allow the system to define the WIF macros if possible. + * It's not our place to do so unless we have to, even then + * it is somewhat dubious... should be done per machine, especially + * the W_COREDUMP() macro. + * + * If your system uses (union wait) as its status parameter it + * is a bit out of touch with modern times. Nevertheless + * add "#define WSTATUS union wait" under a conditional + * for your machine in the code above. + * + * We have NO guarantee that if the system doesn't define these + * (or their POSIX/SYSV equivalents) that the ones we define + * WILL work. It is up to the person porting this code to + * determine this and define the functions beforehand to + * do something else if their functionality is not supported + * by your system. + * + */ + +#include + +#ifndef WSTATUS +#define WSTATUS int +#endif WSTATUS + +#ifndef WIFSTOPPED +#define WIFSTOPPED(status) ((*((int *)&status) & 0xff) == 0177) +#endif WIFSTOPPED + +#ifndef WIFSIGNALED +#define WIFSIGNALED(status) ((*((int *)&status) & 0177) != 0) +#endif WIFSIGNALED + +#ifndef W_TERMSIG +#ifdef WTERMSIG +#define W_TERMSIG WTERMSIG +#else WTERMSIG +#define W_TERMSIG(status) (*((int *)&status) & 0177) +#endif WTERMSIG +#endif W_TERMSIG + +#ifndef W_COREDUMP +#ifdef WCOREDUMP +#define W_COREDUMP WCOREDUMP +#else WCOREDUMP +#define W_COREDUMP(status) (*((int *)&status) & 0200) +#endif WCOREDUMP +#endif W_COREDUMP + +#ifndef W_RETCODE +#ifdef WEXITSTATUS +#define W_RETCODE WEXITSTATUS +#else WEXITSTATUS +#define W_RETCODE(status) (*((int *)&status) >> 8) +#endif WEXITSTATUS +#endif W_RETCODE + +#endif /* _sysvcompat_h_ */ diff --git a/samples/Makefile.m4 b/samples/Makefile.m4 new file mode 100644 index 0000000..ed7f48a --- /dev/null +++ b/samples/Makefile.m4 @@ -0,0 +1,117 @@ +CFLAGS=cflags() specialcflags() +I=includedir() +O= + +# Valid: SFLOWQ=[1,2,3,4,5,6,7,8] +LWFLAGS=lwflags() + +# location of cap.printers file +ifdef([capprinters],[CAPPRINTERS=-DCAPPRINTERS=]capprinters()) + +# Make sure to define needgetopt if your system doesnt have it +GETOPT=ifdef([needgetopt],[att_getopt.o]) + +ifdef([useatis],[],[# ])ATISPROGS=atistest +PROGS=lwpr tlw atlook atlooklws atpinger iwpr isrv ash \ + instappl getzones papstatus ${ATISPROGS} + +DESTDIR=capdestdir() +CAPLIB=libcap() +AFPLIB=libafpc() libafp() + +# for other libraries (like BSD on hpux) +SLIB=libspecial() + +all: ${PROGS} + +atistest: atistest.o ${O} + ${CC} ${LFLAGS} -o atistest atistest.o ${O} ${CAPLIB} ${SLIB} + +getzones: getzones.o ${O} + ${CC} ${LFLAGS} -o getzones getzones.o ${O} ${CAPLIB} ${SLIB} + +papstatus: papstatus.o ${O} + ${CC} ${LFLAGS} -o papstatus papstatus.o ${O} ${CAPLIB} ${SLIB} + +papstatus.o: papstatus.c + ${CC} ${CFLAGS} ${LWFLAGS} ${CAPPRINTERS} -c papstatus.c + +ash.o: ash.c + ${CC} ${CFLAGS} bigcflags() -c ash.c + +ash: ash.o ${CAPFILES} + ${CC} ${LFLAGS} -o ash ash.o ${CAPFILES} ${AFPLIB} ${CAPLIB} ${SLIB} + +instappl: instappl.o $(GETOPT) + ${CC} ${LFLAGS} -o instappl instappl.o $(GETOPT) ${SLIB} + +# iwpr and lwpr share sources... +iwpr: iwpr.o $(O) ${GETOPT} + ${CC} ${LFLAGS} -o iwpr iwpr.o ${GETOPT} $(O) $(CAPLIB) ${SLIB} + +lwpr: lwpr.o $(O) ${GETOPT} + ${CC} ${LFLAGS} -o lwpr lwpr.o ${GETOPT} $(O) $(CAPLIB) ${SLIB} + +lwpr.o: lwpr.c + ${CC} ${CFLAGS} ${LWFLAGS} ${CAPPRINTERS} -c lwpr.c + +iwpr.o: lwpr.c + cp lwpr.c iwpr.c + ${CC} ${CFLAGS} ${LWFLAGS} -c -DIMAGEWRITER iwpr.c + rm iwpr.c + +isrv: isrv.o $(O) + ${CC} $(LFLAGS) -o isrv isrv.o $(O) $(CAPLIB) ${SLIB} + +isrv.o: isrv.c + ${CC} ${CFLAGS} -c isrv.c + +tlw: tlw.o $(O) ${GETOPT} + ${CC} ${LFLAGS} -o tlw tlw.o $(O) ${GETOPT} $(CAPLIB) ${SLIB} + +tlw.o: tlw.c + ${CC} ${CFLAGS} ${LWFLAGS} ${CAPPRINTERS} -c tlw.c + +# +# atlook, atlooklw, and atpinger all have a common source +# +atlook: atlook.o ${GETOPT} $(O) + ${CC} ${LFLAGS} -o atlook atlook.o $(O) ${GETOPT} $(CAPLIB) ${SLIB} + +# copy because some machines won't do it right o.w. +atlooklws.o: atlook.c + cp atlook.c atlooklws.c + ${CC} ${CFLAGS} -c -DATLOOKLWS atlooklws.c + rm atlooklws.c + +atlooklws: atlooklws.o ${GETOPT} ${O} + ${CC} ${LFLAGS} -o atlooklws atlooklws.o ${GETOPT} $(O) $(CAPLIB) ${SLIB} + +atpinger.o: atlook.c + cp atlook.c atpinger.c + ${CC} ${CFLAGS} -c -DATPINGER atpinger.c + rm atpinger.c + +atpinger: atpinger.o $(O) ${GETOPT} + ${CC} ${LFLAGS} -o atpinger atpinger.o ${GETOPT} $(O) $(CAPLIB) ${SLIB} + +att_getopt.c: + ln -s ../extras/att_getopt.c + +install: ${PROGS} + -strip ${PROGS} + ifdef([sysvinstall],[install -f $(DESTDIR) ${PROGS}], + [${INSTALLER} ${PROGS} ${DESTDIR}]) + +clean: + -rm -f ${PROGS} *.o core make.log err *~ att_getopt.c + +spotless: + -rm -f ${PROGS} *.o *.orig core make.log err *~ att_getopt.c + -rm -f Makefile makefile + +cleanexe: + -rm -f ${PROGS} + +dist: + @cat todist diff --git a/samples/README b/samples/README new file mode 100644 index 0000000..cbe5bdd --- /dev/null +++ b/samples/README @@ -0,0 +1,36 @@ + atlook.c - Find AppleTalk entities and print information + - note: look.c contains the code for looks and pinger + atlooklws - Find AppleTalk LaserWriters and print status + atpinger - Utilizes the echo protocol to "ping" hosts + lwpr.c - Send file to AppleTalk LaserWriter + - note: lwpr also contains the code for iwpr + iwpr - send file to appletalk imagewriter + tlw.c - Talks to LaserWriter "Interactive Executive" + isrv.c - Sample Server for ImageWriter (experimental) + ash.c - sample afp client - allows a few things to be done + with appleshare servers + instappl.c - allows an application to be installed into a unix + aufs volume + getzones.c - return list of zones from KIP (1/88 or later) + +You can get a basic idea of what some of these programs do by reading the +"man" pages in ../man: + + CAP.8: overview + atis.8: atistest + atprint.1: lwpr, lsrv, isrv, tlw + atlook.1: atlook, atlooklws, atpinger + +atistest is for verification in the installation procedure. + +General notes: + +tlw, lwpr, iwpr now have a settable "Send Flow Quantum multiplier" +called SFLOWQ that tells them how many pkts they can send in a single +write. You should set this to 1 if you have problems with the +FastPath or LaserWriter dropping packets. By default, it is set to 8 +(the maximum). Setting fastether in m4.setup sets it to 1. + +lwpr makes use of /etc/cap.printers. This really isn't necessary, but +it's nice. + diff --git a/samples/ash.c b/samples/ash.c new file mode 100644 index 0000000..8730010 --- /dev/null +++ b/samples/ash.c @@ -0,0 +1,1102 @@ +/* + * $Author: djh $ $Date: 1996/04/25 13:03:09 $ + * $Header: /mac/src/cap60/samples/RCS/ash.c,v 2.6 1996/04/25 13:03:09 djh Rel djh $ + * $Revision: 2.6 $ +*/ + +/* + * ash.c - afp "shell" - lets us try executing some afp client calls + * + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Edit History: + * + * March 1987 CCKim Created. + * + */ + +char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#ifndef _TYPES +# include /* include if not there */ +#endif +#ifdef AIX +#include +#endif AIX +#include /* so htons() works for non-vax */ +#include +#include +#include +#include /* include appletalk definitions */ +#include +#include +#include +#include +#ifdef NEEDFCNTLDOTH +# include +#endif +#ifdef USESTRINGDOTH +# include +#else +# include +#endif + +usage(s) +char *s; +{ + fprintf(stderr,"usage: %s [-d FLAGS] nbpentity\n",s); + exit(1); +} + +doargs(argc,argv,name) +int argc; +char **argv; +char **name; +{ + char *s,*whoami = argv[0]; + int sawentity; + + sawentity = 0; /* no entity seen yet */ + while (--argc) { + s = *++argv; /* move to next arg */ + if (s[0] == '-') { /* option? */ + switch (s[1]) { + case 'd': + case 'D': + dbugarg(&s[2]); /* some debug flags */ + break; + default: + usage(whoami); + } + continue; + } + if (sawentity) + usage(whoami); + sawentity = 1; /* got here because have non-flag arg */ + *name = s; + } +} + +main(argc,argv) +int argc; +char **argv; +{ + int err,i; + AddrBlock addr; /* Address of entity */ + char *name = "Charlie\250:afpserver@*"; + + abInit(TRUE); /* initialize appletalk driver */ + nbpInit(); /* initialize nbp */ + checksum_error(FALSE); /* ignore these errors */ + doargs(argc,argv,&name); /* handle arguments */ + lookup(name, &addr); + handle_afp(&addr); +} + +lookup(name, addr) +char *name; +AddrBlock *addr; +{ + EntityName en; /* network entity name */ + NBPTEntry nbpt[1]; /* table of entity names */ + nbpProto nbpr; /* nbp protocol record */ + int err; + + create_entity(name, &en); + + nbpr.nbpRetransmitInfo.retransInterval = sectotick(1); + nbpr.nbpRetransmitInfo.retransCount = 10; /* one retransmit */ + nbpr.nbpBufPtr = nbpt; /* place to store entries */ + nbpr.nbpBufSize = sizeof(nbpt); /* size of above */ + nbpr.nbpDataField = 1; /* max entries */ + nbpr.nbpEntityPtr = &en; /* look this entity up */ + +#ifdef ISO_TRANSLATE + cISO2Mac(en.objStr.s); + cISO2Mac(en.typeStr.s); + cISO2Mac(en.zoneStr.s); +#endif ISO_TRANSLATE + + printf("Looking for %s:%s@%s ...\n",en.objStr.s,en.typeStr.s,en.zoneStr.s); + + /* Find all objects in specified zone */ + err = NBPLookup(&nbpr,FALSE); /* try synchronous */ + if (err != noErr || nbpr.abResult != noErr) { + fprintf(stderr,"NBPLookup returned err %d\n",err); + exit(1); + } + if (nbpr.nbpDataField == 0) { + printf("Couldn't find it\n"); + exit(1); + } + NBPExtract(nbpt,nbpr.nbpDataField,1,&en,addr); +} + +GSPRPPtr vpr; + +int goodversion = FALSE; /* valid version of AFP? */ +int valid_uams[3] = {FALSE, FALSE, FALSE}; + +handle_afp(addr) +AddrBlock *addr; +{ + int srn, comp, cnt, attn(); + GetSrvrInfoReplyPkt sr; + + if (FPGetSrvrInfo(addr, &sr) != noErr) { + fprintf(stderr, "Server not responding\n"); + exit(1); + } + initclient(&sr); + + SPOpenSession(addr, attn, &srn, 2, -1, &comp); + while (comp > 0) + abSleep(4*9, TRUE); + fprintf(stderr, "comp = %d\n", comp); + if (comp < 0) { + printf("Can't open session to remote, error %d\n",comp); + return; + } + tryit(srn); + SPCloseSession(srn, 10, -1, &comp); + while (comp > 0) + abSleep(4*9, TRUE); +} + +byte * +makepath(str) +byte *str; +{ + static byte path[256]; + int len, i; + + if (str != NULL && str[0] == ' ') + str++; + len = str == NULL ? 0 : strlen((char *)str); + if (len > 255) + len = 255; + path[0] = len; + for (i=0; i < len; i++) + path[i+1] = (str[i] == ':') ? '\0' : str[i]; + return(path); +} + +initclient(sr) +GetSrvrInfoReplyPkt *sr; +{ + char tbuf[32]; + int i, j, n; + /* Order matters */ + static char *uam_which[3] = { + "No User Authent", + "Cleartxt passwrd", + "Randnum exchange" + }; + + printf("%s server name ",sr->sr_machtype); + dumppstr(sr->sr_servername); + putchar('\n'); + n = IndStrCnt(sr->sr_avo); + for (i = 0; i < n; i++) { + GetIndStr(tbuf, sr->sr_avo, i); + if (strcmp("AFPVersion 1.1", tbuf) == 0) { + goodversion = TRUE; + break; + } + } + /* ignore for now */ + n = IndStrCnt(sr->sr_avo); + printf("There %s %d AVO strings\n", n > 1 ? "are" : "is", n); + PrtIndStr(sr->sr_avo); + n = IndStrCnt(sr->sr_uamo); + printf("There %s %d UAM strings\n", n > 1 ? "are" : "is", n); + PrtIndStr(sr->sr_uamo); + for (i=0; i < n; i++) { + GetIndStr(tbuf, sr->sr_uamo, i); + for (j = 0; j < 3; j++) + if (strcmp(uam_which[j], tbuf) == 0) + valid_uams[j] = TRUE; + } +} + +attn(srn, sessid, attncode) +int srn; +byte sessid; +word attncode; +{ + if ((attncode & 0xf000) == 0x8000) { + if ((attncode & 0xfff) != 0xfff) + fprintf(stderr,"***Server going down in %d minutes***\n",attncode&0xfff); + else + fprintf(stderr,"***Server shutdown canceled***\n"); + } + printf("***ATTN*** Session %d, code %x\n",(int)sessid,(int)attncode); +} + + +/* + * Dump a PAP status message +*/ +dumppstr(pstr) +unsigned char *pstr; +{ + int len = (int)(pstr[0]); + unsigned char *s = &pstr[1]; + + while (len--) { + if (isprint(*s)) + putchar(*s); + else + printf("\\%o",*s&0xff); + s++; + } + return(((int)(pstr[0]))+1); +} + +dumpstr(str) +byte *str; +{ + if (str == NULL) + return; + while (*str) { + if (isprint(*str)) + putchar(*str); + else + printf("\\%o",*str&0xff); + str++; + } +} + +tryit(srn) +int srn; +{ + char *getline(), *getpass(); + char line[256]; + int i, n; + dword curdirid, cr; + word volid; + GetVolParmsReplyPkt op; + char *tokstart; + + setbuf(stdin,NULL); + printf("User name?\n"); + if (getline(line)==NULL) + return; + if (!dologin(srn,line,line[0] == '\0' ? ((char *)0) : getpass("Password:"))) { + fprintf(stderr, "Login failed!\n"); + return; + } + getsrvp(srn); + + if (vpr->gspr_nvols == 0) { + printf("There are no volumes, you can't do anything!\n"); + printf("Bye.\n"); + return; + } + + if (vpr->gspr_nvols == 1) { + strcpy(line, (char *)vpr->gspr_volp[0].volp_name); + } else { + printf("What volume?\n"); + if (getline(line) == NULL) + return; + } + if (eFPOpenVol(srn, (word)VP_VOLID, line, NULL, &op, &cr) < 0) { + if (cr != 0) + aerror("Openvol",cr); + printf("Volume open failed\n"); + return; + } + if (cr != 0) + aerror("Openvol",cr); + volid = op.gvpr_volid; + printf("VOLID = %x\n",volid); + + curdirid = 0x2; /* root directory */ + do { + putchar('>'); fflush(stdout); + if (getline(line) == NULL) + break; + if ((tokstart = index(line, ' ')) != NULL) { + while (*tokstart == ' ') tokstart++; + } + switch (line[0]) { + case 'x': + { + FileDirParm epar; + dostat(srn, volid, 1, "ds", &epar); + printf("dir: %x\n", epar.fdp_parms.dp_parms.dp_dirid); + printf("pdir: %x\n", epar.fdp_pdirid); + } + break; + case 'd': + if (strncmp(line, "delete", 6) == 0) { + dodelete(srn, volid, curdirid, tokstart); + printf("okay\n"); + break; + } + n = nchild(srn, volid, curdirid, tokstart); + if (n >= 0) { + printf("%d items\n",n); + dodir(srn, volid, curdirid, n+1, tokstart, TRUE); + printf("okay\n"); + } else printf("nothing there?"); + break; + case 'l': + n = nchild(srn, volid, curdirid, tokstart); + if (n >= 0) { + printf("%d items\n",n); + dodir(srn, volid, curdirid, n+1, tokstart, FALSE); + printf("okay\n"); + } + break; + case 'c': + if (strncmp(line, "croot",5) == 0) { + curdirid = 0x2; + printf("back to root\n"); + break; + } + movetodir(srn, volid, curdirid, tokstart, &curdirid); + printf("okay\n"); + break; + case 'g': + getfile(srn, volid, curdirid, tokstart); + printf("okay\n"); + break; + case 'p': + putfile(srn, volid, curdirid, tokstart); + printf("okay\n"); + break; + case 'm': + if (strncmp("mkdir",line, 5) == 0) { + domakedirectory(srn, volid, curdirid, tokstart); + printf("okay\n"); + break; + } + break; + case '?': + printf("Valid commands are:\n"); + printf("d[irectory] - long file listing\n"); + printf("l[s] - short file listing\n"); + printf("c[d] - connect to directory\n"); + printf("g[et] - get a file\n"); + printf("p[ut] - put a file\n"); + break; + } + } while (1); +} + +dodelete(srn, volid, dirid, path) +int srn; +word volid; +dword dirid; +byte *path; +{ + dword cr; + int comp; + + comp = eFPDelete(srn, volid, dirid, makepath(path), &cr); + if (comp < 0) + return(FALSE); + if (cr != noErr) + aerror("FPDelete"); + return(TRUE); +} + +domakedirectory(srn, volid, dirid, path) +int srn; +word volid; +dword dirid; +byte *path; +{ + dword cr, newdirid; + int comp; + + comp = eFPCreateDir(srn, volid, dirid, makepath(path), &newdirid, &cr); + if (comp < 0) + return(FALSE); + if (cr != noErr) + aerror("FPCreateDir"); + return(TRUE); +} + +dologin(srn, user, passwd) +int srn; +char *user, *passwd; +{ + char pbuf[9]; + int i; + dword cr = 0; + int uam = 2; + + if (!valid_uams[uam]) + uam = 1; + if (!valid_uams[uam]) + uam = 0; + if (user[0] == '\0') + uam=0; + else { + strcpy(pbuf, passwd); + for (i=strlen(pbuf); i < 8; i++) + pbuf[i] = '\0'; + } + eFPLogin(srn, user, pbuf, uam, &cr); + if (cr != 0) { + if (cr == aeMiscErr) { + fprintf(stderr,"Bad user name\n"); + return(FALSE); + } + aerror("Login",cr); + return(FALSE); + } + return(TRUE); +} + +dodir(srn, volid, dirid, toalloc, path, verbose) +int srn; +word volid; +dword dirid; +int toalloc; +byte *path; +int verbose; +{ + FileDirParm *epar, *ep; /* room for 100 entries */ + FileParm *fp; + DirParm *dp; + int cnt,i,j,comp, totcnt; + dword cr; + int idx = 0; + byte *newpath; + + if (toalloc == 0) + return; + if ((epar = (FileDirParm *)malloc(toalloc*sizeof(FileDirParm))) == + (FileDirParm *)NULL) { + fprintf(stderr, "Out of memory\n"); + return; + } + totcnt = 0; + cr = 0L; + newpath = makepath(path); + while (cr != aeObjectNotFound && idx < toalloc) { + comp = eFPEnumerate(srn, volid, dirid, newpath, idx+1, + (word)0x74e, (word)0x1f42, + epar+idx, toalloc-idx, &cnt, &cr); + if (comp != noErr) { + printf( "bad comp %d\n",comp); + free(epar); + return; + } + if (cr != 0 && cr != aeObjectNotFound) { + aerror("FPEnumerate",cr); + free(epar); + return; + } + if (cr == 0) { + idx += cnt; + totcnt += cnt; + } + } + printf("Count = %d\n",totcnt); + for (ep = &epar[0], i = 0 ; i < totcnt; i++, ep++) { + dumppstr(ep->fdp_lname); + if (ep->fdp_flg) { + dp = &ep->fdp_parms.dp_parms; + printf(" [dir - %d, %d, %d entries]\n", + dp->dp_dirid, ep->fdp_pdirid,dp->dp_nchild); + printf(" owner = %x, group = %x, accright = %x\n", + dp->dp_ownerid, /* owner id */ + dp->dp_groupid, /* group id */ + dp->dp_accright); /* access rights */ + } else { + long clock; + char *created; + char *ctime(); + + fp = &ep->fdp_parms.fp_parms; + printf(" [file %d, %d], ", fp->fp_fileno, ep->fdp_pdirid); + printf("res %d + dat %d = %d", + fp->fp_rflen, fp->fp_dflen, fp->fp_rflen+fp->fp_dflen); + if (verbose) { + long clock; + clock = ep->fdp_cdate; + created = ctime(&clock); + created[24] = '\0'; + printf(", cr: %s, ", created); + clock = ep->fdp_mdate; + printf("mo: %s", ctime(&clock)); + } + else + putchar('\n'); + } + } + free(epar); +} + + +dogetdir(srn, volid, dirid, toalloc) +int srn; +word volid; +dword dirid; +int toalloc; +{ + FileDirParm *epar, *ep; /* room for 100 entries */ + FileParm *fp; + DirParm *dp; + int cnt,i,j,comp, totcnt; + dword cr; + int idx = 0; + byte path[256]; + + if (toalloc == 0) + return; + if ((epar = (FileDirParm *)malloc(toalloc*sizeof(FileDirParm))) == + (FileDirParm *)NULL) { + fprintf(stderr, "Out of memory\n"); + return; + } + totcnt = 0; + cr = 0L; + path[0] = 0; + while (cr != aeObjectNotFound && idx < toalloc) { + comp = eFPEnumerate(srn, volid, dirid, path, idx+1, + (word)0x74e, (word)0x1f42, + epar+idx, toalloc-idx, &cnt, &cr); + if (comp != noErr) { + printf( "bad comp %d\n",comp); + free(epar); + return; + } + if (cr != 0 && cr != aeObjectNotFound) { + aerror("FPEnumerate",cr); + free(epar); + return; + } + if (cr == 0) { + idx += cnt; + totcnt += cnt; + } + } + printf("Count = %d\n",totcnt); + for (ep = &epar[0], i = 0 ; i < totcnt; i++, ep++) { + printf("Copying: "); + dumppstr(ep->fdp_lname); + fflush(stdout); + if (FDP_ISDIR(ep->fdp_flg)) { + putchar('\n'); + dp = &ep->fdp_parms.dp_parms; + dogetdir(srn, volid, dp->dp_dirid, dp->dp_nchild); + } else { + fp = &ep->fdp_parms.fp_parms; + cpyp2cstr(path, ep->fdp_lname); + getfile(srn, volid, dirid, path); + printf("...Okay\n"); + } + } + free(epar); +} + +getfile(srn, volid, dirid, path) +int srn; +word volid; +dword dirid; +byte *path; +{ + int len, i; + int myfd; + char buf[512]; + char *p; + byte *usepath = makepath(path); + FileDirParm epar; + char *cvtmactounix(); + + if (path == NULL) /* NULL name means no file */ + return; + if ((p = rindex((char *)path, ':')) == NULL) /* any ':'s in name? */ + p = (char *)path; /* skip space */ + else + p++; /* skip over colon */ + + p = cvtmactounix(p); + if (!finderinfo(srn, volid, dirid, path, &epar) < 0) { + return; + } + if (FDP_ISDIR(epar.fdp_flg)) { + dogetdir(srn, volid, epar.fdp_parms.dp_parms.dp_dirid, + epar.fdp_parms.dp_parms.dp_nchild); + return; + } + strcpy(buf, "tvol/.finderinfo/"); + strcat(buf, p); + if ((myfd = open(buf,O_CREAT|O_TRUNC|O_RDWR, 0660)) < 0) { + perror(buf); + return; + } + write(myfd, epar.fdp_finfo, sizeof(epar.fdp_finfo)); + close(myfd); + strcpy(buf, "tvol/"); + strcat(buf, p); + if ((myfd = open(buf,O_CREAT|O_TRUNC|O_RDWR, 0660)) < 0) { + perror(buf); + return; + } + if (!dogetfile(srn, myfd, volid, dirid, usepath, FALSE)) { + close(myfd); + unlink(p); /* get rid of incomplete file */ + return; + } + strcpy(buf,"tvol/.resource/"); + strcat(buf,p); + if ((myfd = open(buf,O_CREAT|O_TRUNC|O_RDWR,0770)) < 0) { + perror(buf); + return; + } + dogetfile(srn, myfd, volid, dirid, usepath, TRUE); +} + +dogetfile(srn, myfd, volid, dirid, path, type) +int myfd; +int srn; +word volid; +dword dirid; +byte *path; +{ + char buf[atpMaxData*atpMaxNum]; + int len; + int eof = 0; + dword offset = 0, cr; + word fd; + int i, comp; + FileDirParm epar; + int toread; + word bitmap; + + bitmap = type ? FP_RFLEN : FP_DFLEN; + + comp = eFPOpenFork(srn, volid, dirid, 0x1, path, type, + bitmap, &epar, &fd, &cr); + if (comp < 0) { + return(FALSE); + } + if (cr != noErr) { + aerror("OpenFork", cr); + return(FALSE); + } + toread= type ? epar.fdp_parms.fp_parms.fp_rflen : + epar.fdp_parms.fp_parms.fp_dflen; + do { + /* try to read more in case file size is off */ + len = eFPRead(srn, fd, buf, sizeof(buf), sizeof(buf), offset, &cr); + if (len <= 0) { + if (cr != aeEOFErr) + aerror("FPRead",cr); + break; + } + write(myfd, buf, len); + offset += len; + toread -= len; + } while (cr != aeEOFErr); + close(myfd); + eFPCloseFork(srn, fd, &cr); + if (cr != 0) + aerror("CloseFork",cr); + return(TRUE); +} + +int +ugetsize(fd) +int fd; +{ + struct stat sbuf; + + if (fstat(fd, &sbuf) < 0) { + perror("fstat"); + return(0); + } + return(sbuf.st_size); +} + +putfile(srn, volid, dirid, path) +int srn; +word volid; +dword dirid; +byte *path; +{ + int len, i; + int myfd; + char buf[512]; + char *p; + byte *usepath; + FileDirParm epar; + + if (path == NULL) /* NULL name means no file */ + return; + if ((p = rindex((char *)path, '/')) == NULL) + p = (char *)path; + else + p++; /* skip over the slash */ + usepath = makepath(p); + strcpy(buf, "tvol/"); + strcat(buf, (char *)path); + if ((myfd = open(buf,O_RDONLY)) < 0) { + perror(buf); + return; + } + if (!docreatefile(srn, volid, dirid, usepath)) { + close(myfd); + return; + } + strcpy(buf, "tvol/.finderinfo/"); /* finder info file? */ + strcat(buf, (char *)path); + { + int tmpfd ; + if ((tmpfd = open(buf,O_RDONLY)) >= 0) { + read(tmpfd, epar.fdp_finfo, sizeof(epar.fdp_finfo)); + close(tmpfd); + } else { + bzero(epar.fdp_finfo, sizeof(epar.fdp_finfo)); + strcpy((char *)epar.fdp_finfo, "TEXTEDIT"); + } + } + if (setfinderinfo(srn, volid, dirid, p, &epar) != noErr) + return; + if (!doputfile(srn, myfd, volid, dirid, usepath, FALSE)) { + close(myfd); + return; + } + strcpy(buf,"tvol/.resource/"); + strcat(buf,(char *)path); + if ((myfd = open(buf,O_RDONLY)) < 0) { + perror(buf); + return; + } + doputfile(srn, myfd, volid, dirid, usepath, TRUE); +} + +docreatefile(srn, volid, dirid, path) +int srn; +word volid; +dword dirid; +byte *path; +{ + dword cr; + eFPCreateFile(srn, volid, dirid, TRUE, path, &cr); + if (cr != noErr) { + if (cr == aeObjectExists) + return(TRUE); + aerror("createfile", cr); + return(FALSE); + } + return(TRUE); +} + +doputfile(srn, myfd, volid, dirid, path, type) +int srn; +int myfd; +word volid; +dword dirid; +byte *path; +int type; +{ + char wbuf[atpMaxData*atpMaxNum]; + int towrite, comp, written; + dword offset; + word fd; + dword cr; + int retry = 0; + FileDirParm epar; + int wlen; + + comp = eFPOpenFork(srn, volid, dirid, 0x2, path, type, (word)0, &epar, + &fd, &cr); + if (comp < 0) { + return(FALSE); + } + if (cr != noErr) { + aerror("OpenFork", cr); + return(FALSE); + } + + offset = 0; /* make sure we start at zero */ + towrite = ugetsize(myfd); + wlen = written = 0; /* start these at zero... */ + do { + wlen -= written; /* get number of bytes written */ + wlen = read(myfd, wbuf, (atpMaxData)*(atpMaxNum) - wlen); + if (wlen <= 0) { + break; + } + printf("Write %d, offset %d, left %d\n", wlen, offset, towrite); + comp = eFPWrite(srn, fd, wbuf, wlen, towrite, &written, &offset, &cr); + if (comp < 0) { + fprintf(stderr,"FPWRite err %d, %d\n",cr, comp); + eFPCloseFork(srn, fd, &cr); + return(FALSE); + } + if (cr < 0) { + fprintf(stderr,"FPWrite err %d\n",-cr); + break; + } + printf("Wrote %d, last offset %d\n", written, offset); + if (written > wlen) { + fprintf(stderr, "wrote more than requested?\n"); + break; + } + towrite -= written; + } while (1); + eFPCloseFork(srn, fd, &cr); + close(myfd); + return(TRUE); +} + +nchild(srn,volid,dirid,path) +int srn; +word volid; +dword dirid; +byte *path; +{ + FileDirParm epar; + + if (dostat(srn, volid, dirid, path, &epar) != noErr) + return; + if (FDP_ISDIR(epar.fdp_flg)) + return(epar.fdp_parms.dp_parms.dp_nchild); + else + return(-1); +} + +setfinderinfo(srn, volid, dirid, path, epar) +int srn; +word volid; +dword dirid; +byte *path; +FileDirParm *epar; +{ + int comp; + dword cr; + byte *pp = makepath(path); + + comp = eFPSetFileParms(srn, volid, dirid, (word)FP_FINFO, pp, epar, &cr); + if (comp != noErr) + return(comp); + if (cr != noErr) { + aerror("setfinderinfo: GetFileDirParms",cr); + return(-1); + } + return(noErr); +} + +finderinfo(srn,volid,dirid,path, epar) +int srn; +word volid; +dword dirid; +byte *path; +FileDirParm *epar; +{ + return(dostat(srn, volid, dirid, path, epar)); +} + +movetodir(srn,volid,dirid,path,newdirid) +int srn; +word volid; +dword dirid; +char *path; +dword *newdirid; +{ + FileDirParm epar; + + if (dostat(srn, volid, dirid, path, &epar) != noErr) + return(FALSE); + + if (FDP_ISDIR(epar.fdp_flg)) + *newdirid = epar.fdp_parms.dp_parms.dp_dirid; + else + return(FALSE); + return(TRUE); +} + +dostat(srn, volid, dirid, path, epar) +int srn; +word volid; +dword dirid; +byte *path; +FileDirParm *epar; +{ + int comp; + dword cr; + byte *pp = makepath(path); + + comp = eFPGetFileDirParms(srn, volid, dirid, (word)FP_FILNO|FP_FINFO, + (word)DP_DIRID|DP_CHILD|DP_FINFO, pp, epar, &cr); + if (comp != noErr) + return(comp); + if (cr != noErr) { + aerror("GetFileDirParms",cr); + return(-1); + } +} + +getsrvp(srn) +int srn; +{ + dword cr; + int comp,i; + + comp = eFPGetSrvrParms(srn, &vpr, &cr); + if (comp < 0) { + fprintf(stderr,"GetSrvrParms fails with completetion code %d\n",comp); + return(0); + } + if (cr != 0) { + fprintf(stderr, "getSrvrParms fails with code %d\n",-cr); + aerror("",cr); + return(0); + } + printf("Found %d volumes\n",vpr->gspr_nvols); + for (i = 0; i < (int)vpr->gspr_nvols; i++) { + printf("Volume "); + dumpstr(vpr->gspr_volp[i].volp_name); + printf(" has %s password\n", vpr->gspr_volp[i].volp_flag ? "a" : "no"); + } + { + long clock; + printf("Returned server: %ld\n",vpr->gspr_time); + clock = vpr->gspr_time; + printf("Server time %s",ctime(&clock)); + } +} + +/* + * get a line from stdin - really assumed to be a terminal + * + * we go through contortions to ensure that we don't block on reads + * this is because we MUST run protocol + * +*/ +fd_set aset ; +struct timeval t = {0, 250}; +getinit() +{ + t.tv_sec = 0; + t.tv_usec = 250; /* 1 second? */ + FD_ZERO(&aset); + setbuf(stdin, NULL); /* make sure! */ +} + +int +getch() +{ + int c; + int rdy; + + do { + FD_SET(fileno(stdin), &aset); + rdy = select(fileno(stdin)+1, &aset, 0, 0, &t); + if (rdy > 0) { + break; + } + abSleep(1, TRUE); /* .25 second too */ + } while (1); + c = getchar(); + return(c); +} + +char * +getline(bp) +char *bp; +{ + static int eof = FALSE; + int i, c; + + if (eof) + return(NULL); + getinit(); + for (; (c = getch()) != EOF && c != '\n'; bp++) + *bp = c; + if (c == EOF) { + eof = TRUE; + *bp = '\0'; + return(bp); + } + *bp = '\0'; + return(bp); +} + +#ifdef notdef +char * +getpassword(prompt) +char *prompt; +{ + struct sgttyb ttystat; + int saved_flags, i, c; + static char pass[9]; + int (*sig)(); + char *p; + + sig = signal(SIGINT, SIG_IGN); + gtty(fileno(stdin), &ttystat); + saved_flags = ttystat.sg_flags; + ttystat.sg_flags &= ~ECHO; + stty(fileno(stdin), &ttystat); + fprintf(stderr,"%s",prompt); + for (i=0, p=pass; (c=getch()) != '\n' && c != EOF && i < 8; i++, p++) + *p = c; + *p = '\0'; + fprintf(stderr, "\n"); + ttystat.sg_flags = saved_flags; + stty(fileno(stdin), &ttystat); + signal(SIGINT, sig); + return(pass); +} +#endif + +char * +cvtmactounix(path) +byte *path; +{ + static char buf[1025]; + static char *hexdigits = "0123456789abcdef"; + char *p, *bp, c; + int i, len, bcnt; + + if ((p = rindex((char *)path, ':')) == NULL) + p = (char *)path; /* get right name */ + else + p++; /* skip over colon */ + len = strlen(p); /* length of remaining */ + for (i=0, bp=buf, bcnt = 0; i < len; i++) { + c = *p++; + if (!iscntrl(c) && c != ' ' && isprint(c)) { + *bp++ = c; + if (++bcnt >= 1024) + break; + continue; + } + bcnt += 3; + if (bcnt >= 1024) + break; + *bp++ = '\\'; /* \\ */ + *bp++ = hexdigits[(c&0xf0) >> 4]; + *bp++ = hexdigits[(c&0xf)]; + } + *bp++ = '\0'; + return(buf); +} + +/* + * this is a dummy routine for abasp.c + * + */ + +dsiTCPIPCloseSLS() +{ + return(noErr); +} diff --git a/samples/atistest.c b/samples/atistest.c new file mode 100644 index 0000000..9896d48 --- /dev/null +++ b/samples/atistest.c @@ -0,0 +1,138 @@ +static char rcsid[] = "$Author: djh $ $Date: 1991/05/18 10:16:09 $"; +static char rcsident[] = "$Header: /mac/src/cap60/samples/RCS/atistest.c,v 2.2 1991/05/18 10:16:09 djh Exp djh $"; +static char revision[] = "$Revision: 2.2 $"; + +/* + * atistest.c - simple test program to ensure that atis is functioning + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Edit History: + * + * July 27, 1987 CCKim Created. + * +*/ +#include +#include + +#include + +main(argc, argv) +int argc; +char *argv[]; +{ + AddrBlock useaddr; + int skt, err, remove; + struct cap_version *cv; + + if (argc > 1 && strcmp(argv[1], "-r") == 0) + remove = 1; + else + remove = 0; + + cv = what_cap_version(); + printf("%s distribution %d.%02d using %s, %s %s\n", + cv->cv_name, cv->cv_version, cv->cv_subversion, + cv->cv_type, cv->cv_rmonth, cv->cv_ryear); + printf("%s\n\n", cv->cv_copyright); + + abInit(TRUE); + nbpInit(); + dbugarg("n"); + useaddr.net = useaddr.node = useaddr.skt = 0; /* accept from anywhere */ + skt = 0; /* dynamically allocate skt please */ + if ((err = ATPOpenSocket(&useaddr, &skt)) < 0) { + perror("ATP Open Socket"); + aerror("ATP Open Socket",err); + exit(1); + } + + printf("Registering \"atis test:testing@*\"\n"); + err = nbp_register("atis test", "testing", "*", skt); + if (err != noErr) + aerror("nbp register",err); + else + printf("Okay\n"); + + if (remove) { + err = nbp_remove("atis test", "testing", "*"); + if (err != noErr) + aerror("nbp remove",err); + } +} + +/* + * register the specified entity + * +*/ +nbp_register(sobj, stype, szone, skt) +char *sobj, *stype, *szone; +int skt; +{ + EntityName en; + nbpProto nbpr; /* nbp proto */ + NBPTEntry nbpt[1]; /* table of entity names */ + int err; + + strcpy(en.objStr.s, sobj); + strcpy(en.typeStr.s, stype); + strcpy(en.zoneStr.s, szone); + + + nbpr.nbpAddress.skt = skt; + nbpr.nbpRetransmitInfo.retransInterval = 4; + nbpr.nbpRetransmitInfo.retransCount = 3; + nbpr.nbpBufPtr = nbpt; + nbpr.nbpBufSize = sizeof(nbpt); + nbpr.nbpDataField = 1; /* max entries */ + nbpr.nbpEntityPtr = &en; + + err = NBPRegister(&nbpr,FALSE); /* try synchronous */ + return(err); +} + +/* + * remove the specified entry + * + */ + +nbp_remove(sobj, stype, szone) +char *sobj, *stype, *szone; +{ + EntityName en; + strcpy(en.objStr.s, sobj); + strcpy(en.typeStr.s, stype); + strcpy(en.zoneStr.s, szone); + + return(NBPRemove(&en)); +} + +aerror(msg, err) +char *msg; +int err; +{ + printf("%s error because: ",msg); + switch (err) { + case tooManySkts: + printf("too many sockets open already\n"); + break; + case noDataArea: + printf("internal data area corruption - no room to create socket\n"); + break; + case nbpDuplicate: + printf("name already registered\n"); + break; + case nbpNoConfirm: + printf("couldn't register name - is atis running?\n"); + break; + case nbpBuffOvr: + printf("couldn't register name - too many names already registered\n"); + break; + default: + printf("error: %d\n",err); + break; + } +} diff --git a/samples/atlook.c b/samples/atlook.c new file mode 100644 index 0000000..ed81aee --- /dev/null +++ b/samples/atlook.c @@ -0,0 +1,678 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/06/19 06:57:03 $"; +static char rcsident[] = "$Header: /mac/src/cap60/samples/RCS/atlook.c,v 2.15 1996/06/19 06:57:03 djh Rel djh $"; +static char revision[] = "$Revision: 2.15 $"; + +/* + * atlook - UNIX AppleTalk test program: lookup entities + * with "ATLOOKLWS" defined, will also get pap status and confirm address. + * with "ATPINGER" defined will "ping" remote with "echo request" + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * June 13, 1986 Schilit Created. + * Dec 23, 1986 Schilit Sort result, display in columns, clean up. + * add usage and options. + * Mar 16, 1987 CCKim Clean up some more, merge with looks and pinger + * + */ + +char copyright[] = "Copyright (c) 1986,1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#include +#include +#include /* so htons() works for non-vax */ +#include /* include appletalk definitions */ + +#define NUMNBPENTRY 500 /* max names we can lookup */ + +#ifndef ATPINGER +# define ATPINGER 0 +#endif +#ifndef ATLOOKLWS +# define ATLOOKLWS 0 +#endif + +/* ping defs */ +typedef struct { + u_char echoFunction; /* echo function */ + int echoID; /* internal id */ + int echoSLen; /* send packet length */ + int echoRLen; /* what we got back */ + struct timeval echo_send_time; /* time echo pkt was sent */ + struct timeval echo_response_time; /* time echo pkt came back */ +} PINGPKT; + +typedef struct { + u_char echoFunction; + char epdata[585]; /* cheap */ +} ECHOPKT; + +/* vars */ +int pinger = ATPINGER; +int lwstatus = ATLOOKLWS; +char *deftype = "="; /* default entity type */ + +int nbpretry = 3; /* 3 retries */ +int nbptimeout = 3; /* 3/4 second by default */ +int pingtimeout = sectotick(5); /* 5 seconds by default */ +int pingpktlen = sizeof(PINGPKT); /* size of ping pkt by default (min) */ + + + +/* + * int compare(NBPTEntry *n1, NBPTEntry *n2) + * + * This is the comparison routine for the qsort() library function. + * Our comparison is on the entity's type string, and then the object + * string. In otherwords primary sort is type, secondary sort is object. + * + */ + +int +comparebyname(n1,n2) +NBPTEntry *n1,*n2; +{ + int rslt; + + if ((rslt = strcmp(n1->ent.typeStr.s, /* compare types */ + n2->ent.typeStr.s)) != 0) + return(rslt); /* return if they differ */ + return(strcmp(n1->ent.objStr.s, /* types are the same, return */ + n2->ent.objStr.s)); /* the comparison of objects */ +} + +int +comparebynet(n1,n2) +NBPTEntry *n1,*n2; +{ + if (n1->addr.net != n2->addr.net) { + if (n1->addr.net > n2->addr.net) + return(1); + return(-1); + } + if (n1->addr.node != n2->addr.node) { + if (n1->addr.node > n2->addr.node) + return(1); + return(-1); + } + if (n1->addr.skt != n2->addr.skt) { + if (n1->addr.skt > n2->addr.skt) + return(1); + return(-1); + } + return(0); +} + +int +comparebyskt(n1,n2) +NBPTEntry *n1,*n2; +{ + if (n1->addr.skt != n2->addr.skt) { + if (n1->addr.skt > n2->addr.skt) + return(1); + return(-1); + } + if (n1->addr.net != n2->addr.net) { + if (n1->addr.net > n2->addr.net) + return(1); + return(-1); + } + if (n1->addr.node != n2->addr.node) { + if (n1->addr.node > n2->addr.node) + return(1); + return(-1); + } + return(0); +} + +int (*sortcomp)() = comparebyname; + + +usage(s) +char *s; +{ + fprintf(stderr,"usage: %s [-d FLAGS] [-P] [-S] [-n] [-s] [-t [p]] \ +[-r ] [-l ] nbpentity\n",s); + fprintf(stderr,"\t -n means sort by net numbers\n"); + fprintf(stderr,"\t -s means sort by socket numbers\n"); + fprintf(stderr,"\t -r - specifies number of retries (>0)\n"); + fprintf(stderr,"\t -t [p] - timeout between retries\n"); + fprintf(stderr,"\t unless timeout modified by 'p' for ping timeout\n"); + fprintf(stderr,"\t -l - specifies ping packet length\n"); + fprintf(stderr,"\t -P makes look pings the entities\n"); + fprintf(stderr,"\t -t p or -l turns on pinging\n"); + fprintf(stderr,"\t -S makes look get the \"laserwriter\" status\n"); + fprintf(stderr,"\t and makes the default type \"LaserWriter\"\n"); + fprintf(stderr,"\t -d FLAGS - cap library debugging flags\n"); + fprintf(stderr,"\tTimeouts are in ticks (1/4 units)\n"); + exit(1); +} + +getnum(s) +char *s; +{ + int r; + if (*s == '\0') + return(0); + r = atoi(s); + if (r == 0 && *s != '0') + return(-1); + return(r); +} + +tickout(n) +int n; +{ + int t = n % 4; /* ticks */ + int s = n / 4; /* seconds */ + int m = s / 60; /* minutes */ + + if (m != 0) { + if (m >= 60) /* an hour??????? */ + printf(" (are you crazy?)"); + printf(" %d %s", m, m > 1 ? "minutes" : "minute"); + s %= 60; /* reset seconds to remainder */ + } + if (s) /* print seconds if any */ + printf(" %d",s); + if (t) { /* print ticks */ + if (s) + printf(" and"); + if (t == 2) + printf(" one half"); + else + printf(" %d fourths",t); + } + if (s || t) + printf(" second"); + if (s > 1 || (t && s)) + printf("s"); +} + +doargs(argc,argv) +int argc; +char **argv; +{ + char *whoami = argv[0]; + extern char *optarg; + extern int optind; + extern boolean dochecksum; + int c; + + while ((c = getopt(argc, argv, "d:D:l:r:t:knsSP")) != EOF) { + switch (c) { + case 'd': + case 'D': + dbugarg(optarg); /* some debug flags */ + break; + case 'r': + nbpretry = getnum(optarg); + if (nbpretry <= 0) + usage(whoami); + printf("Number of NBP retries %d\n",nbpretry); + break; + case 't': + if (optarg[0] != 'p') { + nbptimeout = getnum(optarg); + if (nbptimeout < 0) + usage(whoami); + printf("NBP Timeout"); + tickout(nbptimeout); + } else { + pingtimeout = getnum(optarg+1); + if (pingtimeout == 0) { + /* message ? */ + pingtimeout = 1; + } + pinger++; + printf("ping timeout"); + tickout(pingtimeout); + } + putchar('\n'); + break; + case 'l': + pinger++; + pingpktlen = atoi(optarg); + break; + case 'P': + pinger++; + break; + case 'S': + lwstatus++; + deftype = "LaserWriter"; /* switch over */ + break; + case 'n': + sortcomp = comparebynet; + break; + case 's': + sortcomp = comparebyskt; + break; + case 'k': /* no DDP checksum */ + dochecksum = 0; + break; + case '?': + default: + usage(whoami); + } + } + return(optind); +} + +my_create_entity(s, en) +char *s; +EntityName *en; +{ + create_entity(s, en); /* must be fully specified name */ + if (*en->objStr.s == '\0') + en->objStr.s[0] = '='; + if (*en->typeStr.s == '\0') + strcpy(en->typeStr.s,deftype); /* to lookup... */ + if (*en->zoneStr.s == '\0') + en->zoneStr.s[0] = '*'; +} + +/* + * take an \xxx octal char in the argument string + * and convert it to the actual special character + * + * + */ + +void +trans_special_char(str) +char *str; +{ + int i, j, top; + char special[4]; + + top = strlen(str); + for (i = 0, j = 0; i < top; i++, j++) { + if (str[j] == '\\') { + if (isdigit(str[j+1])) { + special[3] = '\0'; + bcopy(&str[j+1], special, 3); + str[i] = (char)strtol(special, (char **)NULL, 8); + j += 3; + } else + str[i] = str[++j]; + } else + str[i] = str[j]; + } +} + +main(argc,argv) +int argc; +char **argv; +{ + int i; + EntityName en; /* network entity name */ +#ifdef PHASE2 + u_char *GetMyZone(); +#endif PHASE2 + + abInit(TRUE); /* initialize appletalk driver */ + nbpInit(); /* initialize nbp */ + checksum_error(FALSE); /* ignore these errors */ + + i = doargs(argc,argv); /* handle arguments */ + if (pinger) + pingInit(); + if (lwstatus) + deftype = "LaserWriter"; + strcpy(en.objStr.s,"="); /* create default entity */ + strcpy(en.typeStr.s,deftype); /* to lookup... */ +#ifdef PHASE2 + strcpy(en.zoneStr.s, (char *)GetMyZone()); +#else PHASE2 + strcpy(en.zoneStr.s,"*"); +#endif PHASE2 + + + if (i == argc) { + dolookup(&en); + } else { + for (; i < argc ; i++) { + trans_special_char(argv[i]); + my_create_entity(argv[i], &en); + dolookup(&en); + } + } + exit(0); +} + +/* + * given to timevals, compute the number of seconds + * +*/ +float +evtime(tvs, tve) +struct timeval *tvs, *tve; +{ + return(((float)(tve->tv_sec - tvs->tv_sec)) + + (((float)(tve->tv_usec - tvs->tv_usec))/1000000.0)); +} + +NBPTEntry nbpt[NUMNBPENTRY]; /* table of entity names */ + +dolookup(en) +EntityName *en; /* network entity name */ +{ + int err,i, len; + AddrBlock addr; /* Address of entity */ + PINGPKT *pingpkt; /* ping packet */ + char *pbuf = NULL; /* ping buf */ + nbpProto nbpr; /* nbp protocol record */ + char name[sizeof(EntityName)*4*3+3]; /* for formatted entity name */ + /* 3 entries, of size entity name */ + /* each char can be up to 4 in length */ + /* +3 for :@ and null */ + + + nbpr.nbpRetransmitInfo.retransInterval = nbptimeout; + nbpr.nbpRetransmitInfo.retransCount = nbpretry; + nbpr.nbpBufPtr = nbpt; /* place to store entries */ + nbpr.nbpBufSize = sizeof(nbpt); /* size of above */ + nbpr.nbpDataField = NUMNBPENTRY; /* max entries */ + nbpr.nbpEntityPtr = en; /* look this entity up */ + + len = dumptostr(name, en->objStr.s); + name[len++] = ':'; + len += dumptostr(name+len, en->typeStr.s); + name[len++] = '@'; + dumptostr(name+len, en->zoneStr.s); + printf("Looking for %s ...\n",name); + +#ifdef ISO_TRANSLATE + cISO2Mac(en->objStr.s); + cISO2Mac(en->typeStr.s); + cISO2Mac(en->zoneStr.s); +#endif ISO_TRANSLATE + + /* + * Find all objects in specified zone + * + */ + err = NBPLookup(&nbpr, FALSE); /* try synchronous */ + + if (err == nbpBuffOvr) + fprintf(stderr, "NBPLookup buffer too small (%d)\n", NUMNBPENTRY); + else if (err != noErr) + fprintf(stderr, "NBPLookup returned err %d\n", err); + + if (nbpr.nbpDataField > NUMNBPENTRY) + nbpr.nbpDataField = NUMNBPENTRY; + + /* + * Sort the result for better viewing + * + */ + if (nbpr.nbpDataField) + qsort((char *)nbpt, (int)nbpr.nbpDataField, sizeof(NBPTEntry), sortcomp); + + /* + * malloc this so we make sure long enough + * + */ + if (pinger) { + if (pingpktlen < sizeof(PINGPKT)) + pingpktlen = sizeof(PINGPKT); + pbuf = (char *)malloc(pingpktlen); /* get ping packet */ + if (pbuf == NULL) { + pinger = 0; + fprintf(stderr,"Can't allocate packet for ping\n"); + } + pingpkt = (PINGPKT *)pbuf; + } + + /* + * Extract and print the items + * + */ + for (i = 1; i <= (int)nbpr.nbpDataField; i++) { + NBPExtract(nbpt,nbpr.nbpDataField,i,en,&addr); +#ifdef ISO_TRANSLATE + cMac2ISO(en->objStr.s); + cMac2ISO(en->typeStr.s); + cMac2ISO(en->zoneStr.s); +#endif ISO_TRANSLATE + len = dumptostr(name, en->objStr.s); + name[len++] = ':'; + len += dumptostr(name+len, en->typeStr.s); + name[len++] = '@'; + dumptostr(name+len, en->zoneStr.s); + printf("%3d - %-40s [Net:%3d.%-3d Node:%3d Skt:%3d]\n", + i,name,htons(addr.net)>>8, htons(addr.net)&0xff,addr.node,addr.skt); + if (lwstatus) + getstatus(en, &addr); + if (pinger) { + printf("Ping..."); + /* do a one sec ping first in case we are missing an arp entry */ + if (PingSend(&addr, sectotick(1), pbuf, pingpktlen) == noErr || + PingSend(&addr, pingtimeout, pbuf, pingpktlen) == noErr) { + if (pingpkt->echoSLen != pingpkt->echoRLen) { + printf("sent length = %d, received %d...", + pingpkt->echoSLen, pingpkt->echoRLen); + } + if (pingpkt->echo_response_time.tv_sec != 0 || + pingpkt->echo_response_time.tv_usec != 0) { + printf("round trip %f...", + evtime(&pingpkt->echo_send_time, + &pingpkt->echo_response_time)); + } + printf("Okay\n"); + } else + printf("no response\n"); + } + } + if (pbuf) + free(pbuf); +} + +getstatus(en, addr) +EntityName *en; +AddrBlock *addr; +{ + PAPStatusRec statusbuf; + char namebuf[100]; /* place to put name of entity */ + nbpProto nbpr; /* NBP protocol record */ + int err; + +#ifdef ISO_TRANSLATE + cISO2Mac(en->objStr.s); + cISO2Mac(en->typeStr.s); + cISO2Mac(en->zoneStr.s); +#endif ISO_TRANSLATE + sprintf(namebuf, "%s:%s@%s", en->objStr.s, en->typeStr.s, en->zoneStr.s); + PAPStatus(namebuf, &statusbuf, addr); +#ifdef ISO_TRANSLATE + pMac2ISO(&statusbuf.StatusStr[0]); +#endif ISO_TRANSLATE + printf("---"); + dumpstatus(&statusbuf); + putchar('\n'); + + nbpr.nbpRetransmitInfo.retransInterval = 4; + nbpr.nbpRetransmitInfo.retransCount = 3; + nbpr.nbpEntityPtr = en; /* entity name */ + nbpr.nbpAddress = *addr; /* all other old values */ + err = NBPConfirm(&nbpr,FALSE); + if (err != noErr) + printf("Confirm failed, code = %d...\n",err); + else + printf("Address confirmed for socket %d\n",nbpr.nbpDataField); +} + +#ifdef ISO_TRANSLATE +# ifdef isprint +# undef isprint +# endif isprint +#define isprint isISOprint +#endif ISO_TRANSLATE + +/* + * Dump a PAP status message +*/ +dumpstatus(statusbuf) +PAPStatusRec *statusbuf; +{ + int len = (int)(statusbuf->StatusStr[0]); + unsigned char *s = &statusbuf->StatusStr[1]; + + while (len--) { + if (isprint(*s)) + putchar(*s); + else + printf("\\%o",*s&0xff); + s++; + } +} + +dumptostr(str, todump) +char *str; +char *todump; +{ + char c; + int i = 0; + + while ((c = *todump++)) { +#ifndef ISO_TRANSLATE + if (c > 0 && isprint(c)) { +#else ISO_TRANSLATE + if (isprint(c)) { +#endif ISO_TRANSLATE + *str++ = c; + i++; + } else { + sprintf(str,"\\%3o",c&0xff); + str+=4; + i+=4; + } + } + *str = '\0'; + return(i); +} + +/* + * PING MODULE: single ping outstanding + * + * Good part of the definitions should be moved to appletalk.h + * Good part of the routines should be rewritten and moved to abping.c + * +*/ + + +private int pingskt; +private int echoID; +private ABusRecord pingAbr; +private int pingout = 0; + +OSErr +pingInit() +{ + void echo_listener(); + int err; + + echoID = 0; + pingout = 0; + pingskt = 0; + err = DDPOpenSocket(&pingskt, echo_listener); + if (err != noErr) + fprintf(stderr, "no dynamic sockets left for ping\n"); + return(err); +} + +void +echo_listener(skt, type, pkt, len, addr) +u_char skt; +u_char type; +char *pkt; +int len; +AddrBlock *addr; +{ + int cmd; + PINGPKT copy_rpp; /* copy of received ping packet */ + PINGPKT *rpp; /* ptr to received ping pkt */ + PINGPKT *spp; /* ptr to send ping pkt */ + ABusRecord *abr; + struct timezone tz; + +#ifdef DEBUG + printf("Got pkt from %d.%d (skt %d) of size %d\n", + ntohs(addr->net), addr->node, addr->skt, len); +#endif + + /* reasons to drop packet */ + if (type != ddpECHO) + return; + if ((u_char )*pkt != echoReply) + return; + /* some day this will go down a queue... */ + abr = (pingout) ? &pingAbr : NULL; + /* no outstanding pings */ + if (abr == NULL) + return; + + /* just in case of byte aligment problems */ + bcopy(pkt, ©_rpp, sizeof(copy_rpp)); + rpp = ©_rpp; + spp = (PINGPKT *)abr->proto.ddp.ddpDataPtr; + if (rpp->echoID != spp->echoID) { + return; /* drop */ + } + spp->echoRLen = len; /* mark: can check for ip frags this way */ + gettimeofday(&spp->echo_response_time, &tz); + abr->abResult = noErr; +} + +echo_timeout(abr) +ABusRecord *abr; +{ + abr->abResult = reqFailed; +} + +PingSend(addr, to, pingpkt, pingpktlen) +AddrBlock *addr; +int to; +PINGPKT *pingpkt; +int pingpktlen; +{ + ABusRecord *abr = &pingAbr; + struct timezone tz; + OSErr err; + + if (pingpktlen < sizeof(PINGPKT)) + return(-1); + if (pingpktlen > ddpMaxData) /* truncate silently */ + pingpktlen = ddpMaxData; + abr->proto.ddp.ddpAddress = *addr; + abr->proto.ddp.ddpAddress.skt = echoSkt; + abr->proto.ddp.ddpSocket = pingskt; + abr->proto.ddp.ddpType = ddpECHO; + pingpkt->echoFunction = echoRequest; + pingpkt->echoID = echoID++; /* mark id */ + /* just in case someone tries to use it */ + bzero(&pingpkt->echo_response_time, sizeof(pingpkt->echo_response_time)); + pingpkt->echoSLen = pingpktlen; /* mark as length */ + pingpkt->echoRLen = 0; /* mark as zero */ + abr->proto.ddp.ddpDataPtr = (u_char *)pingpkt; + abr->proto.ddp.ddpReqCount = pingpktlen; + gettimeofday(&pingpkt->echo_send_time, &tz); /* mark outgoing time */ + if ((err = DDPWrite(abr, FALSE)) < 0) + return(err); + pingout = 1; + abr->abResult=1; + Timeout(echo_timeout, (caddr_t)abr, to); + while (abr->abResult > 0) + abSleep(to, TRUE); + remTimeout(echo_timeout, (caddr_t)abr); + pingout = 1; + return(abr->abResult); +} + diff --git a/samples/getzones.c b/samples/getzones.c new file mode 100644 index 0000000..1c6ede6 --- /dev/null +++ b/samples/getzones.c @@ -0,0 +1,127 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/04/28 13:22:16 $"; +static char rcsident[] = "$Header: /mac/src/cap60/samples/RCS/getzones.c,v 2.9 1996/04/28 13:22:16 djh Rel djh $"; +static char revision[] = "$Revision: 2.9 $"; + +/* + * getzones - retrieves the zone list from our bridge + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia + * University in the City of New York. + * + * Edit History: + * + * March 1988, CCKim, Created + * March 1993, John Huntley, added debug argument processing + * + */ + +char copyright[] = "Copyright (c) 1988 by The Trustees of Columbia \ +University in the City of New York"; + +#include +#include +#include +#include /* so htons() works for non-vax */ +#include /* include appletalk definitions */ + +#define NUMZONES 500 + +main(argc, argv) +int argc; +char **argv; +{ + int i,cnt; + OSErr err; + byte function; + char *zones[NUMZONES]; /* room for pointers to zone names */ + u_char *GetMyZone(); + u_char *myzone; + int verbose = 0; + int sortit = 0; + int mzone = 0; + extern int opterr; + extern char *optarg; + int zonecmp(); + + opterr = 0; + function = zip_GetZoneList; + + while ((i = getopt(argc, argv, "d:D:lmsv")) != EOF) { + switch (i) { + case 'd': + case 'D': + dbugarg(optarg); + break; + case 'l': + function = zip_GetLocZones; + break; + case 'm': + mzone = 1; + break; + case 's': + sortit = 1; + break; + case 'v': + verbose++; + break; + } + } + + abInit(verbose ? TRUE : FALSE); + + myzone = GetMyZone(); + +#ifdef ISO_TRANSLATE + cMac2ISO(myzone); +#endif ISO_TRANSLATE + + if (mzone) { + printf("%s\n", (char *)myzone); + exit(0); + } + + if ((err = GetZoneList(function, zones, NUMZONES, &cnt)) != noErr) { + fprintf(stderr, "error %d getting zone list\n", err); + exit(1); + } + + if (cnt > NUMZONES) { + printf("only asked for %d zones when there were actually %d\n", + NUMZONES,cnt); + cnt = NUMZONES; + } + + /* + * Sort the Zone list, if required + * + */ + if (sortit) + qsort((char *)zones, cnt, sizeof(char *), zonecmp); + + if (verbose) + printf("Count is %d\n", cnt); + + for (i = 0; i < cnt ; i++) { +#ifdef ISO_TRANSLATE + cMac2ISO(zones[i]); +#endif ISO_TRANSLATE + if (verbose) + printf("ZONE %s", zones[i]); + else + printf("%s", zones[i]); + if (verbose && strcmp(zones[i], (char *)myzone) == 0) + putchar('*'); + putchar('\n'); + } + + FreeZoneList(zones, cnt); +} + +int +zonecmp(s1, s2) +void *s1, *s2; +{ + return(strcmp(*(char **)s1, *(char **)s2)); +} diff --git a/samples/instappl.c b/samples/instappl.c new file mode 100644 index 0000000..2e2b028 --- /dev/null +++ b/samples/instappl.c @@ -0,0 +1,181 @@ +/* + * $Author: djh $ $Date: 91/02/15 23:04:00 $ + * $Header: instappl.c,v 2.1 91/02/15 23:04:00 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * instappl - install an unix generated resource file into a aufs volume + * no real need to do this for data forks since you can just copy the + * file in. (Would be nice to have something that diddles with finder + * information though). + * + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia + * University in the City of New York. + * + * Edit History: + * + * March 1987 CCKim Created. + * + */ + +char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#ifdef USESTRINGDOTH +# include +#else +# include +#endif +#ifdef NEEDFCNTLDOTH +# include +#endif + +usage() +{ + fprintf(stderr, "Usage: instappl [dest]\n"); + fprintf(stderr, " -c -t [note: truncated to 4 each]\n"); + fprintf(stderr, " -l - locked\t-m multi-user\t-i invisibile\n"); + exit(1); +} + +main(argc, argv) +char **argv; +int argc; +{ + char *name, *path, *dir; + char *creator = "????"; /* def creator unknown */ + char *type = "APPL"; /* def file type is application */ + FileInfo fi; + int c, ff; + struct timeval tvp[2]; + struct timezone tzp; + extern char *optarg; + extern int optind; + + if (argc < 3) + usage(); + + ff = 0; + while ((c = getopt(argc, argv, "c:t:lmir")) != EOF) + switch (c) { + case 'c': + creator = optarg; + break; + case 't': + type = optarg; + break; + case 'm': + ff |= FI_ATTR_MUSER; /* multiuser */ + break; + case 'r': + case 'l': + ff |= FI_ATTR_READONLY; /* readonly */ + break; + case 'i': + ff |= FI_ATTR_INVISIBLE; /* invisible */ + break; + case '?': + usage(); + break; + } + if (argc - optind < 2) + usage(); + + path = argv[optind++]; + dir = argv[optind++]; + name = NULL; + if (optind < argc) + name = argv[optind++]; + if (optind != argc) + usage(); + if (name == NULL) { + if ((name = rindex(path,'/')) == NULL) + name = path; + else name++; + } +#ifdef DEBUG + printf("copy %s to %s as %s, %s, %s\n",path,dir,name,creator,type); +#endif + bzero(&fi, sizeof(fi)); + bcopy(type,fi.fi_fndr, 4); + bcopy(creator,fi.fi_fndr+4, 4); + fi.fi_attr = ff; +#define COMMENT "Installed via AppleShare by INSTAPPL" + fi.fi_comln = sizeof(COMMENT); + fi.fi_magic1 = FI_MAGIC1; + fi.fi_magic = FI_MAGIC; + fi.fi_version = FI_VERSION; + fi.fi_bitmap = FI_BM_MACINTOSHFILENAME; + strncpy((char *)fi.fi_macfilename, name, sizeof(fi.fi_macfilename)-1); + bcopy(COMMENT, fi.fi_comnt, sizeof(COMMENT)); + writefinder(name, dir, &fi); + writedata(name, dir); + writeres(name, path, dir); +#ifndef NOUTIMES + gettimeofday(&tvp[0], &tzp); + tvp[1] = tvp[0]; + utimes(dir, tvp); +#endif +} + +char endup[1024]; + +writefinder(name, dir, fi) +char *name, *dir; +FileInfo *fi; +{ + int fd; + + sprintf(endup, "%s/.finderinfo/%s", dir, name); + if ((fd = open(endup, O_WRONLY|O_CREAT|O_TRUNC, 0662)) < 0) { + perror("writefinder: open"); + exit(1); + } + write(fd, fi, sizeof(FileInfo)); + close(fd); +} + +writedata(name, dir) +char *name, *dir; +{ + int fd; + sprintf(endup, "%s/%s", dir, name); + if ((fd = open(endup, O_WRONLY|O_CREAT|O_TRUNC, 0662)) < 0) { + perror("writedata: open"); + exit(1); + } + close(fd); +} + +char buf[1024]; +writeres(name, path, dir) +char *name, *path, *dir; +{ + int len, fd2, fd; + sprintf(endup, "%s/.resource/%s", dir, name); + if ((fd = open(endup, O_WRONLY|O_CREAT|O_TRUNC, 0662)) < 0) { + perror("writeres: open"); + exit(1); + } + if ((fd2 = open(path, O_RDONLY)) < 0) { + perror("writeres: open"); + exit(1); + } + do { + len = read(fd2, buf, sizeof(buf)); + if (len > 0) + write(fd, buf, len); + } while (len > 0); + if (len < 0) + perror("read"); + close(fd); +} diff --git a/samples/isrv.c b/samples/isrv.c new file mode 100644 index 0000000..447e33c --- /dev/null +++ b/samples/isrv.c @@ -0,0 +1,245 @@ +static char rcsid[] = "$Author: djh $ $Date: 1993/08/05 15:53:17 $"; +static char rcsident[] = "$Header: /mac/src/cap60/samples/RCS/isrv.c,v 2.4 1993/08/05 15:53:17 djh Rel djh $"; +static char revision[] = "$Revision: 2.4 $"; + +/* + * isrv - UNIX AppleTalk test program: act as a laserwriter + * + * very simple test of printer server capabilities for the ImageWriter. + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * Feb. 1987 CCKim Created. + * + */ + +char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#include +#include +#include + +#include /* include appletalk definitions */ +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +int cno; +#define RFLOWQ 8 +#define BUFMAX 512*RFLOWQ +#ifndef SFLOWQ +# define SFLOWQ 8 +#endif SFLOWQ +#define SBUFMAX 512*SFLOWQ +char rbuf[BUFMAX+10]; +int xdebug = TRUE; + +usage() +{ + fprintf(stderr,"usage: lsrv -P printer [-T Type] [-d flags]\n"); + fprintf(stderr,"usage: Printer in printcap, type is generic type\n"); + fprintf(stderr,"usage: eg. lsrv -Pxx -TImageWriter\n"); + exit(0); +} + +char *prtname = NULL, + *prttype = "ImageWriter", + *prtmodel = ""; + +main(argc,argv) +int argc; +char **argv; +{ + char *s,*arg,buf[100]; + int err; + PAPStatusRec statusbuff; + int rcomp, wcomp, paperr; + int srefnum,i; + void childdone(); + + + while (argc > 1 && argv[1][0] == '-') { + argc--; + arg = *++argv; + + switch(arg[1]) { + case 'D': case 'd': + if (arg[2] != '\0') + dbugarg(&arg[2]); + else if (argc > 1) { + argc--; + dbugarg(*++argv); + } + break; + + case 'P': case 'p': + if (arg[2] != '\0') + prtname = &arg[2]; + else if (argc > 1) { + argc--; + prtname = *++argv; + } + break; + + case 'T': + if (arg[2] != '\0') + prttype = &arg[2]; + else if (argc > 1) { + argc--; + prttype = *++argv; + } + break; + } + } + + if (prtname == NULL) + usage(); + + if (!dbug.db_flgs) { + /* disassociate */ + if (fork()) + exit(0); /* kill parent */ + { + int i; + for (i=0; i < 20; i++) close(i); /* kill */ + (void)open("/",0); +#ifdef NODUP2 + (void)dup(0); /* slot 1 */ + (void)dup(0); /* slot 2 */ +#else + (void)dup2(0,1); + (void)dup2(0,2); +#endif +#ifdef TIOCNOTTY + if ((i = open("/dev/tty",2)) > 0) { + ioctl(i, TIOCNOTTY, 0); + close(i); + } +#endif TIOCNOTTY +#ifdef POSIX + (void) setsid(); +#endif POSIX + } + } + + abInit(xdebug); /* initialize appletalk driver */ + nbpInit(); + PAPInit(); /* init PAP printer routines */ + cpyc2pstr(statusbuff.StatusStr, "Status: initializing"); + + printf("Spooler starting for %s. Type is %s, model %s\n", + prtname,prttype, prtmodel); + + sprintf(buf,"%s:%s@*",prtname,prttype); + err = SLInit(&srefnum, buf, 8, &statusbuff); + if ( err < 0) { + fprintf(stderr,"Errror = %d\n", err); + exit(8); + } + + abSleep(60, TRUE); + + signal(SIGCHLD, childdone); + do { + statusbuff.StatusStr[0] = '\02'; /* some fake out??? */ + statusbuff.StatusStr[1] = 0x00; + statusbuff.StatusStr[2] = 0x80; + err = GetNextJob(srefnum, &cno, &rcomp); + if (err < 0) { + fprintf(stderr, "Open failed with %d\n",err); + exit(1); + } + do { abSleep(4*20, TRUE); } while (rcomp > 0); + +/* strcpy(statusbuff.StatusStr, "Status: busy, processing job"); */ +#ifndef DEBUG + if (fork() == 0) { +#else + { +#endif + char tname[100]; + char buf[256]; + + strcpy(tname, "/tmp/lsrvXXXXXX"); + mktemp(tname); + if (freopen(tname, "w+", stdout) != NULL) { +#ifndef DEBUG + SLClose(srefnum); /* close down server for child */ +#endif + getjob(cno); + /* end eof */ + paperr = PAPWrite(cno, NULL, 0, TRUE, &wcomp); + if (paperr != noErr) + fprintf(stderr,"PAPWrite error %d\n",paperr); + else + do { abSleep(4, TRUE); } while (wcomp > 0); + PAPClose(cno, TRUE); + fclose(stdout); + sprintf(buf,"/usr/ucb/lpr -P%s -r -T 'from AppleTalk' %s\n", + prtname,tname); +#ifdef DEBUG + fprintf(stderr,"Starting: %s\n",buf); +#endif + system(buf); +#ifndef DEBUG + unlink(tname); +#endif + } else perror("freopen"); +#ifndef DEBUG + exit(0); +#endif + } +#ifndef DEBUG + PAPShutdown(cno); /* get rid of it */ +#endif + } while (1); + exit(0); /* exit okay */ +} + +void +childdone() +{ + WSTATUS status; + + (void)wait(&status); + signal(SIGCHLD, childdone); +} + +/* + * handle the incoming job + * +*/ +getjob(cno) +int cno; +{ + int rcomp, rlen, eof, err; + eof = 0; + while (!eof) { + err = PAPRead(cno, rbuf, &rlen, &eof, &rcomp); + if (err < 0) { + return(0); /* ? */ + } + do { + if (rcomp <= 0) { + if (rcomp != noErr) { + fprintf(stderr,"PAPRead error %d\n",rcomp); + return(0); + } + break; + } + abSleep(4, TRUE); + } while (1); + if (rlen > 0) + write(fileno(stdout), rbuf, rlen); + } + return(0); +} diff --git a/samples/look.c b/samples/look.c new file mode 100644 index 0000000..2eadcbb --- /dev/null +++ b/samples/look.c @@ -0,0 +1,455 @@ +static char rcsid[] = "$Author: djh $ $Date: 91/02/15 23:04:09 $"; +static char rcsident[] = "$Header: look.c,v 2.1 91/02/15 23:04:09 djh Rel $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * look - UNIX AppleTalk test program: lookup entities + * with "LOOKS" defined, will also get pap status and confirm address. + * with "PINGER" defined will "ping" remote with "echo request" + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * June 13, 1986 Schilit Created. + * Dec 23, 1986 Schilit Sort result, display in columns, clean up. + * add usage and options. + * Mar 16, 1987 CCKim Clean up some more, merge with looks and pinger + * + */ + +char copyright[] = "Copyright (c) 1986,1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#include +#include /* so htons() works for non-vax */ +#include /* include appletalk definitions */ + +#define NUMNBPENTRY 100 /* max names we can lookup */ + +#ifndef PINGER +# define PINGER 0; +#endif +#ifndef LOOKS +# define LOOKS 0 +#endif + +int pinger = PINGER; +int lwstatus = LOOKS; +char *deftype = "="; /* default entity type */ + +int nbpretry = 3; /* 3 retries */ +int nbptimeout = 3; /* 3/4 second by default */ +/* + * int compare(NBPTEntry *n1, NBPTEntry *n2) + * + * This is the comparison routine for the qsort() library function. + * Our comparison is on the entity's type string, and then the object + * string. In otherwords primary sort is type, secondary sort is object. + * + */ + +int +comparebyname(n1,n2) +NBPTEntry *n1,*n2; +{ + int rslt; + + if ((rslt = strcmp(n1->ent.typeStr.s, /* compare types */ + n2->ent.typeStr.s)) != 0) + return(rslt); /* return if they differ */ + return(strcmp(n1->ent.objStr.s, /* types are the same, return */ + n2->ent.objStr.s)); /* the comparison of objects */ +} + +int +comparebynet(n1,n2) +NBPTEntry *n1,*n2; +{ + if (n1->addr.net != n2->addr.net) { + if (n1->addr.net > n2->addr.net) + return(1); + return(-1); + } + if (n1->addr.node != n2->addr.node) { + if (n1->addr.node > n2->addr.node) + return(1); + return(-1); + } + if (n1->addr.skt != n2->addr.skt) { + if (n1->addr.skt > n2->addr.skt) + return(1); + return(-1); + } + return(0); +} + +int +comparebyskt(n1,n2) +NBPTEntry *n1,*n2; +{ + if (n1->addr.skt != n2->addr.skt) { + if (n1->addr.skt > n2->addr.skt) + return(1); + return(-1); + } + if (n1->addr.net != n2->addr.net) { + if (n1->addr.net > n2->addr.net) + return(1); + return(-1); + } + if (n1->addr.node != n2->addr.node) { + if (n1->addr.node > n2->addr.node) + return(1); + return(-1); + } + return(0); +} + +int (*sortcomp)() = comparebyname; + + +usage(s) +char *s; +{ + fprintf(stderr,"usage: %s [-d FLAGS] [-P] [-S] [-n] [-s] [-t n] [-r n] \ +nbpentity\n",s); + fprintf(stderr,"\t -n means sort by net numbers\n"); + fprintf(stderr,"\t -s means sort by socket numbers\n"); + fprintf(stderr,"\t -r n - specifies number of retries (>=0)\n"); + fprintf(stderr,"\t -t n - specifies nbp timeout between retries (1/4 second units) (>0)\n"); + fprintf(stderr,"\t -P makes look pings the entities\n"); + fprintf(stderr,"\t -S makes look get the \"laserwriter\" status\n"); + fprintf(stderr,"\t and makes the default type \"LaserWriter\"\n"); + fprintf(stderr,"\t -d FLAGS - cap library debugging flags\n"); + exit(1); +} + +getnum(s) +char *s; +{ + int r; + if (*s == '\0') + return(0); + r = atoi(s); + if (r == 0 && *s != '0') + return(-1); + return(r); +} + +tickout(n) +int n; +{ + int t = n % 4; /* ticks */ + int s = n / 4; /* seconds */ + int m = s / 60; /* minutes */ + + if (m != 0) { + if (m >= 60) /* an hour??????? */ + printf(" (are you crazy?)"); + printf(" %d %s", m, m > 1 ? "minutes" : "minute"); + s %= 60; /* reset seconds to remainder */ + } + if (s) /* print seconds if any */ + printf(" %d",s); + if (t) { /* print ticks */ + if (t == 2) + printf(" and one half"); + else + printf(" %d fourths",t); + } + if (s || t) + printf(" second"); + if (s > 1 || (t && s)) + printf("s"); +} + +doargs(argc,argv) +int argc; +char **argv; +{ + char *whoami = argv[0]; + extern char *optarg; + extern int optind; + int c; + + while ((c = getopt(argc, argv, "d:D:r:t:nsSP")) != EOF) { + switch (c) { + case 'd': + case 'D': + dbugarg(optarg); /* some debug flags */ + break; + case 'r': + nbpretry = getnum(optarg); + if (nbptimeout <= 0) + usage(whoami); + printf("Number of NBP retries %d\n",nbpretry); + break; + case 't': + nbptimeout = getnum(optarg); + if (nbptimeout < 0) + usage(whoami); + printf("NBP Timeout"); + tickout(nbptimeout); + putchar('\n'); + break; + case 'P': + pinger++; + break; + case 'S': + lwstatus++; + deftype = "LaserWriter"; /* switch over */ + break; + case 'n': + sortcomp = comparebynet; + break; + case 's': + sortcomp = comparebyskt; + break; + case '?': + default: + usage(whoami); + } + } + return(optind); +} + +my_create_entity(s, en) +char *s; +EntityName *en; +{ + create_entity(s, en); /* must be fully specified name */ + if (*en->objStr.s == '\0') + en->objStr.s[0] = '='; + if (*en->typeStr.s == '\0') + strcpy(en->typeStr.s,deftype); /* to lookup... */ + if (*en->zoneStr.s == '\0') + en->zoneStr.s[0] = '*'; +} + +main(argc,argv) +int argc; +char **argv; +{ + int i; + EntityName en; /* network entity name */ + + abInit(TRUE); /* initialize appletalk driver */ + nbpInit(); /* initialize nbp */ + checksum_error(FALSE); /* ignore these errors */ + + i = doargs(argc,argv); /* handle arguments */ + if (pinger) + pingInit(); + if (lwstatus) + deftype = "LaserWriter"; + strcpy(en.objStr.s,"="); /* create default entity */ + strcpy(en.typeStr.s,deftype); /* to lookup... */ + strcpy(en.zoneStr.s,"*"); + + + if (i == argc) { + dolookup(&en); + } else { + for (; i < argc ; i++) { + my_create_entity(argv[i], &en); + dolookup(&en); + } + } +} +dolookup(en) +EntityName *en; /* network entity name */ +{ + int err,i, len; + AddrBlock addr; /* Address of entity */ + NBPTEntry nbpt[NUMNBPENTRY]; /* table of entity names */ + nbpProto nbpr; /* nbp protocol record */ + char name[sizeof(EntityName)*4*3+3]; /* for formatted entity name */ + /* 3 entries, of size entity name */ + /* each char can be up to 4 in length */ + /* +3 for :@ and null */ + + + nbpr.nbpRetransmitInfo.retransInterval = nbptimeout; + nbpr.nbpRetransmitInfo.retransCount = nbpretry; + nbpr.nbpBufPtr = nbpt; /* place to store entries */ + nbpr.nbpBufSize = sizeof(nbpt); /* size of above */ + nbpr.nbpDataField = NUMNBPENTRY; /* max entries */ + nbpr.nbpEntityPtr = en; /* look this entity up */ + + len = dumptostr(name, en->objStr.s); + name[len++] = ':'; + len += dumptostr(name+len, en->typeStr.s); + name[len++] = '@'; + dumptostr(name+len, en->zoneStr.s); + printf("Looking for %s ...\n",name); + + /* Find all objects in specified zone */ + err = NBPLookup(&nbpr,FALSE); /* try synchronous */ + if (err != noErr) + fprintf(stderr,"NBPLookup returned err %d\n",err); + + /* Sort the result for better viewing */ + + qsort((char *)nbpt,(int) nbpr.nbpDataField,sizeof(NBPTEntry),sortcomp); + + /* Extract and print the items */ + for (i = 1; i <= nbpr.nbpDataField; i++) { + NBPExtract(nbpt,nbpr.nbpDataField,i,en,&addr); + len = dumptostr(name, en->objStr.s); + name[len++] = ':'; + len += dumptostr(name+len, en->typeStr.s); + name[len++] = '@'; + dumptostr(name+len, en->zoneStr.s); + printf("%02d - %-40s [Net:%3d.%02d Node:%3d Skt:%3d]\n", + i,name,htons(addr.net)>>8, htons(addr.net)&0xff,addr.node,addr.skt); + if (lwstatus) + getstatus(en, &addr); + if (pinger) { + printf("Ping..."); + if (ping(&addr)) + printf("Okay\n"); + else + printf("no response\n"); + } + } +} + +getstatus(en, addr) +EntityName *en; +AddrBlock *addr; +{ + PAPStatusRec statusbuf; + char namebuf[100]; /* place to put name of entity */ + nbpProto nbpr; /* NBP protocol record */ + int err; + + sprintf(namebuf, "%s:%s@%s", en->objStr.s, en->typeStr.s, en->zoneStr.s); + PAPStatus(namebuf, &statusbuf, addr); + printf("---"); + dumpstatus(&statusbuf); + putchar('\n'); + + nbpr.nbpRetransmitInfo.retransInterval = 4; + nbpr.nbpRetransmitInfo.retransCount = 3; + nbpr.nbpEntityPtr = en; /* entity name */ + nbpr.nbpAddress = *addr; /* all other old values */ + err = NBPConfirm(&nbpr,FALSE); + if (err != noErr) + printf("Confirm failed, code = %d...\n",err); + else + printf("Address confirmed for socket %d\n",nbpr.nbpDataField); +} + +/* + * Dump a PAP status message +*/ +dumpstatus(statusbuf) +PAPStatusRec *statusbuf; +{ + int len = (int)(statusbuf->StatusStr[0]); + unsigned char *s = &statusbuf->StatusStr[1]; + + while (len--) { + if (isprint(*s)) + putchar(*s); + else + printf("\\%o",*s&0xff); + s++; + } +} + +static int myskt ; +pingInit() +{ + int echo_listener(); + int err; + + myskt = 0; + err = DDPOpenSocket(&myskt, echo_listener); + if (err != noErr) + fprintf(stderr, "no dynamic sockets left for ping\n"); +} + +typedef struct { + u_char echoFunction; + char epdata[585]; /* cheap */ +} ECHO; + +ECHO echo; +ABusRecord abr; + + +echo_listener(skt, type, pkt, len, addr) +u_char skt; +u_char type; +char *pkt; +int len; +AddrBlock *addr; +{ + int i; + + if (type != ddpECHO) + return; + abr.abResult = 1; +#ifdef DEBUG + printf("Got pkt from %d.%d (skt %d) of size %d\n", + ntohs(addr->net), addr->node, addr->skt, len); +#endif + i = (u_char)*pkt; + if (len != 6) { + printf("bad length..."); + return; + } + if (i != echoReply) { + printf("bad reply code..."); + return; + } + if (strncmp(pkt+1,"barf",4) != 0) { + printf("data corruption..."); + return; + } +} + +ping(addr) +AddrBlock *addr; +{ + + echo.echoFunction = echoRequest; + strcpy(echo.epdata, "barf"); + abr.abResult = 0; + abr.proto.ddp.ddpAddress = *addr; + abr.proto.ddp.ddpAddress.skt = echoSkt; + abr.proto.ddp.ddpSocket = myskt; + abr.proto.ddp.ddpType = ddpECHO; + abr.proto.ddp.ddpDataPtr = (u_char *)&echo; + abr.proto.ddp.ddpReqCount = 6; /* code+4bytes of string+null tie */ + DDPWrite(&abr, FALSE); + abSleep(4*5, TRUE); /* wait 5 seconds */ + return(abr.abResult); +} + +dumptostr(str, todump) +char *str; +char *todump; +{ + char c; + int i = 0; + + while ((c = *todump++)) { + if (c > 0 && isprint(c)) { + *str++ = c; + i++; + } else { + sprintf(str,"\\%3o",c&0xff); + str+=4; + i+=4; + } + } + *str = '\0'; + return(i); +} diff --git a/samples/lwpr.c b/samples/lwpr.c new file mode 100644 index 0000000..ccf96e7 --- /dev/null +++ b/samples/lwpr.c @@ -0,0 +1,363 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/06/18 10:51:10 $"; +static char rcsident[] = "$Header: /mac/src/cap60/samples/RCS/lwpr.c,v 2.5 1996/06/18 10:51:10 djh Rel djh $"; +static char revision[] = "$Revision: 2.5 $"; + +/* + * lwpr - UNIX AppleTalk test program: print a ps file to appletalk LaserWriter + * or Appletalk ImageWriter (depending if IMAGEWRITER is defined). + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia + * University in the City of New York. + * + * Edit History: + * + * June 29, 1986 Schilit&CCKim Created. + * July 5, 1986 CCKim Clean up + * Feb. 1987 CCKim Handle ImageWriter. + * + */ + +char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York"; + + +#include +#include +#include +#include +#include + +#include /* include appletalk definitions */ +#ifdef USESTRINGDOTH +# include +#else +# include +#endif +#if defined(xenix5) || defined(SOLARIS) +# include +#endif /* xenix5 || SOLARIS */ +#ifdef linux +# include +#endif /* linux */ + +#ifndef CAPPRINTERS +# define CAPPRINTERS "/etc/cap.printers" +#endif + +char *fname ; +int cno; +int pid; +#ifdef IMAGEWRITER +# define RFLOWQ 1 +#else IMAGEWRITER +# define RFLOWQ 8 +#endif IMAGEWRITER +#define BUFMAX 512*RFLOWQ +#ifndef SFLOWQ +#ifdef IMAGEWRITER +# define SFLOWQ 1 +#else IMAGEWRITER +# define SFLOWQ 8 +#endif IMAGEWRITER +#endif +#define SBUFMAX 512*SFLOWQ +char buf[SBUFMAX+10]; +int xdebug = TRUE; + +usage(pgm) +char *pgm; +{ + fprintf(stderr,"%s [-d] [-p printer] file [file]*\n",pgm); + fprintf(stderr, "\tnote: uses PRINTER environment var if printer name\n"); + fprintf(stderr, "\tnot given (requires %s)\n",CAPPRINTERS); + exit(1); +} + +main(argc,argv) +int argc; +char **argv; +{ + char *s; + void stopall(); + int pstatus(); + PAPStatusRec statusbuff; + int c; + char *LWNAME; + char *getlwname(); + AddrBlock addr; + extern char *optarg; + extern int optind; + + pid = -1; + abInit(xdebug); /* initialize appletalk driver */ + nbpInit(); + PAPInit(); /* init PAP printer routines */ + + LWNAME = NULL; + while ((c = getopt(argc, argv, "d:p:")) != EOF) { + switch (c) { + case 'd': + dbugarg(optarg); + break; + case 'p': + LWNAME = optarg; + break; + case '?': + usage(argv[0]); + break; + } + } + + if (optind == argc) /* no file name given */ + usage(argv[0]); + + if (LWNAME == NULL || strlen(LWNAME) == 0) { + LWNAME = getlwname((char *)getenv("PRINTER")); + } + if (LWNAME == NULL) + usage(argv[0]); + addr.net = 0; /* tell papstatus we don't know */ + + PAPStatus(LWNAME, &statusbuff, &addr); + printf("Status: "); + dumppstr(statusbuff.StatusStr); + + signal(SIGHUP, stopall); + signal(SIGINT, stopall); + + cno = openlw(LWNAME); + + for (; optind < argc; optind++ ) { + s = argv[optind]; + if (access(s, R_OK) == 0) + sendfile(s); + else + perror(s); + } + PAPClose(cno); +} + + +/* + * open laserwriter connection + * log errors every 5 minutes to stderr + * +*/ +int +openlw(lwname) +char *lwname; +{ + int i, cno, ocomp, err; + PAPStatusRec status; + + i = 0; + /* Keep trying to open */ + while ((err = PAPOpen(&cno, lwname, RFLOWQ, &status, &ocomp) ) != noErr) { + if (err != -1) /* should be can't find lw.. */ + fprintf(stderr,"PAPOpen returns %d\n",err); + else { + if ((i % 12) == 0) { /* waited 1 minute? */ + fprintf(stderr, "Problems finding %s\n",lwname); + i = 1; + } else i++; + } + sleep(5); /* wait N seconds */ + } + do { + abSleep(16, TRUE); + dumppstr(status.StatusStr); + } while (ocomp > 0); + return(cno); +} + +/* + * handle the papread + * return: -1 paperr + * 0 ok + * 1 eof +*/ +handleread(cno) +{ + static int rcomp = noErr; + static int rlen = 0; + static char rbuf[BUFMAX+10]; + static int eof = 0; + int paperr, eofgotten; + + if (rcomp > 0) + return(0); + switch (rcomp) { + case noErr: + break; + default: + fprintf(stderr, "PAPRead error %d\n", rcomp); + return(-1); + } + if (rlen) + write(fileno(stdout), rbuf, rlen); + eofgotten = eof; + eof = 0; + paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp); + switch (paperr) { + case noErr: + break; + default: + fprintf(stderr,"PAPRead error\n"); + return(-1); + } + return(eofgotten); +} + +/* + * Send a file + * return TRUE on error on pap connection + * false ow. +*/ +sendfile(fname) +char *fname; +{ + char *getusername(); + int fd, err; + int eof, wcomp, paperr; + + printf("Sending %s\n",fname); + fd = open(fname,0); + if (fd < 0) { + perror(fname); + return(FALSE); + } + wcomp = 0; +#ifndef IMAGEWRITER + strcpy(buf, "/statusdict where {pop statusdict /jobname ("); + strcat(buf, getusername()); + strcat(buf, "; document: "); + strcat(buf, fname); + strcat(buf, ") put} if\n"); + if ((paperr=PAPWrite(cno, buf,strlen(buf), FALSE, &wcomp)) < 0) { + printf("Error in first line\n"); + return(TRUE); + } +#endif + err = SBUFMAX; /* good inital value */ + do { + if ((eof = handleread(cno))) + break; + if (wcomp <= 0) { + if (wcomp != noErr) { + fprintf(stderr,"PAPWrite completion error %d\n",&wcomp); + break; + } else { + err = read(fd, buf, SBUFMAX); + if (err >= 0) + if ((paperr = PAPWrite(cno, buf, err, + err == SBUFMAX ? FALSE : TRUE, &wcomp)) < 0) + break; + } + } + abSleep(4, TRUE); /* wait a bit */ + } while (err == SBUFMAX ); + + if (paperr != noErr) + fprintf(stderr,"PAPWrite call error %d\n",paperr); + if (err < 0) + perror("read"); + while (!eof || wcomp > 0) { + if (!eof) + eof = handleread(cno); + abSleep(4, TRUE); + } + close(fd); + return(((eof<0) || (paperr != noErr) || (wcomp != noErr))?TRUE:FALSE); +} + + +void +stopall() +{ + PAPClose(cno, FALSE); + if (pid != -1) kill(pid, SIGHUP); + exit(1); +} + +/* + * get the laserwriter name of the unix spooled printer + * + * returns NULL if nothing found + * returns 'LaserWriter Plus' if printer is null +*/ +char * +getlwname(printer) +char *printer; +{ + FILE *fd; + static char buf[256]; + char *ep; + +#ifdef IMAGEWRITER + if (printer == NULL || printer[0] == '\0') + return("AppleTalk ImageWriter:ImageWriter@*"); +#else + if (printer == NULL || printer[0] == '\0') + return("LaserWriter Plus:LaserWriter@*"); +#endif + if ((fd = fopen(CAPPRINTERS,"r")) == NULL) { + perror("fopen"); + return(NULL); + } + do { + if (fgets(buf, 256, fd) == NULL) + break; + buf[strlen(buf)-1] = '\0'; /* get rid of the lf */ + if (buf[0] == '#' || buf[0] == '\0') + continue; + if ((ep=index(buf,'=')) == NULL) /* find first = */ + continue; /* no = in string */ + *ep = '\0'; /* set = to null now */ + if (strcmp(buf,printer) == 0) { + if (strlen(ep+1) == 0) /* no name */ + continue; + fclose(fd); + return(ep+1); /* return pointer to value */ + } + } while (1); + fclose(fd); + return(NULL); +} + +/* + * Dump a PAP status message + * +*/ +dumppstr(pstr) +unsigned char *pstr; +{ + int len = (int)(pstr[0]); + unsigned char *s = &pstr[1]; + + while (len--) { + if (isprint(*s)) + putchar(*s); + else + printf("\\%o",*s&0xff); + s++; + } + putchar('\n'); +} + +#include +char * +getusername() +{ + struct passwd *pw; + static char buf[256+20]; /* enough for host + user */ + if (gethostname(buf, 255) < 0) + strcpy(buf, "unknown host"); + strcat(buf, ":"); + if ((pw = getpwuid(getuid())) == NULL) + strcat(buf, "unknown user"); + else + strcat(buf, pw->pw_name); + return(buf); +} + diff --git a/samples/makefile b/samples/makefile new file mode 100644 index 0000000..1a32119 --- /dev/null +++ b/samples/makefile @@ -0,0 +1,116 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 13:59:51 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +I=/usr/include +O= + +# Valid: SFLOWQ=[1,2,3,4,5,6,7,8] +LWFLAGS=-DDOCNAME -DPAGECOUNT=512 + +# location of cap.printers file + + +# Make sure to define needgetopt if your system doesnt have it +GETOPT= + +PROGS=lwpr tlw atlook atlooklws atpinger iwpr isrv atistest ash \ + instappl getzones + +DESTDIR=/usr/local/cap +CAPLIB=-lcap +AFPLIB=-lafpc -lafp + +# for other libraries (like BSD on hpux) +SLIB= + +all: ${PROGS} + +atistest: atistest.o ${O} + ${CC} ${LFLAGS} -o atistest atistest.o ${O} ${CAPLIB} ${SLIB} + +getzones: getzones.o ${O} + ${CC} ${LFLAGS} -o getzones getzones.o ${O} ${CAPLIB} ${SLIB} + +ash.o: ash.c + ${CC} ${CFLAGS} -c ash.c + +ash: ash.o ${CAPFILES} + ${CC} ${LFLAGS} -o ash ash.o ${CAPFILES} ${AFPLIB} ${CAPLIB} ${SLIB} + +instappl: instappl.o $(GETOPT) + ${CC} ${LFLAGS} -o instappl instappl.o $(GETOPT) ${SLIB} + +# iwpr and lwpr share sources... +iwpr: iwpr.o $(O) ${GETOPT} + ${CC} ${LFLAGS} -o iwpr iwpr.o ${GETOPT} $(O) $(CAPLIB) ${SLIB} + +lwpr: lwpr.o $(O) ${GETOPT} + ${CC} ${LFLAGS} -o lwpr lwpr.o ${GETOPT} $(O) $(CAPLIB) ${SLIB} + +lwpr.o: lwpr.c + ${CC} ${CFLAGS} ${LWFLAGS} ${CAPPRINTERS} -c lwpr.c + +iwpr.o: lwpr.c + cp lwpr.c iwpr.c + ${CC} ${CFLAGS} ${LWFLAGS} -c -DIMAGEWRITER iwpr.c + rm iwpr.c + +isrv: isrv.o $(O) + ${CC} $(LFLAGS) -o isrv isrv.o $(O) $(CAPLIB) ${SLIB} + +isrv.o: isrv.c + ${CC} ${CFLAGS} -c isrv.c + +tlw: tlw.o $(O) + ${CC} ${LFLAGS} -o tlw tlw.o $(O) $(CAPLIB) ${SLIB} + +tlw.o: tlw.c + ${CC} ${CFLAGS} ${LWFLAGS} -c tlw.c + +# +# atlook, atlooklw, and atpinger all have a common source +# +atlook: atlook.o ${GETOPT} $(O) + ${CC} ${LFLAGS} -o atlook atlook.o $(O) ${GETOPT} $(CAPLIB) ${SLIB} + +# copy because some machines won't do it right o.w. +atlooklws.o: atlook.c + cp atlook.c atlooklws.c + ${CC} ${CFLAGS} -c -DATLOOKLWS atlooklws.c + rm atlooklws.c + +atlooklws: atlooklws.o ${GETOPT} ${O} + ${CC} ${LFLAGS} -o atlooklws atlooklws.o ${GETOPT} $(O) $(CAPLIB) ${SLIB} + +atpinger.o: atlook.c + cp atlook.c atpinger.c + ${CC} ${CFLAGS} -c -DATPINGER atpinger.c + rm atpinger.c + +atpinger: atpinger.o $(O) ${GETOPT} + ${CC} ${LFLAGS} -o atpinger atpinger.o ${GETOPT} $(O) $(CAPLIB) ${SLIB} + +att_getopt.c: + ln -s ../extras/att_getopt.c + +install: ${PROGS} + -strip ${PROGS} + ${INSTALLER} ${PROGS} ${DESTDIR} + +clean: + -rm -f ${PROGS} *.o core make.log err *~ att_getopt.c + +cleanexe: + -rm -f ${PROGS} + +dist: + @cat todist diff --git a/samples/papstatus.c b/samples/papstatus.c new file mode 100644 index 0000000..db4a8a0 --- /dev/null +++ b/samples/papstatus.c @@ -0,0 +1,331 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/05/31 13:03:31 $"; +static char rcsident[] = "$Header: /mac/src/cap60/samples/RCS/papstatus.c,v 2.5 1995/05/31 13:03:31 djh Rel djh $"; +static char revision[] = "$Revision: 2.5 $"; + +/* + * papstatus - UNIX AppleTalk program: simple status display + * for LaserWriter + * + * Based on papif + * + * Usage: + * papstatus -n nbp status from NBP entry (name:type@zone) + * papstatus printer.. status from printer + * papstatus -a status from all printers in /etc/cap.printers + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * July 1993 MJC Created from papif + * + */ + + +char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#ifndef _TYPES +#include /* in case param doesn't */ +#endif _TYPES +#include + +#include /* include appletalk definitions */ +#include +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH +#ifdef USEVPRINTF +# include +#endif USEVPRINTF +#ifdef xenix5 +# include +#endif xenix5 + +/* Configuration options */ + +#ifndef CAPPRINTERS +# define CAPPRINTERS "/etc/cap.printers" +#endif + + +#ifndef ATPRESPONSETIMEOUT +# define ATPRESPONSETIMEOUT sectotick(60*2) +#endif + + +/* + * GLOBAL VARIABLES +*/ +/* don't know where I got 30 from */ +char printer[30]; /* printer name */ +int verbose = 0; + +char *lwname = NULL; /* entity name */ +int doall = 0; + + + +char *capprinters = CAPPRINTERS; /* location of cap.printers */ +u_long atpresponsetimeout = ATPRESPONSETIMEOUT; /* atp resp. cache timeout */ + + +/* Definitions */ + + +/* logging levels */ +#define log_i dolog /* information */ +#define log_w dolog /* warning */ +#define log_e dolog /* error */ +#define log_r dolog /* return from remote */ +#define log_d dolog /* log debugging */ + + + +main(argc,argv) +int argc; +char **argv; +{ + int pstatus(); + char *getlwname(); + extern boolean dochecksum; + char *c, *getenv(); + char *arg; + + if ((c = getenv("CAPPRINTERS")) != NULL) + capprinters = c; + + argv++; argc--; + while (argc > 0 && **argv == '-') { + arg = *argv++; argc--; arg++; + while (*arg) { + switch (*arg++) { + case 'a': + doall++; + break; + case 'c': + capprinters = *argv++; argc--; + break; + case 'd': + dbugarg(*argv++); argc--; + break; + case 'k': /* no DDP checksum */ + dochecksum = 0; + break; + case 'n': + lwname = *argv++; argc--; + break; + case 'p': /* printer name */ + case 'P': /* printer name */ + lwname = getlwname(*argv++); argc--; + if (lwname == NULL) { + log_e("papstatus: Cannot map name %s to LaserWriter name\n",argv[-1]); + exit(1); + } + break; + case 'v': + verbose++; + break; + default: + log_e("papstatus: Unknown argument %c\n",*arg); + } + } + } + + /* init cap */ + abInit(FALSE); /* don't printout -- messes up with */ + nbpInit(); + PAPInit(); /* init PAP printer routines */ + ATPSetResponseTimeout(atpresponsetimeout); /* set to 2 minutes */ + + if (doall) + status_all (); + else if (lwname) + getstatus (lwname); + else { + while (argc-- > 0) { + lwname = getlwname(*argv); + if (lwname == NULL) { + log_e("papstatus: Cannot map name %s to LaserWriter name\n",*argv); + continue; + } + printf ("%s (%s):\n", *argv, lwname); + getstatus (lwname); + argv++; + } + } + + +} + + +getstatus (name) +char *name; +{ + AddrBlock addr; + PAPStatusRec status; + int nostat; + + addr.net = 0; + addr.node = 0; + addr.skt = 0; + + if (verbose) + log_i("papstatus: Getting status on printer %s\n",name); + + do { + PAPStatus (name, &status, &addr); + + nostat = strcmp ((char *)(status.StatusStr + 1), "%no status"); + if (verbose || nostat != 0) + pstatus(status.StatusStr); + } while (nostat == 0); +} + + +/* + * output status message to stdout. + * Note: input string is a pascal string + * + */ + +pstatus(s) +char *s; +{ + printf ("%*.*s\n\n", *s, *s, s+1); +} + + +status_all () +{ +#ifdef NOCAPDOTPRINTERS + fprintf(stderr, "Sorry, CAP was configured without cap.printers support\n"); +#else NOCAPDOTPRINTERS + FILE *fd; + static char buf[1024]; + char *ep; + + if ((fd = fopen(capprinters,"r")) == NULL) { + perror(capprinters); + return; + } + do { + if (fgets(buf, 256, fd) == NULL) + break; + buf[strlen(buf)-1] = '\0'; /* get rid of the lf */ + if (buf[0] == '#' || buf[0] == '\0') + continue; + if ((ep=index(buf,'=')) == NULL) /* find first = */ + continue; /* no = in string */ + *ep = '\0'; /* set = to null now */ + if (strlen(ep+1) == 0) /* no name */ + continue; + lwname = ep+1; + printf ("%s (%s):\n", buf, lwname); + getstatus (lwname); + } while (1); + fclose(fd); +#endif NOCAPDOTPRINTERS +} + +/* + * get the laserwriter name of the unix spooled printer + * + */ + +char * +getlwname(printer) +char *printer; +{ + FILE *fd; + static char buf[1024]; + char *ep; + +#ifdef NOCAPDOTPRINTERS + sprintf(buf, "/etc/lp/printers/%s/comment", printer); + if ((fd = fopen(buf, "r")) == NULL) { + perror(buf); + return(NULL); + } + if (fgets(buf, sizeof(buf), fd) == NULL) + return(NULL); + buf[strlen(buf)-1] = '\0'; /* get rid of the lf */ + return(buf); +#else NOCAPDOTPRINTERS + if ((fd = fopen(capprinters,"r")) == NULL) { + perror(capprinters); + return(NULL); + } + do { + if (fgets(buf, 256, fd) == NULL) + break; + buf[strlen(buf)-1] = '\0'; /* get rid of the lf */ + if (buf[0] == '#' || buf[0] == '\0') + continue; + if ((ep=index(buf,'=')) == NULL) /* find first = */ + continue; /* no = in string */ + *ep = '\0'; /* set = to null now */ + if (strcmp(buf,printer) == 0) { + if (strlen(ep+1) == 0) /* no name */ + continue; + fclose(fd); + return(ep+1); /* return pointer to value */ + } + } while (1); + fclose(fd); + return(NULL); +#endif NOCAPDOTPRINTERS +} + + +/* + * Setup this so we can be smarter about errors in future + * logging level are setup as: i - information, w - warning + * e - error, r - return from laserwriter, and d - for debugging + * +*/ + +private FILE *jobout; + +#ifndef USEVPRINTF +/* Bletch - gotta do it because pyramids don't work the other way */ +/* (using _doprnt and &args) and don't have vprintf */ +/* of course, there will be something that is just one arg larger :-) */ +/* VARARGS1 */ +dolog(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +char *fmt; +#else +dolog(va_alist) +va_dcl +#endif +{ +#ifdef USEVPRINTF + register char *fmt; + va_list args; + + va_start(args); + fmt = va_arg(args, char *); + if (jobout) + vfprintf(jobout, fmt, args); + vfprintf(stderr, fmt, args); + va_end(args); +#else + /* + * Keep buffers flushed to avoid double-output after fork(); + */ + if (jobout) { + fprintf(jobout, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); + fflush(jobout); + } + fprintf(stderr, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); + fflush(stderr); +#endif +} + +/* END MODULE: log */ diff --git a/samples/ruiwpr.c b/samples/ruiwpr.c new file mode 100644 index 0000000..0ce84f9 --- /dev/null +++ b/samples/ruiwpr.c @@ -0,0 +1,357 @@ +static char rcsid[] = "$Author: djh $ $Date: 91/03/14 20:56:37 $"; +static char rcsident[] = "$Header: ruiwpr.c,v 2.1 91/03/14 20:56:37 djh Exp $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * lwpr - UNIX AppleTalk test program: print a ps file to appletalk LaserWriter + * or Appletalk ImageWriter (depending if IMAGEWRITER is defined). + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia + * University in the City of New York. + * + * Edit History: + * + * June 29, 1986 Schilit&CCKim Created. + * July 5, 1986 CCKim Clean up + * Feb. 1987 CCKim Handle ImageWriter. + * + */ + +char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York"; + + +#include +#include +#include +#include +#include + +#include /* include appletalk definitions */ +#ifdef USESTRINGDOTH +# include +#else +# include +#endif + +#ifndef CAPPRINTERS +# define CAPPRINTERS "/etc/cap.printers" +#endif + +char *fname ; +int cno; +int pid; +#define RFLOWQ 8 +#define BUFMAX 512*RFLOWQ +#ifndef SFLOWQ +# define SFLOWQ 8 +#endif +#define SBUFMAX 512*SFLOWQ +char buf[SBUFMAX+10]; +int xdebug = TRUE; +int usestdin; + +usage(pgm) +char *pgm; +{ + fprintf(stderr,"%s [-d] [-p printer] file [file]*\n",pgm); + fprintf(stderr, "\tnote: uses PRINTER environment var if printer name\n"); + fprintf(stderr, "\tnot given (requires %s)\n",CAPPRINTERS); + exit(1); +} + +main(argc,argv) +int argc; +char **argv; +{ + char *s; + int stopall(); + int pstatus(); + PAPStatusRec statusbuff; + int c; + char *LWNAME; + char *getlwname(); + AddrBlock addr; + extern char *optarg; + extern int optind; + extern boolean dochecksum; + + pid = -1; + dochecksum = 0; /* crude but effective */ + abInit(xdebug); /* initialize appletalk driver */ + nbpInit(); + PAPInit(); /* init PAP printer routines */ + + while ((c = getopt(argc, argv, "d:p:")) != EOF) { + switch (c) { + case 'd': + dbugarg(optarg); + break; + case 'p': + LWNAME = optarg; + break; + case '?': + usage(argv[0]); + break; + } + } + + + if (LWNAME == NULL || strlen(LWNAME) == 0) { + LWNAME = getlwname((char *)getenv("PRINTER")); + } + if (LWNAME == NULL) + usage(argv[0]); + addr.net = 0; /* tell papstatus we don't know */ + + PAPStatus(LWNAME, &statusbuff, &addr); + printf("Status: "); + dumppstr(statusbuff.StatusStr); + + signal(SIGHUP, stopall); + signal(SIGINT, stopall); + + cno = openlw(LWNAME); + + if (optind == argc) /* no file name given */ + sendfile(NULL); + else + for (; optind < argc; optind++ ) { + s = argv[optind]; + if (access(s, R_OK) == 0) + sendfile(s); + else + perror(s); + } + PAPClose(cno); +} + + +/* + * open laserwriter connection + * log errors every 5 minutes to stderr + * +*/ +int +openlw(lwname) +char *lwname; +{ + int i, cno, ocomp, err; + PAPStatusRec status; + + i = 0; + /* Keep trying to open */ + while ((err = PAPOpen(&cno, lwname, RFLOWQ, &status, &ocomp) ) != noErr) { + if (err != -1) /* should be can't find lw.. */ + fprintf(stderr,"PAPOpen returns %d\n",err); + else { + if ((i % 12) == 0) { /* waited 1 minute? */ + fprintf(stderr, "Problems finding %s\n",lwname); + i = 1; + } else i++; + } + sleep(5); /* wait N seconds */ + } + do { + abSleep(16, TRUE); + dumppstr(status.StatusStr); + } while (ocomp > 0); + return(cno); +} + +/* + * handle the papread + * return: -1 paperr + * 0 ok + * 1 eof +*/ +handleread(cno) +{ + static int rcomp = noErr; + static int rlen = 0; + static char rbuf[BUFMAX+10]; + static int eof = 0; + int paperr, eofgotten; + + if (rcomp > 0) + return(0); + switch (rcomp) { + case noErr: + break; + default: + fprintf(stderr, "PAPRead error %d\n", rcomp); + return(-1); + } + if (rlen) + write(fileno(stdout), rbuf, rlen); + eofgotten = eof; + eof = 0; + paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp); + switch (paperr) { + case noErr: + break; + default: + fprintf(stderr,"PAPRead error\n"); + return(-1); + } + return(eofgotten); +} + +/* + * Send a file + * return TRUE on error on pap connection + * false ow. +*/ +sendfile(fname) +char *fname; +{ + char *getusername(); + int fd, err; + int eof, wcomp, paperr; + + if (fname == NULL) { + printf("Sending file from stdin\n"); + fd = 0; + } + else { + printf("Sending %s\n",fname); + fd = open(fname,0); + } + if (fd < 0) { + perror(fname); + return(FALSE); + } + wcomp = 0; +#ifndef IMAGEWRITER + strcpy(buf, "/statusdict where {pop statusdict /jobname ("); + strcat(buf, getusername()); + strcat(buf, "; document: "); + strcat(buf, fname); + strcat(buf, ") put} if\n"); + if ((paperr=PAPWrite(cno, buf,strlen(buf), FALSE, &wcomp)) < 0) { + printf("Error in first line\n"); + return(TRUE); + } +#endif + err = SBUFMAX; /* good inital value */ + do { + if ((eof = handleread(cno))) + break; + if (wcomp <= 0) { + if (wcomp != noErr) { + fprintf(stderr,"PAPWrite completion error %d\n",&wcomp); + break; + } else { + err = read(fd, buf, SBUFMAX); + if (err >= 0) + if ((paperr = PAPWrite(cno, buf, err, + err == SBUFMAX ? FALSE : TRUE, &wcomp)) < 0) + break; + } + } + abSleep(4, TRUE); /* wait a bit */ + } while (err == SBUFMAX ); + + if (paperr != noErr) + fprintf(stderr,"PAPWrite call error %d\n",paperr); + if (err < 0) + perror("read"); + while (!eof || wcomp > 0) { + if (!eof) + eof = handleread(cno); + abSleep(4, TRUE); + } + close(fd); + return(((eof<0) || (paperr != noErr) || (wcomp != noErr))?TRUE:FALSE); +} + + +stopall() +{ + PAPClose(cno, FALSE); + if (pid != -1) kill(pid, SIGHUP); + exit(1); +} + +/* + * get the laserwriter name of the unix spooled printer + * + * returns NULL if nothing found + * returns 'LaserWriter Plus' if printer is null +*/ +char * +getlwname(printer) +char *printer; +{ + FILE *fd; + static char buf[256]; + char *ep; + +#ifdef IMAGEWRITER + if (printer == NULL || printer[0] == '\0') + return("AppleTalk ImageWriter:ImageWriter@*"); +#else + if (printer == NULL || printer[0] == '\0') + return("LaserWriter Plus:LaserWriter@*"); +#endif + if ((fd = fopen(CAPPRINTERS,"r")) == NULL) { + perror("fopen"); + return(NULL); + } + do { + if (fgets(buf, 256, fd) == NULL) + break; + buf[strlen(buf)-1] = '\0'; /* get rid of the lf */ + if (buf[0] == '#' || buf[0] == '\0') + continue; + if ((ep=index(buf,'=')) == NULL) /* find first = */ + continue; /* no = in string */ + *ep = '\0'; /* set = to null now */ + if (strcmp(buf,printer) == 0) { + if (strlen(ep+1) == 0) /* no name */ + continue; + fclose(fd); + return(ep+1); /* return pointer to value */ + } + } while (1); + fclose(fd); + return(NULL); +} + +/* + * Dump a PAP status message + * +*/ +dumppstr(pstr) +unsigned char *pstr; +{ + int len = (int)(pstr[0]); + unsigned char *s = &pstr[1]; + + while (len--) { + if (isprint(*s)) + putchar(*s); + else + printf("\\%o",*s&0xff); + s++; + } + putchar('\n'); +} + +#include +char * +getusername() +{ + struct passwd *pw; + static char buf[256+20]; /* enough for host + user */ + if (gethostname(buf, 255) < 0) + strcpy(buf, "unknown host"); + strcat(buf, ":"); + if ((pw = getpwuid(getuid())) == NULL) + strcat(buf, "unknown user"); + else + strcat(buf, pw->pw_name); + return(buf); +} + diff --git a/samples/tlw.c b/samples/tlw.c new file mode 100644 index 0000000..4d3dccb --- /dev/null +++ b/samples/tlw.c @@ -0,0 +1,329 @@ +static char rcsid[] = "$Author: djh $ $Date: 1992/07/27 16:08:44 $"; +static char rcsident[] = "$Header: /mac/src/cap60/samples/RCS/tlw.c,v 2.7 1992/07/27 16:08:44 djh Rel djh $"; +static char revision[] = "$Revision: 2.7 $"; + +/* + * tlw - UNIX AppleTalk test program - talk to laserwriter + * + * Talk to the LaserWriter - interactive session + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986,1988 by The Trustees of Columbia University in the + * City of New York. + * + * Edit History: + * + * June 13, 1986 Schilit Created. + * June 30, 1986 CCKim Convert to TLW from lwpr + * July 2, 1986 Schilit Make work with new stuff + * July 5, 1986 CCKim Really make work with new stuff + * July 5, 1991 jjchew + * Lookup names in cap.printers + */ + +char copyright[] = "Copyright (c) 1986, 1988 by The Trustees of Columbia University in the City of New York"; + +#include +#include +#ifndef _TYPES + /* assume included by param.h */ +# include +#endif + +#include +#include + +#include /* include appletalk definitions */ +#include /* overrides for non-4.3 systems */ + +#ifdef USESTRINGDOTH +# include +#else USESTRINGDOTH +# include +#endif USESTRINGDOTH + +#ifndef CAPPRINTERS +#define CAPPRINTERS "/etc/cap.printers" +#endif CAPPRINTERS + +int cno; +#define RFLOWQ 8 +#ifndef SFLOWQ +# define SFLOWQ 8 +#endif +#define BUFMAX 512*RFLOWQ +#define SBUFMAX 512*SFLOWQ +char buf[SBUFMAX+10]; +char rbuf[BUFMAX+10]; +boolean useunixname = FALSE; + +main(argc,argv) +int argc; +char **argv; +{ + char *LWNAME; + char tbuf[sizeof(EntityName)*3+1]; + void stopall(); + int hangup(); + int pstatus(); + char *getlwname(); + int c; + extern char *optarg; + extern int optind; + extern boolean dochecksum; + boolean errflag = FALSE; + + while ((c = getopt(argc, argv, "akd:u")) != EOF) { + switch (c) { + case 'a': + useunixname=FALSE; + break; + case 'd': + dbugarg(optarg); + break; + case 'k': + dochecksum = 0; + break; + case 'u': + useunixname=TRUE; + break; + case '?': + errflag = TRUE; + break; + } + } + + if (errflag || argc-optind > 1) { + fprintf(stderr, "Usage: %s [-d flags] [-a|-u] \n",argv[0]); + exit(1); + } + + LWNAME = argc==optind ? "LaserWriter Plus:LaserWriter@*" : argv[optind]; + if (useunixname) { + char *s; + s = getlwname(LWNAME); + if (s == NULL) { + fprintf(stderr, "Cannot find printer %s in %s\n", LWNAME, CAPPRINTERS); + exit(2); + } + LWNAME = s; + } + else if (index(LWNAME,':') == NULL) { + (void)sprintf(tbuf,"%s:LaserWriter@*", LWNAME); + LWNAME = tbuf; + } + + abInit(TRUE); /* initialize appletalk driver */ + PAPInit(); /* init pap */ + nbpInit(); /* init nbp */ + + printf("Starting session with %s\n",LWNAME); + + setbuf(stdin, (char *)NULL); + + cno = openlw(LWNAME); + signal(SIGHUP, stopall); + signal(SIGINT, stopall); + talk(cno); + PAPClose(cno); /* close connection */ + exit(0); /* exit okay */ +} + +/* + * get the laserwriter name of the unix spooled printer + * (stolen from lwpr.c, also found in papif.c) + * + * returns NULL if nothing found + * returns 'LaserWriter Plus' if printer is null +*/ +char * +getlwname(printer) +char *printer; +{ + FILE *fd; + static char buf[256]; + char *ep; + + if (printer == NULL || printer[0] == '\0') + return("LaserWriter Plus:LaserWriter@*"); + if ((fd = fopen(CAPPRINTERS,"r")) == NULL) { + perror("fopen"); + return(NULL); + } + do { + if (fgets(buf, 256, fd) == NULL) + break; + buf[strlen(buf)-1] = '\0'; /* get rid of the lf */ + if (buf[0] == '#' || buf[0] == '\0') + continue; + if ((ep=(char *)index(buf,'=')) == NULL) /* find first = */ + continue; /* no = in string */ + *ep = '\0'; /* set = to null now */ + if (strcmp(buf,printer) == 0) { + if (strlen(ep+1) == 0) /* no name */ + continue; + fclose(fd); + return(ep+1); /* return pointer to value */ + } + } while (1); + fclose(fd); + return(NULL); +} + +/* + * open laserwriter connection + * log errors every 5 minutes to stderr + * +*/ +int +openlw(lwname) +char *lwname; +{ + int i, cno, ocomp, err; + PAPStatusRec status; + + i = 0; + /* Keep trying to open */ + while ((err = PAPOpen(&cno, lwname, RFLOWQ, &status, &ocomp) ) != noErr) { + if (err != -1) /* should be can't find lw.. */ + fprintf(stderr,"PAPOpen returns %d\n",err); + else { + if ((i % 10) == 0) { /* waited 5 minutes? */ + fprintf(stderr, "Problems finding %s\n",lwname); + i = 1; + } else i++; + } + sleep(30); /* wait N seconds */ + } + do { + abSleep(16, TRUE); + pstatus(status.StatusStr); + } while (ocomp > 0); + return(cno); +} + +dataready(fd, tcomp, dummy) +int fd; +int *tcomp; +int dummy; +{ + fdlistensuspend(fd); /* no more select until we read */ + *tcomp = noErr; /* data ready */ +} + +/* + * Send a file to the specified connection + */ +talk(cno) +int cno; +{ + int eof, rlen, rcomp, wcomp, paperr, err; + int tcomp; + char *getusername(); + + printf("\nOkay\n"); + wcomp = 0; + strcpy(buf, "/statusdict where {pop statusdict /jobname"); + strcat(buf, "(interactive session for "); + strcat(buf, getusername()); + strcat(buf, ") put} if\nexecutive\n"); + if ((paperr=PAPWrite(cno, buf,strlen(buf), FALSE, &wcomp)) < 0) { + printf("Error in first line\n"); + } + + /* post initial read from LW */ + if ((paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp)) < 0) { + fprintf(stderr,"PAPRead error %d\n",paperr); + } + tcomp = 1; /* no data */ + fdlistener(fileno(stdin), dataready, &tcomp, 0); + /* this is the main read/write loop */ + do { + if (rcomp <= 0) { + if (rcomp != noErr) { + fprintf(stderr,"PAPRead error %d\n",rcomp); + break; + } else if (rlen > 0) + (void)write(fileno(stdout), rbuf, rlen); + if ((paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp)) < 0) { + fprintf(stderr,"PAPRead error %d\n",paperr); + break; + } + } + if (wcomp <= 0) + if (wcomp != noErr) { + fprintf(stderr,"PAPWrite error %d\n",wcomp); + break; + } + else if (tcomp == noErr) { + tcomp = 1; /* no data */ + fdlistenresume(fileno(stdin)); /* can do here, because not until */ + /* abSleep */ + /* should only read up to ready */ + err = read(fileno(stdin), buf, SBUFMAX); + if (err <= 0) /* eof */ + break; + paperr = PAPWrite(cno, buf, err, FALSE, &wcomp); + if (paperr != noErr) + break; + } + abSleep(4, TRUE); /* wait a bit */ + } while (err >= 0 ); + + strcpy(buf, "quit\n"); /* toss ourselves into the */ + if ((paperr = PAPWrite(cno, buf,sizeof("quit\n")-1, TRUE, &wcomp)) < 0) { + printf("Error in first line\n"); + } + while (!eof) { /* wait for completion */ + if (rcomp <= 0) { + if (rcomp != noErr) { + fprintf(stderr,"PAPRead error %d\n",rcomp); + break; + } else if (rlen > 0) + (void)write(fileno(stdout),rbuf,rlen); + if (eof) break; + PAPRead(cno, rbuf, &rlen, &eof, &rcomp); + } + abSleep(4,TRUE); + } + if (paperr != noErr) + fprintf(stderr,"PAPWrite error %d\n",paperr); + else + do { abSleep(4, TRUE); } while (wcomp > 0); +} + + +pstatus(s) +byte *s; +{ + (void)write(1, s+1, *s); + write(1, "\n", 1); /* put out a cr */ +} + +/* + * user sent interrupt - close down shop +*/ +void +stopall() +{ + PAPClose(cno); + exit(1); +} + +#include +char * +getusername() +{ + struct passwd *pw; + static char buf[256+20]; /* enough for host + user */ + if (gethostname(buf, 255) < 0) + strcpy(buf, "unknown host"); + strcat(buf, ":"); + if ((pw = getpwuid(getuid())) == NULL) + strcat(buf, "unknown user"); + else + strcat(buf, pw->pw_name); + return(buf); +} + diff --git a/support/capd/Makefile.m4 b/support/capd/Makefile.m4 new file mode 100644 index 0000000..e3b9172 --- /dev/null +++ b/support/capd/Makefile.m4 @@ -0,0 +1,39 @@ +CFLAGS=-DDEBUG cflags() specialcflags() +DESTDIR=capsrvrdestdir() +PROGS=capdprogs() +POBJS=capdpobjs() +CAPLIB=libcap() +LFLAGS= + +SRCS=capd.c +OBJS=capd.o + +all: ${PROGS} + +capd: ${OBJS} ${POBJS} + ${CC} ${LFLAGS} -o capd ${OBJS} ${POBJS} ${CAPLIB} + +install: ${PROGS}.install + +.install: + +capd.install: capd + -strip capd + ifdef([sysvinstall],[install -f $(DESTDIR) capd], + [${INSTALLER} capd ${DESTDIR}]) + +lint: + lint -h capd.c ${SRCS} + +clean: + rm -f *.o capd + +spotless: + rm -f *.o *.orig capd + +capd.o: capd.c + ${CC} -c capd.c ${CFLAGS} + +capd.kas.o: capd.kas.c + ${CC} -c capd.kas.c ${CFLAGS} + diff --git a/support/capd/README b/support/capd/README new file mode 100644 index 0000000..a562cd8 --- /dev/null +++ b/support/capd/README @@ -0,0 +1,11 @@ +CAPD +---- + +CAPD is a general purpose CAP daemon. The initial version for use with +Kernel AppleTalk simply sets up reasonable values in the etalk.local +configuration file and exits. + +The usual arguments are identical to those used with aarpd, ie: + + capd [-D level] [-d opt] [-l log] interface zone + diff --git a/support/capd/capd.c b/support/capd/capd.c new file mode 100644 index 0000000..57c9cdd --- /dev/null +++ b/support/capd/capd.c @@ -0,0 +1,441 @@ +/* + * $Author: djh $ $Date: 1996/09/10 16:15:17 $ + * $Header: /mac/src/cap60/support/capd/RCS/capd.c,v 2.6 1996/09/10 16:15:17 djh Rel djh $ + * $Revision: 2.6 $ + * + */ + +/* + * capd - general purpose CAP daemon + * + * djh@munnari.OZ.AU + * + */ + +#include +#include +#include +#include +#ifdef PHASE2 +#include +#if (!(defined(ultrix) || defined(linux))) +#include +#endif ultrix || linux +#include +#endif PHASE2 +#include +#include +#include + +/* + * etalkdbm globals + * + */ +extern char interface[50]; /* which ethernet device */ +extern char this_zone[34]; /* zone for this host */ +extern struct in_addr bridge_addr; /* IP address for local bridge */ +extern byte bridge_node; /* the local bridge */ +extern word bridge_net; /* the local bridge */ +extern byte this_node; /* this host node */ +extern word this_net; /* this host node */ +extern byte nis_node; /* atis running here */ +extern word nis_net; /* atis running here */ +extern word net_range_start; /* phase 2 network range start */ +extern word net_range_end; /* phase 2 network range end */ + +extern short lap_proto; /* our LAP mechanism */ + +int node, net, net_lo, net_hi; +char *zonename = NULL; +char *ifname = NULL; + +private char mcaddr[6] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff }; + +int dlevel=0; /* debug level */ + +main(argc, argv) +int argc; +char *argv[]; +{ + int c; + char *cp, *ep; + extern int optind; + extern char *optarg; + struct ifreq ifreq; + short capdIdent(); + void run(); + + while ((c = getopt(argc, argv, "D:d:l:")) != EOF) { + switch (c) { + case 'D': + dlevel = atoi(optarg); + if (dlevel > L_LVLMAX) + dlevel = L_LVLMAX; + break; + case 'd': + dbugarg(optarg); + dlevel = 1; + break; + case 'l': + logitfileis(optarg, "w"); + break; + } + } + set_debug_level(dlevel); + + openetalkdb(NULL); /* open/create etalk.local */ + + /* + * initialise externals + * + */ + this_node = 0; + this_net = htons(0xff00); + nis_net = nis_node = bridge_net = bridge_node = 0; + bridge_addr.s_addr = inet_addr("127.0.0.1"); + net_range_start = htons(0); + net_range_end = htons(0xfffe); + + lap_proto = capdIdent(); + + /* + * get supplied interface & zone names + * + */ + if (argc == (optind+2)) { + ifname = argv[optind++]; + zonename = argv[optind++]; + strncpy(interface, ifname, sizeof(interface)); + strncpy(this_zone, zonename, sizeof(this_zone)); + } + + /* + * wrong number of args ? + * + */ + if (optind != argc) { + fprintf(stderr, + "usage: capd [-D level] [-d opt] [-l log] [interface zone]\n"); + exit(1); + } + + if (ifname == NULL + || *ifname == '\0') { + fprintf(stderr, "No ethernet interface specified\n"); + exit(1); + } + ep = NULL; + for (cp = ifname; *cp != '\0'; cp++) { + if (*cp >= '0' && *cp <= '9') + ep = cp; + } + if (ep == NULL) { /* interface, but no number */ + fprintf(stderr, "Specified interface invalid: %s\n", ifname); + exit(1); + } + if (zonename == NULL + || *zonename == '\0') { + fprintf(stderr, "No zone name specified\n"); + exit(1); + } + + /* + * config Kernel AppleTalk + * startup range, any node + * + */ + node = 0x00; + net = 0xff00; + net_lo = 0x0000; + net_hi = 0xfffe; + + if (ifconfig(&node, &net, &net_lo, &net_hi) < 0) { + fprintf(stderr, "Can't initialise AppleTalk on %s\n", ifname); + exit(1); + } + +#ifdef PHASE2 + /* + * add multicast address to interface + * + */ + strncpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name)); + if (pi_addmulti(mcaddr, (caddr_t)&ifreq) < 0) { + fprintf(stderr, "Can't add multicast address!\n"); + exit(1); + } + + /* + * if phase 2, ask network for net + * ranges, get zone multicast address and + * verify user supplied zone name. + * + */ + if (getNetInfo() >= 0) { + net = net_lo; + strncpy(interface, ifname, sizeof(interface)); + if (ifconfig(&node, &net, &net_lo, &net_hi) < 0) { + fprintf(stderr, "Can't reinitialise AppleTalk on %s\n", ifname); + exit(1); + } + net_range_start = htons(net_lo); + net_range_end = htons(net_hi); + } +#endif /* PHASE2 */ + + nis_net = this_net = net; + nis_node = this_node = node; + strncpy(interface, ifname, sizeof(interface)); + strncpy(this_zone, zonename, sizeof(this_zone)); + bridge_addr.s_addr = inet_addr("127.0.0.1"); + bridge_net = bridge_node = 0; + + etalkdbupdate(NULL); /* rewrite gleaned information */ + + if (!dbug.db_flgs && (dlevel == 0)) + disassociate(); + + run(); /* do all the CAPD work */ + + (void)fprintf(stderr, "capd: run() returned!\n"); + + exit(1); +} + +disassociate() +{ + int i; + /* disassociate */ + if (fork()) + _exit(0); /* kill parent */ + for (i=0; i < 3; i++) close(i); /* kill */ + (void)open("/",0); +#ifdef NODUP2 + (void)dup(0); /* slot 1 */ + (void)dup(0); /* slot 2 */ +#else NODUP2 + (void)dup2(0,1); + (void)dup2(0,2); +#endif NODUP2 +#ifdef TIOCNOTTY + if ((i = open("/dev/tty",2)) > 0) { + (void)ioctl(i, TIOCNOTTY, (caddr_t)0); + (void)close(i); + } +#endif TIOCNTTY +#ifdef POSIX + (void) setsid(); +#endif POSIX +} + +#ifdef PHASE2 + +#define ZIPQuery 1 +#define ZIPReply 2 +#define ZIPEReply 8 +#define ZIPGetInfoReq 5 +#define ZIPGetInfoRepl 6 + +#define ZIPATTEMPTS 4 + +#define ZIPZoneBad 0x80 +#define ZIPNoMultiCast 0x40 +#define ZIPSingleZone 0x20 + +private int zisSkt; +private int heardFrom; +private char zmcaddr[6]; +private char defzone[34]; + +/* + * This is the ZIP ZIS listener + * (at present we are only interested + * in receiving one GetNetInfo packet + * and that only at startup) + * + */ + +void +zis_listener(skt, type, zis, len, addr) +u_char skt; +u_char type; +u_char *zis; +int len; +AddrBlock *addr; +{ + int x; + struct ifreq ifreq; + + if (heardFrom) + return; /* drop it */ + + if (type == ddpATP) + return; /* drop it */ + + if (type != ddpZIP) { + logit(3, "ZIS listener - bad packet"); + return; /* drop it */ + } + + defzone[0] = '\0'; + + switch (*zis) { + case ZIPQuery: + case ZIPReply: + case ZIPEReply: + case ZIPGetInfoReq: + break; /* drop it */ + case ZIPGetInfoRepl: + heardFrom = 1; + net_lo = (zis[2] << 8) | zis[3]; /* net byte order */ + net_hi = (zis[4] << 8) | zis[5]; /* net byte order */ + if ((x = zis[6]) < sizeof(defzone)) { + bcopy(&zis[7], defzone, x); + defzone[x] = '\0'; + } + + /* + * check multicast address length + * + */ + x += 7; + if (zis[x] != sizeof(zmcaddr)) { + fprintf(stderr, "Bogus Multicast Address length %d\n", zis[x]); + exit(1); + } + + /* + * get zone multicast address + * + */ + bcopy((char *)&zis[x+1], zmcaddr, sizeof(zmcaddr)); + + logit(3, "GetNetInfo reply packet arrived:"); + logit(3, "%sFlags 0x%02x, rangeStart %04x, rangeEnd %04x", + " ", zis[1], net_lo, net_hi); + logit(3, "%sZone %s (len %d), MCZAddr %x:%x:%x:%x:%x:%x", + " ", defzone, zis[6], (u_char) zmcaddr[0], + (u_char) zmcaddr[1], (u_char) zmcaddr[2], + (u_char) zmcaddr[3], (u_char) zmcaddr[4], + (u_char) zmcaddr[5]); + + /* + * supplied zone name valid ? + * + */ + if (zis[1] & ZIPZoneBad) { + x += 7; + if (zis[x] < sizeof(defzone)) { + bcopy((char *)&zis[x+1], defzone, zis[x]); + defzone[zis[x]] = '\0'; + } + fprintf(stderr, "Zone \"%s\" unknown, network default is \"%s\"\n", + zonename, defzone); + exit(1); + } + + /* + * set zone multicast address on interface + * + */ + strncpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name)); + if (pi_addmulti(zmcaddr, (caddr_t)&ifreq) < 0) { + fprintf(stderr, "Can't add zone multicast address on %s!\n", ifname); + exit(1); + } + + break; + } +} + +/* + * open the ZIS socket, start the listener, probe for netinfo + * + */ + +int +getNetInfo() +{ + void zis_listener(); + ABusRecord ddpr; + u_char zipbuf[48]; + int count, error; + + abInit(FALSE); + heardFrom = 0; + zisSkt = ddpZIP; + + if (DDPOpenSocket(&zisSkt, zis_listener) != noErr) { + logit(0, "Failed to start ZIS listener!"); + exit(1); + } + + zipbuf[0] = ZIPGetInfoReq; + zipbuf[1] = 0x0; + zipbuf[2] = 0x0; + zipbuf[3] = 0x0; + zipbuf[4] = 0x0; + zipbuf[5] = 0x0; + zipbuf[6] = strlen(zonename); + strncpy(&zipbuf[7], zonename, sizeof(zipbuf)-8); + + ddpr.abResult = 0; + ddpr.proto.ddp.ddpAddress.net = 0x0000; /* local net */ + ddpr.proto.ddp.ddpAddress.node = 0xff; /* broadcast */ + ddpr.proto.ddp.ddpAddress.skt = zisSkt; /* to ZIS at GW */ + ddpr.proto.ddp.ddpSocket = zisSkt; /* from our ZIS */ + ddpr.proto.ddp.ddpType = ddpZIP; + ddpr.proto.ddp.ddpDataPtr = (u_char *)zipbuf; + ddpr.proto.ddp.ddpReqCount = zipbuf[6] + 7; + + for (count = 0 ; count < ZIPATTEMPTS ; count++) { + logit(3, "sending GetNetInfo request ..."); + DDPWrite(&ddpr, FALSE); /* send it to GW */ + abSleep(sectotick(4), FALSE); /* wait for a reply */ + if (heardFrom) + break; /* don't ask again */ + } + + error = (heardFrom) ? 0 : -1; + + if (!heardFrom) + heardFrom = 1; /* ignore it later */ + + return(error); +} + +/* + * add a multicast address to the interface + * + */ + +int +pi_addmulti(multi, ifr) +char *multi; +struct ifreq *ifr; +{ + int sock; + + ifr->ifr_addr.sa_family = AF_UNSPEC; + bcopy(multi, ifr->ifr_addr.sa_data, 6); + + /* + * open a socket, temporarily, to use for SIOC* ioctls + * + */ + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return(-1); + } + + if (ioctl(sock, SIOCADDMULTI, (caddr_t)ifr) < 0) { + perror("SIOCADDMULTI"); + close(sock); + return(-1); + } + + close(sock); + + return(0); +} +#endif PHASE2 diff --git a/support/capd/capd.kas.c b/support/capd/capd.kas.c new file mode 100644 index 0000000..5606252 --- /dev/null +++ b/support/capd/capd.kas.c @@ -0,0 +1,29 @@ +/* + * $Author: djh $ $Date: 1992/03/07 11:03:35 $ + * $Header: /mac/src/cap60/support/capd/RCS/capd.kas.c,v 2.1 1992/03/07 11:03:35 djh Rel djh $ + * $Revision: 2.1 $ + */ + +#include + +/* + * CAPD dummy run routine for Kernel AppleTalk + * + */ + +void +run() +{ + exit(0); +} + +/* + * identify LAP method being used + * + */ + +short +capdIdent() +{ + return(LAP_KERNEL); +} diff --git a/support/enet/README b/support/enet/README new file mode 100644 index 0000000..dbdb38b --- /dev/null +++ b/support/enet/README @@ -0,0 +1,167 @@ +This directory contains all the files needed to install and use the +Ethernet packet filter under SunOS 4.0.3c. This code has been tested +on sun3, sun4, and sun4c kernel architectures, with the 4.0.3c kernel. +It "should" work with 4.0.3 and all releases later than 4.0.3, but it +has only been tested with 4.0.3c. It will not work in its current +form with 4.0.1 and earlier releases. + +You do not need kernel source to install it. The necessary "hooks" +into the network code already exist in the standard Sun code. The +primary interactions are + + - The packet filter needs a way to get packets from the network + device drivers. It registers interest in specific Ethernet + packet types, using the ether_family structure documented + in /usr/include/net/if_arp.h. All packets of those types + are passed to the routine enet_filter in enet.c + + - The packet filter needs a way to output packets to the network. + It simply passes packet to the network drivers using + a raw address type. + + - Initialization is done the first time a program opens the + packet filter device. Thus no special provisions are + needed to call the code at system startup time. + +The ether_family interface changed slightly between 4.0.1 and 4.0.3c. +ef_infunc is called with one additional argument in 4.0.3c. The +packet filter needs this argument. Thus the code will not work as is +under 4.0.1. We hope that few, if any, changes will be needed under +4.1 and later releases. We believe that it will run under 4.0.3, but +it has only been tested under 4.0.3c. + +Here is how to install the packet filter. In the following, sunX +refers to your architecture type, e.g. sun3, sun3x, sun4, sun4c. /sys +refers to the location where you normally build kernels, which may be +/sys, /usr/sys, etc. + + - create a directory /sys/enet, and put the files enet.c and *.h from + this distribution into that directory. (You may put this + entire distribution there if you prefer.) + + - edit /sys/sunX/conf/files, inserting the line + + enet/enet.c optional enetfilter + + If you want to put the packet filter into all of your architectures, + you can instead add this line to /sys/conf.common/files.cmn. The + line can be anywhere in the file. I have added a tab at the + beginning of the line for readability. When you put the line + into the file, it should start at the left margin. + + - edit /sys/sun/conf.c. Add the following lines in the first half + of the file, where devices are being defined. The exact location + is not critical, however you should make sure you add them + between existing devices, not in the middle of an entry. E.g. + a safe place would be right before the line + + #include "cgthree.h" + + Here are the lines to insert: + + #include "enetfilter.h" + #if NENETFILTER > 0 + int enetopen(),enetclose(),enetread(),enetwrite(),enetioctl(),enetselect(); + #else + #define enetopen nodev + #define enetclose nodev + #define enetread nodev + #define enetwrite nodev + #define enetioctl nodev + #define enetselect nodev + #endif + + (Leading tabs are added to make this document readable. When you + put the lines into conf.c, they should begin at the left margin.) + Now look for entry number 4 in cdevsw. In your editor, find + for the following. (You can simply search for the first occurence + of "cdevsw".) + + struct cdevsw cdevsw[] = + + Now look down a few lines, for lines labeled /*4*/ and /* was ip */ + Replace those lines, so that they look as follows. (You are replacing + lines one for one.) + + { + enetopen, enetclose, enetread, enetwrite, /*4*/ + enetioctl, nulldev, enetselect, 0, /* was ip */ + 0, + }, + + - cd to /sys/sunX/conf + + - edit the config file you normally use, e.g. GENERIC. Add the + following line: + + pseudo-device enetfilter + + (As usual, I have inserted a tab for readability. The line should + start at the left margin.) The exact location in the file is not + critical, but one reasonable place would be right after the + following lines: + + pseudo-device snit # streams NIT + pseudo-device pf # packet filter + pseudo-device nbuf # NIT buffering module + + - config and build a new kernel, as you normally do. If you use + a generic kernel (which however is not recommended under 4.0.x, + since tailored kernels use less space), you would do + + config GENERIC + cd ../GENERIC + make + + - install the new kernel, which will be called vmunix, in root: + + mv /vmunix /vmunix.OLD + cp vmunix /vmunix + + - install the header files enet.h and enetdefs.h in /usr/include/net + + - install the man page enet.4 in /usr/man/man4 + + - make the devices needed by the packet filter. You need one + device for each Ethernet or Ethernet-like interface you have. + (It has not been tested with things like FDDI, but might work.) + Each device will be associated with one network interface. + The association is established when the first program opens + one of the devices. In general, /dev/enet0 will be associated + with the first network interface, /dev/enet1 with the second, etc. + They should be in the order that the interfaces are listed by + "netstat -i". However only broadcast interfaces will be included. + Serial lines and the loopback device (lo0) will be skipped. + Suppose you have two Ethernets. Here's how to create the two + devices: + + mknod /dev/enet0 c 4 0 + mknod /dev/enet1 c 4 1 + + - reboot + +Now your kernel has the necessary support for the Ethernet packet +filter. Of course this won't do you any good unless you have software +that uses it. We use it with a special version of the CAP software, +to support Appletalk over Ethertalk. Software to support PUP is +probably also available. + +This distribution also contains source for a program "etherstat". It +can be used to see what connections are currently using the packet +filter. See the man page, etherstat.8. To build and install +etherstat: + + - type "make etherstat" + + - install etherstat whereever you prefer to keep your network + debugging tools. /usr/etc would be a reasonable place, or + /usr/ucb, which is where netstat is. If you want normal users to + be able to use it, make sure it is set up the same way as + /usr/ucb/netstat. Depending upon your system, this may be + setuid to root or setgid to some special group. Try + + ls -lg /usr/ucb/netstat + + to see. The program needs to be able to read /dev/kmem. + + - install etherstat.8 into /usr/man/man8 diff --git a/support/enet/enet.4 b/support/enet/enet.4 new file mode 100644 index 0000000..bd7c3bd --- /dev/null +++ b/support/enet/enet.4 @@ -0,0 +1,752 @@ +.TH ENET 4 "17 January 1986" Stanford +.SH "NAME" +enet \- ethernet packet filter +.SH SYNOPSIS +.B "pseudo-device enetfilter 64" +.SH "DESCRIPTION" +The packet filter +provides a raw interface to Ethernets and similar network data link layers. +Packets received that are not used by the kernel +(i.e., to support IP, ARP, and on some systems XNS, protocols) +are available through this mechanism. +The packet filter appears as a set of character special files, one +per hardware interface. +Each +.I enet +file may be opened multiple times, allowing each interface to be used by +many processes. +The total number of open ethernet +files is limited to the value given in the kernel configuration; the +example given in the SYNOPSIS above sets the limit to 64. +.PP +The minor device numbers +are associated with interfaces when the system is booted. +Minor device 0 +is associated with the first Ethernet interface ``attached'', +minor device 1 with the second, and so forth. +(These character special files are, for historical reasons, +given the names +.IR /dev/enet0 , +.IR /dev/eneta0 , +.IR /dev/enetb0 , +etc.) +.PP +Associated with each open instance of an +.I enet +file is a user-settable packet filter which is used to deliver +incoming ethernet packets to the appropriate process. +Whenever a packet is received from the net, +successive packet filters from the list of filters for +all open +.I enet +files are applied to the packet. +When a filter accepts the packet, +it is placed on the packet input queue of the +associated file. +If no filters accept the packet, it is discarded. +The format of a packet filter is described below. +.PP +Reads from these files return the next packet +from a queue of packets that have matched the filter. +If insufficient buffer space to store the entire packet is specified in the +read, the packet will be truncated and the trailing contents lost. +Writes to these devices transmit packets on the +network, with each write generating exactly one packet. +.PP +The packet filter currently supports a variety of different ``Ethernet'' +data-link levels: +.IP "3mb Ethernet" 1.5i +packets consist of 4 or more bytes with the first byte +specifying the source ethernet address, the second +byte specifying the destination ethernet address, +and the next two bytes specifying the packet type. +(Actually, on the network the source and destination addresses +are in the opposite order.) +.IP "byte-swapping 3mb Ethernet" 1.5i +packets consist of 4 or more bytes with the first byte +specifying the source ethernet address, the second +byte specifying the destination ethernet address, +and the next two bytes specifying the packet type. +\fBEach short word (pair of bytes) is swapped from the network +byte order\fR; this device type is only provided as a concession +to backwards-compatibility. +.IP "10mb Ethernet" 1.5i +packets consist of 14 or more bytes with the first six +bytes specifying the destination ethernet address, +the next six bytes the source ethernet address, +and the next two bytes specifying the packet type. +.PP +The remaining words are interpreted according to the packet type. +Note that 16-bit and 32-bit quantities may have to be byteswapped +(and possible short-swapped) to be intelligible on a Vax. +.PP +The packet filter mechanism does not know anything about the data portion +of the packets it sends and receives. The user must supply +the headers for transmitted packets (although the system makes sure that +the source address is correct) and the headers of received packets +are delivered to the user. The packet filters treat the entire packet, +including headers, as uninterpreted data. +.SH "IOCTL CALLS" +In addition to FIONREAD, +ten special +.I +ioctl +calls may be applied to an open +.I +enet +file. +The first two set and fetch parameters +for the file and are of the form: +.sp +.nf +.RS +.B #include +.B #include +.B ioctl(fildes, code, param) +.B +struct eniocb *param; +.RE +.fi +.sp +where +.I +param +is defined in +.I + +as: +.br +.sp +.nf +.RS +.ta \w'struct 'u +\w'u_char 'u +.ft B +struct eniocb +{ + u_char en_addr; + u_char en_maxfilters; + u_char en_maxwaiting; + u_char en_maxpriority; + long en_rtout; +}; +.DT +.RE +.fi +.ft R +.sp +with the applicable codes being: +.TP +EIOCGETP +Fetch the parameters for this file. +.TP +EIOCSETP +Set the parameters for this file. +.i0 +.DT +.PP +The maximum filter length parameter en_maxfilters indicates +the maximum possible packet filter command +list length (see EIOCSETF below). +The maximum input wait queue size parameter en_maxwaitingindicates +the maximum number of packets which may be queued for an +ethernet file at one time (see EIOCSETW below). +The maximum priority parameter en_maxpriority indicates the highest +filter priority which may be set for the file (see EIOCSETF below). +The en_addr field is no longer maintained by the driver; see +EIOCDEVP below. +.PP +The read timeout parameter en_rtout specifies the number of clock ticks to +wait before timing out on a read request and returning an EOF. +This parameter is initialized to zero by +.IR open (2), +indicating no timeout. If it is negative, then read requests will return an +EOF immediately if there are no packets in the input queue. +(Note that all parameters except for the read timeout are read-only +and are ignored when changed.) +.PP +A different +.I ioctl +is used to get device parameters of the ethernet underlying the +minor device. It is of the form: +.sp +.nf +.RS +.B #include +.br +.B #include +.B ioctl(fildes, EIOCDEVP, param) +.RE +.fi +.sp +where +.I param +is defined in +.I +as: +.br +.sp +.nf +.RS +.ta \w'struct 'u +\w'u_short 'u +.ft B +struct endevp { + u_char end_dev_type; + u_char end_addr_len; + u_short end_hdr_len; + u_short end_MTU; + u_char end_addr[EN_MAX_ADDR_LEN]; + u_char end_broadaddr[EN_MAX_ADDR_LEN]; +}; +.DT +.RE +.fi +.ft R +.sp +The fields are: +.IP end_dev_type 1.5i +Specifies the device type; currently one of ENDT_3MB, ENDT_BS3MB or ENDT_10MB. +.IP end_addr_len 1.5i +Specifies the address length in bytes (e.g., 1 or 6). +.IP end_hdr_len 1.5i +Specifies the total header length in bytes (e.g., 4 or 14). +.IP end_MTU 1.5i +Specifies the maximum packet size, including header, in bytes. +.IP end_addr 1.5i +The address of this interface; aligned so that the low order +byte of the address is the first byte in the array. +.IP end_broadaddr 1.5i +The hardware destination address for broadcasts on this network. +.PP +The next two calls enable and disable the input +packet signal mechanism +for the file and are of the form: +.sp +.nf +.RS +.B #include +.B #include +.B ioctl(fildes, code, signp) +.B +u_int *signp; +.RE +.fi +.sp +where +.I +signp +is a pointer to a word containing the number +of the signal +to be sent when an input packet arrives and +with the applicable codes being: +.TP +EIOCENBS +Enable the specified signal when an input packet +is received for this file. +If the ENHOLDSIG flag (see EIOCMBIS below) is not set, +further signals are automatically disabled +whenever a signal is sent to prevent nesting and hence +must be specifically re-enabled after processing. +When a signal number of 0 is supplied, +this call is equivalent to EIOCINHS. +.TP +EIOCINHS +Disable any signal when an input +packet is received for this file +(the +.I signp +parameter is ignored). +This is the default when the file is first opened. +.i0 +.DT +.PP +.sp +The next two calls set and clear ``mode bits'' for the +for the file and are of the form: +.sp +.nf +.RS +.B #include +.B #include +.B ioctl(fildes, code, bits) +.B +u_short *bits; +.RE +.fi +.sp +where +.I bits +is a short work bit-mask specifying which bits to set or clear. +Currently, the only bit mask recognized is ENHOLDSIG, which (if +.IR clear ) +means that the driver should +disable the effect of EIOCENBS once it has delivered a signal. +Setting this bit +means that you need use EIOCENBS only once. (For historical reasons, the +default is that ENHOLDSIG is set.) +The applicable codes are: +.TP +EIOCMBIS +Sets the specified mode bits +.TP +EIOCMBIC +Clears the specified mode bits +.PP +Another +.I +ioctl +call is used to set the maximum size +of the packet input queue for +an open +.I +enet +file. +It is of the form: +.sp +.nf +.RS +.B #include +.B #include +.B ioctl(fildes, EIOCSETW, maxwaitingp) +.B u_int *maxwaitingp; +.RE +.fi +.sp +where +.I +maxwaitingp +is a pointer +to a word containing +the input queue size to be set. +If this is greater than maximum allowable +size (see EIOCGETP above), it is set to the maximum, +and if it is zero, it is set to +a default value. +.sp +Another +.I +ioctl +call flushes the queue of incoming packets. +It is of the +form: +.sp +.nf +.RS +.B #include +.B #include +.B ioctl(fildes, EIOCFLUSH, 0) +.RE +.fi +.sp +The final generic +.I +ioctl +call is used to set the packet filter +for an open +.I +enet +file. +It is of the form: +.sp +.nf +.RS +.B #include +.B #include +.B ioctl(fildes, EIOCSETF, filter) +.B struct enfilter *filter +.RE +.fi +.sp +where enfilter is defined in +.I + +as: +.sp +.nf +.RS +.ft B +.ta \w'struct 'u \w'struct u_short 'u +struct enfilter +{ + u_char enf_Priority; + u_char enf_FilterLen; + u_short enf_Filter[ENMAXFILTERS]; +}; +.DT +.ft R +.RE +.fi +.PP +A packet filter consists of a priority, +the filter command list length (in shortwords), +and the filter command list itself. +Each filter command list specifies +a sequence of actions which +operate on an internal stack. +Each shortword of the +command list specifies an action from the set { +.B +ENF_PUSHLIT, +.B +ENF_PUSHZERO, +.B +ENF_PUSHWORD+N +} which respectively push the next shortword of the +command list, zero, +or shortword +.B +N +of the incoming packet on the stack, and a binary operator +from the set { +.B +ENF_EQ, +.B +ENF_NEQ, +.B +ENF_LT, +.B +ENF_LE, +.B +ENF_GT, +.B +ENF_GE, +.B +ENF_AND, +.B +ENF_OR, +.B +ENF_XOR +} +which then operates on the +top two elements of the stack and replaces them with its result. +When both an action and operator are specified in the +same shortword, the action is performed followed by the +operation. +.PP +The binary operator can also be from the set { +.B +ENF_COR, +.B +ENF_CAND, +.B +ENF_CNOR, +.B +ENF_CNAND +}. These are ``short-circuit'' operators, in that they terminate +the execution of the filter immediately if the condition they are checking +for is found, and continue otherwise. +All pop two elements from the stack and compare them for equality; +.B +ENF_CAND +returns false if the result is false; +.B +ENF_COR +returns true if the result is true; +.B +ENF_CNAND +returns true if the result is false; +.B +ENF_CNOR +returns false if the result is true. +Unlike the other binary operators, these four do not leave a result +on the stack, even if they continue. +.PP +The short-circuit operators should be used when possible, to reduce the +amount of time spent evaluating filters. When they are used, you should +also arrange the order of the tests so that the filter will succeed or fail +as soon as possible; for example, checking the Socket field of a Pup packet +is more likely to indicate failure than the packet type field. +.PP +The +special action +.B +ENF_NOPUSH +and the special operator +.B +ENF_NOP +can be used to only +perform the binary operation or to only push a value on the stack. +Since both are (conveniently) defined to be zero, indicating +only an action actually specifies the action followed by +.BR ENF_NOP , +and +indicating only an operation actually specifies +.B +ENF_NOPUSH +followed +by the operation. +.PP +After executing the filter command list, a non-zero value (true) +left on top of the stack +(or an empty stack) causes the incoming +packet to be accepted for the corresponding +.I +enet +file and a zero value (false) causes the packet to +be passed through the next packet filter. +(If the filter exits as the result of a short-circuit operator, +the top-of-stack value is ignored.) +Specifying an undefined operation or action in the command list +or performing an illegal operation or action (such as pushing +a shortword offset +past the end of the packet or executing a binary operator +with fewer than two shortwords on the stack) causes a filter to +reject the packet. +.sp +In an attempt to deal with the problem of +overlapping and/or conflicting packet filters, +the filters for each open +.I +enet +file are ordered by the driver +according to their priority +(lowest +priority is 0, highest is 255). +When processing incoming +ethernet +packets, filters are applied according to their +priority (from highest to lowest) and +for identical priority values according to their +relative ``busyness'' (the filter that has previously +matched the most packets is checked first) until one or more filters +accept the packet or all filters reject it and +it is discarded. +.PP +Filters at a priority of 2 or higher are called "high priority" +filters. +Once a packet is delivered to one of these "high priority" +.I +enet +files, +no further filters are examined, +i.e. +the packet is delivered only +to the first +.I +enet +file with a +"high priority" filter +which accepts the packet. +A packet may be delivered to more than one filter with a priority +below 2; this might be useful, for example, in building replicated programs. +However, the use of low-priority filters imposes an additional cost on +the system, as these filters each must be checked against all packets not +accepted by a high-priority filter. +.PP +The packet filter for an +.I +enet +file is initialized +with length 0 at priority 0 by +.IR open (2), +and hence by default accepts all +packets which no "high priority" filter +is interested in. +.PP +Priorities should be assigned so that, in general, the more packets a +filter is expected to match, the higher its priority. This will prevent +a lot of needless checking of packets against filters that aren't likely +to match them. +.i0 +.DT +.PP +.sp +There is a special +.I +ioctl +for use on the Sun. Because of the way the packet filter is interfaced +to SunOS, it is necessary to specify what Ethernet type codes are to +be handled by the packet filter. This is done using the +.TP +EIOCETHERT +call. +.sp +.nf +.RS +.B #include +.B #include +.B ioctl(fildes, EIOCETHERT, ethert) +.B int *ethert +.RE +.fi +.sp + +This will permanently (i.e. until the next reboot) place packets with +the specified type code under control of the packet filter mechanism. +If more than one type code is under control of the packet filter, you +must use filters to make sure that the right packet types are handed +to the right file descriptors. This ioctl affects the entire system. +That is, access to packets of the specified packet type is not limited +to the specific file descriptor (or even process) for which it is done + +.sp + +If the specified type code is already under control of the packet filter, +the error EEXIST will be returned. Rather than trying to figure out +whether some program has already run that specifies a given packet type, +it is probably best just to ignore the EEXIST error. + +.SH "FILTER EXAMPLES" +The following filter would accept all incoming +.I Pup +packets on a 3mb ethernet with Pup types in the range 1-0100: +.sp +.nf +.ft B +.ta \w'stru'u \w'struct ENF_PUSHWORD+8, ENF_PUSHLIT, 2, 'u +struct enfilter f = +{ + 10, 19, /* priority and length */ + ENF_PUSHWORD+1, ENF_PUSHLIT, 2, + ENF_EQ, /* packet type == PUP */ + ENF_PUSHWORD+3, ENF_PUSHLIT, + 0xFF00, ENF_AND, /* mask high byte */ + ENF_PUSHZERO, ENF_GT, /* PupType > 0 */ + ENF_PUSHWORD+3, ENF_PUSHLIT, + 0xFF00, ENF_AND, /* mask high byte */ + ENF_PUSHLIT, 0100, ENF_LE, /* PupType <= 0100 */ + ENF_AND, /* 0 < PupType <= 0100 */ + ENF_AND /* && packet type == PUP */ +}; +.DT +.ft R +.fi +.sp +Note that shortwords, such as the packet type field, are byte-swapped +and so the literals you compare them to must be byte-swapped. Also, +although for this example the word offsets are constants, code that +must run with either 3mb or 10mb ethernets must use +offsets that depend on the device type. +.PP +By taking advantage of the ability to +specify both an action and operation in each word of +the command list, the filter could be abbreviated to: +.sp +.nf +.ta \w'stru'u \w'struct ENF_PUSHWORD+3, ENF_PUSHLIT | ENF_AND, 'u +.ft B +struct enfilter f = +{ + 10, 14, /* priority and length */ + ENF_PUSHWORD+1, ENF_PUSHLIT | ENF_EQ, 2, /* packet type == PUP */ + ENF_PUSHWORD+3, ENF_PUSHLIT | ENF_AND, + 0xFF00, /* mask high byte */ + ENF_PUSHZERO | ENF_GT, /* PupType > 0 */ + ENF_PUSHWORD+3, ENF_PUSHLIT | ENF_AND, + 0xFF00, /* mask high byte */ + ENF_PUSHLIT | ENF_LE, 0100, /* PupType <= 0100 */ + ENF_AND, /* 0 < PupType <= 0100 */ + ENF_AND /* && packet type == PUP */ +}; +.ft R +.DT +.fi +.sp +A different example shows the use of "short-circuit" operators to +create a more efficient filter. This one accepts Pup packets (on a 3Mbit +ethernet) with a Socket field of 12345. Note that we check the Socket field +before the packet type field, since in most +packets the Socket is not likely to match. +.sp +.nf +.ta \w'stru'u \w'struct ENF_PUSHWORD+3, ENF_PUSHLIT | ENF_CAND, 'u +.ft B +struct enfilter f = +{ + 10, 9, /* priority and length */ + ENF_PUSHWORD+7, ENF_PUSHLIT | ENF_CAND, + 0, /* High word of socket */ + ENF_PUSHWORD+8, ENF_PUSHLIT | ENF_CAND, + 12345, /* Low word of socket */ + ENF_PUSHWORD+1, ENF_PUSHLIT | ENF_CAND, + 2 /* packet type == Pup */ +}; +.ft R +.DT +.fi +.SH "PROGRAMMING NOTES" + +In order to get a working program, you don't need most of the ioctl's. +Generally it's good enough simply to open /dev/enet, then to do +IOCSETF to establish a packet filter that specifies what packets you +want to see, and if you are on a Sun, IOCETHERT to make sure that the +packet filter sees the packet type involved. It does not matter which +order you do IOCSETF and IOCETHERT. IOCETHERT only needs to be done +once for each packet type. If you open several descriptors to process +packets of the same packet type, IOCETHERT only needs to be done once. +However it's probably more convenient to do it for each file +descriptor. You'll get EEXIST most of the time, but that is harmless. + +There is a good chance that you'll want to do IOCSETW, to set the +number of packets that can be queued for input at one time. The +default is 2. This may be a bit low for some applications. + +In order to get good performance, you will probably want to make sure +that all of your applications use the same priority in their filters. +The driver will automatically reorder the list so that often-used +filters migrate to the front of the list, but it will only sort +filters with the same priority. Assuming that you are using a "high +priority" (which is currently defined as any priority above 1), the +code runs all filters in turn until it finds one that succeeds. If +you have a number of filters it is very important to make sure they +are tried in the right order. You can use etherstat(8) to look at all +the filters and make sure that the order is sensible. + +.SH "SEE ALSO" +de(4), ec(4), en(4), il(4), enstat(8) +.SH "FILES" +/dev/enet{,a,b,c,...}0 +.SH BUGS +The current implementation can only filter on words within +the first "mbuf" of the packet; this is around 100 bytes (or +50 words). +.PP +Because packets are streams of bytes, yet the filters operate +on short words, and standard network byte order is usually opposite +from Vax byte order, the relational operators +.B ENF_LT, ENF_LE, +.B ENF_GT, +and +.B ENF_GE +are not all that useful. Fortunately, they were not often used +when the packets were treated as streams of shorts, so this is +probably not a severe problem. If this becomes a severe problem, +a byte-swapping operator could be added. +.PP +Many of the "features" of this driver are there for historical +reasons; the manual page could be a lot cleaner if these were +left out. +.PP +It is not at all clear that the algorithm for reordering the +filters make sense. It uses the count of the number of times +the filter has been used, and only moves a filter up if its +count is 100 more than the one above it. This is claimed to +prevent thrashing. Since moving a filter is fairly low in +cost compared to running a filter, it would probably +make more sense to move a filter up whenever it is used. Then +the list would rearrange itself with the filters currently being +used near the top. +.SH "HISTORY" +.TP +8-Oct-85 Jeffrey Mogul at Stanford University +Revised to describe 4.3BSD version of driver. +.TP +18-Oct-84 Jeffrey Mogul at Stanford University +Added short-circuit operators, changed discussion of priorities to +reflect new arrangement. +.TP +18-Jan-84 Jeffrey Mogul at Stanford University +Updated for 4.2BSD (device-independent) version, including +documentation of all non-kernel ioctls. +.TP +17-Nov-81 Mike Accetta (mja) at Carnegie-Mellon University +Added mention of to include examples. +.TP +29-Sep-81 Mike Accetta (mja) at Carnegie-Mellon University +Changed to describe new EIOCSETW and EIOCFLUSH ioctl +calls and the new multiple packet queuing features. +.TP +12-Nov-80 Mike Accetta (mja) at Carnegie-Mellon University +Added description of signal mechanism for input packets. +.TP +07-Mar-80 Mike Accetta (mja) at Carnegie-Mellon University +Created. diff --git a/support/enet/enet.c b/support/enet/enet.c new file mode 100644 index 0000000..fbaef34 --- /dev/null +++ b/support/enet/enet.c @@ -0,0 +1,1824 @@ +/* enet.c Stanford 25 April 1983 */ + +/* + * Ethernet packet filter layer, + * formerly: Ethernet interface driver + * + ********************************************************************** + * HISTORY + * 7 October 1985 Jeff Mogul Stanford + * Removed ENMAXOPENS limitation; available minors are now + * dynamically allocated to interfaces, out of pool of NENETFILTER + * minors. + * Certain arrays formerly in the enState structure are now global. + * Depends on modified openi() function so that enetopen() need + * only be called once. + * Remove support for "kernel access", it won't ever be used again. + * Added EIOCMFREE ioctl. + * + * 17 October 1984 Jeff Mogul Stanford + * More performance improvements: + * Added ENF_CAND, ENF_COR, ENF_CNAND, and ENF_CNOR, short-circuit + * operators, to make filters run faster. + * All evaluate "(*sp++ == *sp++)": + * ENF_CAND: returns false immediately if result is false, otherwise + * continue + * ENF_COR: returns true immediately if result is true, otherwise + * continue + * ENF_CNAND: returns true immediately if result is false, otherwise + * continue + * ENF_CNOR: returns false immediately if result is true, otherwise + * continue + * Also added ENF_NEQ to complement ENF_EQ + * - Maintain count of received packets per filter, dynamically + * re-organize filter queue to keep highly active filters in + * front of queue (but maintaining priority order), if they are + * "high priority" filters. + * + * 2 October 1984 Jeff Mogul Stanford + * Made a few changes to enDoFilter() to speed it up, since profiling + * shows it to be rather popular: + * - precompute maximum word in packet and address of end of + * filters (thereby moving this code out of "inner loop"). + * - minor re-arrangement to avoid re-evaluating a + * common subexpression. + * - changed #ifdef DEBUG in a few routines to #ifdef INNERDEBUG, + * so that code in inner loops isn't always testing the enetDebug + * flag; this not only costs directly, but also breaks up some + * basic blocks that the optimizer could play with. + * - added enOneCopy flag; if true, then never deliver more than + * one copy of a packet. This is equivalent to giving everyone + * a "high priority" device, and cuts down the number of superfluous + * calls to enDoFilter(). [Temporary hack, will remove this!] + * + * 24 August 1984 Jeff Mogul Stanford + * YA bug with sleeping in enetwrite(); straightened out handling + * of counts in enKludgeSleep so that they indicate the number + * of sleeps in progress. Maybe I've got this right, now? + * Also, don't sleep forever (since the net might be down). + * + * 17 July 1984 Jeff Mogul Stanford + * Bug fix: in enetwrite(), several problems with sleeping on + * IF_QFULL: + * - don't do it for kernel mode writes. + * - count # of procs sleeping, to avoid lost wakeups. Old + * scheme would only wake up the first sleeper. + * - using sleeper-count, avoid using more than one timeout + * table entry per device; old scheme caused timeout table panics + * - trap interupted sleeps using setjmp, so that we can deallocate + * packet header and mbufs; otherwise we lost them and panicked. + * + * 5 July 1984 Jeff Mogul Stanford + * Bug fix: in enetwrite() make sure enP_RefCount is zero before + * deallocating "packet". Otherwise, "packets" get lost, and + * take mbufs (and ultimately, the system) with them. + * + * 8 December 1983 Jeffrey Mogul Stanford + * Fixed bug in enetwrite() that eventually caused allocator + * to run out of packets and panic. If enetwrite() returns + * an error it should first deallocate any packets it has allocated. + * + * 10 November 1983 Jeffrey Mogul Stanford + * Slight restructuring for support of 10mb ethers; + * - added the EIOCDEVP ioctl + * - removed the EIOCMTU ioctl (subsumed by EIOCDEVP) + * This requires an additional parameter to the enetattach + * call so that the device driver can specify things. + * + * Also, cleaned up the enetDebug scheme by adding symbolic + * definitions for the bits. + * + * 25-Apr-83 Jeffrey Mogul Stanford + * Began conversion to 4.2BSD. This involves removing all + * references to the actual hardware. + * Changed read/write interface to use uio scheme. + * Changed ioctl interface to "new style"; this places a hard + * limit on the size of a filter (about 128 bytes). + * "Packets" now point to mbufs, not private buffers. + * Filter can only access data in first mbuf (about 50 words worst case); + * this is long enough for all Pup purposes. + * Added EIOCMTU ioctl to get MTU (max packet size). + * Added an enetselect() routine and other select() support. + * Other stuff is (more or less) left intact. + * Most previous history comments removed. + * Changed some names from enXXXX to enetXXXX to avoid confusion(?) + * + * 10-Aug-82 Mike Accetta (mja) at Carnegie-Mellon University + * Added new EIOCMBIS and EIOCMBIC ioctl calls to set and clear + * bits in mode word; added mode bit ENHOLDSIG which suppresses + * the resetting of an enabled signal after it is sent (to be + * used inconjunction with the SIGHOLD mechanism); changed + * EIOCGETP to zero pad word for future compatibility; changed enwrite() + * to enforce correct source host address on output packets (V3.05e). + * (Stanford already uses long timeout value and has no pad word - JCM) + * [Last change before 4.2BSD conversion starts.] + * + * 01-Dec-81 Mike Accetta (mja) at Carnegie-Mellon University + * Fixed bug in timeout handling caused by missing "break" in the + * "switch" state check within enetread(). This caused all reads + * to be preceeded by a bogus timeout. In addition, fixed another + * bug in signal processing by also recording process ID of + * process to signal when an input packet is available. This is + * necessary because it is possible for a process with an enabled + * signal to fork and exit with no guarantee that the child will + * reenable the signal. Thus under appropriately bizarre race + * conditions, an incoming packet to the child can cause a signal + * to be sent to the unsuspecting process which inherited the + * process slot of the parent. Of course, if the PID's wrap around + * AND the inheriting process has the same PID, well ... (V3.03d). + * + * 22-Feb-80 Rick Rashid (rfr) at Carnegie-Mellon University + * Rewritten to provide multiple user access via user settable + * filters (V1.05). + * + * 18-Jan-80 Mike Accetta (mja) at Carnegie-Mellon University + * Created (V1.00). + * + ********************************************************************** + */ + +#include "enetfilter.h" + +/* number of potential units. Max number of Ethernet devices. Should + * read ie.h, le.h, etc. and add + */ +#define NENET 4 + +#if (NENETFILTER > 0) + +#define SUN_OPENI + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#undef queue +#undef dequeue +#include "enet.h" +#include "enetdefs.h" + +#if (NENETFILTER < 32) +#undef NENETFILTER +#define NENETFILTER 32 +#endif + +#if (NENETFILTER > 256) +#undef NENETFILTER +#define NENETFILTER 256 /* maximum number of minor devices */ +#endif + +#define DEBUG 1 +/* #define INNERDEBUG 1 */ /* define only when debugging enDoFilter() + or enInputDone() */ + +#define enprintf(flags) if (enetDebug&(flags)) printf + +/* + * Symbolic definitions for enetDebug flag bits + * ENDBG_TRACE should be 1 because it is the most common + * use in the code, and the compiler generates faster code + * for testing the low bit in a word. + */ + +#define ENDBG_TRACE 1 /* trace most operations */ +#define ENDBG_DESQ 2 /* trace descriptor queues */ +#define ENDBG_INIT 4 /* initialization info */ +#define ENDBG_SCAV 8 /* scavenger operation */ +#define ENDBG_ABNORM 16 /* abnormal events */ + + +#define min(a,b) ( ((a)<=(b)) ? (a) : (b) ) + +#define splenet splimp /* used to be spl6 but I'm paranoid */ + +#define PRINET 26 /* interruptible */ + +/* + * 'enQueueElts' is the pool of packet headers used by the driver. + * 'enPackets' is the pool of packets used by the driver (these should + * be allocated dynamically when this becomes possible). + * 'enFreeq' is the queue of available packets + * 'enState' is the driver state table per logical unit number + * 'enUnit' is the physical unit number table per logical unit number; + * the first "attach"ed ethernet is logical unit 0, etc. + * 'enUnitMap' maps minor device numbers onto interface unit #s + * 'enAllocMap' indicates if minor device is allocated or free + * 'enAllDescriptors' stores OpenDescriptors, indexed by minor device # + * 'enFreeqMin' is the minimum number of packets ever in the free queue + * (for statistics purposes) + * 'enScavenges' is the number of scavenges of the active input queues + * (for statustics purposes) + * 'enetDebug' is a collection of debugging bits which enable trace and/or + * diagnostic output as defined above (ENDBG_*) + * 'enUnits' is the number of attached units + * 'enOneCopy' if true, then no packet is delivered to more than one minor + * device + */ +struct enPacket enQueueElts[ENPACKETS]; +struct enQueue enFreeq; +struct enState enState[NENET]; +char enUnitMap[NENETFILTER]; +char enAllocMap[NENETFILTER]; +struct enOpenDescriptor + enAllDescriptors[NENETFILTER]; +int enFreeqMin = ENPACKETS; +int enScavenges = 0; +int enetDebug = ENDBG_ABNORM; +int enUnits = 0; +int enOneCopy = 0; +int enMaxMinors = NENETFILTER; +int enInitDone = 0; +unsigned char etherbdcastaddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +/* + * Forward declarations for subroutines which return other + * than integer types. + */ +extern boolean enDoFilter(); + + +/* + * Linkages to if_en.c + */ + +struct enet_info { + struct ifnet *ifp; /* which ifp for output */ +} enet_info[NENET]; + +struct sockaddr enetaf = { AF_UNSPEC }; + +struct ifqueue *enetFilter(); /* input function */ + +struct ether_family enet_family = { + 0x80f3, /* must use ether type as family number */ + 0x80f3, /* ether type */ + enetFilter, + NULL, + NULL, + NULL + }; + + +/**************************************************************** + * * + * Various Macros & Routines * + * * + ****************************************************************/ + +/* + * forAllOpenDescriptors(p) -- a macro for iterating + * over all currently open devices. Use it in place of + * "for ( ...; ... ; ... )" + * and supply your own loop body. The loop variable is the + * parameter p which is set to point to the descriptor for + * each open device in turn. + */ + +#define forAllOpenDescriptors(p) \ + for ((p) = (struct enOpenDescriptor *)enDesq.enQ_F; \ + (struct Queue *)(&enDesq) != &((p)->enOD_Link); \ + (p) = (struct enOpenDescriptor *)(p)->enOD_Link.F) + +/* + * enEnqueue - add an element to a queue + */ + +#define enEnqueue(q, elt) \ +{ \ + enqueue((struct Queue *)(q), (struct Queue *)(elt)); \ + (q)->enQ_NumQueued++; \ +} + +/* + * enFlushQueue - release all packets from queue, freeing any + * whose reference counts drop to 0. Assumes caller + * is at high IPL so that queue will not be modified while + * it is being flushed. + */ + +enFlushQueue(q) +register struct enQueue *q; +{ + + register struct enPacket *qelt; + + while((qelt=(struct enPacket *)dequeue((struct Queue *)q)) != NULL) + { + if (0 == --(qelt->enP_RefCount)) + { + enEnqueue(&enFreeq, qelt); + } + } + q->enQ_NumQueued = 0; + +} + +/* + * enInitWaitQueue - initialize an empty packet wait queue + */ + +enInitWaitQueue(wq) +register struct enWaitQueue *wq; +{ + + wq->enWQ_Head = 0; + wq->enWQ_Tail = 0; + wq->enWQ_NumQueued = 0; + wq->enWQ_MaxWaiting = ENDEFWAITING; + +} + +/* + * enEnWaitQueue - add a packet to a wait queue + */ + +enEnWaitQueue(wq, p) +register struct enWaitQueue *wq; +struct enPacket *p; +{ + + wq->enWQ_Packets[wq->enWQ_Tail] = p; + wq->enWQ_NumQueued++; + enNextWaitQueueIndex(wq->enWQ_Tail); + +} + +/* + * enDeWaitQueue - remove a packet from a wait queue + */ + +struct enPacket * +enDeWaitQueue(wq) +register struct enWaitQueue *wq; +{ + + struct enPacket *p; + + wq->enWQ_NumQueued--; + if (wq->enWQ_NumQueued < 0) + panic("enDeWaitQueue"); + p = wq->enWQ_Packets[wq->enWQ_Head]; + enNextWaitQueueIndex(wq->enWQ_Head); + + return(p); + +} + +/* + * enTrimWaitQueue - cut a wait queue back to size + */ +enTrimWaitQueue(wq, threshold) +register struct enWaitQueue *wq; +{ + + register int Counter = (wq->enWQ_NumQueued - threshold); + register struct enPacket *p; + +#ifdef DEBUG + enprintf(ENDBG_SCAV) + ("enTrimWaitQueue(%x, %d): %d\n", wq, threshold, Counter); +#endif + while (Counter-- > 0) + { + wq->enWQ_NumQueued--; + enPrevWaitQueueIndex(wq->enWQ_Tail); + p = wq->enWQ_Packets[wq->enWQ_Tail]; + if (0 == --(p->enP_RefCount)) + { + m_freem(p->enP_mbuf); + enEnqueue(&enFreeq, p); + } + } +} +/* + * enFlushWaitQueue - remove all packets from wait queue + */ + +#define enFlushWaitQueue(wq) enTrimWaitQueue(wq, 0) + +/* + * scavenging thresholds: + * + * index by number of active files; for N open files, each queue may retain + * up to 1/Nth of the packets not guaranteed to be freed on scavenge. The + * total number of available packets is computed less one for sending. + * + * (assumes high IPL) + */ +char enScavLevel[NENETFILTER+1]; + +/* + * enInitScavenge -- set up ScavLevel table + */ +enInitScavenge() +{ + register int PoolSize = (ENPACKETS-ENMINSCAVENGE); + register int i = sizeof(enScavLevel); + + PoolSize--; /* leave one for transmitter */ + while (--i>0) + enScavLevel[i] = (PoolSize / i); +} + +/* + * enScavenge -- scan all OpenDescriptors for all ethernets, releasing + * any queued buffers beyond the prescribed limit and freeing any whose + * refcounts drop to 0. + * Assumes caller is at high IPL so that it is safe to modify the queues. + */ +enScavenge() +{ + + register struct enOpenDescriptor *d; + register int threshold = 0; + register struct enState *enStatep; + + for (enStatep=enState; enStatep < &enState[NENET]; enStatep++) + threshold += enCurOpens; + threshold = enScavLevel[threshold]; + + /* recalculate thresholds based on current allocations */ + enInitScavenge(); + + enScavenges++; +#ifdef DEBUG + enprintf(ENDBG_SCAV)("enScavenge: %d\n", threshold); +#endif + for (enStatep=enState; enStatep < &enState[NENET]; enStatep++) + { + if (enDesq.enQ_F == 0) + continue; /* never initialized */ + forAllOpenDescriptors(d) + { + enTrimWaitQueue(&(d->enOD_Waiting), threshold); + } + } + +} + +/* + * enAllocatePacket - allocate the next packet from the free list + * + * Assumes IPL is at high priority so that it is safe to touch the + * packet queue. If the queue is currently empty, scavenge for + * more packets. + */ + +struct enPacket * +enAllocatePacket() +{ + + register struct enPacket *p; + + if (0 == enFreeq.enQ_NumQueued) + enScavenge(); + p = (struct enPacket *)dequeue((struct Queue *)&enFreeq); + if (p == NULL) + panic("enAllocatePacket"); + if (enFreeqMin > --enFreeq.enQ_NumQueued) + enFreeqMin = enFreeq.enQ_NumQueued; + + p->enP_RefCount = 0; /* just in case */ + + return(p); + +} + +/* + * enDeallocatePacket - place the packet back on the free packet queue + * + * (High IPL assumed). + */ + +#define enDeallocatePacket(p) \ +{ \ + if (p->enP_RefCount) panic("enDeallocatePacket: refcount != 0");\ + enqueue((struct Queue *)&enFreeq, (struct Queue *)(p)); \ + enFreeq.enQ_NumQueued++; \ +} + +/**************************************************************** + * * + * Routines to move uio data to/from mbufs * + * * + ****************************************************************/ + +/* + * These two routines were inspired by/stolen from ../sys/uipc_socket.c + * Both return error code (or 0 if success). + */ + +/* + * read: return contents of mbufs to user. DO NOT free them, since + * there may be multiple claims on the packet! + */ +enrmove(m, uio, count) +register struct mbuf *m; +register struct uio *uio; +register int count; +{ + register int len; + register int error = 0; + + count = min(count, uio->uio_resid); /* # of bytes to return */ + + while ((count > 0) && m && (error == 0)) { + len = min(count, m->m_len); /* length of this transfer */ + count -= len; + error = uiomove(mtod(m, caddr_t), (int)len, UIO_READ, uio); + + m = m->m_next; + } + return(error); +} + +enwmove(uio, mbufp) +register struct uio *uio; +register struct mbuf **mbufp; /* top mbuf is returned by reference */ +{ + struct mbuf *mtop = 0; + register struct mbuf *m; + register struct mbuf **mp = &mtop; + register struct iovec *iov; + register int len; + int error = 0; + + while ((uio->uio_resid > 0) && (error == 0)) { + iov = uio->uio_iov; + + if (iov->iov_len == 0) { + uio->uio_iov++; + uio->uio_iovcnt--; + if (uio->uio_iovcnt < 0) + panic("enwmove: uio_iovcnt < 0 while uio_resid > 0"); + } + MGET(m, M_WAIT, MT_DATA); + if (m == NULL) { + error = ENOBUFS; + break; + } + if (iov->iov_len >= MCLBYTES) { /* big enough to use a page */ + register struct mbuf *p; + if (mclget(m) == 0) + goto nopages; + len = MCLBYTES; + } + else { +nopages: + len = MIN(MLEN, iov->iov_len); + } + error = uiomove(mtod(m, caddr_t), len, UIO_WRITE, uio); + m->m_len = len; + *mp = m; + mp = &(m->m_next); + } + + if (error) { /* probably uiomove fouled up */ + if (mtop) + m_freem(mtop); + } + else { + *mbufp = mtop; /* return ptr to top mbuf */ + } + return(error); +} + +/* + * enetopen - open ether net device + * + * Errors: ENXIO - illegal minor device number + * EBUSY - minor device already in use + */ + +/* ARGSUSED */ +enetopen(dev, flag, newmin) +dev_t dev; +int flag; +dev_t *newmin; +{ + register int md; + register int unit = minor(dev); + register struct enState *enStatep; +#ifndef SUN_OPENI + register int error; +#endif SUN_OPENI + + /* + * Each open enet file has a different minor device number. + * When a user tries to open any of them, we actually open + * any available minor device and associate it with the + * corresponding unit. + * + * This is not elegant, but UNIX will call + * open for each new open file using the same inode but calls + * close only when the last open file referring to the inode + * is released. This means that we cannot know inside the + * driver code when the resources associated with a particular + * open of the same inode should be deallocated. Thus, we have + * to make up a temporary inode to represent each simultaneous + * open of the ethernet. Each inode has a different minor device number. + */ + + if ( ! enInitDone) { + enetallattach(); + enInitDone = 1; + } + +#ifdef DEBUG + enprintf(ENDBG_TRACE)("enetopen(%o, %x):\n", unit, flag); +#endif + + /* check for illegal minor dev */ + if ( (unit >= enUnits) /* bad unit */ + || (enet_info[unit].ifp == 0) /* ifp not known */ + || ((enet_info[unit].ifp->if_flags & IFF_UP) == 0) ) + /* or if down */ + { + return(ENXIO); + } + + md = enFindMinor(); +#ifdef DEBUG + enprintf(ENDBG_TRACE)("enetopen: md = %d\n", md); +#endif + if (md < 0) + { + return(EBUSY); + } + + enUnitMap[md] = unit; + enAllocMap[md] = TRUE; + +#ifdef SUN_OPENI + *newmin = makedev(major(dev), md); +#else + error = mkpseudo(makedev(major(dev), md))); + if (error) { + enAllocMap[md] = FALSE; + return(error); + } +#endif SUN_OPENI + + enStatep = &enState[unit]; + enprintf(ENDBG_DESQ) + ("enetopen: Desq: %x, %x\n", enDesq.enQ_F, enDesq.enQ_B); + enInitDescriptor(&enAllDescriptors[md], flag); + enInsertDescriptor(&(enDesq), &enAllDescriptors[md]); + + return(0); +} + +/* + * enFindMinor - find a free logical device on specified unit + */ +enFindMinor() +{ + register int md; + + for (md = 0; md < enMaxMinors; md++) { + if (enAllocMap[md] == FALSE) + return(md); + } + return(-1); +} + +/* + * enInit - intialize ethernet unit (called by enetattach) + */ + +enInit(enStatep, unit) +register struct enState *enStatep; +register int unit; +{ + +#ifdef DEBUG + enprintf(ENDBG_INIT)("enInit(%x %d):\n", enStatep, unit); +#endif + + /* initialize free queue if not already done */ + if (enFreeq.enQ_F == 0) + { + register int i; + + initqueue((struct Queue *)&enFreeq); + for (i=0; ienP_RefCount = 0; + enDeallocatePacket(p); + } + /* also a good time to init enAllocMap */ + for (i = 0; i < enMaxMinors; i++) + enAllocMap[i] = FALSE; + } + initqueue((struct Queue *)&enDesq); /* init descriptor queue */ +} + +/* + * enetclose - ether net device close routine + */ + +/* ARGSUSED */ +enetclose(dev, flag) +{ + register int md = ENINDEX(dev); + register struct enState *enStatep = &enState[ENUNIT(dev)]; + register struct enOpenDescriptor *d = &enAllDescriptors[md]; + int ipl; + + enAllocMap[md] = FALSE; + +#ifdef DEBUG + enprintf(ENDBG_TRACE)("enetclose(%d, %x):\n", md, flag); +#endif + + /* + * insure that receiver doesn't try to queue something + * for the device as we are decommissioning it. + * (I don't think this is necessary, but I'm a coward.) + */ + ipl = splenet(); + dequeue((struct Queue *)d->enOD_Link.B); + enCurOpens--; + enprintf(ENDBG_DESQ) + ("enetclose: Desq: %x, %x\n", enDesq.enQ_F, enDesq.enQ_B); + enFlushWaitQueue(&(d->enOD_Waiting)); + splx(ipl); + +} + +/* + * enetread - read next packet from net + */ + +/* VARARGS */ +enetread(dev, uio) +dev_t dev; +register struct uio *uio; +{ + register struct enOpenDescriptor *d = &enAllDescriptors[ENINDEX(dev)]; + register struct enPacket *p; + int ipl; + int error; + extern enTimeout(); + +#if DEBUG + enprintf(ENDBG_TRACE)("enetread(%x):", dev); +#endif + + ipl = splenet(); + /* + * If nothing is on the queue of packets waiting for + * this open enet file, then set timer and sleep until + * either the timeout has occurred or a packet has + * arrived. + */ + + while (0 == d->enOD_Waiting.enWQ_NumQueued) + { + if (d->enOD_Timeout < 0) + { + splx(ipl); + return(0); + } + if (d->enOD_Timeout) + { + /* + * If there was a previous timeout pending for this file, + * cancel it before setting another. This is necessary since + * a cancel after the sleep might never happen if the read is + * interrupted by a signal. + */ + if (d->enOD_RecvState == ENRECVTIMING) + untimeout(enTimeout, (caddr_t)d); + timeout(enTimeout, (caddr_t)d, (int)(d->enOD_Timeout)); + d->enOD_RecvState = ENRECVTIMING; + } + else + d->enOD_RecvState = ENRECVIDLE; + + sleep((caddr_t)d, PRINET); + + switch (d->enOD_RecvState) + { + case ENRECVTIMING: + { + untimeout(enTimeout, (caddr_t)d); + d->enOD_RecvState = ENRECVIDLE; + break; + } + case ENRECVTIMEDOUT: + { + splx(ipl); + return(0); + } + } + } + + p = enDeWaitQueue(&(d->enOD_Waiting)); + splx(ipl); + + /* + * Move data from packet into user space. + */ + error = enrmove(p->enP_mbuf, uio, p->enP_ByteCount); + + ipl = splenet(); + if (0 == --(p->enP_RefCount)) /* if no more claims on this packet */ + { + m_freem(p->enP_mbuf); /* release mbuf */ + enDeallocatePacket(p); /* and packet */ + } + splx(ipl); + + return(error); +} + + + +/* + * enTimeout - process ethernet read timeout + */ + +enTimeout(d) +register struct enOpenDescriptor * d; +{ + register int ipl; + +#ifdef DEBUG + enprintf(ENDBG_TRACE)("enTimeout(%x):\n", d); +#endif + ipl = splenet(); + d->enOD_RecvState = ENRECVTIMEDOUT; + wakeup((caddr_t)d); + enetwakeup(d); + splx(ipl); + +} + +/* + * enetwrite - write next packet to net + */ + +int enKludgeSleep[NENET]; /* Are we sleeping on IF_QFULL? */ + /* really, # of procs sleeping on IF_QFULL */ + +/* VARARGS */ +enetwrite(dev, uio) +dev_t dev; +register struct uio *uio; +{ + register int unit = ENUNIT(dev); + register struct enState *enStatep = &enState[unit]; + struct mbuf *mp; + register struct ifnet *ifp = enet_info[unit].ifp; + struct arpcom *ap = (struct arpcom *)ifp; + int ipl; + int error; + int sleepcount; + int enKludgeTime(); + struct ether_header *ehi, *eho; + +#if DEBUG + enprintf(ENDBG_TRACE)("enetwrite(%x):\n", dev); +#endif + + if (uio->uio_resid == 0) + return(0); + if (uio->uio_resid > ifp->if_mtu || uio->uio_resid < 14) /* too large */ + return(EMSGSIZE); + + /* + * Copy user data into mbufs + */ + if (error = enwmove(uio, &mp)) { + return(error); + } + + ipl = splenet(); + /* + * if the queue is full, + * hang around until there's room or until process is interrupted + */ + sleepcount = 0; + while (IF_QFULL(&(ifp->if_snd))) { + extern int hz; + if (sleepcount++ > 2) { /* don't sleep too long */ + splx(ipl); + return(ETIMEDOUT); + } + /* if nobody else has a timeout pending for this unit, set one */ + if (enKludgeSleep[unit] == 0) + timeout(enKludgeTime, (caddr_t)unit, 2 * hz); + enKludgeSleep[unit]++; /* record that we are sleeping */ + if (setjmp(&u.u_qsave)) { + /* sleep (following) was interrupted, clean up */ +#if DEBUG + enprintf(ENDBG_ABNORM) + ("enetwrite(%x): enet%d sleep %d interrupted\n", dev, + unit, enKludgeSleep[unit]); +#endif DEBUG + enKludgeSleep[unit]--; /* we're no longer sleeping */ + m_freem(mp); + splx(ipl); + return(EINTR); + } + sleep((caddr_t)&(enKludgeSleep[unit]), PRINET); + enKludgeSleep[unit]--; /* we are no longer sleeping */ + } + +/* + * there is an outfunc that we could use to implement out own + * output family. It turns out that the code is easier if we + * simply use AF_UNSPEC. We have to remove the header from + * the first mbuf, since the output code will supply a header. + * Fortunately the way our uiomove routine works, we know that + * the header is all in one mbuf. AF_UNSPEC builds the header + * from the data in the sockaddr, so we just copy the header there. + * This is all sort of stupid, since if_subr will just put this + * stuff right back, but this seems the cleanest (and fastest) way. + */ + + ehi = mtod(mp, struct ether_header *); + eho = (struct ether_header *)enetaf.sa_data; + bcopy(ehi, eho, 14); + bcopy(&ap->ac_enaddr, &eho->ether_shost, 6); + mp->m_off += 14; + mp->m_len -= 14; + + /* place mbuf chain on outgoing queue & start if necessary */ + error = (*ifp->if_output)(ifp, mp, &enetaf); + /* this always frees the mbuf chain */ + enXcnt++; + + splx(ipl); + + return(error); +} + +enKludgeTime(unit) +int unit; +{ + /* XXX perhaps we should always wakeup? */ + if (enKludgeSleep[unit]) { + wakeup((caddr_t)&(enKludgeSleep[unit])); + /* XXX should we restart transmitter? */ + } +} + +/* + * enetioctl - ether net control + * + * EIOCGETP - get ethernet parameters + * EIOCSETP - set ethernet read timeout + * EIOCSETF - set ethernet read filter + * EIOCENBS - enable signal when read packet available + * EIOCINHS - inhibit signal when read packet available + * FIONREAD - check for read packet available + * EIOCSETW - set maximum read packet waiting queue length + * EIOCFLUSH - flush read packet waiting queue + * EIOCMBIS - set mode bits + * EIOCMBIC - clear mode bits + * EICODEVP - get device parameters + * EIOCMFREE - number of free minors + * EIOCETHERT - Ethernet type to watch + */ + +/* ARGSUSED */ +enetioctl(dev, cmd, addr, flag) +caddr_t addr; +dev_t flag; +{ + + register struct enState *enStatep = &enState[ENUNIT(dev)]; + register struct enOpenDescriptor * d = &enAllDescriptors[ENINDEX(dev)]; + int ipl; + +#if DEBUG + enprintf(ENDBG_TRACE) + ("enetioctl(%x, %x, %x, %x):\n", dev, cmd, addr, flag); +#endif + + switch (cmd) + { + case EIOCGETP: + { + struct eniocb t; + + t.en_maxwaiting = ENMAXWAITING; + t.en_maxpriority = ENMAXPRI; + t.en_rtout = d->enOD_Timeout; + t.en_addr = -1; + t.en_maxfilters = ENMAXFILTERS; + + bcopy((caddr_t)&t, addr, sizeof t); + } + endcase + + case EIOCSETP: + { + struct eniocb t; + + bcopy(addr, (caddr_t)&t, sizeof t); + d->enOD_Timeout = t.en_rtout; + } + endcase + + case EIOCSETF: + { + struct enfilter f; + unsigned short *fp; + + bcopy(addr, (caddr_t)&f, sizeof f); + if (f.enf_FilterLen > ENMAXFILTERS) + { + return(EINVAL); + } + /* insure that filter is installed indivisibly */ + ipl = splenet(); + bcopy((caddr_t)&f, (caddr_t)&(d->enOD_OpenFilter), sizeof f); + /* pre-compute length of filter */ + fp = &(d->enOD_OpenFilter.enf_Filter[0]); + d->enOD_FiltEnd = &(fp[d->enOD_OpenFilter.enf_FilterLen]); + d->enOD_RecvCount = 0; /* reset count when filter changes */ + dequeue((struct Queue *)d->enOD_Link.B); + enDesq.enQ_NumQueued--; + enInsertDescriptor(&(enDesq), d); + splx(ipl); + } + endcase + + /* + * Enable signal n on input packet + */ + case EIOCENBS: + { + int snum; + + bcopy(addr, (caddr_t)&snum, sizeof snum); + if (snum < NSIG) { + d->enOD_SigProc = u.u_procp; + d->enOD_SigPid = u.u_procp->p_pid; + d->enOD_SigNumb = snum; /* This must be set last */ + } else { + goto bad; + } + } + endcase + + /* + * Disable signal on input packet + */ + case EIOCINHS: + { + d->enOD_SigNumb = 0; + } + endcase + + /* + * Check for packet waiting + */ + case FIONREAD: + { + int n; + register struct enWaitQueue *wq; + + ipl = splenet(); + if ((wq = &(d->enOD_Waiting))->enWQ_NumQueued) + n = wq->enWQ_Packets[wq->enWQ_Head]->enP_ByteCount; + else + n = 0; + splx(ipl); + bcopy((caddr_t)&n, addr, sizeof n); + } + endcase + + /* + * Set maximum recv queue length for a device + */ + case EIOCSETW: + { + unsigned un; + + bcopy(addr, (caddr_t)&un, sizeof un); + /* + * unsigned un MaxQueued + * ---------------- ------------ + * 0 -> DEFWAITING + * 1..MAXWAITING -> un + * MAXWAITING..-1 -> MAXWAITING + */ + d->enOD_Waiting.enWQ_MaxWaiting = (un) ? min(un, ENMAXWAITING) + : ENDEFWAITING; + } + endcase + + /* + * Flush all packets queued for a device + */ + case EIOCFLUSH: + { + ipl = splenet(); + enFlushWaitQueue(&(d->enOD_Waiting)); + splx(ipl); + } + endcase + + /* + * Set mode bits + */ + case EIOCMBIS: + { + u_short mode; + + bcopy(addr, (caddr_t)&mode, sizeof mode); + if (mode&ENPRIVMODES) + return(EINVAL); + else + d->enOD_Flag |= mode; + } + endcase + + /* + * Clear mode bits + */ + case EIOCMBIC: + { + u_short mode; + + bcopy(addr, (caddr_t)&mode, sizeof mode); + if (mode&ENPRIVMODES) + return(EINVAL); + else + d->enOD_Flag &= ~mode; + } + endcase + + /* + * Return hardware-specific device parameters. + */ + case EIOCDEVP: + { + bcopy((caddr_t)&(enDevParams), addr, sizeof(struct endevp)); + } + endcase; + + /* + * Return # of free minor devices. + */ + case EIOCMFREE: + { + register int md; + register int sum = 0; + + for (md = 0; md < enMaxMinors; md++) + if (enAllocMap[md] == FALSE) + sum++; + *(int *)addr = sum; + } + endcase; + + /* + * Register interest in an Ethernet type + */ + case EIOCETHERT: + { + int ethert; + struct ether_family *efp; + extern struct ether_family *ether_families; + + bcopy(addr, (caddr_t)ðert, sizeof ethert); + + /* error if type already registered */ + + for (efp = ether_families; efp != NULL; efp = efp->ef_next) + if (efp->ef_ethertype == ethert) + return(EEXIST); + + /* make up data structure */ + + efp = (struct ether_family *) + kmem_alloc(sizeof(struct ether_family)); + if (! efp) + return(ENOMEM); + + efp->ef_family = ethert; + efp->ef_ethertype = ethert; + efp->ef_infunc = enetFilter; + efp->ef_outfunc = NULL; + efp->ef_netisr = NULL; + + ether_register(efp); + + } + endcase; + + default: + { + bad: + return(EINVAL); + } + } + + return(0); + +} + +/**************************************************************** + * * + * Support for select() system call * + * * + * Other hooks in: * + * enInitDescriptor() * + * enInputDone() * + * enTimeout() * + ****************************************************************/ +/* + * inspired by the code in tty.c for the same purpose. + */ + +/* + * enetselect - returns true iff the specific operation + * will not block indefinitely. Otherwise, return + * false but make a note that a selwakeup() must be done. + */ +enetselect(dev, rw) +register dev_t dev; +int rw; +{ + register struct enOpenDescriptor *d; + register struct enWaitQueue *wq; + register int ipl; + register int avail; + + switch (rw) { + + case FREAD: + /* + * an imitation of the FIONREAD ioctl code + */ + d = &(enAllDescriptors[ENINDEX(dev)]); + + ipl = splenet(); + wq = &(d->enOD_Waiting); + if (wq->enWQ_NumQueued) + avail = 1; /* at least one packet queued */ + else { + avail = 0; /* sorry, nothing queued now */ + /* + * If there's already a select() waiting on this + * minor device then this is a collision. + * [This shouldn't happen because enet minors + * really should not be shared, but if a process + * forks while one of these is open, it is possible + * that both processes could select() us.] + */ + if (d->enOD_SelProc + && d->enOD_SelProc->p_wchan == (caddr_t)&selwait) + d->enOD_SelColl = 1; + else + d->enOD_SelProc = u.u_procp; + } + splx(ipl); + return(avail); + + case FWRITE: + /* + * since the queueing for output is shared not just with + * the other enet devices but also with the IP system, + * we can't predict what would happen on a subsequent + * write. However, since we presume that all writes + * complete eventually, and probably fairly fast, we + * pretend that select() is true. + */ + return(1); + + default: /* hmmm. */ + return(1); /* don't block in select() */ + } +} + +enetwakeup(d) +register struct enOpenDescriptor *d; +{ + if (d->enOD_SelProc) { + selwakeup(d->enOD_SelProc, d->enOD_SelColl); + d->enOD_SelColl = 0; + d->enOD_SelProc = 0; + } +} + +/* + * enetFilter - incoming linkage from ../vaxif/if_en.c + */ + +struct ifqueue *enetFilter(ifp, m, header) +struct ifnet *ifp; +register struct mbuf *m; +struct ether_header *header; +{ + register struct enState *enStatep; + register struct enPacket *p; + register int pullcount; /* bytes, not words */ + int s = splenet(); + int count = 0; + struct mbuf *n; + int en; + + n = m; + while (n) { + count += n->m_len; + n = n->m_next; + } + + count += 10; /* will be adding ethernet header */ + + for (en = 0; en < enUnits; en++) + if (ifp == enet_info[en].ifp) + break; + + if (en >= enUnits) { /* from unknown interface */ + m_freem(m); + enRdrops++; + goto out; + } + + enStatep = &enState[en]; + +#if DEBUG + enprintf(ENDBG_TRACE)("enetFilter(%d):\n", en); +#endif + + p = enAllocatePacket(); /* panics if not possible */ + + p->enP_ByteCount = count; + +/* + * mbuf chain starts with a pointer to an ifnet, which we don't + * really need. However we do need the ether header, which + * isn't there. First get rid of the ifnet. It is known to + * be entirely in the first mbuf. + */ + + m->m_off += sizeof (struct ifnet *); + m->m_len -= sizeof (struct ifnet *); + +/* + * If there is no room for the ether header in the first mbuf, then + * tack another one on the front; then stuff it in. + */ + + + if (m->m_len == 0) { + m->m_off = MMINOFF; + m->m_len = 14; + } + else if (m->m_off > MMAXOFF || + MMINOFF + 14 > m->m_off) { + register struct mbuf *m1; + + MGET(m1, M_DONTWAIT, MT_IFADDR); + if (m1 == 0) { + m_freem(m); + enDeallocatePacket(p); + enRdrops++; + goto out; + } + m1->m_next = m; + m1->m_len = 14; + m = m1; + } else { + m->m_off -= 14; + m->m_len += 14; + } + bcopy(header, mtod(m, struct ether_header *), 14); + + pullcount = min(MLEN, count); /* largest possible first mbuf */ + if (m->m_len < pullcount) { + /* first mbuf not as full as it could be - fix this */ + if ((m = m_pullup(m, pullcount)) == 0) { + /* evidently no resources; bloody m_pullup discarded mbuf */ + enDeallocatePacket(p); + enRdrops++; + goto out; + } + } + + p->enP_mbuf = m; + p->enP_Data = mtod(m, u_short *); + + enInputDone(enStatep, p); +out: + splx(s); + return(NULL); +} + +/* + * enInputDone - process correctly received packet + */ + +enInputDone(enStatep, p) +register struct enState *enStatep; +register struct enPacket *p; +{ + register struct enOpenDescriptor *d; + int queued = 0; + register int maxword; + register unsigned long rcount; + register struct enOpenDescriptor *prevd; + +#if INNERDEBUG + enprintf(ENDBG_TRACE)("enInputDone(%x): %x\n", enStatep, p); +#endif + /* precompute highest possible word offset */ + /* can't address beyond end of packet or end of first mbuf */ + maxword = (min(p->enP_ByteCount, p->enP_mbuf->m_len)>>1); + + forAllOpenDescriptors(d) + { + if (enDoFilter(p, d, maxword)) + { + if (d->enOD_Waiting.enWQ_NumQueued < d->enOD_Waiting.enWQ_MaxWaiting) + { + enEnWaitQueue(&(d->enOD_Waiting), p); + p->enP_RefCount++; + queued++; + wakeup((caddr_t)d); + enetwakeup(d); +#if INNERDEBUG + enprintf(ENDBG_TRACE)("enInputDone: queued\n"); +#endif + } + /* send notification when input packet received */ + if (d->enOD_SigNumb) { + if (d->enOD_SigProc->p_pid == d->enOD_SigPid) + psignal(d->enOD_SigProc, d->enOD_SigNumb); + if ((d->enOD_Flag & ENHOLDSIG) == 0) + d->enOD_SigNumb = 0; /* disable signal */ + } + rcount = ++(d->enOD_RecvCount); + + /* see if ordering of filters is wrong */ + if (d->enOD_OpenFilter.enf_Priority >= ENHIPRI) { + prevd = (struct enOpenDescriptor *)d->enOD_Link.B; + /* + * If d is not the first element on the queue, and + * the previous element is at equal priority but has + * a lower count, then promote d to be in front of prevd. + */ + if (((struct Queue *)prevd != &(enDesq.enQ_Head)) && + (d->enOD_OpenFilter.enf_Priority == + prevd->enOD_OpenFilter.enf_Priority)) { + /* threshold difference to avoid thrashing */ + if ((100 + prevd->enOD_RecvCount) < rcount) { + enReorderQueue(&(prevd->enOD_Link), &(d->enOD_Link)); + } + } + break; /* high-priority filter => no more deliveries */ + } + else if (enOneCopy) + break; + } + } + if (queued == 0) /* this buffer no longer in use */ + { + m_freem(p->enP_mbuf); /* free mbuf */ + enDeallocatePacket(p); /* and packet */ + enRdrops++; + } + else + enRcnt++; + +} + +#define opx(i) (i>>ENF_NBPA) + +boolean +enDoFilter(p, d, maxword) +struct enPacket *p; +struct enOpenDescriptor *d; +register int maxword; +{ + + register unsigned short *sp; + register unsigned short *fp; + register unsigned short *fpe; + register unsigned op; + register unsigned arg; + register unsigned oparg; + unsigned short stack[ENMAXFILTERS+1]; + struct fw {unsigned arg:ENF_NBPA, op:ENF_NBPO;}; + +#ifdef INNERDEBUG + enprintf(ENDBG_TRACE)("enDoFilter(%x,%x):\n", p, d); +#endif + sp = &stack[ENMAXFILTERS]; + fp = &d->enOD_OpenFilter.enf_Filter[0]; + fpe = d->enOD_FiltEnd; + /* ^ is really: fpe = &fp[d->enOD_OpenFilter.enf_FilterLen]; */ + *sp = TRUE; + + for (; fp < fpe; ) + { +/* + * op = ((struct fw *)fp)-bbop; + * arg = ((struct fw *)fp)->arg; + */ + oparg = *fp; + op = oparg >> ENF_NBPA; + arg = oparg & ((1<= 0 (the compiler + * knows this and emits no code). If arg were + * less than ENF_PUSHWORD before the subtract, + * it is certaintly going to be more than maxword + * afterward, so the code does work "right" + */ + if ((arg >= 0) && (arg < maxword)) +#else + if (arg < maxword) +#endif lint + *--sp = p->enP_Data[arg]; + else + { +#ifdef INNERDEBUG + enprintf(ENDBG_TRACE)("=>0(len)\n"); +#endif + return(false); + } + break; + case ENF_PUSHLIT: + *--sp = *fp++; + break; + case ENF_PUSHZERO: + *--sp = 0; + case ENF_NOPUSH: + break; + } + if (sp < &stack[2]) /* check stack overflow: small yellow zone */ + { + enprintf(ENDBG_TRACE)("=>0(--sp)\n"); + return(false); + } + if (op == ENF_NOP) + continue; + /* + * all non-NOP operators binary, must have at least two operands + * on stack to evaluate. + */ + if (sp > &stack[ENMAXFILTERS-2]) + { + enprintf(ENDBG_TRACE)("=>0(sp++)\n"); + return(false); + } + arg = *sp++; + switch (op) + { + default: +#ifdef INNERDEBUG + enprintf(ENDBG_TRACE)("=>0(def)\n"); +#endif + return(false); + case opx(ENF_AND): + *sp &= arg; + break; + case opx(ENF_OR): + *sp |= arg; + break; + case opx(ENF_XOR): + *sp ^= arg; + break; + case opx(ENF_EQ): + *sp = (*sp == arg); + break; + case opx(ENF_NEQ): + *sp = (*sp != arg); + break; + case opx(ENF_LT): + *sp = (*sp < arg); + break; + case opx(ENF_LE): + *sp = (*sp <= arg); + break; + case opx(ENF_GT): + *sp = (*sp > arg); + break; + case opx(ENF_GE): + *sp = (*sp >= arg); + break; + + /* short-circuit operators */ + + case opx(ENF_COR): + if (*sp++ == arg) { +#ifdef INNERDEBUG + enprintf(ENDBG_TRACE)("=>COR %x\n", *sp); +#endif + return(true); + } + break; + case opx(ENF_CAND): + if (*sp++ != arg) { +#ifdef INNERDEBUG + enprintf(ENDBG_TRACE)("=>CAND %x\n", *sp); +#endif + return(false); + } + break; + case opx(ENF_CNOR): + if (*sp++ == arg) { +#ifdef INNERDEBUG + enprintf(ENDBG_TRACE)("=>COR %x\n", *sp); +#endif + return(false); + } + break; + case opx(ENF_CNAND): + if (*sp++ != arg) { +#ifdef INNERDEBUG + enprintf(ENDBG_TRACE)("=>CAND %x\n", *sp); +#endif + return(true); + } + break; + } + } +#ifdef INNERDEBUG + enprintf(ENDBG_TRACE)("=>%x\n", *sp); +#endif + return((boolean)*sp); + +} + +enInitDescriptor(d, flag) +register struct enOpenDescriptor *d; +{ + +#if DEBUG + enprintf(ENDBG_TRACE)("enInitDescriptor(%x):\n", d); +#endif + d->enOD_RecvState = ENRECVIDLE; + d->enOD_OpenFilter.enf_FilterLen = 0; + d->enOD_OpenFilter.enf_Priority = 0; + d->enOD_FiltEnd = &(d->enOD_OpenFilter.enf_Filter[0]); + d->enOD_RecvCount = 0; + d->enOD_Timeout = 0; + d->enOD_SigNumb = 0; + d->enOD_Flag = flag; + d->enOD_SelColl = 0; + d->enOD_SelProc = 0; /* probably unnecessary */ + /* + * Remember the PID that opened us, at least until some process + * sets a signal for this minor device + */ + d->enOD_SigPid = u.u_procp->p_pid; + + enInitWaitQueue(&(d->enOD_Waiting)); +#if DEBUG + enprintf(ENDBG_TRACE)("=>eninitdescriptor\n"); +#endif + +} + +/* + * enInsertDescriptor - insert open descriptor in queue ordered by priority + */ + +enInsertDescriptor(q, d) +register struct enQueue *q; +register struct enOpenDescriptor *d; +{ + struct enOpenDescriptor * nxt; + register int ipl; + + ipl = splenet(); + nxt = (struct enOpenDescriptor *)q->enQ_F; + while ((struct Queue *)q != &(nxt->enOD_Link)) + { + if (d->enOD_OpenFilter.enf_Priority > nxt->enOD_OpenFilter.enf_Priority) + break; + nxt = (struct enOpenDescriptor *)nxt->enOD_Link.F; + } + enqueue((struct Queue *)&(nxt->enOD_Link),(struct Queue *)&(d->enOD_Link)); + enprintf(ENDBG_DESQ)("enID: Desq: %x, %x\n", q->enQ_F, q->enQ_B); + q->enQ_NumQueued++; + splx(ipl); + +} + +int enReorderCount = 0; /* for external monitoring */ + +/* + * enReorderQueue - swap order of two elements in queue + * assumed to be called at splenet + */ +enReorderQueue(first, last) +register struct Queue *first; +register struct Queue *last; +{ + register struct Queue *prev; + register struct Queue *next; + + enprintf(ENDBG_DESQ)("enReorderQ: %x, %x\n", first, last); + + enReorderCount++; + + /* get pointers to other queue elements */ + prev = first->B; + next = last->F; + + /* + * no more reading from queue elements; this ensures that + * the code works even if there are fewer than 4 elements + * in the queue. + */ + + prev->F = last; + next->B = first; + + last->B = prev; + last->F = first; + + first->F = next; + first->B = last; +} + +enetallattach() +{ + register struct ifnet *ifp; + +#ifdef DEBUG + enprintf(ENDBG_TRACE)("enetallattach\n"); +#endif + + for (ifp = ifnet; ifp; ifp = ifp->if_next) + if (ifp->if_flags & IFF_BROADCAST) { + struct endevp enp; + struct arpcom *ap = (struct arpcom *)ifp; + + enp.end_dev_type = ENDT_10MB; + enp.end_addr_len = 6; + enp.end_hdr_len = 14; + enp.end_MTU = ifp->if_mtu; + bcopy((caddr_t)&ap->ac_enaddr, (caddr_t)(enp.end_addr), 6); + bcopy((caddr_t)etherbdcastaddr, (caddr_t)(enp.end_broadaddr), 6); +/* should put result of enetattach somewhere */ + enetattach(ifp, &enp); + } + +} + +enetattach(ifp, devp) +struct ifnet *ifp; +struct endevp *devp; +{ + register struct enState *enStatep = &enState[enUnits]; + +#ifdef DEBUG + enprintf(ENDBG_INIT) ("enetattach: type %d, addr ", devp->end_dev_type); + if (enetDebug&ENDBG_INIT) { + register int i; + for (i = 0; i < devp->end_addr_len; i++) + printf("%o ", devp->end_addr[i]); + printf("\n"); + } +#endif DEBUG + + enet_info[enUnits].ifp = ifp; + + bcopy((caddr_t)devp, (caddr_t)&(enDevParams), sizeof(struct endevp)); + + enInit(enStatep, enUnits); + + return(enUnits++); +} + +#endif (NENETFILTER > 0) diff --git a/support/enet/enet.h b/support/enet/enet.h new file mode 100644 index 0000000..fefd879 --- /dev/null +++ b/support/enet/enet.h @@ -0,0 +1,174 @@ +/* enet.h Stanford 25 April 1983 */ + +/* + * Ethernet definitions needed for user processes + * + ********************************************************************** + * HISTORY + * 7 October 1985 Jeff Mogul Stanford + * Added EIOCMFREE ioctl to indicate # of free minor devices; + * may or may not be useful. + * 17 October 1984 Jeff Mogul Stanford + * Added ENF_CAND, ENF_COR, ENF_CNAND, and ENF_CNOR, short-circuit + * operators, to make filters run faster. + * All evaluate "(*sp++ == *sp++)": + * ENF_CAND: returns false immediately if result is false, otherwise + * continue + * ENF_COR: returns true immediately if result is true, otherwise + * continue + * ENF_CNAND: returns true immediately if result is false, otherwise + * continue + * ENF_CNOR: returns false immediately if result is true, otherwise + * continue + * Also added ENF_NEQ to complement ENF_EQ + * + * 10 November 1983 Jeffrey Mogul Stanford + * Slight restructuring for support of 10mb ethers; + * added the EIOCDEVP ioctl and associated definitions + * and removed the EIOCMTU ioctl (subsumed by EIOCDEVP) + * + * 25-Apr-83 Jeffrey Mogul Stanford + * Began conversion to 4.2BSD. This involves removing all + * references to the actual hardware. + * Incompatible change: ioctl encodings! + * Added EIOCMTU ioctl to get MTU (max packet size). + * Most previous history comments removed. + * Definitions of interest only to kernel now are in enetdefs.h + * + * 10-Aug-82 Mike Accetta (mja) at Carnegie-Mellon University + * Added EIOCMBIS and EIOCMBIC definitions, and new ENHOLDSIG mode + * bit and ENPRIVMODES defintions (V3.05e). [Last change before + * 4.2BSD conversion starts.] + * + * 22-Feb-80 Rick Rashid (rfr) at Carnegie-Mellon University + * Rewritten for multiple simultaneous opens with filters (V1.05). + * + * 18-Jan-80 Mike Accetta (mja) at Carnegie-Mellon University + * Created (V1.00). + * + ********************************************************************** + */ +#ifdef KERNEL +#include +#else +#include +#endif KERNEL + +#define ENMAXFILTERS 40 /* maximum filter short words */ + +/* + * filter structure for SETF + */ +struct enfilter +{ + u_char enf_Priority; /* priority of filter */ + u_char enf_FilterLen; /* length of filter command list */ + u_short enf_Filter[ENMAXFILTERS]; /* the filter command list */ +}; + +/* set/get parameters, set filter ioctl commands */ +#define EIOCSETP _IOW(E,100, struct eniocb) +#define EIOCGETP _IOR(E,101, struct eniocb) +#define EIOCSETF _IOW(E,102, struct enfilter) +#define EIOCENBS _IOW(E,103, int) +#define EIOCINHS _IO(E,104) +#define EIOCSETW _IOW(E,105, u_int) +#define EIOCFLUSH _IO(E,106) +#define EIOCALLOCP _IO(E,107) +#define EIOCDEALLOCP _IO(E,108) +#define EIOCMBIS _IOW(E,109, u_short) +#define EIOCMBIC _IOW(E,110, u_short) +#define EIOCDEVP _IOR(E,111, struct endevp) +#define EIOCMFREE _IOR(E,112, int) +#define EIOCETHERT _IOW(E,113, int) + +/* + * Bits in mode word modified by EIOCMBIS and EIOCMBIC. + */ +#define ENHOLDSIG (0x0001) /* don't disable signal after sending */ +#define ENPRIVMODES (~(ENHOLDSIG)) + +/* + * We now allow specification of up to MAXFILTERS (short) words of a filter + * command list to be applied to incoming packets to determine if + * those packets should be given to a particular open ethernet file. + * + * Each open enet file specifies the filter command list via iocontrl. + * Each filter command list specifies a sequences of actions which leave a + * boolean value on the top of an internal stack. Each word of the + * command list specifies an action from the set {PUSHLIT, PUSHZERO, + * PUSHWORD+N} which respectively push the next word of the stack, zero, + * or word N of the incoming packet on the stack, and a binary operator + * from the set {EQ, LT, LE, GT, GE, AND, OR, XOR} which operates on the + * top two elements of the stack and replaces them with its result. The + * special action NOPUSH and the special operator NOP can be used to only + * perform the binary operation or to only push a value on the stack. + * + * If the final value of the filter operation is true, then the packet is + * accepted for the open file which specified the filter. + * + */ + +/* these must sum to 16! */ +#define ENF_NBPA 10 /* # bits / action */ +#define ENF_NBPO 6 /* # bits / operator */ + +/* binary operators */ +#define ENF_NOP (0<F = (head);(elt)->B = (head)->B;(head)->B = (elt);((elt)->B)->F = (elt); }; + +#define remqueue(head,elt) \ + { ((elt)->B)->F = (elt)->F; ((elt)->F)->B = (elt)->B; }; + +#define dequeue(head) \ + (((q_tmp = (head)->F) == (head)) ? NULL : \ + (((q_tmp->B)->F = q_tmp->F), \ + ((q_tmp->F)->B = q_tmp->B), \ + (q_tmp))) + +#define queueempty(head) \ + ((head)->F == (head)) + +#define initqueue(head) \ + { (head)->F = (head); (head)->B = (head); }; + +/* + * The ethernet packet structure. + * + */ + +struct enPacket +{ + struct Queue enP_Link; /* queue pointers */ + u_short *enP_Data; /* pointer to start of packet */ + short enP_ByteCount; /* # of bytes in Data */ + u_short enP_RefCount; /* # of outstanding references to */ + /* this packet */ + struct mbuf *enP_mbuf; /* first mbuf of packet */ +}; +#define enP_F enP_Link.F +#define enP_B enP_Link.B + +/* + * Ethernet queue header + */ +struct enQueue +{ + struct Queue enQ_Head; /* queue header and trailer pointers */ + short enQ_NumQueued; /* number of elements in queue */ +}; +#define enQ_F enQ_Head.F +#define enQ_B enQ_Head.B + +/* + * Wait queue header + */ +struct enWaitQueue +{ + struct enPacket *enWQ_Packets[ENMAXWAITING];/* pointers to queued packets */ + char enWQ_Head; /* index into Packets (for dequeue) */ + char enWQ_Tail; /* index into Packets (for enqueue) */ + u_char enWQ_NumQueued; /* current queue size */ + u_char enWQ_MaxWaiting; /* threshold for additions */ +}; +#define enNextWaitQueueIndex(idx) \ + if (++(idx) >= ENMAXWAITING) (idx) = 0 +#define enPrevWaitQueueIndex(idx) \ + if (--(idx) < 0) (idx) = (ENMAXWAITING-1) + +/* + * States of receive side of open enet file. + */ +enum enStates {ENRECVIDLE, ENRECVTIMING, ENRECVTIMEDOUT}; + +struct enOpenDescriptor +{ + struct Queue enOD_Link; /* Linked list of OpenDescriptors */ + struct enWaitQueue enOD_Waiting; /* fixed Queue of waiting packets */ + struct proc *enOD_SigProc; /* Process to signal (user mode) */ + short enOD_SigPid; /* Process ID of process to signal */ + short enOD_SigNumb; /* Signal number for input packet */ + /* notification */ + long enOD_Timeout; /* Length of time to wait for packet */ + struct enfilter enOD_OpenFilter; /* Packet filter */ + enum enStates enOD_RecvState; /* see enStates enumeration (above) */ + short enOD_Flag; /* option bits */ + + /* following are for enetselect() */ + short enOD_SelColl; /* true if selects collide */ + struct proc *enOD_SelProc; /* process that last selected us */ + + unsigned long enOD_RecvCount; /* number of packets received */ + + /* store precomputed address of end of filter */ + unsigned short *enOD_FiltEnd; /* addr of short AFTER end of filt */ +}; + +/* + * State descriptor for each enet device + */ +struct enState +{ + long ens_Rcnt; /* input packets queued since */ + /* system startup */ + long ens_Xcnt; /* output packets sent since */ + /* system startup */ + long ens_Rdrops; /* input packets dropped since */ + /* system startup */ + long ens_Xdrops; /* output packets dropped since */ + /* system startup */ + struct enQueue /* queue of active open */ + ens_Desq; /* file descriptors */ + struct endevp ens_DevParams; /* device parameters, see enet.h */ +}; +#define enRcnt (enStatep->ens_Rcnt) +#define enXcnt (enStatep->ens_Xcnt) +#define enRdrops (enStatep->ens_Rdrops) +#define enXdrops (enStatep->ens_Xdrops) +#define enDesq (enStatep->ens_Desq) +#define enCurOpens (enStatep->ens_Desq.enQ_NumQueued) +#define enDevParams (enStatep->ens_DevParams) diff --git a/support/enet/enetfilter.h b/support/enet/enetfilter.h new file mode 100644 index 0000000..ccfaac9 --- /dev/null +++ b/support/enet/enetfilter.h @@ -0,0 +1 @@ +#define NENETFILTER 64 diff --git a/support/enet/etherstat.8 b/support/enet/etherstat.8 new file mode 100644 index 0000000..3db7158 --- /dev/null +++ b/support/enet/etherstat.8 @@ -0,0 +1,30 @@ +.TH ETHERSTAT 1 +.SH NAME +etherstat \- Ethernet packet filter (PUP, Ethertalk) connections and statistics +.SH SYNOPSIS +.B etherstat +.SH DESCRIPTION +.I Etherstat +is used to print the internal data structures for the Ethernet packet +filter, enet(4). It will show information +for all network protocols that use the packet filter. Currently +this includes Ethertalk and PUP. +.I Etherstat +prints information about each open connection, and then a summary. +The information is organized by device. Each packet filter device +corresponds to an Ethernet interface (e.g. ie0, le1). +.I Etherstat +will print both the minor device number and the corresponding +Ethernet interface name. +.PP +The Ethernet packet filter allows software to specify which packets +it is interested in using "filters" described as Polish operator +strings. In the most general case, +.I Etherstat +will print the filter associated with each connection as a Polish +operator string. However it will recognize a few commonly used +operator strings, and print them in a more human-readable form, +as Appletalk port numbers for Ethertalk connections, and as PUP +address specifications for PUP connections. +.SH "SEE ALSO" +enet(4) diff --git a/support/enet/etherstat.c b/support/enet/etherstat.c new file mode 100644 index 0000000..6b03707 --- /dev/null +++ b/support/enet/etherstat.c @@ -0,0 +1,350 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef queue +#undef dequeue +#include +#include + +struct nlist nl[6]; + +int debug; +int kmem; +struct stat statblock; +char *kernelfile; +char *varname; +struct enState *enState; +struct enOpenDescriptor *enAllDescriptors; +struct enet_info { + struct ifnet *ifp; /* which ifp for output */ +}; +struct enet_info *enet_info; +char namebuf[64]; +struct ifnet ifnet; +int enUnits; +int enMaxMinors; +unsigned long enOffset; + +main(argc,argv) +char *argv[]; +{ + long newval; + int negval = 0; + int changeit = 1; + int dev; + + if ((kmem = open("/dev/kmem",0))<0) { + perror("open /dev/kmem"); + exit(1); + } + kernelfile = "/vmunix"; + if (stat(kernelfile,&statblock)) { + fprintf(stderr,"%s not found.\n",kernelfile); + exit(1); + } + + initnlistvars(); + + for (dev = 0; dev < enUnits; dev++) { + struct enOpenDescriptor *des; + struct enState *enStatep; + u_short *filtp; + int i, command, pos; + + lseek(kmem,enet_info[dev].ifp,0); + if (read(kmem,&ifnet,sizeof(ifnet)) != sizeof(ifnet)) { + perror("read kmem"); + exit(5); + } + + lseek(kmem,ifnet.if_name,0); + if (read(kmem,namebuf,sizeof(namebuf)) != sizeof(namebuf)) { + perror("read kmem"); + exit(5); + } + + printf("Ethernet device %d assigned to %s%d\n\n", + dev, namebuf, ifnet.if_unit); + + printf(" Pid Prio Instate Rcvd Filter\n"); + enStatep = &enState[dev]; + for (des = (struct enOpenDescriptor *) + ((caddr_t)enDesq.enQ_F - enOffset); + des >= enAllDescriptors && + des < &enAllDescriptors[enMaxMinors]; + des = (struct enOpenDescriptor *) + ((caddr_t)des->enOD_Link.F - enOffset)) { + + filtp = &des->enOD_OpenFilter.enf_Filter[0]; + printf("%7d %4d ", + des->enOD_SigPid, des->enOD_OpenFilter.enf_Priority); + switch (des->enOD_RecvState) { + case ENRECVIDLE: printf(" idle "); break; + case ENRECVTIMING: printf("timing "); break; + case ENRECVTIMEDOUT: printf("tmdout "); break; + default: printf("%8d ",des->enOD_RecvState); break; + } + printf("%6d ", des->enOD_RecvCount); + pos = 31; + +/* + * We are going to try to print the filter in a human-readable form. + * This means matching it against certain common patterns. If we + * fail, then we print the actual Polish postfix string. + */ + +/* + * First see if we can decode the Ethernet type code. + */ + i = des->enOD_OpenFilter.enf_FilterLen; + /* printf("i %d last %x %x %x\n", i, filtp[i-3], filtp[i-2], filtp[i-1]); */ + if ((i >= 3 && filtp[i-2] == (ENF_PUSHLIT | ENF_CAND) && + filtp[i-3] == (ENF_PUSHWORD + 6)) || + (i == 3 && filtp[0] == (ENF_PUSHWORD + 6) && + filtp[1] == (ENF_PUSHLIT | ENF_EQ))) + i = filtp[i-1]; + else if (i >= 3 && filtp[0] == (ENF_PUSHWORD + 6) && + filtp[1] == (ENF_PUSHLIT | ENF_CAND)) + i = filtp[2]; + else + i = 0; +/* + * If i is non-zero, we know the type. If filterlen == 3, the filter is + * just based on the Ether type, e.g. rarpd, so we just print it. + * Otherwise, the filter is more complex and we can't decode it. THe + * one exception is PUP, for which we are prepared to decode the most + * commonly used filters. + */ + if (i == 0x201 && des->enOD_OpenFilter.enf_FilterLen == 3) { + printf("PUP ARP\n"); + continue; + } + if (i == 0x8035 && des->enOD_OpenFilter.enf_FilterLen == 3) { + printf("RARP\n"); + continue; + } + if (i == 0x80f3 && des->enOD_OpenFilter.enf_FilterLen == 3) { + printf("AARP\n"); + continue; + } + + if (i == 0x200) { +/* + * We have a PUP connection. We look for certain patterns of operators + * that we know about. If the filter is entirely explained by things + * we know about, then we can print a human-readable form. + */ + u_short *f = filtp; + int dstport1 = -1, dstport2, srcport1 = -1, srcport2, + puptype = -1; + + i = des->enOD_OpenFilter.enf_FilterLen; + while (i > 3) { + if (i >= 6 && f[0] == (ENF_PUSHWORD + 13) && + f[1] == (ENF_PUSHLIT | ENF_CAND) && + f[3] == (ENF_PUSHWORD + 12) && + f[4] == (ENF_PUSHLIT | ENF_CAND)) { + dstport1 = (f[5] & 0xffff); dstport2 = (f[2] & 0xffff); + f += 6; + i -= 6; + } + else if (i >= 6 && f[0] == (ENF_PUSHWORD + 16) && + f[1] == (ENF_PUSHLIT | ENF_CAND) && + f[3] == (ENF_PUSHWORD + 15) && + f[4] == (ENF_PUSHLIT | ENF_CAND)) { + srcport1 = (f[5] & 0xffff); srcport2 = (f[2] & 0xffff); + f += 6; + i -= 6; + } + else if (i >= 5 && f[0] == (ENF_PUSHWORD + 8) && + f[1] == (ENF_PUSHLIT | ENF_AND) && + f[2] == 0xff && + f[3] == (ENF_PUSHLIT | ENF_CAND)) { + puptype = f[4]; + f += 5; + i -= 5; + } + else break; + } +/* + * If we decoded it all, there will be 3 bytes left, for the Ethernet + * type, which we have already determined to be PUP. + */ + if (i == 3) { + printf("PUP"); + if (dstport1 != -1) + if (dstport1 == 0) + printf(" dst ##%o", dstport2); + else + printf(" dst ##%o|%o", dstport1, dstport2); + if (srcport1 != -1) + if (srcport1 == 0) + printf(" src ##%o", srcport2); + else + printf(" src ##%o|%o", srcport1, srcport2); + if (puptype != -1) + printf(" type %o", puptype); + printf("\n"); + continue; + } + } + if (i == 0x809b && des->enOD_OpenFilter.enf_FilterLen == 23) { +/* + * We have an Ethertalk connection. We look for certain patterns of operators + * that we know about. If the filter is entirely explained by things + * we know about, then we can print a human-readable form. + */ + u_short *f = filtp; + int dstport1 = -1, dstport2, srcport1 = -1, srcport2, + puptype = -1; + + printf("Ethertalk port %d\n", f[11] & 0xff); + continue; + } +/* + * Here is the general-purpose code for printing the filter as a + * Polish command string. + */ + for (i = 0; i < des->enOD_OpenFilter.enf_FilterLen; i++) { + char buffer[100],*cp=buffer; + + command = *filtp; filtp++; + switch (command & 0x3ff) { + case ENF_NOPUSH: + *cp = 0; + break; + case ENF_PUSHLIT: + sprintf(cp,"0x%x ", *filtp); filtp++; i++; + break; + case ENF_PUSHZERO: + sprintf(cp,"0 "); + break; + default: + sprintf(cp,"pkt[%d] ", ((command & 0x3FF) - ENF_PUSHWORD) * 2); + break; + } + cp = buffer + strlen(buffer); + command = command & 0xFC00; + switch(command) { + case ENF_EQ: sprintf(cp,"EQ "); break; + case ENF_LT: sprintf(cp,"LT "); break; + case ENF_LE: sprintf(cp,"LE "); break; + case ENF_GT: sprintf(cp,"GT "); break; + case ENF_GE: sprintf(cp,"GE "); break; + case ENF_AND: sprintf(cp,"AND "); break; + case ENF_OR: sprintf(cp,"OR "); break; + case ENF_XOR: sprintf(cp,"XOR "); break; + case ENF_COR: sprintf(cp,"COR "); break; + case ENF_CAND: sprintf(cp,"CAND "); break; + case ENF_CNOR: sprintf(cp,"CNOR "); break; + case ENF_CNAND: sprintf(cp,"CNAND "); break; + case ENF_NEQ: sprintf(cp,"NEQ "); break; + } + if (pos + strlen(buffer) > 78) { + printf("\n "); + pos = 31; + } + printf("%s", buffer); + pos += strlen(buffer); + } + printf("\n"); + } + printf("\nTotals: Input processed: %d, Input dropped: %d\n Output sent: %d, Output dropped: %d\n\n", + enRcnt, enRdrops, enXcnt, enXdrops); + } +exit(0); + } + +initnlistvars() + +{ + nl[0].n_un.n_name = "_enState"; + nl[1].n_un.n_name = "_enUnits"; + nl[2].n_un.n_name = "_enMaxMinors"; + nl[3].n_un.n_name = "_enAllDescriptors"; + nl[4].n_un.n_name = "_enet_info"; + nl[5].n_un.n_name = ""; + nlist(kernelfile,nl); + + if (nl[1].n_type == 0) { + fprintf(stderr, "%s: Can't find _enUnits\n"); + exit(4); + } + (void) lseek(kmem,(nl[1].n_value),0); + if (read(kmem,&enUnits,sizeof(enUnits)) != sizeof(enUnits)) { + perror("read kmem"); + exit(5); + } + + if (nl[0].n_type == 0) { + fprintf(stderr, "%s: Can't find _enState\n"); + exit(4); + } + (void) lseek(kmem,(nl[0].n_value),0); + enState = (struct enState *)malloc(enUnits * sizeof(struct enState)); + if (! enState) { + fprintf(stderr, "Can't malloc enState\n"); + exit(1); + } + if (read(kmem,enState,enUnits * sizeof(struct enState)) != + enUnits * sizeof(struct enState)) { + perror("read kmem"); + exit(5); + } + + if (nl[2].n_type == 0) { + fprintf(stderr, "%s: Can't find _enMaxMinors\n"); + exit(4); + } + (void) lseek(kmem,(nl[2].n_value),0); + if (read(kmem,&enMaxMinors,sizeof(enMaxMinors)) != sizeof(enMaxMinors)) { + perror("read kmem"); + exit(5); + } + + if (nl[3].n_type == 0) { + fprintf(stderr, "%s: Can't find _enAllDescriptors\n"); + exit(4); + } + (void) lseek(kmem,(nl[3].n_value),0); + enAllDescriptors = (struct enOpenDescriptor *) + malloc(enMaxMinors * sizeof(struct enOpenDescriptor)); + if (! enAllDescriptors) { + fprintf(stderr, "Can't malloc enAllDescriptors\n"); + exit(1); + } + if (read(kmem,enAllDescriptors,enMaxMinors * + sizeof(struct enOpenDescriptor)) != + enMaxMinors * sizeof(struct enOpenDescriptor)) { + perror("read kmem"); + exit(5); + } + enOffset = nl[3].n_value; + enOffset -= (unsigned long)enAllDescriptors; + + if (nl[4].n_type == 0) { + fprintf(stderr, "%s: Can't find _enet_info\n"); + exit(4); + } + (void) lseek(kmem,(nl[4].n_value),0); + enet_info = (struct enet_info *) + malloc(enUnits * sizeof(struct enet_info)); + if (! enet_info) { + fprintf(stderr, "Can't malloc enet_info\n"); + exit(1); + } + if (read(kmem,enet_info,enUnits * sizeof(struct enet_info)) != + enUnits * sizeof(struct enet_info)) { + perror("read kmem"); + exit(5); + } + +} + diff --git a/support/ethertalk/Makefile.m4 b/support/ethertalk/Makefile.m4 new file mode 100644 index 0000000..175c3a5 --- /dev/null +++ b/support/ethertalk/Makefile.m4 @@ -0,0 +1,76 @@ +CFLAGS=cflags() caposdefs() specialcflags() +DESTDIR=capsrvrdestdir() +PROGS=etherprogs() +POBJS=etherpobjs() +ETCDIR=etcdest() +CAPLIB=libcap() +SLIB=libspecial() + +LIBABSRCS=abelap.c ethertalk.c ../uab/aarp.c ../uab/hash.c +LIBABOBJS=abelap.o ethertalk.o aarp.o hash.o + +# +# abetalk.o provides EtherTalk support for CAP +# +all: ${PROGS} + +abetalk.o: ${LIBABOBJS} ${POBJS} aarpd_clnt.o aarpd_xdr.o aarpd.h + ld -r -o abetalk.o ${LIBABOBJS} ${POBJS} aarpd_clnt.o aarpd_xdr.o + +aarpd: aarpd.o aarpd_svc.o aarpd.h + ${CC} ${LFLAGS} -o aarpd aarpd.o aarpd_svc.o ${CAPLIB} ${SLIB} + +aarptest: aarptest.o aarpd.h + ${CC} ${LFLAGS} -o aarptest aarptest.o ${CAPLIB} ${SLIB} + +rtmptest: rtmptest.o aarpd.h + ${CC} ${LFLAGS} -o rtmptest rtmptest.o ${CAPLIB} ${SLIB} + +rangetest: rangetest.o aarpd.h + ${CC} ${LFLAGS} -o rangetest rangetest.o ${CAPLIB} ${SLIB} + +aarpd.o: aarpd.c aarpd.h + +abelap.o: abelap.c + +ethertalk.o: ethertalk.c ../uab/ethertalk.h + +snitp.o: snitp.c ../uab/proto_intf.h + +sdlpi.o.o: sdlpi.o.c ../uab/proto_intf.h + +# explict command because on pyramid we don't want -q for this +senetp.o: senetp.c ../uab/proto_intf.h +ifelse(os,[pyr],[ cc -O -c senetp.c]) + +aarpd_clnt.o: aarpd_clnt.c aarpd.h + +aarpd_svc.o: aarpd_svc.c aarpd.h + +aarpd_xdr.o: aarpd_xdr.c aarpd.h + +aarp.o: ../uab/aarp.c ../uab/hash.h ../uab/proto_intf.h \ + ../uab/ethertalk.h ../uab/aarp_defs.h ../uab/aarp.h + ${CC} $(CFLAGS) -DAARPD -c ../uab/aarp.c + +hash.o: ../uab/hash.c ../uab/hash.h + ${CC} $(CFLAGS) -c ../uab/hash.c + +aarptest.o: aarptest.c aarpd.h + +rtmptest.o: rtmptest.c aarpd.h + +install: ${PROGS}.install + +.install: + +aarpd.install: aarpd + -strip aarpd + ifdef([sysvinstall],[install -f $(DESTDIR) aarpd], + [${INSTALLER} aarpd ${DESTDIR}]) + +clean: + rm -f *.o core aarpd aarptest rtmptest + +spotless: + rm -f *.o *.orig core aarpd aarptest rtmptest Makefile makefile diff --git a/support/ethertalk/README b/support/ethertalk/README new file mode 100644 index 0000000..c42f477 --- /dev/null +++ b/support/ethertalk/README @@ -0,0 +1,88 @@ +Native EtherTalk +---------------- + +This directory contains support files for the CAP libraries. They provide +the ability to send and receive EtherTalk packets (Phase 1 or Phase 2) on +the EtherNet from UNIX hosts. + +Currently they support ... + + NIT interface on SUNs + ENET ethernet driver on SUNs (refer ../enet) + ULTRIX packet filter (ULTRIX 4.0 or greater) + Berkeley Packet Filter on 386/BSD, FreeBSD (Phase 1) + +The files aarpd.c et. al. also build an AARP daemon for address resolution. +In CAP 6.0, UAB, UAR and Native EtherTalk use the file /etc/etalk.local for +information about the local network. For Native EtherTalk, the file can be +intialised with entries for "interface" and "thisZone". It is preferable, +however, to provide these as arguments when aarpd is run, IE: + + aarpd ie0 unimelb-CompSci # ie0 for NIT + +When aarpd has dynamically obtained a node number, it rewrites (or creates) +/etc/etalk.local with the values for thisNode, interface and thisZone. +Before CAP programs can run, however, the values of the local network number +(thisNet) and the default bridge address (bridgeNode) must be found. + +With Native EtherTalk, atis is also an RTMP listener (in addition to ECHO +and NBP). ATIS determines the local network number (initially zero) and the +default bridge from the RTMP packets (the default bridge may randomly change +due to network disruptions, load changes etc). This information is conveyed +via SetBridgeAddress() to aarpd which maintains /etc/etalk.local. + +CAP programs read /etc/etalk.local on startup and can call GetBridgeAddress() +at any time to get the latest value for the bridge address. + +SetBridgeAddress() and GetBridgeAddress() are new CAP library calls. With +Native EtherTalk, the calls use RPC for interprocess communication. + +For correct operation, it is thus necessary to run aarpd followed by atis +before any CAP servers are started. It is not necessary to add a delay +after aarpd as it does not return until the node number is determined. The +usual sleep after atis should be increased to 15 seconds to allow an RTMP +packet to be found and processed. + + +Notes on CAP port for 386BSD/FreeBSD port. + +The code provides support for Native Ethertalk and UAB using the Berkeley +Packet filter in 386bsd pk0.2.4 and FreeBSD. I would expect it to work in +NetBSD, but since I don't have access to a system running that, I haven't +been able to test it. It supports Phase 1 only because the "ed" ethernet +driver does not currently support multicast addresses. + +You will need to install the patches to the packet filter code and ethernet +driver I posted to comp.os.386bsd.bugs if they haven't been included in the +standard release yet. If they are not installed, or if you are using an +ethernet driver other than "ed", you will get a message about SIOCGIFADDR +when you try to run aarpd or uab. + +You will need to build a kernel with packet filter support and create a +number of packet filter devices (/dev/bpf[0-n]). If you are running with +UAB you can get by with very few: if you use native ethertalk you may need +at least 8 to do anything very much. I currently run with 12. + +aarpd should be run with the name of the interface e.g. + + aarpd ed0 my_zone + +With UAB put ed:0 into the bridge_desc file. + +Dave Matthews + + +SECURITY NOTE: + +The default permissions on /dev/nit, /dev/enet or /dev/pf/pfilt* devices +normally preclude average users from running CAP programs such as atlook, +getzones etc. If this is a problem at your site, then the options are as +follows, in decreasing order of preference ... + + 1. make the user CAP programs set-group-ID and then have + /dev/nit etc. writeable by this group. + + 2. make the CAP programs set-user-ID to the owner of /dev/nit (root). + + 3. make /dev/nit world writeable. This is the LEAST PREFERRED method + because of the gaping security hole. diff --git a/support/ethertalk/STILL_TO_DO b/support/ethertalk/STILL_TO_DO new file mode 100644 index 0000000..ebf5c7a --- /dev/null +++ b/support/ethertalk/STILL_TO_DO @@ -0,0 +1,15 @@ +Native EtherTalk +---------------- + +*** Still to do as of Feb 14th 1991 + +Test the supplied enet driver (../enet/* & senetp.c, easy, should just work). +Make the DLI interface work under ULTRIX (dlip.c, harder, socket problem ??). + +Determine the need for locking of /etc/etalk.local during updates on nets +that suffer frequent changes of default bridge. + +*** Already done: + +Make the native SUN NIT interface (snitp.c) work. + diff --git a/support/ethertalk/aarpd.c b/support/ethertalk/aarpd.c new file mode 100644 index 0000000..35d25b0 --- /dev/null +++ b/support/ethertalk/aarpd.c @@ -0,0 +1,651 @@ +/* + * RPC AARP daemon for CAP services via Native EtherTalk + * (also maintains information about the current bridge) + * + * Created: Charles Hedrick, Rutgers University + * Modified: David Hornsby, Melbourne University + * 16/02/91: add support for CAP 6.0 atalkdbm routines, back out + * of shared memory in favour of RPC based [SG]etBridgeAddress() + * Add the SVC descriptors to the low level scheduler. + * 28/04/91: Add Phase 2 support, SetNetRange() + */ + +#include +#include +#include +#include +#ifdef PHASE2 +#include +#if !defined(ultrix) && !defined(__osf__) && !defined(__386BSD__) && !defined(__FreeBSD__) && !defined(__bsdi__) && !defined(NeXT) +#include +#endif /* ultrix && __osf__ && __386BSD__ && __FreeBSD__ */ +#include +#endif PHASE2 +#include +#include +#include +#include + +#if (defined(__386BSD__) || defined(SOLARIS) || defined(__FreeBSD__)) && !defined(__bsdi__) +#ifdef SOLARIS +#define PORTMAP +#endif SOLARIS +#include +#else /* __386BSD__ or SOLARIS or __FreeBSD__ */ +#include +#include +#include +#endif /* __386BSD__ or SOLARIS or __FreeBSD__ */ +#include "../uab/ethertalk.h" /* iso: level 1 */ +#include "../uab/if_desc.h" +#include "../uab/log.h" +#include "aarpd.h" + +extern struct lap_description ethertalk_lap_description; +extern u_char *etalk_resolve(); /* address resolver */ +extern void aarpdprog(); /* the RPC program interface */ +extern u_char interface[50]; /* which ethernet device */ +extern u_char this_zone[34]; /* zone for this host */ +extern struct in_addr bridge_addr; /* IP address for local bridge */ +extern byte bridge_node; /* the local bridge */ +extern word bridge_net; /* the local bridge */ +extern byte this_node; /* this host node */ +extern word this_net; /* this host node */ +extern byte nis_node; /* atis running here */ +extern word nis_net; /* atis running here */ +#ifdef PHASE2 +extern word net_range_start; /* phase 2 network range start */ +extern word net_range_end; /* phase 2 network range end */ +extern int etalk_set_mynode(); /* update our node address */ +#endif PHASE2 + +extern short lap_proto; /* our LAP mechanism */ + +#ifndef ultrix +extern fd_set svc_fdset; +#endif ultrix + +byte pzone[INTZONESIZE+1]; /* zone (pascal string) */ +char intrfc[INTFSIZE]; /* interface description */ +u_char null_ether[6]; /* null ethernet address */ +AddrBlock baddr; /* current bridge address */ +IDESC_TYPE id; /* our node information */ +int dlevel=0; /* debug level */ + +u_char *aarp_resolve_svc(); /* the aarpd resolver */ +u_char *rtmp_getbaddr_svc(); /* get the bridge address */ +u_char *rtmp_setbaddr_svc(); /* set the bridge address */ + +#ifdef PHASE2 +struct ifreq ifreq; +u_char e_broad[6] = {0x09, 0x00, 0x07, 0xff, 0xff, 0xff}; +#endif PHASE2 + +main(argc, argv) +int argc; +char *argv[]; +{ + int c; + fd_set rdfds; + char *cp, *ep; + SVCXPRT *transp; + extern int optind; + extern char *optarg; + void init_enet(), svc_run(), set_svc_listen(), run(); +#ifdef ISO_TRANSLATE + void cISO2Mac(); +#endif ISO_TRANSLATE + + while ((c = getopt(argc, argv, "D:d:l:")) != EOF) { + switch (c) { + case 'D': + dlevel = atoi(optarg); + if (dlevel > L_LVLMAX) + dlevel = L_LVLMAX; + break; + case 'd': + dbugarg(optarg); + dlevel = 1; + break; + case 'l': + logitfileis(optarg, "w"); + break; + } + } + set_debug_level(dlevel); + + openetalkdb(NULL); /* open/create etalk.local */ + + nis_net = this_net = 0; /* assume that we don't know */ + bridge_net = bridge_node = 0; + bridge_addr.s_addr = inet_addr("127.0.0.1"); + +#ifdef PHASE2 + this_net = htons(0xff00); /* the startup range */ + net_range_start = htons(0); /* the total range */ + net_range_end = htons(0xfffe); +#endif PHASE2 + + baddr.node = bridge_node; /* set bridge addr hint */ + baddr.net = bridge_net; + baddr.skt = 0; + + if (argc == (optind+2)) { + /* arg list supplied interface & zone names */ + strncpy((char *)interface, argv[optind++], sizeof(interface)); + strncpy((char *)this_zone, argv[optind++], sizeof(this_zone)); +#ifdef ISO_TRANSLATE + cISO2Mac(this_zone); +#endif ISO_TRANSLATE + } + + if (optind != argc) { + fprintf(stderr, + "usage: aarpd [-D level] [-d opt] [-l log] [interface zone]\n"); + exit(1); + } + + if (*interface == '\0') { + fprintf(stderr, "No ethernet interface specified\n"); + exit(1); + } + ep = NULL; + strncpy(intrfc, interface, sizeof(intrfc)); + for (cp = intrfc; *cp != '\0'; cp++) { + if (*cp >= '0' && *cp <= '9') + ep = cp; + } + if (ep == NULL) { /* interface, but no number */ + fprintf(stderr, "Specified interface invalid: %s\n", interface); + exit(1); + } + id.id_intfno = *ep - '0'; + id.id_intf = intrfc; + *ep = '\0'; + + if (*this_zone == '\0') { + fprintf(stderr, "No zone name specified\n"); + exit(1); + } + /* convert zone to pascal string */ + strncpy(pzone+1, (char *)this_zone, sizeof(pzone)-1); + pzone[0] = strlen(this_zone); + id.id_zone = pzone; + + init_enet(); /* init & get a node number */ + etalkdbupdate(NULL); /* rewrite gleaned information */ + + bzero(null_ether, sizeof(null_ether)); +#ifdef PHASE2 + strncpy(ifreq.ifr_name, interface, sizeof(ifreq.ifr_name)); + if (pi_addmulti(e_broad, (caddr_t)&ifreq) < 0) { + fprintf(stderr, "Can't add multicast address!\n"); + exit(1); + } +#endif PHASE2 + + /* set up aarpd RPC services */ + + (void)pmap_unset(AARPDPROG, AARPDVERS); + (void)svc_unregister(AARPDPROG, AARPDVERS); + + transp = svcudp_create(RPC_ANYSOCK); + if (transp == NULL) { + (void)fprintf(stderr, "aarpd: cannot create udp service.\n"); + exit(1); + } + if (!svc_register(transp,AARPDPROG,AARPDVERS,aarpdprog,IPPROTO_UDP)) { + (void)fprintf(stderr, + "unable to register (AARPDPROG, AARPDVERS, udp).\n"); + exit(1); + } + + transp = svctcp_create(RPC_ANYSOCK, 0, 0); + if (transp == NULL) { + (void)fprintf(stderr, "aarpd: cannot create tcp service.\n"); + exit(1); + } + if (!svc_register(transp,AARPDPROG,AARPDVERS,aarpdprog,IPPROTO_TCP)) { + (void)fprintf(stderr, + "unable to register (AARPDPROG, AARPDVERS, tcp).\n"); + exit(1); + } + + set_svc_listen(); /* add RPC descriptors to scheduler */ + +#ifdef PHASE2 + getNetInfo(); /* find out about our network */ +#endif PHASE2 + + if (!dbug.db_flgs && (dlevel == 0)) + disassociate(); + + run(); /* do all the CAP and RPC work */ + + (void)fprintf(stderr, "aarpd: run() returned!\n"); + exit(1); +} + +/* + * initialise network and get node number + * + */ + +void +init_enet() +{ + struct ethertalkaddr *ea, *etalk_mynode(); +#ifdef ISO_TRANSLATE + void pMac2ISO(), pISO2Mac(); +#endif ISO_TRANSLATE + + id.id_ld = ðertalk_lap_description; + id.id_local = NULL; + id.id_isabridge = 0; + id.id_network = 0; + id.id_next = NULL; + id.id_ifuse = 0; + + if (dbug.db_flgs || (dlevel != 0)) { +#ifdef ISO_TRANSLATE + pMac2ISO(id.id_zone); + printf("Interface %s%d, zone %s, ", + id.id_intf, id.id_intfno, id.id_zone+1); + pISO2Mac(id.id_zone); +#else ISO_TRANSLATE + printf("Interface %s%d, zone %s, ", + id.id_intf, id.id_intfno, id.id_zone+1); +#endif ISO_TRANSLATE + } + + init_fdlistening(); /* low level scheduler */ + if (!etalk_init(&id, FALSE)) { /* set up EtherTalk */ + fprintf(stderr, "init_enet: network initialization failed\n"); + exit(1); + } + + ea = etalk_mynode(&id); + this_node = nis_node = ea->node; +#ifdef PHASE2 + this_net = nis_net = htons((ea->dummy[1] << 8) | ea->dummy[2]); +#endif PHASE2 + + if (dbug.db_flgs || (dlevel != 0)) +#ifdef PHASE2 + printf("acquired node number %d/%d.%d\n", + this_node, (ntohs(this_net) >> 8) & 0xff, ntohs(this_net) & 0xff); +#else PHASE2 + printf("acquired node number %d\n", this_node); +#endif PHASE2 +} + +disassociate() +{ + int i; + /* disassociate */ + if (fork()) + _exit(0); /* kill parent */ +#ifdef POSIX + (void)setsid(); +#endif POSIX + for (i=0; i < 3; i++) close(i); /* kill */ + (void)open("/",0); +#ifdef NODUP2 + (void)dup(0); /* slot 1 */ + (void)dup(0); /* slot 2 */ +#else NODUP2 + (void)dup2(0,1); + (void)dup2(0,2); +#endif NODUP2 +#ifndef POSIX +#ifdef TIOCNOTTY + if ((i = open("/dev/tty",2)) >= 0) { + (void)ioctl(i, TIOCNOTTY, (caddr_t)0); + (void)close(i); + } +#endif /* TIOCNTTY */ +#else /* POSIX */ + (void) setsid(); +#endif /* POSIX */ +} + +/* + * these routines do the work for the RPC calls + * + */ + +u_char * +aarp_resolve_svc(node, rqstp) + int *node; + struct svc_req *rqstp; +{ + u_char *ea; + +#ifdef PHASE2 + struct ethertalkaddr *pa = (struct ethertalkaddr *) node; + logit(2, "request for %d/%d.%d", pa->node, pa->dummy[1], pa->dummy[2]); +#else PHASE2 + logit(2, "request for %d", ntohl(*node)); +#endif PHASE2 + ea = etalk_resolve(&id, *node); + return((ea) ? ea : null_ether); +} + +/* + * return the current bridge address + * + */ + +u_char * +rtmp_getbaddr_svc(i, rqstp) + int *i; + struct svc_req *rqstp; +{ + logit(2, "request for bridge address: %d.%d", ntohs(baddr.net), baddr.node); + return((u_char *) &baddr); +} + +/* + * set the bridge address + * (normally called from atis, which is listening to RTMP packets) + * + */ + +u_char * +rtmp_setbaddr_svc(bad, rqstp) + AddrBlock *bad; + struct svc_req *rqstp; +{ + logit(2, "setting bridge address"); + bcopy(bad, &baddr, sizeof(baddr)); + bridge_node = baddr.node; + bridge_net = baddr.net; +#ifndef PHASE2 + this_net = nis_net = baddr.net; +#endif PHASE2 + bridge_addr.s_addr = inet_addr("127.0.0.1"); + etalkdbupdate(NULL); /* write the info. back out */ + return((u_char *) &baddr); +} + +#ifdef PHASE2 +/* + * Set the network range (Phase 2). Check that the current + * node number is free for use in the new range by aarping + * for the new address to see if anybody responds. + * + * (could be called from atis, which is listening to + * RTMP packets or for an arriving GetNetInfo packet) + * + */ + +u_char * +range_set_svc(range, rqstp) + unsigned long *range; + struct svc_req *rqstp; +{ + int attempts, totalattempts; + static struct ethertalkaddr eaddr; + short new_net, new_net_start, new_net_end; + + new_net_start = ntohs((*range >> 16) & 0xffff); /* host byte order */ + new_net_end = ntohs((*range & 0xffff)); /* host byte order */ + new_net = new_net_start; /* host byte order */ + logit(5, "Changing network range: %04x-%04x -> %04x-%04x", + net_range_start, net_range_end, new_net_start, new_net_end); + eaddr.dummy[0] = 0; + eaddr.dummy[1] = (new_net >> 8) & 0xff; + eaddr.dummy[2] = (new_net & 0xff); + eaddr.node = this_node; + /* probe a few times for the new address */ + totalattempts = 0; + for (attempts = 0 ; attempts < 20 ; attempts++) { + logit(5, "Probing for %d/%d.%d", eaddr.node, + eaddr.dummy[1], eaddr.dummy[2]); + /* this is a hack, just sending aarp probes doesn't */ + /* work so we alternate them with aarp requests :-( */ + eaddr.dummy[0] = ((attempts & 0x01) == 0); + if (etalk_resolve(&id, *(long *)&eaddr) != NULL) { + logit(5, "Node number %d already in use!", eaddr.node); + /* oops, pick another */ + if (++eaddr.node >= 0xfe) /* reserved */ + eaddr.node = 1; + attempts = 0; /* same again */ + if (++totalattempts > 252) { + /* oh dear, have to try another net */ + if (++new_net > new_net_end) { + logit(5, "OOPS: no spare nodes available on net!!"); + return(NULL); + } + eaddr.dummy[1] = (new_net >> 8) & 0xff; + eaddr.dummy[2] = (new_net & 0xff); + totalattempts = 0; + } + } + abSleep(1, FALSE); /* allow protocols to run */ + } + eaddr.dummy[0] = 0; + /* adopt it by updating our internal tables */ + if (etalk_set_mynode(&id, &eaddr) < 0) { + logit(5, "Couldn't update net and node numbers"); + return(NULL); + } + logit(5, "Adopting %d/%d.%d", eaddr.node, eaddr.dummy[1], eaddr.dummy[2]); + net_range_start = htons(new_net_start); + net_range_end = htons(new_net_end); + this_net = nis_net = htons(new_net); + this_node = nis_node = eaddr.node; + etalkdbupdate(NULL); + return((u_char *) range); +} +#endif PHASE2 + +/* + * add the RPC descriptors to the low level scheduler + * + */ + +void +set_svc_listen() +{ + int i; + private int svc_listener(); + +#ifdef ultrix + for(i = 0 ; i < 32 ; i++) + if (svc_fds & (1< + * Modified: David Hornsby, Melbourne University + * 16/02/91: add rtmp_[sg]etbaddr_clnt() + * 28/04/91: add range_set_clnt() + */ + +#ifdef SOLARIS +#define PORTMAP +#endif SOLARIS +#include +#include +#include +#include "aarpd.h" + +bool_t xdr_etheraddr(); +bool_t xdr_bridgeaddr(); + +/* Default timeout can be changed using clnt_control() */ +static struct timeval TIMEOUT = { 25, 0 }; + +/* + * AARP resolver + */ + +u_char * +aarp_resolve_clnt(argp, clnt) + int *argp; + CLIENT *clnt; +{ + static etheraddr res; + bzero((char *)res, sizeof(res)); + if (clnt_call(clnt, AARP_RESOLVE, xdr_int, (caddr_t)argp, + xdr_etheraddr, (char *)res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (res); +} + +/* + * Get the bridge address + */ + +u_char * +rtmp_getbaddr_clnt(argp, clnt) + int *argp; + CLIENT *clnt; +{ + static bridgeaddr res; + bzero((char *)res, sizeof(res)); + if (clnt_call(clnt, RTMP_GETBADDR, xdr_int, (caddr_t)argp, + xdr_bridgeaddr, (char *)res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (res); +} + +/* + * Set the bridge address + */ + +u_char * +rtmp_setbaddr_clnt(argp, clnt) + int *argp; + CLIENT *clnt; +{ + static bridgeaddr res; + bzero((char *)res, sizeof(res)); + if (clnt_call(clnt, RTMP_SETBADDR, xdr_int, (caddr_t)argp, + xdr_bridgeaddr, (char *)res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (res); +} + +#ifdef PHASE2 +/* + * Set the network range + */ + +u_char * +range_set_clnt(argp, clnt) + int *argp; + CLIENT *clnt; +{ + static bridgeaddr res; /* convenient size */ + bzero((char *)res, sizeof(res)); + if (clnt_call(clnt, NET_RANGE_SET, xdr_int, (caddr_t)argp, + xdr_bridgeaddr, (char *)res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (res); +} +#endif PHASE2 diff --git a/support/ethertalk/aarpd_svc.c b/support/ethertalk/aarpd_svc.c new file mode 100644 index 0000000..755371b --- /dev/null +++ b/support/ethertalk/aarpd_svc.c @@ -0,0 +1,90 @@ +/* + * RPC dispatcher + * + * Created: Charles Hedrick, Rutgers University + * Modified: David Hornsby, Melbourne University + * 16/02/91: add RTMP_[SG]ETBADDR + * 28/04/91: add NET_RANGE_SET + * + */ + +#include +#ifdef SOLARIS +#define PORTMAP +#endif SOLARIS +#include +#include +#include "aarpd.h" + +bool_t xdr_etheraddr(); +bool_t xdr_bridgeaddr(); + +extern char *aarp_resolve_svc(); +extern char *rtmp_getbaddr_svc(); +extern char *rtmp_setbaddr_svc(); +#ifdef PHASE2 +extern char *range_set_svc(); +#endif PHASE2 + +void +aarpdprog(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + int aarp_resolve_svc_arg; + } argument; + + char *result, *(*local)(); + bool_t (*xdr_argument)(), (*xdr_result)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + return; + + case AARP_RESOLVE: + xdr_argument = xdr_int; + xdr_result = xdr_etheraddr; + local = (char *(*)()) aarp_resolve_svc; + break; + + case RTMP_GETBADDR: + xdr_argument = xdr_int; + xdr_result = xdr_bridgeaddr; + local = (char *(*)()) rtmp_getbaddr_svc; + break; + + case RTMP_SETBADDR: + xdr_argument = xdr_int; + xdr_result = xdr_bridgeaddr; + local = (char *(*)()) rtmp_setbaddr_svc; + break; + +#ifdef PHASE2 + case NET_RANGE_SET: + xdr_argument = xdr_int; + xdr_result = xdr_bridgeaddr; + local = (char *(*)()) range_set_svc; + break; +#endif PHASE2 + + default: + svcerr_noproc(transp); + return; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { + svcerr_decode(transp); + return; + } + result = (*local)(&argument, rqstp); + if (result != NULL + && !svc_sendreply(transp, xdr_result, (caddr_t)result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +} diff --git a/support/ethertalk/aarpd_xdr.c b/support/ethertalk/aarpd_xdr.c new file mode 100644 index 0000000..f77a7ed --- /dev/null +++ b/support/ethertalk/aarpd_xdr.c @@ -0,0 +1,30 @@ +/* + * Created: Charles Hedrick, Rutgers University + * + */ + +#include +#include "aarpd.h" + +bool_t +xdr_etheraddr(xdrs, objp) + XDR *xdrs; + etheraddr objp; +{ + if (!xdr_vector(xdrs, (char *)objp, 6, sizeof(u_char), xdr_u_char)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_bridgeaddr(xdrs, objp) + XDR *xdrs; + bridgeaddr objp; +{ + if (!xdr_vector(xdrs, (char *)objp, 4, sizeof(u_char), xdr_u_char)) { + return (FALSE); + } + return (TRUE); +} + diff --git a/support/ethertalk/aarptest.c b/support/ethertalk/aarptest.c new file mode 100644 index 0000000..e2de105 --- /dev/null +++ b/support/ethertalk/aarptest.c @@ -0,0 +1,87 @@ +/* + * Simple RPC AARP test program + * + * Created: Charles Hedrick, Rutgers University + * + */ + +#include +#include +#include +#include +#include +#include "../uab/ethertalk.h" +#include "aarpd.h" + +#ifdef ultrix +#define ALT_RPC +#include +#include +#endif ultrix + +#ifdef pyr +#define ALT_RPC +#endif pyr + +extern u_char *aarp_resolve_clnt(); + +extern u_short this_net, bridge_net, nis_net, async_net; +extern u_char this_node, bridge_node, nis_node; +extern char this_zone[34], async_zone[34], interface[50]; + +main(argc, argv) +int argc; +char **argv; +{ + CLIENT *cl; + u_char *ether; + struct ethertalkaddr node; +#ifdef ALT_RPC + int sock; + struct timeval tv; + struct sockaddr_in sin; +#endif ALT_RPC + +#ifdef ALT_RPC + sin.sin_family = AF_INET; + sin.sin_port = 0; + bzero(sin.sin_zero, sizeof(sin.sin_zero)); + sin.sin_addr.s_addr = htonl(0x7f000001); + sock = RPC_ANYSOCK; + tv.tv_sec = 5; + tv.tv_usec = 0; + cl = clntudp_create(&sin, AARPDPROG, AARPDVERS, tv, &sock); +#else ALT_RPC + cl = clnt_create("localhost", AARPDPROG, AARPDVERS, "udp"); +#endif ALT_RPC + if (cl == NULL) { + clnt_pcreateerror("localhost/clnt_create"); + exit(1); + } + + if (argc > 1) { +#ifdef PHASE2 + if (argc > 2) { + node.dummy[0] = 0; + node.dummy[1] = (atoi(argv[2]) >> 8) & 0xff; + node.dummy[2] = (atoi(argv[2]) & 0xff); + } +#else PHASE2 + node.dummy[0] = node.dummy[1] = node.dummy[2] = 0; +#endif PHASE2 + node.node = atoi(argv[1]) & 0xff; + ether = aarp_resolve_clnt(&node, cl); + if (ether == NULL) { + clnt_perror(cl, "localhost"); + exit(1); + } + printf("ether addr %x:%x:%x:%x:%x:%x\n", ether[0], ether[1], ether[2], + ether[3], ether[4], ether[5]); + } + + openetalkdb(NULL); + + printf("zone %s this %d.%d gw %d.%d nis %d.%d intf %s\n", + this_zone, ntohs(this_net), this_node, ntohs(bridge_net), + bridge_node, ntohs(nis_net), nis_node, interface); +} diff --git a/support/ethertalk/abelap.c b/support/ethertalk/abelap.c new file mode 100644 index 0000000..5ddc98b --- /dev/null +++ b/support/ethertalk/abelap.c @@ -0,0 +1,973 @@ +/* + * $Author: djh $ $Date: 1996/04/25 00:37:02 $ + * $Header: /mac/src/cap60/support/ethertalk/RCS/abelap.c,v 2.12 1996/04/25 00:37:02 djh Rel djh $ + * $Revision: 2.12 $ +*/ + +/* + * abelap.c - Ethertalk network module (can fall back to KIP) + * + * AppleTalk package for UNIX (4.2 BSD). + * + * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University + * in the City of New York. + * + * + * Edit History: + * + * June 14, 1986 Schilit Created. + * June 18, 1986 CCKim Chuck's handler runs protocol + * April 28,1991 djh Add Phase 2 support + * + */ +/* + * The following list of exported routines is provided so you'll know what + * have to be done to do another interface type (ethertalk, etc) + * + * EXPORTED ROUTINES: + * + * OSErr GetNodeAddress(int *mynode, int *mynet) + * Return node addresses + * OSErr GetBridgeAddress(AddrBlock *addr) + * Return bridge addresses + * OSErr SetBridgeAddress(AddrBlock *addr) + * Set bridge addresses + * OSErr SetNetRange(u_short range_start, u_short range_end) + * Set Network Range (Phase 2) + * int abInit(boolean dispay_message) + * Initialize AppleTalk + * int abOpen(int *returnsocket, int wantsocket, struct iovec iov[], iovlen) + * Open a DDP socket + * int abClose(int socket) + * Close a DDP socket + * void abnet_cacheit(word srcNet, byte srcNode) + * Call in DDP protocol layer to tell the lower layer that + * the last packet that came in was from srcNet, srcNode + * int routeddp(struct iovec *iov, int iovlen) + * This is the DDP incursion. With a full AppleTalk implementation, + * this would be part of DDP (abddp2). This routes the DDP packet: + * normally would decide where to send and then send via lap, with KIP + * decides where and sends via UDP. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(pyr) && !defined(__386BSD__) +#include +#endif /* pyr && __386BSD__ */ +#include +#include +#include /* for nbpNIS */ +#include /* to cover difference between bsd systems */ +#include +#include "../uab/ethertalk.h" /* iso: level 1 */ +#include "../uab/if_desc.h" /* describes "if" */ +#include "../uab/proto_intf.h" +#include "../uab/aarp_defs.h" +#include "aarpd.h" + +#ifdef ultrix +#define ALT_RPC +#endif ultrix +#ifdef pyr +#define ALT_RPC +#endif pyr + +/* RPC clients */ + +extern u_char *aarp_resolve_clnt(); +extern u_char *rtmp_getbaddr_clnt(); +extern u_char *rtmp_setbaddr_clnt(); + +/* imported network information */ + +extern word this_net, bridge_net, nis_net, async_net; +extern byte this_node, bridge_node, nis_node; +extern char this_zone[34], async_zone[34], interface[50]; + +#ifdef PHASE2 +extern word net_range_start, net_range_end; +#endif PHASE2 + +extern struct in_addr bridge_addr; + +#ifdef PHASE2 /* not a dynamic choice yet ... */ +short lap_proto = LAP_ETALK; /* default to EtherTalk Phase 2 */ +#else PHASE2 +short lap_proto = LAP_ETALK; /* default to EtherTalk Phase 1 */ +#endif PHASE2 + +/* + * Configuration defines + * + * NORECVMSG - no recvmsg + * NOSENDMSG - no sendmsg + * NEEDMSGHDR - no msghdr in sockets.h - define our own + * +*/ +#ifdef NORECVMSG +# ifndef NEEDNETBUF +# define NEEDNETBUF +# endif +#endif +#ifdef NOSENDMSG +# ifndef NEEDNETBUF +# define NEEDNETBUF +# endif +#endif + +/* for forwarding using ip_resolve */ +private struct in_addr ipaddr_src; /* ip address */ +private word ddp_srcnet; /* ddp network part */ +private byte ddp_srcnode; /* ddp node part */ + +private int lastnet; /* net last heard from */ +private int lastnode; /* node last heard from */ +private int lastlnode; +private u_char *lasteaddr; +private u_char eaddrbuf[16]; + +#ifdef PHASE2 +u_char etherbroad[6] = {0x09, 0x00, 0x07, 0xff, 0xff, 0xff}; +#else PHASE2 +u_char etherbroad[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +#endif PHASE2 + +private struct sockaddr_in from_sin; /* network struct of last packet rec. */ +private struct sockaddr_in abfsin; /* apple bus foreign socketaddr/internet */ + +word rebport; /* used to hold swabbed rebPort */ +#define rebPort 902 /* 0x386 */ + +private CLIENT *cl; /* RPC client */ + +import int ddp_protocol(); /* DDP protocol handler */ +private int etalk_listener(); /* EtherTalk listener */ +private int kip_get(); /* KIP listener */ +export DBUG dbug; /* debug flags */ + +/* BUG: bind doesn't work when lsin is on the stack! */ +private struct sockaddr_in lsin; /* local socketaddr/internet */ +private int skt2fd[ddpMaxSkt+1]; /* translate socket to file descriptor */ +private int skt2iovlen[ddpMaxSkt+1]; /* translate socket to file descriptor */ +private struct iovec *skt2iov[ddpMaxSkt+1]; /* translate socket to file desc */ +private int skt2pifd[ddpMaxSkt+1]; /* translate socket to file descriptor */ +private int ddpskt2udpport[ddpMaxWKS+1]; /* ddp "wks" socket to udp port */ + +AI_HANDLE aih; +private LAP laph; +extern int errno; +extern aarptab_scan(); +extern AARP_ENTRY *aarptab_find(); + +/* + * OSErr GetNodeAddress(int *myNode,*myNet) + * + * GetNodeAddress returns the net and node numbers for the current + * host. + * + * N.B. - the myNet address is in net (htons) format. + * + */ + +export OSErr +GetNodeAddress(myNode,myNet) +int *myNode,*myNet; +{ + *myNode = this_node; + *myNet = this_net; + return(noErr); /* is ok */ +} + +/* + * ddp to udp wks translate table + * + */ + +struct wks { + char *name; /* name of wks (as in /etc/services) */ + int ddpport; /* ddp port to map from */ + int udpport; /* udp port to map to */ + int notinited; /* tried /etc/services? */ +}; + +/* udpport is initially set to the old (768) values for compatibility */ +#define WKS_entry(name, ddpsock) {(name), (ddpsock), ddpWKSUnix+(ddpsock), 1} + +private struct wks wks[] = { + WKS_entry("at-rtmp",rtmpSkt), + WKS_entry("at-nbp",nbpNIS), + WKS_entry("at-echo",echoSkt), + WKS_entry("at-zis",zipZIS), + WKS_entry(NULL, 0) +}; + +/* + * Translate ddp socket to UDP port: returns 0 if no mapping + * + */ + +ddp2ipskt(ddpskt) +int ddpskt; +{ + struct wks *wksp; + struct servent *serv; + + if (ddpskt < 0 || ddpskt > ddpMaxSkt) + return(0); + + if (ddpskt & ddpWKS) /* 128+x means non-wks */ + return(ddpskt + ddpNWKSUnix); + +#ifdef STEAL_PORTS + if (ddpskt >= ddpEWKS) /* 64-128 experimental */ + return(ddpskt + ddpNWKSUnix); +#endif STEAL_PORTS + + if (ddpskt2udpport[ddpskt] < 0) { + for (wksp = wks; wksp->name != NULL; wksp++) + if (wksp->ddpport == ddpskt) { + if ((serv = getservbyname(wksp->name, "udp")) != NULL) + wksp->udpport = ntohs(serv->s_port); /* replace with new */ + if (dbug.db_ini) + fprintf(stderr, "port for %s is %d\n",wksp->name,wksp->udpport); + endservent(); + ddpskt2udpport[ddpskt] = wksp->udpport; + return(wksp->udpport); + } + ddpskt2udpport[ddpskt] = 0; + } + return(ddpskt2udpport[ddpskt]); +} + +/* + * initialize + * +*/ +export +abInit(disp) +int disp; +{ + int i; + extern int aarp_inited; + static int here_before = 0; +#ifdef ALT_RPC + struct sockaddr_in sin; + int sock; +#endif ALT_RPC + + for (i=0; i < ddpMaxSkt+1; i++) { + skt2fd[i] = -1; /* mark all these as unused */ + skt2pifd[i] = -1; /* mark all these as unused */ + } + for (i=0; i < ddpMaxWKS; i++) + ddpskt2udpport[i] = -1; /* mark unknown */ + + if (!here_before) { + struct timeval tv; + + openetalkdb(NULL); /* set up variables */ + + if (*interface == '\0') /* fall back to vanilla KIP mode */ + lap_proto = LAP_KIP; + else { + here_before = 1; + if (!aarp_inited) { + tv.tv_sec = AARP_SCAN_TIME; + tv.tv_usec = 0; + relTimeout(aarptab_scan, 0, &tv, TRUE); + } + } + } + + rebport = htons(rebPort); /* swap to netorder */ + init_fdlistening(); + + abfsin.sin_family = AF_INET; + abfsin.sin_addr.s_addr = INADDR_ANY; + + if (disp) { + printf("abInit: [ddp: %3d.%02d, %d]", + ntohs(this_net) >> 8, ntohs(this_net) & 0xff, this_node); + if (this_net != nis_net || this_node != nis_node) + printf(", [NBP (atis) Server: %3d.%02d, %d]", + ntohs(nis_net) >> 8, ntohs(nis_net) & 0xff, nis_node); + if (bridge_node) + printf(", [GW: %3d.%02d, %d]", + ntohs(this_net) >> 8, ntohs(this_net) & 0xff, bridge_node); + printf(" starting\n"); + } + + DDPInit(); + + if (lap_proto == LAP_ETALK) { +#ifdef ALT_RPC + struct timeval tv; +#endif ALT_RPC + aih.ai_aarptab = (caddr_t)aarptab_init(); +#ifdef ALT_RPC + sin.sin_family = AF_INET; + sin.sin_port = 0; + bzero(sin.sin_zero, sizeof(sin.sin_zero)); + sin.sin_addr.s_addr = htonl(0x7f000001); + sock = RPC_ANYSOCK; + tv.tv_sec = 5; + tv.tv_usec = 0; + cl = clntudp_create(&sin, AARPDPROG, AARPDVERS, tv, &sock); +#else ALT_RPC + cl = clnt_create("localhost", AARPDPROG, AARPDVERS, "udp"); +#endif ALT_RPC + if (cl == NULL) { + clnt_pcreateerror("localhost"); + exit(1); + } + pi_setup(); + } + return(0); +} + +/* + * int abOpen(int *skt,rskt, iov, iovlen) + * + * abOpen opens the ddp socket in "skt" or if "skt" is zero allocates + * and opens a new socket. Upon return "skt" contains the socket number + * and the returned value is >=0 if no error, < 0 if error. + * + * iov should be an array of type "struct iov" of length at least + * IOV_LAP_SIZE+1. Levels after IOV_LAP_LVL are assume to filled. + * +*/ + +int abOpen(skt,rskt, iov, iovlen) +int *skt; +int rskt; +struct iovec *iov; +int iovlen; +{ + int i,fd,err; + word ipskt; + int etph = -1; + byte this_intfno; + int sktlimit = 128; + char *ep, *cp, this_intf[50]; + + /* good enough for now */ + if (iov == NULL || iovlen < IOV_LAP_SIZE+1 || iovlen > IOV_READ_MAX) + return(-1); + + if ((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0) { + perror("abopen"); + return(fd); + } + lsin.sin_family = AF_INET; + lsin.sin_addr.s_addr = INADDR_ANY; + +#ifdef STEAL_PORTS + *skt = (rskt == 0 ? ddpEWKS : rskt); /* zero rskt is free choice */ + sktlimit += 64; +#else STEAL_PORTS + *skt = (rskt == 0 ? ddpWKS : rskt); /* zero rskt is free choice */ +#endif STEAL_PORTS + ipskt = ddp2ipskt(*skt); /* translate into ip socket number */ + if (ipskt == 0) /* bad socket? */ + return(ddpSktErr); + for (i=0; i < sktlimit; i++,ipskt++,(*skt)++) { + lsin.sin_port = htons(ipskt); + if ((err = bind(fd, (struct sockaddr *)&lsin, sizeof(lsin))) == 0) + break; + if (rskt != 0) /* bind failed and wanted exact? */ + return(err); /* yes... */ + } + if (err == 0 && i < sktlimit) { + iov[IOV_LAP_LVL].iov_base = (caddr_t)&laph; /* remember this */ + iov[IOV_LAP_LVL].iov_len = lapSize; /* and this */ + + if (lap_proto == LAP_ETALK) { + ep = NULL; + strncpy(this_intf, interface, sizeof(this_intf)); + for (cp = this_intf; *cp != '\0'; cp++) { + if (*cp >= '0' && *cp <= '9') + ep = cp; + } + if (ep == NULL) + return(ENODEV); + this_intfno = *ep - '0'; + *ep = '\0'; + + if ((etph = pi_open(ETHERTYPE_APPLETALK,*skt,this_intf,this_intfno))<=0) { + close(fd); + skt2fd[*skt] = -1; + if (etph == 0) + return(EMFILE); + else + return(errno); + } + pi_listener_2(etph, etalk_listener, iov, iovlen); + } + fdlistener(fd, kip_get, iov, iovlen); /* remember for later */ + skt2fd[*skt] = fd; /* remember file descriptor for socket */ + skt2pifd[*skt] = etph; + return(noErr); + } + perror("abopen bind"); + close(fd); + return(err); +} + +/* + * close off socket opened by abOpen() + * +*/ +export int +abClose(skt) +int skt; +{ + int fd; + + if (skt < 0 || skt > ddpMaxSkt) { + fprintf(stderr,"abClose: skt out of range\n"); + exit(0); + } + if (lap_proto == LAP_ETALK) + pi_close(skt2pifd[skt]); + if ((fd = skt2fd[skt]) < 0) + return(0); + if (close(fd) != 0) + perror("abClose"); /* some error... */ + fdunlisten(fd); + skt2fd[skt] = -1; /* mark as unused */ + skt2pifd[skt] = -1; /* mark as unused */ + return(0); +} + +#ifdef NEEDNETBUF +#ifdef NEEDMSGHDR +struct msghdr { + caddr_t msg_name; /* name to send to */ + int msg_namelen; /* size of name */ + struct iovec *msg_iov; /* io vec */ + int msg_iovlen; /* length */ + int msg_accrights; /* dummy */ + int msg_accrightslen; +}; +#endif NEEDMSGHDR + +/* buffer larger than maximum ddp pkt by far */ +private char net_buffer[ddpMaxData*2]; + +#ifdef NOSENDMSG +/* + * limited sendmsg - limits to sizeof(net_buffer) + * +*/ +sendmsg(fd, msg, flags) +int fd; +struct msghdr *msg; +int flags; +{ + int err; + int i, pos, len; + struct iovec *iov; + + iov = msg->msg_iov; + for (i=0, pos=0; i < msg->msg_iovlen; i++, iov++) { + len = iov->iov_len; + if (len+pos > sizeof(net_buffer)) /* if overflow */ + len = sizeof(net_buffer)-pos; /* then limit */ + bcopy(iov->iov_base, net_buffer+pos, len); + pos+= len; + if (len != iov->iov_len) /* we don't have any more space */ + break; + } + len = pos; + if ((err=sendto(fd,net_buffer,len,0,msg->msg_name,msg->msg_namelen)) < 0) + perror("abwrite"); + return(err); +} +#endif NOSENDMSG + +#ifdef NORECVMSG +recvmsg(fd, msg, flags) +int fd; +struct msghdr *msg; +int flags; +{ + int err; + int i, pos, len, blen; + struct iovec *iov; + + err = recvfrom(fd, net_buffer, sizeof(net_buffer), 0, + msg->msg_name, &msg->msg_namelen); + if (err < 0) + perror("abread"); + for (blen=err,pos=0,i=0,iov=msg->msg_iov; i < msg->msg_iovlen; i++, iov++) { + len = min(iov->iov_len, blen); + if ((pos + len) > sizeof(net_buffer)) /* if asking for too much */ + len = sizeof(net_buffer) - pos; /* then limit */ + bcopy(net_buffer+pos, iov->iov_base, len); + pos += len; + blen -= len; + /* either no more room or no more data */ + if (len != iov->iov_len) + break; + } + return(err); +} +#endif NORECVMSG +#endif NEEDNETBUF + +private int +kip_get(fd, iov, iovlen) +int fd; +struct iovec *iov; +int iovlen; +{ + struct msghdr msg; + int len; + LAP *lap; + + msg.msg_name = (caddr_t) &from_sin; + msg.msg_namelen = sizeof(from_sin); + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; +#if defined(__386BSD__) || defined(__FreeBSD__) + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; +#else /* __386BSD__ */ + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; +#endif /* __386BSD__ */ + if ((len = recvmsg(fd,&msg,0)) < 0) { + perror("abread"); + return(len); + } + if (iov->iov_len != lapSize) /* check */ + return(-1); + lap = (LAP *)iov->iov_base; + switch (lap->type) { + case lapDDP: + return(ddp_protocol(iov+1, iovlen-1, len-lapSize)); + break; + default: + return(-1); + } + return(-1); +} + +#define elapSize (lapSize+14) + +private int etalk_listener(fd, iov, iovlen) +int fd; +struct iovec *iov; +int iovlen; +{ + /* room for packet and then some */ + static unsigned char rbuf[ddpMaxData+ddpSize+lapSize+100]; +#define pack (rbuf+1) +#define packsize (sizeof(rbuf)-1) + int cc; + struct iovec *liov; + int pos, i, len, lcc, liovlen; + struct ethertalkaddr etaddr; + DDP *ddp; + ShortDDP *sddp; +#ifdef USING_FDDI_NET + extern int source_offset; +#endif USING_FDDI_NET + + lastnet = -1; + lastnode = -1; + + if ((cc = pi_reada(fd, pack, packsize, eaddrbuf)) <= 0) + return(-1); + +#ifdef USING_FDDI_NET + lasteaddr = eaddrbuf + source_offset; +#else USING_FDDI_NET + lasteaddr = eaddrbuf + 6; +#endif USING_FDDI_NET + + if (cc <= lapSize) /* not much use if only lap */ + return(-1); + + pos = lapSize; + cc -= pos; + iov++; + iovlen--; + + bcopy(pack, &laph, lapSize); + + if (laph.src == 0xff) /* bad, bad, bad */ + return(-1); + + /* + * lap dest isn't right + * + */ + if (laph.dst != 0xff && laph.dst != this_node) + return(-1); + + liovlen=iovlen; + liov=iov; + lcc=cc; + + switch (laph.type) { + case lapShortDDP: /* handle shortDDP by hand */ + if (cc < ddpSSize || iovlen < 1) + return(-1); + + sddp = (ShortDDP *)(pack + pos); + ddp = (DDP *)iov->iov_base; + pos += ddpSSize; + lcc -= ddpSSize; + liov++; + liovlen--; + cc += ddpSize - ddpSSize; + + ddp->length = sddp->length + ddpSize - ddpSSize; + ddp->checksum = 0; + ddp->dstNet = this_net; + ddp->srcNet = this_net; + ddp->dstNode = laph.dst; + ddp->srcNode = laph.src; + ddp->dstSkt = sddp->dstSkt; + ddp->srcSkt = sddp->srcSkt; + ddp->type = sddp->type; + + /* fall through */ + + case lapDDP: + + for (; liovlen; liovlen--, liov++) { + len = min(liov->iov_len, lcc); + if ((pos + len) > packsize) /* if asking for too much */ + len = packsize - pos; /* then limit */ + bcopy(pack+pos, liov->iov_base, len); + pos += len; + lcc -= len; + /* either no more room or no more data */ + if (len != liov->iov_len) + break; + } + + ddp = (DDP *)iov->iov_base; + lastnode = ddp->srcNode; + lastnet = ddp->srcNet; + lastlnode = laph.src; + + /* + * pick out source address for aarp table management if not self + * + */ + if (ddp->srcNode != this_node) { +#ifdef PHASE2 + etaddr.dummy[0] = 0; + etaddr.dummy[1] = (ntohs(ddp->srcNet) >> 8) & 0xff; + etaddr.dummy[2] = (ntohs(ddp->srcNet) & 0xff); +#else PHASE2 + etaddr.dummy[0] = etaddr.dummy[1] = etaddr.dummy[2] = 0; +#endif PHASE2 + etaddr.node = ddp->srcNode; +#ifdef USING_FDDI_NET + aarp_insert(&aih, eaddrbuf+source_offset, &etaddr, FALSE); +#else USING_FDDI_NET + aarp_insert(&aih, eaddrbuf+6, &etaddr, FALSE); +#endif USING_FDDI_NET + } + + return(ddp_protocol(iov, iovlen, cc)); + + default: + return(-1); + } +} + +/* + * This is the DDP/UDP interface + * +*/ + +/* srcNet and node of last incoming packet sent to DDP */ +/* and valid */ +export void +abnet_cacheit(srcNet, srcNode) +word srcNet; +byte srcNode; +{ + ddp_srcnet = srcNet; /* remember where last packet came from */ + ddp_srcnode = srcNode; + ipaddr_src.s_addr = (from_sin.sin_port == rebport) ? 0 : + from_sin.sin_addr.s_addr; +} + +private int +ip_resolve(ddpnet, ddpnode, iphost) +word ddpnet; +byte ddpnode; +struct in_addr *iphost; +{ + if (ipaddr_src.s_addr != 0 && ddpnet == ddp_srcnet && ddpnode == ddp_srcnode) + iphost->s_addr = ipaddr_src.s_addr; + else + iphost->s_addr = bridge_addr.s_addr; +} + +private LAP lap; + +export int +routeddp(iov, iovlen) +struct iovec *iov; +int iovlen; +{ + struct msghdr msg; + word destskt; + struct in_addr desthost; + DDP *ddp; + int err; + int fd; + unsigned char *eaddr; + int destnode; + int destnet; + AARP_ENTRY *ae; + struct ethertalkaddr etaddr; + AddrBlock baddr; + OSErr GetBridgeAddress(); + + ddp = (DDP *)iov[IOV_DDP_LVL].iov_base; /* pick out ddp header */ + + /* check ddp socket(s) for validity */ + if ( ddp->srcSkt == 0 || ddp->srcSkt == ddpMaxSkt || + ddp->dstSkt == 0 || ddp->dstSkt == ddpMaxSkt || + (fd = skt2fd[ddp->srcSkt]) == -1 ) + return(ddpSktErr); + + /* establish a dummy lap header */ + lap.type = lapDDP; + lap.dst = ddp->dstNode; + lap.src = this_node; + iov[IOV_LAP_LVL].iov_base = (caddr_t) ⪅ /* LAP header */ + iov[IOV_LAP_LVL].iov_len = lapSize; /* size */ + + /* Use KIP if loopback, else ether */ + if ((lap_proto != LAP_ETALK) + || ((ddp->dstNet == this_net +#ifdef PHASE2 + || (ddp->dstNet == 0x0000 && ddp->dstNode == 0xff) +#endif PHASE2 + ) && (ddp->dstNode == this_node || ddp->dstNode == 0xff))) { + /* establish dest socket */ + destskt = (word)htons(ddp2ipskt(ddp->dstSkt)); + if (destskt == 0) /* byte swapped zero is still zero */ + return(ddpSktErr); + /* resolve mapping */ + ip_resolve(ddp->dstNet, ddp->dstNode, &desthost); + + /* send through */ + abfsin.sin_addr = desthost; + abfsin.sin_port = destskt; + + msg.msg_name = (caddr_t) &abfsin; + msg.msg_namelen = sizeof(abfsin); + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; +#if defined(__386BSD__) || defined(__FreeBSD__) + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; +#else /* __386BSD__ */ + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; +#endif /* __386BSD__ */ + if ((err = sendmsg(fd,&msg,0)) < 0) + perror("abwrite"); + if (ddp->dstNode != 0xff) + return(err); + } +#ifdef PHASE2 + /* delete the unwanted LAP header */ + iov[IOV_LAP_LVL].iov_len = 0; +#endif PHASE2 + if ((lap_proto == LAP_ETALK) + && (ddp->dstNet != this_net || ddp->dstNode != this_node)) { + eaddr = NULL; +#ifdef PHASE2 + if ((ddp->dstNet == 0 && ddp->dstNode == 0xff) /* local broadcast */ + || (ntohs(ddp->dstNet) >= ntohs(net_range_start) + && ntohs(ddp->dstNet) <= ntohs(net_range_end)) + || (ntohs(ddp->dstNet) >= 0xff00 && ntohs(ddp->dstNet) <= 0xfffe)) { + destnode = ddp->dstNode; + destnet = ddp->dstNet; + } else { +#else PHASE2 + if (ddp->dstNet == this_net) { + destnode = ddp->dstNode; + destnet = ddp->dstNet; + } else { +#endif PHASE2 + if (ddp->dstNet == lastnet && ddp->dstNode == lastnode) { + destnode = lastlnode; + destnet = lastnet; + eaddr = lasteaddr; + } else { + if (GetBridgeAddress(&baddr) == noErr && baddr.node) { + destnode = baddr.node; + destnet = baddr.net; + } else + return(-1); + } + } + + lap.dst = destnode; + + if (! eaddr) { + if (destnode == 0xff) + eaddr = etherbroad; + else { +#ifdef PHASE2 + etaddr.dummy[0] = 0; + etaddr.dummy[1] = (ntohs(destnet) >> 8) & 0xff; + etaddr.dummy[2] = (ntohs(destnet) & 0xff); +#else PHASE2 + etaddr.dummy[0] = etaddr.dummy[1] = etaddr.dummy[2] = 0; +#endif PHASE2 + etaddr.node = destnode; + if ((ae = aarptab_find(&aih, &etaddr)) != NULL && + ae->aae_flags & AARP_OKAY) { + if (ae->aae_flags & (AARP_PERM)) + eaddr = ae->aae_eaddr; + else if (ae->aae_ttl > 0) + eaddr = ae->aae_eaddr; + } + } + } + + if (! eaddr) { + eaddr = aarp_resolve_clnt(&etaddr, cl); + if (eaddr == NULL) { + clnt_perror(cl, "localhost"); + return(-1); + } + if (eaddr[0] == 0 && eaddr[1] == 0 && eaddr[2] == 0 && + eaddr[3] == 0 && eaddr[4] == 0 && eaddr[5] == 0) { +/* + * no arp entry. We have sent an arp request. We want to pretend + * that the write succeeded, since some applications abort on a + * failed write. Presumably they'll be expecting to read some sort + * of response, and when that fails, will time out + */ + int len, i; + for (len = 0, i = 0 ; i < iovlen ; i++) + if (iov[i].iov_base && iov[i].iov_len >= 0) + len += iov[i].iov_len; + return (len); + } + aarp_insert (&aih, eaddr, &etaddr, 1); + } + + if ((err = pi_writev(skt2pifd[ddp->srcSkt], iov, iovlen, eaddr)) < 0) + perror("abwrite"); + return(err); + } + return(noErr); +} + +export void +dumpether(lvl,msg, ea) +int lvl; +char *msg; +byte *ea; +{ + logit(lvl, "%s: %x %x %x %x %x %x",msg,ea[0],ea[1],ea[2],ea[3],ea[4],ea[5]); +} + +/* + * Get Bridge Address (via RPC for Native EtherTalk) + * + */ + +OSErr +GetBridgeAddress(addr) +AddrBlock *addr; +{ + AddrBlock *baddr; + + if (lap_proto == LAP_ETALK) { + baddr = (AddrBlock *) rtmp_getbaddr_clnt(addr, cl); + if (baddr == NULL) { + clnt_perror(cl, "localhost"); + exit(1); + } + bridge_net = baddr->net; + bridge_node = baddr->node; +#ifndef PHASE2 + this_net = nis_net = bridge_net; +#endif PHASE2 + } + addr->net = bridge_net; + addr->node = bridge_node; + return(noErr); +} + +/* + * Set Bridge Address (via RPC for Native EtherTalk) + * + */ + +OSErr +SetBridgeAddress(addr) +AddrBlock *addr; +{ + AddrBlock *baddr; + + if (lap_proto == LAP_ETALK) { + baddr = (AddrBlock *) rtmp_setbaddr_clnt(addr, cl); + if (baddr == NULL) { + clnt_perror(cl, "localhost"); + exit(1); + } +#ifndef PHASE2 + this_net = nis_net = addr->net; +#endif PHASE2 + } + bridge_net = addr->net; + bridge_node = addr->node; + return(noErr); +} + +#ifdef PHASE2 +/* + * Set Network Range (via RPC for Native EtherTalk) + * + */ + +OSErr +SetNetRange(range_start, range_end) +u_short range_start, range_end; +{ + AddrBlock *baddr; + long range; + + if (lap_proto == LAP_ETALK) { + range = range_start & 0xffff; + range <<= 16; + range |= range_end & 0xffff; + baddr = (AddrBlock *) range_set_clnt(&range, cl); + if (baddr == NULL) { + clnt_perror(cl, "localhost"); + exit(1); + } + } + this_net = nis_net = net_range_start = range_start; + net_range_end = range_end; + return(noErr); +} +#endif PHASE2 diff --git a/support/ethertalk/bpfiltp.c b/support/ethertalk/bpfiltp.c new file mode 100644 index 0000000..13b54c4 --- /dev/null +++ b/support/ethertalk/bpfiltp.c @@ -0,0 +1,958 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/09/24 13:55:14 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/ethertalk/RCS/bpfiltp.c,v 2.8 1996/09/24 13:55:14 djh Rel djh $"; +static char revision[] = "$Revision: 2.8 $"; + +/* + * bpfiltp.c - Simple "protocol" level interface to BPF + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * July 1988 CCKim Created + * June 1991 Rakesh Patel/David Hornsby Add Phase 2 support + * Oct 1993 David Matthews. Modified for Berkley Packet Filter + * + */ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../uab/proto_intf.h" + +#ifdef bsdi +#include +#define MULTI_BPF_PKT +#define USE_SIOCGIFCONF +#endif bsdi + +#ifdef __NetBSD__ +#define MULTI_BPF_PKT +#define USE_SIOCGIFCONF +#endif __NetBSD__ + +#ifdef __FreeBSD__ +#define MULTI_BPF_PKT +#define USE_SIOCGIFCONF +#endif __FreeBSD__ + +#ifdef NeXT +#include +#define MULTI_BPF_PKT +#undef USE_SIOCGIFCONF +#endif NeXT + +#ifdef USE_SIOCGIFCONF +#include +#include +#endif USE_SIOCGIFCONF + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int fd; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ + int socket; /* ddp socket */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +extern char interface[50]; + +static u_int pf_bufsize; +static char *read_buf; + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, socket, dev, devno) +int protocol; +int socket; +char *dev; +int devno; +{ + private int init_nit(); + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "%s%d",dev,devno); + strncpy(eph->ifr.ifr_name, devnamebuf, sizeof eph->ifr.ifr_name); + + if ((s = init_nit(1024, protocol, socket, &eph->ifr)) < 0) { + return(-1); + } + + eph->inuse = TRUE; + eph->fd = s; + eph->protocol = protocol; + eph->socket = socket; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(TRUE); +} + +export int +pi_close(edx) +int edx; +{ +#ifdef MULTI_BPF_PKT + extern int read_buf_len; +#endif /* MULTI_BPF_PKT */ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].fd); /* toss listener */ + close(ephlist[edx-1].fd); + ephlist[edx-1].inuse = 0; +#ifdef MULTI_BPF_PKT + read_buf_len = 0; +#endif /* MULTI_BPF_PKT */ + return(0); +} + +/* + * Initialize nit on a particular protocol type + * Return: socket if no error, < 0 o.w. + * +*/ +private int +init_nit(chunksize, protocol, socket, ifr) +u_long chunksize; +u_short protocol; +int socket; +struct ifreq *ifr; +{ + int s; + u_long if_flags; + u_int imm; + struct bpf_version vers; + private int setup_pf(); + + { + register int i; + register int failed = 0; + char enetd[256]; + + /* + * try all ethernet minors from (devname)0 on up. + * (e.g., /dev/enet0 .... ) + * + * Algorithm: assumption is that names start at 0 + * and run up as decimal numbers without gaps. We search + * until we get an error that is not EBUSY (i.e., probably + * either ENXIO or ENOENT), or until we sucessfully open one. + */ + + for (i = 0; !failed ; i++) { + sprintf (enetd, "/dev/bpf%d", i); + if ((s = open (enetd, O_RDWR)) >= 0) + break; + /* if we get past the break, we got an error */ + if (errno != EBUSY) failed++; + } + + if (failed) { + perror("open: /dev/bpfXX"); + return(-1); + } + } + + /* Check the version number. */ + if ((ioctl(s, BIOCVERSION, &vers)) < 0) { + perror("ioctl: get version"); + return(-1); + } + + if (vers.bv_major != BPF_MAJOR_VERSION || + vers.bv_minor < BPF_MINOR_VERSION) { + fprintf(stderr, "Incompatible packet filter version\n"); + return -1; + } + + if (setup_pf(s, protocol, socket) < 0) + return(-1); + + /* We have to read EXACTLY the buffer size. */ + + if ((ioctl(s, BIOCGBLEN, &pf_bufsize)) < 0) { + perror("ioctl: get buffer length "); + return(-1); + } + + read_buf = (char *)malloc(pf_bufsize); + + if (ioctl(s, BIOCSETIF, ifr) < 0) { + perror("ioctl: set interface"); + return(-1); + } + + imm = 1; + if (ioctl(s, BIOCIMMEDIATE, &imm) < 0) { + perror("ioctl: set immediate mode"); + return(-1); + } + + return(s); +} + +#ifdef PHASE2 +/* + * add a multicast address to the interface + * + */ + +#ifdef NeXT + +/* + * For NEXTSTEP 3.1 and later + * + * Multicast is controled through an enhanced version of BPF for NeXT. + * You can get it from "ftp.aa.ap.titech.ac.jp". (S.Adachi, 96/02/17) + * + * It was announced that NEXTSTEP (>=3.3) would officially support multicast. + * However, it does not actually work. + * + */ + +#define SIOCADDMULTI _IOW('i', 49, struct ifreq) /* add m'cast addr */ +#define SIOCDELMULTI _IOW('i', 50, struct ifreq) /* del m'cast addr */ + +int +pi_addmulti(multi, ifr) +char *multi; +struct ifreq *ifr; +{ + int s; + int i; + int failed = 0; + char enetd[256]; + + /* Prepare the interface request struct. */ + ifr->ifr_addr.sa_family = AF_UNSPEC; + bcopy(multi, ifr->ifr_addr.sa_data, EHRD); + + /* Open a bpf device. */ + for (i = 0; !failed ; i++) { + sprintf (enetd, "/dev/bpf%d", i); + if ((s = open (enetd, O_RDWR)) >= 0) + break; + /* if we get past the break, we got an error */ + if (errno != EBUSY) failed++; + } + if (failed) { + perror("open: /dev/bpfXX"); + return(-1); + } + + /* Attach the Ethernet device /dev/en0 to the BPF. */ + if (ioctl(s, BIOCSETIF, ifr) < 0) { + perror("ioctl: set interface"); + return(-1); + } + + /* Set the multicast address. */ + if (ioctl(s, SIOCADDMULTI, (caddr_t)ifr) < 0) { + perror("SIOCADDMULTI"); + close(s); + return(-1); + } + close(s); + return(0); +} + +#else /* NeXT */ + +int +pi_addmulti(multi, ifr) +char *multi; +struct ifreq *ifr; +{ + int sock; + + ifr->ifr_addr.sa_family = AF_UNSPEC; + bcopy(multi, ifr->ifr_addr.sa_data, EHRD); + /* + * open a socket, temporarily, to use for SIOC* ioctls + * + */ + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return(-1); + } + if (ioctl(sock, SIOCADDMULTI, (caddr_t)ifr) < 0) { + perror("SIOCADDMULTI"); + close(sock); + return(-1); + } + close(sock); + return(0); +} +#endif /* NeXT */ +#endif PHASE2 + + +/* + * establish protocol filter + * +*/ +private int +setup_pf(s, prot, sock) +int s; +u_short prot; +int sock; +{ + u_short offset; + struct ether_header eh; + struct bpf_program pf; +#define PROG_SIZE 20 + struct bpf_insn pf_insns[PROG_SIZE]; + int ppf=0; + extern int errno; + +#define s_offset(structp, element) (&(((structp)0)->element)) + offset = ((int)s_offset(struct ether_header *, ether_type)); +#ifdef PHASE2 + offset += 8; /* shorts: 2 bytes length + 6 bytes of 802.2 and SNAP */ +#endif PHASE2 + + pf_insns[ppf].code = BPF_LD+BPF_H+BPF_ABS; + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = offset; + ppf++; + pf_insns[ppf].code = BPF_JMP+BPF_JEQ+BPF_K; + pf_insns[ppf].jt = 1; + pf_insns[ppf].jf = 0; + pf_insns[ppf].k = prot; + ppf++; + pf_insns[ppf].code = BPF_RET+BPF_K; /* Fail. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = 0; + ppf++; + if (sock >= 0) { +#ifdef PHASE2 + pf_insns[ppf].code = BPF_LD+BPF_B+BPF_ABS; /* Dest. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = offset+12; + ppf++; + pf_insns[ppf].code = BPF_JMP+BPF_JEQ+BPF_K; + pf_insns[ppf].jt = 1; + pf_insns[ppf].jf = 0; + pf_insns[ppf].k = sock & 0xff; + ppf++; + pf_insns[ppf].code = BPF_RET+BPF_K; /* Fail. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = 0; + ppf++; +#else PHASE2 +/* short form */ + pf_insns[ppf].code = BPF_LD+BPF_B+BPF_ABS; /* LAP type. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = offset+4; + ppf++; + pf_insns[ppf].code = BPF_JMP+BPF_JEQ+BPF_K; + pf_insns[ppf].jt = 0; + pf_insns[ppf].jf = 2; + pf_insns[ppf].k = 1; + ppf++; + pf_insns[ppf].code = BPF_LD+BPF_B+BPF_ABS; /* Dest. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = offset+7; + ppf++; + pf_insns[ppf].code = BPF_JMP+BPF_JEQ+BPF_K; + pf_insns[ppf].jt = 5; + pf_insns[ppf].jf = 4; + pf_insns[ppf].k = sock & 0xff; + ppf++; +/* long form */ + pf_insns[ppf].code = BPF_LD+BPF_B+BPF_ABS; /* LAP type. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = offset+4; + ppf++; + pf_insns[ppf].code = BPF_JMP+BPF_JEQ+BPF_K; + pf_insns[ppf].jt = 0; + pf_insns[ppf].jf = 2; + pf_insns[ppf].k = 2; + ppf++; + pf_insns[ppf].code = BPF_LD+BPF_B+BPF_ABS; /* Dest. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = offset+15; + ppf++; + pf_insns[ppf].code = BPF_JMP+BPF_JEQ+BPF_K; + pf_insns[ppf].jt = 1; + pf_insns[ppf].jf = 0; + pf_insns[ppf].k = sock & 0xff; + ppf++; + pf_insns[ppf].code = BPF_RET+BPF_K; /* Fail. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = 0; + ppf++; +#endif PHASE2 + } + pf_insns[ppf].code = BPF_RET+BPF_K; /* Success. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = (u_int)-1; + ppf++; + + pf.bf_len = ppf; + pf.bf_insns = pf_insns; + if (ioctl(s, BIOCSETF, &pf) < 0) { + perror("ioctl: protocol filter"); + return(-1); + } + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + +#ifdef USE_SIOCGIFCONF + { + int len, sock; + struct ifconf ifconf; + struct sockaddr_dl *sadl; + struct ifreq ifrbuf[32], *ifra, *ifrb; + + ifconf.ifc_len = sizeof(ifrbuf); + ifconf.ifc_buf = (caddr_t)ifrbuf; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("SOCK_DGRAM"); + return(-1); + } + if (ioctl(sock, SIOCGIFCONF, &ifconf) < 0) { + perror("SIOCGIFCONF"); + close(sock); + return(-1); + } + close(sock); + ifra = ifrbuf; + ifrb = (struct ifreq *)((char *)ifra + ifconf.ifc_len); + while (ifra < ifrb) { + if (strcmp(eph->ifr.ifr_name, ifra->ifr_name) == 0) { + if (ifra->ifr_addr.sa_family == AF_LINK) { + sadl = (struct sockaddr_dl *)&ifra->ifr_addr; + if (sadl->sdl_type == IFT_ETHER) { + bcopy((char *)LLADDR(sadl), ea, 6); + return(0); + } +#ifdef bsdi + if (sadl->sdl_alen == 0) /* no addr, try other method */ + return(get_eth_addr(eph->fd, eph->ifr.ifr_name, ea)); +#endif bsdi + } + } + if ((len = ifra->ifr_addr.sa_len+sizeof(ifra->ifr_name)) < sizeof(*ifra)) + len = sizeof(*ifra); + ifra = (struct ifreq *)((char *)ifra + len); + } + } + return(-1); +#else USE_SIOCGIFCONF + if (ioctl(eph->fd, SIOCGIFADDR, &eph->ifr) < 0) { + perror("Ioctl: SIOCGIFADDR"); + return(-1); + } + sa = (struct sockaddr *)&eph->ifr.ifr_data; + bcopy(sa->sa_data, ea, 6); + return(0); +#endif USE_SIOCGIFCONF +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg, edx); +} + +export +pi_listener_2(edx, listener, arg1, arg2) +int edx; +int (*listener)(); +caddr_t arg1; +int arg2; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg1, arg2); +} + +/* + * Note: BPF can return multiple packets in one read + * + */ + +#ifdef MULTI_BPF_PKT +#define NUMRDS 32 + +struct RDS { + u_short dataLen; + u_char *dataPtr; +}; + +struct RDS RDS[NUMRDS]; +export int read_buf_len = 0; +#endif /* MULTI_BPF_PKT */ + +static int +bp_readv(fd, iov, iovlen) +int fd; +struct iovec *iov; +int iovlen; +{ + int cc; + struct bpf_hdr *bp; + char *p; + int i, size; + +#ifdef MULTI_BPF_PKT + static int rds_index; + char *q; + + if (read_buf_len == 0) { + /* Must read exactly the buffer size. */ + if ((cc = read(fd, read_buf, pf_bufsize)) < 0) + return(cc); + /* fill in RDS */ + p = read_buf; + q = read_buf+cc; + read_buf_len = cc; + for (i = 0; i < (NUMRDS-1) && p < q; i++) { + bp = (struct bpf_hdr *)p; + RDS[i].dataLen = bp->bh_caplen; + RDS[i].dataPtr = (u_char *)(p + bp->bh_hdrlen); + p += BPF_WORDALIGN(bp->bh_hdrlen+bp->bh_caplen); + } + RDS[i].dataLen = 0; + rds_index = 0; + } + if ((size = (int)RDS[rds_index].dataLen) == 0) { + read_buf_len = 0; + return(0); + } + p = (char *)RDS[rds_index].dataPtr; + for (i = 0; i < iovlen && size > 0; i++) { + if (size < iov[i].iov_len) + cc = size; + else + cc = iov[i].iov_len; + bcopy(p, iov[i].iov_base, cc); + p += cc; + size -= cc; + } + cc = RDS[rds_index].dataLen; + if (size > 0) + cc -= size; + rds_index++; + read_buf_len = RDS[rds_index].dataLen; + return(cc); +#else /* MULTI_BPF_PKT */ + /* Must read exactly the buffer size. */ + if ((cc = read(fd, read_buf, pf_bufsize)) < 0) { + return(cc); + } + /* The data begins with a header. */ + bp = (struct bpf_hdr*) read_buf; + p = read_buf + bp->bh_hdrlen; + size = bp->bh_caplen; + for (i=0; i 0; i++) { + if (size < iov[i].iov_len) bcopy(p, iov[i].iov_base, size); + else bcopy(p, iov[i].iov_base, iov[i].iov_len); + p += iov[i].iov_len; + size -= iov[i].iov_len; + } + if (size > 0) return (bp->bh_caplen-size); + else return bp->bh_caplen; +#endif /* MULTI_BPF_PKT */ +} + + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = bp_readv(eph->fd, iov, iovlen)) < 0) { + perror("abread"); + } + return cc; +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[3]; + struct ethernet_addresses ea; +#ifdef PHASE2 + char header[8]; +#endif PHASE2 + int cc; + +#ifdef PHASE2 + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP */ + iov[1].iov_len = sizeof(header); + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; + cc = pi_readv(edx, iov, 3); + return(cc - sizeof(ea) - sizeof(header)); +#else PHASE2 + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +#endif PHASE2 +} + +pi_reada(fd, buf, bufsiz, eaddr) +int fd; +caddr_t buf; +int bufsiz; +char *eaddr; +{ + struct iovec iov[3]; +#ifdef PHASE2 + char header[5]; /* must be 5! */ +#endif PHASE2 + int cc; + +#ifdef PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP but */ + iov[1].iov_len = sizeof(header); /* make room for our fake LAP header */ + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; +#else PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; +#endif PHASE2 + +#ifdef PHASE2 + if ((cc = bp_readv(fd, iov, 3)) < 0) { +#else PHASE2 + if ((cc = bp_readv(fd, iov, 2)) < 0) { +#endif PHASE2 + perror("abread"); + return(cc); + } +#ifdef PHASE2 + /* make a fake LAP header to fool the higher levels */ + buf[0] = buf[11]; /* destination node ID */ + buf[1] = buf[12]; /* source node ID */ + buf[2] = 0x02; /* always long DDP */ + return(cc - 14 - sizeof(header)); +#else PHASE2 + return(cc - 14); +#endif PHASE2 + return(cc - 14); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct ephandle *eph; + struct ether_header eh; + struct sockaddr sa; + struct iovec iov[2]; +#ifdef PHASE2 + char *q; +#endif PHASE2 + + iov[0].iov_base = (caddr_t)&eh; + iov[0].iov_len = sizeof(struct ether_header); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = buflen; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + bcopy(eaddr, &eh.ether_dhost, 6); +#ifdef PHASE2 +#ifdef __FreeBSD__ + /* This should really be fixed in the kernel. */ + eh.ether_type = buflen; +#else + eh.ether_type = htons(buflen); +#endif + /* + * Fill in the remainder of the 802.2 and SNAP header bytes. + * Clients have to leave 8 bytes free at the start of buf as + * NIT won't let us send any more than 14 bytes of header :-( + */ + q = (char *) buf; + *q++ = 0xaa; /* destination SAP */ + *q++ = 0xaa; /* source SAP */ + *q++ = 0x03; /* control byte */ + *q++ = (eph->protocol == 0x809b) ? 0x08 : 0x00; + *q++ = 0x00; /* always zero */ + *q++ = (eph->protocol == 0x809b) ? 0x07 : 0x00; + *q++ = (eph->protocol >> 8) & 0xff; + *q++ = (eph->protocol & 0xff); +#else PHASE2 +#ifdef __FreeBSD__ + /* This should really be fixed in the kernel. */ + eh.ether_type = eph->protocol; +#else + eh.ether_type = htons(eph->protocol); +#endif +#endif PHASE2 + + if (writev(eph->fd, iov, 2) < 0) { + return(-1); + } + return(buflen); +} + +private char ebuf[2000]; /* big enough */ + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + +#ifdef PHASE2 /* leave room for rest of ELAP hdr */ + for (len = 8, p = ebuf+8, i = 0 ; i < iovlen ; i++) +#else PHASE2 + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) +#endif PHASE2 + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} + +#ifdef bsdi +/* + * lifted from UAR pf.c + * + */ + +struct nlist nlst[] = { + { "_ifnet" }, + "", +}; + +/* + * BSDI + * + * Get interface address from the kernel since the SIOCGIFADDR + * ioctl isn't implemented in a number of ethernet drivers. + * + */ + +int +get_eth_addr(s, interface, addr) +int s; +char *interface; +u_char *addr; +{ + kvm_t *kvmd; + int err = -1; + off_t ifnetptr; + off_t aifnetptr; + off_t ifaddrptr; + struct sockaddr sa; + struct ifnet ifnet; + struct ifaddr ifaddr; + struct arpcom arpcom; + struct sockaddr_dl *sdl; + char *cp, if_name[16]; + + if ((kvmd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL) { + fprintf(stderr, "kvm_open: can't open kernel!\n"); + return(-1); + } + if (kvm_nlist(kvmd, nlst) < 0 || nlst[0].n_type == 0) { + fprintf(stderr, "kvm_list: can't find namelist!\n"); + kvm_close(kvmd); + return(-1); + } + if (nlst[0].n_value == 0) { + fprintf(stderr, "kvm_list: _ifnet symbol not defined!\n"); + kvm_close(kvmd); + return(-1); + } + if (kvm_read(kvmd, nlst[0].n_value, (char *)&ifnetptr, + sizeof(ifnetptr)) != sizeof(ifnetptr)) { + fprintf(stderr, "kvm_read: bogus read!\n"); + kvm_close(kvmd); + return(-1); + } + while (ifnetptr) { + aifnetptr = ifnetptr; + if (kvm_read(kvmd, ifnetptr, (char *)&ifnet, + sizeof(ifnet)) == sizeof(ifnet)) { + if (kvm_read(kvmd, (off_t)ifnet.if_name, if_name, + sizeof(if_name)) == sizeof(if_name)) { + ifnetptr = (off_t)ifnet.if_next; + if_name[15] = '\0'; + cp = (char *)index(if_name, '\0'); + sprintf(cp, "%d", ifnet.if_unit); + if (strcmp(if_name, interface) != 0) + continue; + ifaddrptr = (off_t)ifnet.if_addrlist; + while (ifaddrptr) { + if (kvm_read(kvmd, ifaddrptr, (char *)&ifaddr, + sizeof(ifaddr)) == sizeof(ifaddr)) { + ifaddrptr = (off_t)ifaddr.ifa_next; + if (kvm_read(kvmd, (off_t)ifaddr.ifa_addr, (char *)&sa, + sizeof(sa)) == sizeof(sa)) { + if (sa.sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)&sa; + cp = (char *)LLADDR(sdl); + if (sdl->sdl_alen == 0) { + /* no address here, try ethernet driver */ + if (kvm_read(kvmd, aifnetptr, (char *)&arpcom, + sizeof(arpcom)) == sizeof(arpcom)) + cp = (char *)arpcom.ac_enaddr; + } + bcopy(cp, (char *)addr, 6); + fprintf(stderr, "BSDI: %-5s eth address %x:%x:%x:%x:%x:%x\n", + interface, addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5]); + err = 0; + break; + } + continue; + } + } + break; + } + } + } + break; + } + kvm_close(kvmd); + return(err); +} +#endif bsdi diff --git a/support/ethertalk/dlip.c b/support/ethertalk/dlip.c new file mode 100644 index 0000000..f258312 --- /dev/null +++ b/support/ethertalk/dlip.c @@ -0,0 +1,315 @@ +static char rcsid[] = "$Author: djh $ $Date: 91/02/15 23:06:01 $"; +static char rcsident[] = "$Header: dlip.c,v 2.1 91/02/15 23:06:01 djh Rel $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * dlip.c - Simple "protocol" level interface to DLI + * + * Provides ability to read/write packets at ethernet level + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * April 3, 1988 CCKim Created + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "../uab/proto_intf.h" + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int fd; /* file descriptor of socket */ + int protocol; /* ethernet protocol */ + int socket; /* ddp socket */ + struct sockaddr_dl sdli; /* dli interface: to send with */ + struct sockaddr_dl rdli; /* dli interface: to receive with */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +extern char interface[50]; + +/* + * setup for particular device devno + * all pi_open's will go this device + * +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return 0 for okay +*/ +export int +pi_open(protocol, sock, dev, devno) +int protocol; +int sock; +char *dev; +int devno; +{ + struct ephandle *eph; + struct sockaddr_dl *dl; + int s, i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + dl = &eph->sdli; /* point */ + dl->dli_family = AF_DLI; + strcpy(dl->dli_device.dli_devname, dev); + dl->dli_device.dli_devnumber = devno; + dl->dli_substructype = DLI_ETHERNET; + /* update these */ + dl->choose_addr.dli_eaddr.dli_ioctlflg = DLI_EXCLUSIVE; + dl->choose_addr.dli_eaddr.dli_protype = protocol; + + if ((s = socket(AF_DLI, SOCK_DGRAM, DLPROTO_DLI)) < 0) + return(-1); + if (bind(s, dl, sizeof(struct sockaddr_dl)) < 0) { + close(s); + return(-1); + } + bcopy(dl, &eph->rdli, sizeof(struct sockaddr_dl)); + + eph->inuse = TRUE; + eph->fd = s; + eph->protocol = protocol; + eph->socket = sock; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(TRUE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].fd); /* toss listener */ + close(ephlist[edx-1].fd); + ephlist[edx-1].inuse = 0; + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct ifdevea buf; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; /* find handle */ + sprintf(buf.ifr_name, "%s%d",eph->sdli.dli_device.dli_devname, + eph->sdli.dli_device.dli_devnumber); + if (ioctl(eph->fd,SIOCRPHYSADDR, &buf) < 0) { + perror("iotcl"); + return(-1); + } + bcopy(buf.current_pa, ea, DLI_EADDRSIZE); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg, edx); + return(0); +} + +export +pi_listener_2(edx, listener, arg1, arg2) +int edx; +int (*listener)(); +caddr_t arg1; +int arg2; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg1, arg2); +} + + +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct msghdr msg; + int cc; + struct ephandle *eph ; + struct ethernet_addresses *ea; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + msg.msg_iov = iov+1; + msg.msg_iovlen = iovlen-1; + msg.msg_name = (caddr_t)&eph->rdli; + msg.msg_namelen = sizeof(eph->rdli); + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; + if ((cc = recvmsg(eph->fd, &msg, 0)) < 0) { + perror("recvmsg"); + return(cc); + } + ea = (struct ethernet_addresses *)iov[0].iov_base; + ea->etype = eph->protocol; + /* check length -- naw */ + bcopy(eph->rdli.choose_addr.dli_eaddr.dli_target, ea->saddr, EHRD); + bcopy(eph->rdli.choose_addr.dli_eaddr.dli_dest, ea->daddr, EHRD); + return(cc+iov[0].iov_len); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[2]; + struct ethernet_addresses ea; + int cc; + + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +} + +export int +pi_reada(fd, buf, bufsiz, eaddr) +int fd; +caddr_t buf; +int bufsiz; +char *eaddr; +{ + struct iovec iov[2]; + int cc; + + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + + if ((cc = readv(fd, iov, 2)) < 0) { + perror("abread"); + return(cc); + } + return(cc - 14); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct iovec iov[1]; + + iov[0].iov_base = buf; + iov[0].iov_len = buflen; + return(pi_writev(edx, iov, 1, eaddr)); +} + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +char *eaddr; +{ + struct ephandle *eph; + struct msghdr msg; + int cc; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + bcopy(eaddr, eph->sdli.choose_addr.dli_eaddr.dli_target, DLI_EADDRSIZE); + msg.msg_name = (caddr_t)&eph->sdli; + msg.msg_namelen = sizeof(eph->sdli); + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; + cc = sendmsg(eph->fd, &msg, 0); + return(cc); +} diff --git a/support/ethertalk/ethertalk.c b/support/ethertalk/ethertalk.c new file mode 100644 index 0000000..e074ce1 --- /dev/null +++ b/support/ethertalk/ethertalk.c @@ -0,0 +1,519 @@ +static char rcsid[] = "$Author: djh $ $Date: 1994/10/10 08:55:05 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/ethertalk/RCS/ethertalk.c,v 2.6 1994/10/10 08:55:05 djh Rel djh $"; +static char revision[] = "$Revision: 2.6 $"; + +/* + * ethertalk.c - ethertalk interface + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * April 3, 1988 CCKim Created + * April 28, '91 djh Added Phase 2 support + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../uab/proto_intf.h" /* iso: level 0 */ +#include "../uab/ethertalk.h" /* iso: level 1 */ +#include "../uab/aarp.h" /* iso: level 1 */ +#include "../uab/if_desc.h" /* describes "if" */ +#include "../uab/ddpport.h" /* describes a ddp port to "lap" */ +#include "../uab/log.h" + +/* some logging ideas */ +#define LOG_LOG 0 +#define LOG_PRIMARY 1 +#define LOG_LOTS 9 + +export int etalk_init(); +private int etalk_getnode(); /* basis */ +private etalk_initfinish(); + +private int etalk_send_ddp(); +private int etalk_listener(); +private NODE *etalk_ddpnode_to_node(); +private int etalk_stats(); +private int etalk_tables(); + +extern byte this_node; +#ifdef PHASE2 +extern u_short this_net; +#endif PHASE2 + +/* describe our interface to the world */ +private char *ethertalk_lap_keywords[] = { + "ethertalk", + "elap", + NULL + }; + +export struct lap_description ethertalk_lap_description = { + "EtherTalk Link Access Protocol", + ethertalk_lap_keywords, + TRUE, /* need more than just key */ + etalk_init, /* init routine */ + etalk_stats, /* stats routine */ + etalk_tables /* tables */ +}; + + +/* interface statistics */ + +private char *estat_names[] = { +#define ES_PKT_INPUT 0 /* packets input */ + "packets input", +#define ES_PKT_ACCEPTED 1 /* accepted input packets */ + "packets accepted", +#define ES_PKT_BAD 2 /* bad packets */ + "bad packets", +#define ES_PKT_NOTFORME 3 /* not for me packets */ + "packets not for me", +#define ES_BYTES_INPUT 4 /* accepted bytes */ + "bytes input", +#define ES_ERR_INPUT 5 /* number of input errors */ + "input errors", +#define ES_PKT_NOHANDLER 6 /* no handler */ + "packets without handlers", +#define ES_PKT_OUTPUT 7 /* packets output */ + "packets output", +#define ES_BYTES_OUTPUT 8 /* bytes output */ + "bytes output", +#define ES_ERR_OUTPUT 9 /* output errors */ + "output errors", +#define ES_RESOLVE_ERR_OUTPUT 10 /* could not resolvve */ + "output resolve error" +#define ES_NUM_COUNTERS 11 +}; + +typedef struct ethertalk_handle { + int eh_state; /* this connection state */ +#define ELAP_WAITING -1 +#define ELAP_BAD 0 /* error */ +#define ELAP_READY 1 /* okay */ + PORT_T eh_port; /* ethertalk port */ + int eh_ph; /* ethertalk protocol handle */ + caddr_t eh_ah; /* aarp module handle */ + NODE eh_enode; /* host node id */ + IDESC_TYPE *eh_id; /* interface description */ + int eh_stats[ES_NUM_COUNTERS]; /* statistics */ +} E_HANDLE; + + +/* + * call with provisional network number, interface name and number + * + * provisional number should be 0 if not seeding + * +*/ +export int +etalk_init(id, async) +IDESC_TYPE *id; +int async; +{ + E_HANDLE *eh; + int hostid; + + + if ((eh = (E_HANDLE *)malloc(sizeof(E_HANDLE))) == NULL) + return(FALSE); + + pi_setup(); + + /* init for a single node */ + eh->eh_ah = (caddr_t)aarp_init(id->id_intf, id->id_intfno, 1); + if (eh->eh_ah == NULL) { + free(eh); + return(FALSE); + } + /* link in both directions */ + id->id_ifuse = (caddr_t)eh; /* mark */ + eh->eh_id = id; /* remember this */ + + eh->eh_state = ELAP_WAITING; /* mark waiting */ + + /* acquire node address */ +#ifdef PHASE2 + if ( this_node <= 0 || this_node >= 254) +#else PHASE2 + if ( this_node <= 0 || this_node >= 255) +#endif PHASE2 + hostid = 0xff & gethostid(); /* use last byte of hostid as hint */ + else + hostid = this_node; + + if (etalk_getnode(eh, hostid, etalk_initfinish) < 0) { + free(eh); + return(FALSE); + } + + if (async) /* async means to stop early */ + return(TRUE); + + /* wait for node acquisition? */ + while (eh->eh_state == ELAP_WAITING) + abSleep(10, TRUE); + + return(eh->eh_state == ELAP_READY); /* true if okay, 0 o.w. */ +} + +/* + * try to acquire an ethertalk host node address using hint as the basis + * callback to who (cbarg is E_HANDLE, result where -1 if address in use + * host node address index o.w.) + * +*/ +private int +etalk_getnode(eh, hint, who) +E_HANDLE *eh; +int hint; +int (*who)(); +{ + struct ethertalkaddr pa; + int n; + +#ifdef PHASE2 + pa.dummy[0] = 0; /* always zero */ + pa.dummy[1] = (ntohs(this_net) >> 8) & 0xff; + pa.dummy[2] = (ntohs(this_net) & 0xff); +#else PHASE2 + pa.dummy[0] = pa.dummy[1] = pa.dummy[2] = 0; +#endif PHASE2 + pa.node = hint; + while ((n=aarp_acquire_etalk_node(eh->eh_ah, &pa, who, eh)) != 0) { + if (n < 0) { + /* error */ + /* clean up */ + return(-1); + } + pa.node++; /* try next */ + } + return(0); +} + +/* + * finish off the init + * +*/ +private +etalk_initfinish(eh, result) +E_HANDLE *eh; +int result; +{ + PORT_T eh_port; /* ethertalk port */ + struct ethertalkaddr pa; + int flags; + int nodesize; + IDESC_TYPE *id = eh->eh_id; /* get interface description */ + + if (result < 0) { +#ifdef PHASE2 + if ((result = etalk_getnode(eh,(rand()%253)+1, etalk_initfinish)) < 0) { +#else PHASE2 + if ((result = etalk_getnode(eh,(rand()%254)+1, etalk_initfinish)) < 0) { +#endif PHASE2 + logit(LOG_LOG, "could not acquire node on interface %s%d", + id->id_intf, id->id_intfno); + eh->eh_state = ELAP_BAD; + } + return; + } + + if ((nodesize = aarp_get_host_addr(eh->eh_ah, &pa, result)) < 0) { + logit(LOG_PRIMARY, "aarp get host node address failed for %d", result); + logit(LOG_PRIMARY, "interface %s%d can't be intialized", + id->id_intf, id->id_intfno); + eh->eh_state = ELAP_BAD; /* mark bad */ + return; + } + eh->eh_enode.n_size = 8*nodesize; /* 8 or 32 bits */ + eh->eh_enode.n_bytes = nodesize; /* 1 or 4 bytes */ +#ifdef PHASE2 + eh->eh_enode.n_id[0] = pa.dummy[0]; + eh->eh_enode.n_id[1] = pa.dummy[1]; + eh->eh_enode.n_id[2] = pa.dummy[2]; + eh->eh_enode.n_id[3] = pa.node; +#else PHASE2 + eh->eh_enode.n_id[0] = pa.node; /* this is it */ +#endif PHASE2 + + flags = PORT_WANTSLONGDDP; + if (!pi_delivers_self_broadcasts()) + flags |= PORT_NEEDSBROADCAST; + if (id->id_isabridge) + flags |= PORT_FULLRTMP; + + eh->eh_state = ELAP_READY; + logit(LOG_PRIMARY,"acquired node %d on interface %s%d", + pa.node, id->id_intf, id->id_intfno); +} + +export u_char *etalk_resolve(id, node) +IDESC_TYPE *id; +int node; +{ + E_HANDLE *eh; + u_char *eaddr; + struct ethertalkaddr tpa; + + eh = (E_HANDLE *)id->id_ifuse; + +#ifdef PHASE2 + bcopy(&node, &tpa, ETPL); +#else PHASE2 + tpa.dummy[0] = tpa.dummy[1] = tpa.dummy[2] = 0; + tpa.node = ntohl(node); +#endif PHASE2 + + if (aarp_resolve(eh->eh_ah, &tpa, tpa.node == 0xff, &eaddr) <= 0) { +#ifdef PHASE2 + logit (2, "etalk_resolve: node %d/%d.%d try again later", + tpa.node, tpa.dummy[1], tpa.dummy[2]); +#else PHASE2 + logit (2, "etalk_resolve: node %d try again later", tpa.node); +#endif PHASE2 + return(NULL); + } + return(eaddr); +} + +#ifdef PHASE2 +export int etalk_set_mynode(id, eaddr) +IDESC_TYPE *id; +struct ethertalkaddr *eaddr; +{ + E_HANDLE *eh; + int hostid = eaddr->node; + + eh = (E_HANDLE *)id->id_ifuse; + return(aarp_set_host_addr(eh->eh_ah, eaddr)); +} +#endif PHASE2 + +export struct ethertalkaddr *etalk_mynode(id) +IDESC_TYPE *id; +{ + E_HANDLE *eh; + static struct ethertalkaddr ea; + + eh = (E_HANDLE *)id->id_ifuse; +#ifdef PHASE2 + ea.dummy[0] = eh->eh_enode.n_id[0]; + ea.dummy[1] = eh->eh_enode.n_id[1]; + ea.dummy[2] = eh->eh_enode.n_id[2]; + ea.node = eh->eh_enode.n_id[3]; +#else PHASE2 + ea.dummy[0] = 0; + ea.dummy[1] = 0; + ea.dummy[2] = 0; + ea.node = eh->eh_enode.n_id[0]; +#endif PHASE2 + return (&ea); +} + +/* + * resolve a ddp node number to a node address on the specified port + * (note: do we need more information in some cases?) +*/ +private NODE * +etalk_ddpnode_to_node(port, ddpnet, ddpnode) +PORT_T port; +word ddpnet; +byte ddpnode; +{ +#ifdef PHASE2 + static NODE node = { 4, 32}; /* initialize */ +#else PHASE2 + static NODE node = { 1, 8 }; /* initialize */ +#endif PHASE2 + int myddpnet = PORT_DDPNET(port); + + if (ddpnet != 0 && myddpnet != ddpnet) /* only allow this net! */ + return(NULL); + +#ifdef PHASE2 + node.n_id[0] = 0; + node.n_id[1] = (ntohs(ddpnet) >> 8) & 0xff; + node.n_id[2] = (ntohs(ddpnet) & 0xff); + node.n_id[3] = ddpnode; +#else PHASE2 + node.n_id[0] = ddpnode; /* make node */ +#endif PHASE2 + + return(&node); +} + +/* resolve a node to a ddp node (do we want this?) */ +/* think we will need it + one that resolves it to a net in the future ? */ +private byte +etalk_node_to_ddpnode(port, node) +PORT_T port; +NODE *node; +{ + if (node->n_size == 8) /* 8 bits? */ + return(node->n_id[0]); +#ifdef PHASE2 + if (node->n_size == 32) /* 32 bits? */ + return(node->n_id[3]); +#endif PHASE2 + return(0); +} + + +/* + * send a ddp packet on ethertalk + * (should we convert short to long ddp?) + * + * port = port to send on + * dstnode == destination ethertalk node + * laptype == laptype of packet (header) + * header = packet header (for laptype) + * hsize = packet header length + * data = data + * dlen = datalength +*/ +private +etalk_send_ddp(port, dstnode, laptype, header, hsize, data, dlen) +PORT_T port; +NODE *dstnode; +int laptype; +byte *header; +int hsize; +u_char *data; +int dlen; +{ + struct iovec iov[3]; + u_char *eaddr; + LAP lap; + struct ethertalk_handle *eh = PORT_GETLOCAL(port, struct ethertalk_handle *); + struct ethertalkaddr tpa; + int *stats = eh->eh_stats; + int i; + + if (eh->eh_state != ELAP_READY) { /* drop */ + stats[ES_ERR_OUTPUT]++; + return(-1); + } + if (dstnode == NULL) { /* can't! */ + stats[ES_ERR_OUTPUT]++; /* can't */ + return(-1); + } + + /* should be higher? */ + if (dstnode->n_size != eh->eh_enode.n_size) { /* for now? */ + stats[ES_ERR_OUTPUT]++; /* can't */ + return(-1); + } + lap.type = laptype; +#ifdef PHASE2 + tpa.dummy[0] = dstnode->n_id[0]; + tpa.dummy[1] = dstnode->n_id[1]; + tpa.dummy[2] = dstnode->n_id[2]; + tpa.node = dstnode->n_id[3]; + lap.dst = dstnode->n_id[3]; /* get dest node */ + /* source is always us! */ + lap.src = eh->eh_enode.n_id[3]; /* get source node */ +#else PHASE2 + tpa.dummy[0] = tpa.dummy[1] = tpa.dummy[2] = 0; + tpa.node = dstnode->n_id[0]; + lap.dst = dstnode->n_id[0]; /* get dest node */ + /* source is always us! */ + lap.src = eh->eh_enode.n_id[0]; /* get source node */ +#endif PHASE2 + + if (aarp_resolve(eh->eh_ah, &tpa, lap.dst == 0xff, &eaddr) <= 0) { + stats[ES_RESOLVE_ERR_OUTPUT]++; + return(-1); + } +#ifdef PHASE2 + /* delete LAP hdr */ + iov[0].iov_len = 0; +#else PHASE2 + iov[0].iov_len = lapSize; +#endif PHASE2 + iov[0].iov_base = (caddr_t)⪅ + iov[1].iov_len = hsize; + iov[1].iov_base = (caddr_t)header; + iov[2].iov_len = dlen; + iov[2].iov_base = (caddr_t)data; + if ((i = pi_writev(eh->eh_ph, iov, (dlen == 0) ? 2 : 3, eaddr)) < 0) { + stats[ES_ERR_OUTPUT]++; + return(i); + } + stats[ES_PKT_OUTPUT]++; + stats[ES_BYTES_OUTPUT] += i; + return(i); +} + + +private int +etalk_stats(fd, id) +FILE *fd; +IDESC_TYPE *id; +{ + E_HANDLE *eh = (E_HANDLE *)id->id_ifuse; /* get handle */ + int i; + + fprintf(fd, "Interface %s%d statisitics\n", id->id_intf, + id->id_intfno); + fprintf(fd, " Interface counters\n"); + for (i = 0; i < ES_NUM_COUNTERS; i++) { + fprintf(fd, " %8d\t%s\n", eh->eh_stats[i], estat_names[i]); + } + putc('\n', fd); /* carriage return */ + /* call up aarp too */ + aarp_dump_stats(fd, eh->eh_ah); + putc('\n', fd); /* finish */ +} + +private int +etalk_tables(fd, id) +FILE *fd; +IDESC_TYPE *id; +{ + E_HANDLE *eh = (E_HANDLE *)id->id_ifuse; /* get handle */ + + fprintf(fd, "Interface dump for %s%d\n",id->id_intf, id->id_intfno); + aarp_dump_tables(fd, eh->eh_ah); + putc('\n', fd); +} + +#ifdef SOLARIS +#include + +int +gethostid() +{ + char buf[32]; + + sysinfo(SI_HW_SERIAL, buf, sizeof(buf)-2); + + return(atoi(buf)); +} +#endif SOLARIS diff --git a/support/ethertalk/makefile b/support/ethertalk/makefile new file mode 100644 index 0000000..17961cd --- /dev/null +++ b/support/ethertalk/makefile @@ -0,0 +1,75 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 14:00:26 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS= -O -DSHORT_NAMES -DMELBOURNE +DESTDIR=/usr/local/cap +PROGS= +POBJS= +ETCDIR=/etc +CAPLIB=-lcap + +LIBABSRCS=abelap.c ethertalk.c ../uab/aarp.c ../uab/hash.c +LIBABOBJS=abelap.o ethertalk.o aarp.o hash.o + +# +# abetalk.o provides EtherTalk support for CAP +# +all: ${PROGS} + +abetalk.o: ${LIBABOBJS} ${POBJS} aarpd_clnt.o aarpd_xdr.o aarpd.h + ld -r -o abetalk.o ${LIBABOBJS} ${POBJS} aarpd_clnt.o aarpd_xdr.o + +aarpd: aarpd.o aarpd_svc.o aarpd.h + cc -o aarpd aarpd.o aarpd_svc.o ${CAPLIB} + +aarptest: aarptest.o aarpd.h + cc -o aarptest aarptest.o ${CAPLIB} + +rtmptest: rtmptest.o aarpd.h + cc -o rtmptest rtmptest.o ${CAPLIB} + +aarpd.o: aarpd.c aarpd.h + +abelap.o: abelap.c + +ethertalk.o: ethertalk.c ../uab/ethertalk.h + +snitp.o: snitp.c ../uab/proto_intf.h + +senetp.o: senetp.c ../uab/proto_intf.h + +aarpd_clnt.o: aarpd_clnt.c aarpd.h + +aarpd_svc.o: aarpd_svc.c aarpd.h + +aarpd_xdr.o: aarpd_xdr.c aarpd.h + +aarp.o: ../uab/aarp.c ../uab/hash.h ../uab/proto_intf.h \ + ../uab/ethertalk.h ../uab/aarp_defs.h ../uab/aarp.h + cc $(CFLAGS) -DAARPD -c ../uab/aarp.c + +hash.o: ../uab/hash.c ../uab/hash.h + cc $(CFLAGS) -c ../uab/hash.c + +aarptest.o: aarptest.c aarpd.h + +rtmptest.o: rtmptest.c aarpd.h + +install: ${PROGS}.install + +.install: + +aarpd.install: aarpd + -strip aarpd + ${INSTALLER} aarpd ${DESTDIR} + +clean: + rm -f *.o core aarpd aarptest rtmptest diff --git a/support/ethertalk/rangetest.c b/support/ethertalk/rangetest.c new file mode 100644 index 0000000..76b3f45 --- /dev/null +++ b/support/ethertalk/rangetest.c @@ -0,0 +1,85 @@ +/* + * simple RPC net range test + * + * Created: David Hornsby, Melbourne University + * + */ + +#include +#include +#include +#include +#include +#include "aarpd.h" + +#ifdef ultrix +#define ALT_RPC +#include +#include +#endif ultrix +#ifdef pyr +#define ALT_RPC +#endif pyr + +extern u_char *rtmp_getbaddr_clnt(); +extern u_char *rtmp_setbaddr_clnt(); + +extern u_short this_net, bridge_net, nis_net, async_net; +extern u_char this_node, bridge_node, nis_node; +extern char this_zone[34], async_zone[34], interface[50]; + +extern u_short net_range_start, net_range_end; + +main(argc, argv) +int argc; +char **argv; +{ + CLIENT *cl; + u_char *addr; + u_short r_start, r_end; + unsigned long range; +#ifdef ALT_RPC + int sock; + struct timeval tv; + struct sockaddr_in sin; +#endif ALT_RPC + +#ifdef ALT_RPC + sin.sin_family = AF_INET; + sin.sin_port = 0; + bzero(sin.sin_zero, sizeof(sin.sin_zero)); + sin.sin_addr.s_addr = htonl(0x7f000001); + sock = RPC_ANYSOCK; + tv.tv_sec = 5; + tv.tv_usec = 0; + cl = clntudp_create(&sin, AARPDPROG, AARPDVERS, tv, &sock); +#else ALT_RPC + cl = clnt_create("localhost", AARPDPROG, AARPDVERS, "udp"); +#endif ALT_RPC + if (cl == NULL) { + clnt_pcreateerror("localhost"); + exit(1); + } + + if (argc == 3) { + r_start = htons(atoi(argv[1])); + r_end = htons(atoi(argv[2])); + range = r_start & 0xffff; + range <<= 16; + range |= r_end & 0xffff; + addr = (u_char *) range_set_clnt(&range, cl); + if (addr == NULL) { + clnt_perror(cl, "localhost"); + exit(1); + } + printf("set net range: %d - %d\n", ntohs(r_start), ntohs(r_end)); + } + + openetalkdb(NULL); + + printf("zone %s this %d.%d gw %d.%d nis %d.%d intf %s\n", + this_zone, ntohs(this_net), this_node, ntohs(bridge_net), + bridge_node, ntohs(nis_net), nis_node, interface); + printf("range start %d, range end %d\n", ntohs(net_range_start), + ntohs(net_range_end)); +} diff --git a/support/ethertalk/rtmptest.c b/support/ethertalk/rtmptest.c new file mode 100644 index 0000000..17ba7ab --- /dev/null +++ b/support/ethertalk/rtmptest.c @@ -0,0 +1,83 @@ +/* + * simple RPC RTMP test + * + * Created: David Hornsby, Melbourne University + * + */ + +#include +#include +#include +#include +#include +#include "aarpd.h" + +#ifdef ultrix +#define ALT_RPC +#include +#include +#endif ultrix +#ifdef pyr +#define ALT_RPC +#endif pyr + +extern u_char *rtmp_getbaddr_clnt(); +extern u_char *rtmp_setbaddr_clnt(); + +extern u_short this_net, bridge_net, nis_net, async_net; +extern u_char this_node, bridge_node, nis_node; +extern char this_zone[34], async_zone[34], interface[50]; + +main(argc, argv) +int argc; +char **argv; +{ + CLIENT *cl; + u_char *addr; + AddrBlock baddr; +#ifdef ALT_RPC + int sock; + struct timeval tv; + struct sockaddr_in sin; +#endif ALT_RPC + +#ifdef ALT_RPC + sin.sin_family = AF_INET; + sin.sin_port = 0; + bzero(sin.sin_zero, sizeof(sin.sin_zero)); + sin.sin_addr.s_addr = htonl(0x7f000001); + sock = RPC_ANYSOCK; + tv.tv_sec = 5; + tv.tv_usec = 0; + cl = clntudp_create(&sin, AARPDPROG, AARPDVERS, tv, &sock); +#else ALT_RPC + cl = clnt_create("localhost", AARPDPROG, AARPDVERS, "udp"); +#endif ALT_RPC + if (cl == NULL) { + clnt_pcreateerror("localhost"); + exit(1); + } + + if (argc == 3) { + baddr.net = htons(atoi(argv[1])); + baddr.node = atoi(argv[2]); + addr = rtmp_setbaddr_clnt(&baddr, cl); + if (addr == NULL) { + clnt_perror(cl, "localhost"); + exit(1); + } + printf("set bridge addr %x.%x %x %x\n", addr[0], addr[1], addr[2], addr[3]); + addr = rtmp_getbaddr_clnt(&baddr, cl); + if (addr == NULL) { + clnt_perror(cl, "localhost"); + exit(1); + } + printf("get bridge addr %x.%x %x %x\n", addr[0], addr[1], addr[2], addr[3]); + } + + openetalkdb(NULL); + + printf("zone %s this %d.%d gw %d.%d nis %d.%d intf %s\n", + this_zone, ntohs(this_net), this_node, ntohs(bridge_net), + bridge_node, ntohs(nis_net), nis_node, interface); +} diff --git a/support/ethertalk/sdlpi.c b/support/ethertalk/sdlpi.c new file mode 100644 index 0000000..a35aa1d --- /dev/null +++ b/support/ethertalk/sdlpi.c @@ -0,0 +1,986 @@ +/* + * sdlpi.c - Simple "protocol" level interface to Streams based DLPI + * (SunOS 5.x) (derived from snitp.c SunOS 4.x module) + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * July 1988 CCKim Created + * April '91 djh Add Phase 2 support + * May-June 93 montjoy@thor.ece.uc.EDU, + * appro@fy.chalmers.se SunOS 5.x support + * + */ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "../uab/proto_intf.h" + +#define IEEE802_2 16 /* IEEE 802.2 SAP field */ + /* actually 0 < any_number < 1500 */ + +private int init_nit(); +private int stream_readv (); + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int fd; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ + int socket; /* ddp socket */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; +private int setup_pf(); + +extern char interface[50]; + +#ifdef PHASE2 +extern char this_zone[34]; +private u_char *zone_mcast(); +private int zip_toupper(); +private u_short chkSum(); +#endif + +/* + * setup for particular device devno + * all pi_open's will go this device + * + */ + +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(FALSE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay + * + */ + +export int +pi_open(protocol, socket, dev, devno) +int protocol; +int socket; +char *dev; +int devno; +{ + int s, i; + struct ephandle *eph; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + strncpy(eph->ifr.ifr_name, interface, sizeof eph->ifr.ifr_name); + + if ((s = init_nit(1024, protocol, socket, &eph->ifr)) < 0) + return(-1); + + eph->inuse = TRUE; + eph->fd = s; + eph->protocol = protocol; + eph->socket = socket; + return(i+1); /* skip zero */ +} + +/* + * returns TRUE if machine will see own broadcasts + * + */ + +export int +pi_delivers_self_broadcasts() +{ + return(FALSE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].fd); /* toss listener */ + close(ephlist[edx-1].fd); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize nit on a particular protocol type + * + * Return: socket if no error, < 0 o.w. + * + */ + +private int +init_nit(chunksize, protocol, socket, ifr) +u_long chunksize; +u_short protocol; +int socket; +struct ifreq *ifr; +{ + int s,devno = 0; + char *p; + char device[64]; +#ifdef PHASE2 + u_char e_broad[6] = {0x09, 0x00, 0x07, 0xff, 0xff, 0xff}; + u_char *zmulti; +#endif PHASE2 + + sprintf(device, "/dev/%s", interface); + for (p = device; *p != '\0'; p++) { + if (*p >= '0' && *p <= '9') { + devno = atoi(p); + *p = '\0'; + break; + } + } + + if ((s = open(device, O_RDWR)) < 0) { + perror(device); + return(-1); + } + + + if(!AttachDevice(s,devno)) + return(-1); +#ifdef PHASE2 + if(!BindProtocol(s,IEEE802_2,0,DL_CLDLS, 0, 0 )) + return(-1); +#else PHASE2 + if(!BindProtocol(s,protocol,0,DL_CLDLS, 0, 0 )) + return(-1); +#endif PHASE2 + + /*if(!PromMode(s,DL_PROMISC_MULTI)) + return(-1);*/ + +#ifdef PHASE2 + if((zmulti = zone_mcast(this_zone, strlen(this_zone))) == NULL) + { + fprintf(stderr, "Unable to get Zone Multicast Address\n"); + return(-1); + } + if(!AddMultiAddress(e_broad,s)) + return(-1); + if(!AddMultiAddress(zmulti,s)) + return(-1); +#endif PHASE2 + /* warning: Sun specific */ + if (strioctl (s, DLIOCRAW, -1, 0, NULL) < 0) { + perror("DLIORAW"); + return(-1); + } + + /* set up messages */ + if (ioctl(s, I_SRDOPT, (char *)RMSGD) < 0) { /* want messages */ + perror("ioctl: discretem"); + return(-1); + } + + + if (setup_pf(s, protocol, socket) < 0) + return(-1); +#define NOBUF +#ifndef NOBUF + if( setup_buf(s, chunksize) < 0) + return(-1); +#endif NOBUF + + /* flush read queue */ + ioctl(s, I_FLUSH, (char *)FLUSHR); + + return(s); +} + +#ifdef PHASE2 +/* + * add a multicast address to the interface + * + */ + +int +pi_addmulti(multi, ifr) +u_char *multi; +struct ifreq *ifr; +{ + /* + * multicast addresses are per-stream now + * so just a NO OP + * + */ + + return(0); +} + +#endif PHASE2 + + +#ifndef NOBUF +/* + * setup buffering + * + */ + +setup_buf(s, chunksize) +int s; +u_long chunksize; +{ + struct timeval timeout; + + /* Push and configure the buffering module. */ + if (ioctl(s, I_PUSH, "bufmod") < 0) { + perror("ioctl: nbuf"); + return(-1); + } + timeout.tv_sec = 1; + timeout.tv_usec = 0; + if(strioctl (s, SBIOCSTIME, INFTIM, sizeof(timeout), (char *)&timeout) < 0){ + perror("ioctl: timeout"); + return(-1); + } + + if(strioctl (s,SBIOCSCHUNK,INFTIM,sizeof(chunksize),(char *)&chunksize) < 0){ + perror("ioctl: chunk size"); + return(-1); + } + return(0); +} +#endif + +/* + * establish protocol filter + * + * + */ + +private int +setup_pf(s, prot, sock) +int s; +u_short prot; +int sock; +{ + u_short offset; + struct ether_header eh; + struct packetfilt pf; + register u_short *fwp = pf.Pf_Filter; + +#define s_offset(structp, element) (&(((structp)0)->element)) + offset = ((int)s_offset(struct ether_header *, ether_type))/sizeof(u_short); +#ifdef PHASE2 + offset += 4; /* shorts: 2 bytes length + 6 bytes of 802.2 and SNAP */ +#endif PHASE2 + *fwp++ = ENF_PUSHWORD + offset; + *fwp++ = ENF_PUSHLIT | (sock >= 0 ? ENF_CAND : ENF_EQ); + *fwp++ = htons(prot); + pf.Pf_FilterLen = 3; + if (sock >= 0) { +#ifdef PHASE2 + *fwp++ = ENF_PUSHWORD + offset + 6; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have dest socket */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((sock & 0xff) << 8); +/* if not wanted, fail it */ + *fwp++ = ENF_PUSHLIT ; + *fwp++ = 0; + pf.Pf_FilterLen += 7; +#else PHASE2 +/* short form */ + *fwp++ = ENF_PUSHWORD + offset + 2; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have lap type in LH */ + *fwp++ = ENF_PUSHWORD + offset + 3; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0x00ff); /* now have dest in RH */ + *fwp++ = ENF_NOPUSH | ENF_OR; /* now have lap,,dest */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((1 << 8) | (sock & 0xff)); +/* long form */ + *fwp++ = ENF_PUSHWORD + offset + 2; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have lap type in LH */ + *fwp++ = ENF_PUSHWORD + offset + 7; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0x00ff); /* now have dest in RH */ + *fwp++ = ENF_NOPUSH | ENF_OR; /* now have lap,,dest */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((2 << 8) | (sock & 0xff)); +/* if neither, fail it */ + *fwp++ = ENF_PUSHLIT ; + *fwp++ = 0; + pf.Pf_FilterLen += 20; +#endif PHASE2 + } + + if (ioctl(s, I_PUSH, "pfmod") < 0) { + perror("ioctl: push protocol filter"); + return(-1); + } + if(strioctl (s, PFIOCSETF, 10, sizeof(pf), (char *)&pf) < 0) { + perror("ioctl: protocol filter"); + return(-1); + } + return(0); +} + +private u_char my_eaddr [EHRD]; +private int my_eaddr_valid = 0; + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + char buffer[120]; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + if (!my_eaddr_valid) { /* take it once */ + eph = &ephlist[edx-1]; + if((GetEthernetAddress(buffer,eph->fd)) < 0) + return(-1); + + my_eaddr_valid = 1; + memcpy(my_eaddr, buffer, EHRD); + } + if (ea) memcpy(ea, buffer, EHRD); + return(1); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg, edx); +} + +export +pi_listener_2(edx, listener, arg1, arg2) +int edx; +int (*listener)(); +caddr_t arg1; +int arg2; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg1, arg2); +} + + +/* + * cheat - iov[0] == struct etherheader + * + * + */ + +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc=stream_readv(eph->fd, iov, iovlen)) < 0) + perror ("pi_readv"); + return (cc); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[3]; + struct ethernet_addresses ea; +#ifdef PHASE2 + char header[8]; +#endif PHASE2 + int cc; + +#ifdef PHASE2 + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP */ + iov[1].iov_len = sizeof(header); + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; + cc = pi_readv(edx, iov, 3); + return(cc - sizeof(ea) - sizeof(header)); +#else PHASE2 + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +#endif PHASE2 +} + +pi_reada(fd, buf, bufsiz, eaddr) +int fd; +caddr_t buf; +int bufsiz; +char *eaddr; +{ + struct iovec iov[3]; +#ifdef PHASE2 + char header[5]; /* must be 5! */ +#endif PHASE2 + int cc; + +#ifdef PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP but */ + iov[1].iov_len = sizeof(header); /* make room for our fake LAP header */ + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; +#else PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; +#endif PHASE2 + +#ifdef PHASE2 + if ((cc = stream_readv(fd, iov, 3)) < 0) { +#else PHASE2 + if ((cc = stream_readv(fd, iov, 2)) < 0) { +#endif PHASE2 + perror("pi_reada"); + return(cc); + } +#ifdef PHASE2 + /* make a fake LAP header to fool the higher levels */ + buf[0] = buf[11]; /* destination node ID */ + buf[1] = buf[12]; /* source node ID */ + buf[2] = 0x02; /* always long DDP */ + return(cc - 14 - sizeof(header)); +#else PHASE2 + return(cc - 14); +#endif PHASE2 +} + +private u_char buf[2048]; + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int ret,len; + struct ephandle *eph; + struct strbuf dbuf; + u_char *bufp; + struct ether_header *eh; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + if (!my_eaddr_valid) pi_get_ethernet_address (edx,NULL); + + eh = (struct ether_header *)buf; + bufp = buf + sizeof (struct ether_header); + memcpy (&eh->ether_dhost, eaddr, sizeof(eh->ether_dhost)); + memcpy (&eh->ether_shost, my_eaddr, sizeof(eh->ether_shost)); +#ifndef PHASE2 + eh->ether_type = htons(eph->protocol); +#else PHASE2 + /* + * Fill in the remainder of the 802.2 and SNAP header bytes. + */ + *bufp++ = 0xaa; /* destination SAP */ + *bufp++ = 0xaa; /* source SAP */ + *bufp++ = 0x03; /* control byte */ + *bufp++ = (eph->protocol == 0x809b) ? 0x08 : 0x00; + *bufp++ = 0x00; /* always zero */ + *bufp++ = (eph->protocol == 0x809b) ? 0x07 : 0x00; + *bufp++ = (eph->protocol >> 8) & 0xff; + *bufp++ = (eph->protocol & 0xff); +#endif PHASE2 + /* assemble a packet */ + for (ret=0,len=bufp-buf;iovlen;iovlen--,iov++) + if (iov->iov_base && iov->iov_len >= 0) { + memcpy(bufp, iov->iov_base, iov->iov_len); + bufp += iov->iov_len; + len += iov->iov_len; + ret += iov->iov_len; + } +#ifdef PHASE2 + eh->ether_type = htons(ret+8); /* see below */ +#endif + dbuf.len = len; + dbuf.buf = (caddr_t)buf; + + if (putmsg(eph->fd, NULL, &dbuf, 0) < 0) { + return(-1); + } + return(ret); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = buflen; +#ifdef PHASE2 + iov.iov_base += 8; /* what a bad design */ + iov.iov_len -= 8; /* but not my fault anyway */ +#endif PHASE2 + return (pi_writev (edx,&iov,1,eaddr)); +} + +/* handy functions */ + +private int +stream_readv (fd, iov, iovlen) +int fd; +struct iovec *iov; +int iovlen; +{ + int cc,flag=0,left,bytes; + char *bufp,buf [2048]; + struct strbuf dat_ctl; + + dat_ctl.maxlen = sizeof (buf); + dat_ctl.buf = buf; + if ((cc=getmsg (fd,NULL,&dat_ctl,&flag)) >= 0) + for (cc=0,bufp=dat_ctl.buf,left=dat_ctl.len; + iovlen && left>0;iovlen--,iov++) + { bytes = (iov->iov_leniov_len : left; + memcpy (iov->iov_base,bufp,bytes); + cc += bytes; /* bytes read */ + bufp += bytes; + left -= bytes; /* bytes left */ + } + return(cc); +} + +strioctl(fd, cmd, timout, len, dp) +int fd; +int cmd; +int timout; +int len; +char *dp; +{ + struct strioctl sioc; + int rc; + + sioc.ic_cmd = cmd; + sioc.ic_timout = timout; + sioc.ic_len = len; + sioc.ic_dp = dp; + rc = ioctl (fd, I_STR, &sioc); + + if (rc < 0) + return (rc); + else + return (sioc.ic_len); +} + +/* + * DLPI Support Routines + * + */ + +Acknoledge (dlp_p,ack,msg) +union DL_primitives *dlp_p; +int ack; +char *msg; +{ + if (dlp_p->dl_primitive != ack) { + fprintf(stderr,"dlpi: %s is nacked.\n",msg); + if (dlp_p->dl_primitive == DL_ERROR_ACK) + fprintf(stderr, "dlpi: dlpi_errno %d\n" + "dlpi: unix_errno %d\n", + dlp_p->error_ack.dl_errno, + dlp_p->error_ack.dl_unix_errno); + else + fprintf(stderr,"dlpi: spiritual primitive %d.\n", + dlp_p->dl_primitive); + return(0); + } + return(1); +} + +AttachDevice(fd,devno) +int fd,devno; +{ + int retval; + int flags = RS_HIPRI; + struct strbuf ctlbuf; + union DL_primitives rcvbuf; + dl_attach_req_t Request; + + + /* bind to underlying interface */ + Request.dl_primitive = DL_ATTACH_REQ; + Request.dl_ppa = devno; + ctlbuf.len = sizeof(Request); + ctlbuf.buf = (caddr_t)&Request; + + if (putmsg(fd, &ctlbuf ,NULL,0) < 0) { + perror("Attach Device:"); + return(0); + } + + ctlbuf.maxlen = sizeof(union DL_primitives); + ctlbuf.len = 0; + ctlbuf.buf = (char *)&rcvbuf; + if ((retval = getmsg(fd, &ctlbuf ,NULL, &flags)) < 0) { + perror("Attach Device ack!"); + return(0); + } + + return (Acknoledge(&rcvbuf,DL_OK_ACK,"DL_ATTACH_REQ")); +} + +BindProtocol(fd,sap,max_conind,service_mode, conn_mgmt, xidtest_flg ) +int fd,sap,max_conind,service_mode, conn_mgmt, xidtest_flg ; +{ + int retval; + int flags = RS_HIPRI; + struct strbuf ctlbuf; + union DL_primitives rcvbuf; + dl_bind_req_t BindRequest; + + + BindRequest.dl_primitive = DL_BIND_REQ; + BindRequest.dl_sap = sap; + BindRequest.dl_max_conind = max_conind; + BindRequest.dl_service_mode = service_mode; + BindRequest.dl_conn_mgmt = conn_mgmt; + BindRequest.dl_xidtest_flg = xidtest_flg; + + ctlbuf.len = sizeof(BindRequest); + ctlbuf.buf = (caddr_t)&BindRequest; + + if (putmsg(fd, &ctlbuf ,NULL,0) < 0) { + perror("Bind Protocol:"); + return(0); + } + + ctlbuf.maxlen = sizeof(union DL_primitives); + ctlbuf.len = 0; + ctlbuf.buf = (char *)&rcvbuf; + if ((retval = getmsg(fd, &ctlbuf ,NULL, &flags)) < 0) { + perror("Bind Protocol ACK!"); + return(0); + } + + return (Acknoledge(&rcvbuf,DL_BIND_ACK,"DL_BIND_REQ")); +} + +PromMode(fd,level) +int fd,level; +{ + int retval; + int flags = RS_HIPRI; + struct strbuf ctlbuf; + union DL_primitives rcvbuf; + dl_promiscon_req_t PromRequest; + + + PromRequest.dl_primitive = DL_PROMISCON_REQ; + PromRequest.dl_level = level; + + ctlbuf.len = sizeof(PromRequest); + ctlbuf.buf = (caddr_t)&PromRequest; + + if (putmsg(fd, &ctlbuf ,NULL,0) < 0) { + perror("Prom Mode:"); + return(0); + } + + ctlbuf.maxlen = sizeof(union DL_primitives); + ctlbuf.len = 0; + ctlbuf.buf = (char *)&rcvbuf; + if ((retval = getmsg(fd, &ctlbuf ,NULL, &flags)) < 0) { + perror("Prom Mode ack!"); + return(0); + } + + return (Acknoledge(&rcvbuf,DL_OK_ACK,"DL_PROMISCON_REQ")); +} + +GetEthernetAddress(EtherBuf,fd) +u_char *EtherBuf; +int fd; +{ + int retval; + int flags = RS_HIPRI; + char buf[80]; + union DL_primitives rcvbuf; + dl_phys_addr_req_t PRequest; + struct strbuf ctlbuf; + + + PRequest.dl_primitive = DL_PHYS_ADDR_REQ; + PRequest.dl_addr_type = DL_CURR_PHYS_ADDR; + ctlbuf.len = sizeof(PRequest); + ctlbuf.buf = (caddr_t)&PRequest; + + if (putmsg(fd, &ctlbuf ,NULL,0) < 0) + { + perror("Ethernet Address:"); + return(-1); + } + + ctlbuf.maxlen = sizeof(union DL_primitives); + ctlbuf.len = 0; + ctlbuf.buf = (char *)&rcvbuf; + if ((retval = getmsg(fd, &ctlbuf ,NULL, &flags)) < 0) + { + perror("Ethernet Address ack!"); + return(-1); + } + + if (Acknoledge(&rcvbuf,DL_PHYS_ADDR_ACK,"DL_PHYS_ADDR_REQ")) { + memcpy( EtherBuf, + &ctlbuf.buf[rcvbuf.physaddr_ack.dl_addr_offset], + EHRD); + return(1); + } + return(0); +} + +int +AddMultiAddress(multi,fd) +u_char *multi; +int fd; +{ + int retval; + int flags = RS_HIPRI; + u_char buf[512]; + union DL_primitives rcvbuf; + struct strbuf databuf; + struct strbuf ctlbuf; + dl_enabmulti_req_t *MultiRequest = (dl_enabmulti_req_t *)buf; + + + MultiRequest->dl_primitive = DL_ENABMULTI_REQ; + MultiRequest->dl_addr_length = EHRD; + MultiRequest->dl_addr_offset = DL_ENABMULTI_REQ_SIZE; + + memcpy(&buf[DL_ENABMULTI_REQ_SIZE],multi,EHRD); + + ctlbuf.maxlen = 0; + ctlbuf.len = DL_ENABMULTI_REQ_SIZE + EHRD; + ctlbuf.buf = (caddr_t)buf; + + if ((retval = putmsg(fd, &ctlbuf ,NULL, flags)) < 0) { + perror("bogus2"); + return(0); + } + + + ctlbuf.maxlen = sizeof(rcvbuf); + ctlbuf.len = 0; + ctlbuf.buf = (char *)&rcvbuf; + + databuf.maxlen = 512; + databuf.len = 0; + databuf.buf = (char *)buf; + + if ((retval = getmsg(fd, &ctlbuf, &databuf, &flags)) < 0) { + perror("bogus!"); + return(0); + } + + return (Acknoledge(&rcvbuf,DL_OK_ACK,"DL_ENABMULTI_REQ")); +} + +#ifdef PHASE2 +/* + * return pointer to zone multicast address + * + */ + +private u_char * +zone_mcast(znam, zlen) +u_char *znam; +short zlen; +{ + int i; + u_char zone[34]; + u_short chkSum(); + static u_char zmcaddr[7] = {0x09,0x00,0x07,0x00,0x00,0x00}; + + if (zlen > sizeof(zone)) + return(NULL); + + for (i = 0; i < (int)zlen; i++) + zone[i] = (u_char)zip_toupper(znam[i]); + + zmcaddr[5] = (u_char)(chkSum(zone, zlen) % 253); + + return(zmcaddr); +} + +/* + * DDP checksum calculator + * + */ + +private +u_short chkSum(pkt, len) +u_char *pkt; +int len; +{ + int sum = 0; + + while (len-- > 0) { + sum = ((sum&0xffff)+*pkt++) << 1; + if (sum&0x10000) + sum++; + } + sum &= 0xffff; + + return((sum == 0) ? 0xffff : sum); +} + +/* + * convert lowercase to uppercase + * + */ + +private int +zip_toupper(c) +int c; +{ + if (!(c & 0x80)) + return(toupper(c)); + + switch (c) { + case 0x88: + return(0xcb); + break; + case 0x8a: + return(0x80); + break; + case 0x8b: + return(0xcc); + break; + case 0x8c: + return(0x81); + break; + case 0x8d: + return(0x82); + break; + case 0x8e: + return(0x83); + break; + case 0x96: + return(0x84); + break; + case 0x9a: + return(0x85); + break; + case 0x9b: + return(0xcd); + break; + case 0x9f: + return(0x86); + break; + case 0xbe: + return(0xae); + break; + case 0xbf: + return(0xaf); + break; + case 0xcf: + return(0xce); + break; + } + return(c); +} + +#endif PHASE2 diff --git a/support/ethertalk/senetp.c b/support/ethertalk/senetp.c new file mode 100644 index 0000000..8786bf2 --- /dev/null +++ b/support/ethertalk/senetp.c @@ -0,0 +1,740 @@ +static char rcsid[] = "$Author: djh $ $Date: 1994/10/10 08:55:05 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/ethertalk/RCS/senetp.c,v 2.9 1994/10/10 08:55:05 djh Rel djh $"; +static char revision[] = "$Revision: 2.9 $"; + +/* + * senetp.c - Simple "protocol" level interface to enet + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * July 1988 CCKim Created + * June 1991 Rakesh Patel/David Hornsby Add Phase 2 support + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef pyr +#include +extern int errno; +#else pyr +#include +#endif pyr +#include +#ifdef pyr +#include +#else pyr +#include +#endif pyr +#include +#include + +#include +#include +#include "../uab/proto_intf.h" + +#ifdef PHASE2 +#include +#include +#include +#include +#endif PHASE2 + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int fd; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ + int socket; /* ddp socket */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +extern char interface[50]; + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, socket, dev, devno) +int protocol; +int socket; +char *dev; +int devno; +{ + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "%s%d",dev,devno); + strncpy(eph->ifr.ifr_name, devnamebuf, sizeof eph->ifr.ifr_name); + + if ((s = init_nit(1024, protocol, socket, &eph->ifr)) < 0) { + return(-1); + } + + eph->inuse = TRUE; + eph->fd = s; + eph->protocol = protocol; + eph->socket = socket; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(TRUE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].fd); /* toss listener */ + close(ephlist[edx-1].fd); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize nit on a particular protocol type + * Return: socket if no error, < 0 o.w. + * +*/ +private int +init_nit(chunksize, protocol, socket, ifr) +u_long chunksize; +u_short protocol; +int socket; +struct ifreq *ifr; +{ + int s; + u_long if_flags; + char device[64]; + + sprintf(device, "/dev/%s", interface); + +#ifdef EIOCETHERT + + /* get clone */ + if ((s = open(device, O_RDWR)) < 0) { + perror("open: /dev/enetXX"); + return(-1); + } + +#else EIOCETHERT + + { + register int i, fid; + register int failed = 0; + char enetd[256]; + + /* + * try all ethernet minors from (devname)0 on up. + * (e.g., /dev/enet0 .... ) + * + * Algorithm: assumption is that names start at 0 + * and run up as decimal numbers without gaps. We search + * until we get an error that is not EBUSY (i.e., probably + * either ENXIO or ENOENT), or until we sucessfully open one. + */ + + for (i = 0; !failed ; i++) { + sprintf (enetd, "%s%d", device, i); + if ((s = open (enetd, 2)) >= 0) + break; + /* if we get past the break, we got an error */ + if (errno != EBUSY) failed++; + } + + if (failed) { + perror("open: /dev/enetXX"); + return(-1); + } + } + +#endif EIOCETHERT + + if (setup_pf(s, protocol, socket) < 0) + return(-1); + +#define NOBUF +#ifndef NOBUF + setup_buf(s, chunksize); +#endif NOBUF + + /* flush read queue */ +#ifdef EIOCFLUSH + ioctl(s, EIOCFLUSH, 0); +#endif EIOCFLUSH + return(s); +} + +#ifdef PHASE2 +/* + * add a multicast address to the interface + */ +int +pi_addmulti(multi, ifr) +char *multi; +struct ifreq *ifr; +{ + int sock; + char device[64]; + + sprintf(device, "/dev/%s", interface); + + ifr->ifr_addr.sa_family = AF_UNSPEC; + bcopy(multi, ifr->ifr_addr.sa_data, EHRD); + + /* place the "real" interface name (ie0, le0 etc.) into ifr */ + if (pi_ifname(device, ifr) < 0) + exit(1); + + /* + * open a socket, temporarily, to use for SIOC* ioctls + * + */ + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return(-1); + } + if (ioctl(sock, SIOCADDMULTI, (caddr_t)ifr) < 0) { + perror("SIOCADDMULTI"); + close(sock); + return(-1); + } + close(sock); + return(0); +} + +/* + * ferret around in /dev/kmem for real interface details + * (sometime it would be nice to have an ioctl to do this) + * + */ + +struct enet_info { + struct ifnet *ifp; +}; + +int +pi_ifname(devName, ifr) +char *devName; +struct ifreq *ifr; +{ + int kmem; + int dataLen; + int enUnits; + int minorDev; + char *kernelfile; + char namebuf[64]; + struct stat sbuf; + struct nlist nl[3]; + struct ifnet ifnet; + struct enet_info *enet_info; + + kernelfile = "/vmunix"; + if (stat(kernelfile, &sbuf) != 0) { + perror("stat /vmunix"); + return(-1); + } + if ((kmem = open("/dev/kmem", 0)) < 0) { + perror("open /dev/kmem"); + return(-1); + } + + nl[0].n_un.n_name = "_enUnits"; + nl[1].n_un.n_name = "_enet_info"; + nl[2].n_un.n_name = ""; + + nlist(kernelfile,nl); + + if (nl[0].n_type == 0 || nl[1].n_type == 0) { + fprintf(stderr, "pi_ifname() can't find _enUnits or _enet_info\n"); + close(kmem); + return(-1); + } + + (void) lseek(kmem, (nl[0].n_value), 0); + if (read(kmem, &enUnits, sizeof(enUnits)) != sizeof(enUnits)) { + perror("read /dev/kmem #1"); + close(kmem); + return(-1); + } + dataLen = enUnits*sizeof(struct enet_info); + if ((enet_info = (struct enet_info *) malloc(dataLen)) == 0) { + fprintf(stderr, "pi_ifname() can't malloc enet_info\n"); + close(kmem); + return(-1); + } + (void) lseek(kmem, (nl[1].n_value), 0); + if (read(kmem, enet_info, dataLen) != dataLen) { + perror("read /dev/kmem #2"); + close(kmem); + return(-1); + } + + if (stat(devName, &sbuf) != 0) { + perror(devName); + close(kmem); + return(-1); + } + if (!S_ISCHR(sbuf.st_mode)) { + fprintf(stderr, "%s: not character special device!\n", devName); + close(kmem); + return(-1); + } + minorDev = sbuf.st_rdev & 0xff; /* minor device number */ + + if (minorDev >= enUnits) { + fprintf(stderr,"%s: invalid minor device number %d\n", devName, minorDev); + close(kmem); + return(-1); + } + (void) lseek(kmem, enet_info[minorDev].ifp, 0); + if (read(kmem, &ifnet, sizeof(ifnet)) != sizeof(ifnet)) { + perror("read /dev/kmem #3"); + close(kmem); + return(-1); + } + (void) lseek(kmem, ifnet.if_name, 0); + if (read(kmem, namebuf, sizeof(namebuf)) != sizeof(namebuf)) { + perror("read /dev/kmem #4"); + close(kmem); + return(-1); + } + sprintf(ifr->ifr_name, "%s%d", namebuf, ifnet.if_unit); + logit(7, "ENET %s maps to interface %s", devName, ifr->ifr_name); + + free(enet_info); + close(kmem); + return(0); +} +#endif PHASE2 + +#ifndef NOBUF +/* + * setup buffering (not wanted) + * +*/ +setup_buf(s, chunksize) +int s; +u_long chunksize; +{ + struct timeval timeout; + + /* Push and configure the buffering module. */ + if (ioctl(s, I_PUSH, "nbuf") < 0) { + perror("ioctl: nbuf"); + } + timeout.tv_sec = 0; + timeout.tv_usec = 200; + si.ic_cmd = NIOCSTIME; + si.ic_timout = 10; + si.ic_len = sizeof timeout; + si.ic_dp = (char *)&timeout; + if (ioctl(s, I_STR, (char *)&si) < 0) { + perror("ioctl: timeout"); + return(-1); + } + + si.ic_cmd = NIOCSCHUNK; + + si.ic_len = sizeof chunksize; + si.ic_dp = (char *)&chunksize; + if (ioctl(s, I_STR, (char *)&si)) { + perror("ioctl: chunk size"); + return(-1); + } +} +#endif NOBUF + +/* + * establish protocol filter + * +*/ +private int +setup_pf(s, prot, sock) +int s; +u_short prot; +int sock; +{ + u_short offset; + int ethert; + unsigned queuelen; + struct ether_header eh; + struct enfilter pf; + register u_short *fwp = pf.enf_Filter; + extern int errno; + +#ifdef PHASE2 + ethert = htons(EF_8023_TYPE); /* special 802.3 type */ +#else PHASE2 + ethert = htons(prot); +#endif PHASE2 +#ifdef EIOCETHERT + if (ioctl(s, EIOCETHERT, ðert) < 0 && errno != EEXIST ) { + perror("ioctl: protocol filter"); + return(-1); + } +#endif EIOCETHERT + + queuelen = 8; + if (ioctl(s, EIOCSETW, &queuelen) < 0) { + perror("ioctl: set recv queue length"); + return(-1); + } + + pf.enf_Priority = 128; + +#define s_offset(structp, element) (&(((structp)0)->element)) + offset = ((int)s_offset(struct ether_header *, ether_type))/sizeof(u_short); +#ifdef PHASE2 + offset += 4; /* shorts: 2 bytes length + 6 bytes of 802.2 and SNAP */ +#endif PHASE2 + *fwp++ = ENF_PUSHWORD + offset; + *fwp++ = ENF_PUSHLIT | (sock >= 0 ? ENF_CAND : ENF_EQ); + *fwp++ = htons(prot); + pf.enf_FilterLen = 3; + if (sock >= 0) { +#ifdef PHASE2 + *fwp++ = ENF_PUSHWORD + offset + 6; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have dest socket */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((sock & 0xff) << 8); +/* if not wanted, fail it */ + *fwp++ = ENF_PUSHLIT ; + *fwp++ = 0; + pf.enf_FilterLen += 7; +#else PHASE2 +/* short form */ + *fwp++ = ENF_PUSHWORD + offset + 2; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have lap type in LH */ + *fwp++ = ENF_PUSHWORD + offset + 3; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0x00ff); /* now have dest in RH */ + *fwp++ = ENF_NOPUSH | ENF_OR; /* now have lap,,dest */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((1 << 8) | (sock & 0xff)); +/* long form */ + *fwp++ = ENF_PUSHWORD + offset + 2; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have lap type in LH */ + *fwp++ = ENF_PUSHWORD + offset + 7; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0x00ff); /* now have dest in RH */ + *fwp++ = ENF_NOPUSH | ENF_OR; /* now have lap,,dest */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((2 << 8) | (sock & 0xff)); +/* if neither, fail it */ + *fwp++ = ENF_PUSHLIT ; + *fwp++ = 0; + pf.enf_FilterLen += 20; +#endif PHASE2 + } + + if (ioctl(s, EIOCSETF, &pf) < 0) { + perror("ioctl: protocol filter"); + return(-1); + } + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct endevp endev; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + if (ioctl(eph->fd, EIOCDEVP, &endev) < 0) { + perror("Ioctl: SIOCGIFADDR"); + return(-1); + } + bcopy(endev.end_addr, ea, 6); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg, edx); +} + +export +pi_listener_2(edx, listener, arg1, arg2) +int edx; +int (*listener)(); +caddr_t arg1; +int arg2; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg1, arg2); +} + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = readv(eph->fd, iov, iovlen)) < 0) { + perror("abread"); + return(cc); + } + return(cc); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[3]; + struct ethernet_addresses ea; +#ifdef PHASE2 + char header[8]; +#endif PHASE2 + int cc; + +#ifdef PHASE2 + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP */ + iov[1].iov_len = sizeof(header); + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; + cc = pi_readv(edx, iov, 3); + return(cc - sizeof(ea) - sizeof(header)); +#else PHASE2 + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +#endif PHASE2 +} + +pi_reada(fd, buf, bufsiz, eaddr) +int fd; +caddr_t buf; +int bufsiz; +char *eaddr; +{ + struct iovec iov[3]; +#ifdef PHASE2 + char header[5]; /* must be 5! */ +#endif PHASE2 + int cc; + +#ifdef PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP but */ + iov[1].iov_len = sizeof(header); /* make room for our fake LAP header */ + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; +#else PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; +#endif PHASE2 + +#ifdef PHASE2 + if ((cc = readv(fd, iov, 3)) < 0) { +#else PHASE2 + if ((cc = readv(fd, iov, 2)) < 0) { +#endif PHASE2 + perror("abread"); + return(cc); + } +#ifdef PHASE2 + /* make a fake LAP header to fool the higher levels */ + buf[0] = buf[11]; /* destination node ID */ + buf[1] = buf[12]; /* source node ID */ + buf[2] = 0x02; /* always long DDP */ + return(cc - 14 - sizeof(header)); +#else PHASE2 + return(cc - 14); +#endif PHASE2 +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct ephandle *eph; + struct ether_header eh; + struct sockaddr sa; + struct iovec iov[2]; +#ifdef PHASE2 + char *q; +#endif PHASE2 + + iov[0].iov_base = (caddr_t)&eh; + iov[0].iov_len = sizeof(struct ether_header); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = buflen; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + bcopy(eaddr, &eh.ether_dhost, 6); +#ifdef PHASE2 + eh.ether_type = htons(buflen); + /* + * Fill in the remainder of the 802.2 and SNAP header bytes. + * Clients have to leave 8 bytes free at the start of buf as + * we can't send more than 14 bytes of header :-( + */ + q = (char *) buf; + *q++ = 0xaa; /* destination SAP */ + *q++ = 0xaa; /* source SAP */ + *q++ = 0x03; /* control byte */ + *q++ = (eph->protocol == 0x809b) ? 0x08 : 0x00; + *q++ = 0x00; /* always zero */ + *q++ = (eph->protocol == 0x809b) ? 0x07 : 0x00; + *q++ = (eph->protocol >> 8) & 0xff; + *q++ = (eph->protocol & 0xff); +#else PHASE2 + eh.ether_type = htons(eph->protocol); +#endif PHASE2 + + if (writev(eph->fd, iov, 2) < 0) { + return(-1); + } + return(buflen); +} + +private char ebuf[2000]; /* big enough */ + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + +#ifdef PHASE2 /* leave room for rest of ELAP hdr */ + for (len = 8, p = ebuf+8, i = 0 ; i < iovlen ; i++) +#else PHASE2 + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) +#endif PHASE2 + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} diff --git a/support/ethertalk/snitp.c b/support/ethertalk/snitp.c new file mode 100644 index 0000000..7f884bb --- /dev/null +++ b/support/ethertalk/snitp.c @@ -0,0 +1,580 @@ +static char rcsid[] = "$Author: djh $ $Date: 1992/07/25 14:16:37 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/ethertalk/RCS/snitp.c,v 2.9 1992/07/25 14:16:37 djh Rel djh $"; +static char revision[] = "$Revision: 2.9 $"; + +/* + * snitp.c - Simple "protocol" level interface to Streams based NIT + * (SunOS 4.0) + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * July 1988 CCKim Created + * April '91 djh Add Phase 2 support + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../uab/proto_intf.h" + +#ifndef DEV_NIT +#define DEV_NIT "/dev/nit" +#endif DEV_NIT + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int fd; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ + int socket; /* ddp socket */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +extern char interface[50]; + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, socket, dev, devno) +int protocol; +int socket; +char *dev; +int devno; +{ + int s, i; + struct ephandle *eph; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + strncpy(eph->ifr.ifr_name, interface, sizeof eph->ifr.ifr_name); + + if ((s = init_nit(1024, protocol, socket, &eph->ifr)) < 0) + return(-1); + + eph->inuse = TRUE; + eph->fd = s; + eph->protocol = protocol; + eph->socket = socket; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(FALSE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].fd); /* toss listener */ + close(ephlist[edx-1].fd); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize nit on a particular protocol type + * + * Return: socket if no error, < 0 o.w. +*/ +private int +init_nit(chunksize, protocol, socket, ifr) +u_long chunksize; +u_short protocol; +int socket; +struct ifreq *ifr; +{ + u_long if_flags; + struct strioctl si; + int s; + + /* get clone */ + if ((s = open(DEV_NIT, O_RDWR)) < 0) { + perror(DEV_NIT); + return(-1); + } + + /* set up messages */ + if (ioctl(s, I_SRDOPT, (char *)RMSGD) < 0) { /* want messages */ + perror("ioctl: discretem"); + return(-1); + } + + if (setup_pf(s, protocol, socket) < 0) + return(-1); + +#define NOBUF +#ifndef NOBUF + setup_buf(s, chunksize); +#endif NOBUF + + /* bind */ + si.ic_cmd = NIOCBIND; /* bind to underlying interface */ + si.ic_timout = 10; + si.ic_len = sizeof(*ifr); + si.ic_dp = (caddr_t)ifr; + if (ioctl(s, I_STR, (caddr_t)&si) < 0) { + perror(DEV_NIT); + return(-1); + } + + /* flush read queue */ + ioctl(s, I_FLUSH, (char *)FLUSHR); + return(s); +} + +#ifdef PHASE2 +/* + * add a multicast address to the interface + */ +int +pi_addmulti(multi, ifr) +char *multi; +struct ifreq *ifr; +{ + int sock; + + ifr->ifr_addr.sa_family = AF_UNSPEC; + bcopy(multi, ifr->ifr_addr.sa_data, EHRD); + /* + * open a socket, temporarily, to use for SIOC* ioctls + * + */ + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return(-1); + } + if (ioctl(sock, SIOCADDMULTI, (caddr_t)ifr) < 0) { + perror("SIOCADDMULTI"); + close(sock); + return(-1); + } + close(sock); + return(0); +} +#endif PHASE2 + +/* + * setup buffering + * +*/ +setup_buf(s, chunksize) +int s; +u_long chunksize; +{ + struct strioctl si; + struct timeval timeout; + + /* Push and configure the buffering module. */ + if (ioctl(s, I_PUSH, "nbuf") < 0) { + perror("ioctl: nbuf"); + return(-1); + } + si.ic_timout = INFTIM; + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + si.ic_cmd = NIOCSTIME; + si.ic_len = sizeof timeout; + si.ic_dp = (char *)&timeout; + if (ioctl(s, I_STR, (char *)&si) < 0) { + perror("ioctl: timeout"); + return(-1); + } + + si.ic_cmd = NIOCSCHUNK; + si.ic_len = sizeof chunksize; + si.ic_dp = (char *)&chunksize; + if (ioctl(s, I_STR, (char *)&si)) { + perror("ioctl: chunk size"); + return(-1); + } + return(0); +} + +/* + * establish protocol filter + * +*/ +private int +setup_pf(s, prot, sock) +int s; +u_short prot; +int sock; +{ + u_short offset; + struct ether_header eh; + struct packetfilt pf; + register u_short *fwp = pf.Pf_Filter; + struct strioctl si; +#define s_offset(structp, element) (&(((structp)0)->element)) + offset = ((int)s_offset(struct ether_header *, ether_type))/sizeof(u_short); +#ifdef PHASE2 + offset += 4; /* shorts: 2 bytes length + 6 bytes of 802.2 and SNAP */ +#endif PHASE2 + *fwp++ = ENF_PUSHWORD + offset; + *fwp++ = ENF_PUSHLIT | (sock >= 0 ? ENF_CAND : ENF_EQ); + *fwp++ = htons(prot); + pf.Pf_FilterLen = 3; + if (sock >= 0) { +#ifdef PHASE2 + *fwp++ = ENF_PUSHWORD + offset + 6; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have dest socket */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((sock & 0xff) << 8); +/* if not wanted, fail it */ + *fwp++ = ENF_PUSHLIT ; + *fwp++ = 0; + pf.Pf_FilterLen += 7; +#else PHASE2 +/* short form */ + *fwp++ = ENF_PUSHWORD + offset + 2; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have lap type in LH */ + *fwp++ = ENF_PUSHWORD + offset + 3; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0x00ff); /* now have dest in RH */ + *fwp++ = ENF_NOPUSH | ENF_OR; /* now have lap,,dest */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((1 << 8) | (sock & 0xff)); +/* long form */ + *fwp++ = ENF_PUSHWORD + offset + 2; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have lap type in LH */ + *fwp++ = ENF_PUSHWORD + offset + 7; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0x00ff); /* now have dest in RH */ + *fwp++ = ENF_NOPUSH | ENF_OR; /* now have lap,,dest */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((2 << 8) | (sock & 0xff)); +/* if neither, fail it */ + *fwp++ = ENF_PUSHLIT ; + *fwp++ = 0; + pf.Pf_FilterLen += 20; +#endif PHASE2 + } + + si.ic_cmd = NIOCSETF; + si.ic_timout = 10; + si.ic_len = sizeof(pf); + si.ic_dp = (char *)&pf; + if (ioctl(s, I_PUSH, "pf") < 0) { + perror("ioctl: push protocol filter"); + return(-1); + } + if (ioctl(s, I_STR, (char *)&si) < 0) { + perror("ioctl: protocol filter"); + return(-1); + } + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + if (ioctl(eph->fd, SIOCGIFADDR, &eph->ifr) < 0) { + perror("ioctl: SIOCGIFADDR"); + return(-1); + } + sa = (struct sockaddr *)eph->ifr.ifr_data; + bcopy(sa->sa_data, ea, 6); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg, edx); +} + +export +pi_listener_2(edx, listener, arg1, arg2) +int edx; +int (*listener)(); +caddr_t arg1; +int arg2; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg1, arg2); +} + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = readv(eph->fd, iov, iovlen)) < 0) { + perror("abread"); + return(cc); + } + return(cc); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[3]; + struct ethernet_addresses ea; +#ifdef PHASE2 + char header[8]; +#endif PHASE2 + int cc; + +#ifdef PHASE2 + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP */ + iov[1].iov_len = sizeof(header); + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; + cc = pi_readv(edx, iov, 3); + return(cc - sizeof(ea) - sizeof(header)); +#else PHASE2 + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +#endif PHASE2 +} + +pi_reada(fd, buf, bufsiz, eaddr) +int fd; +caddr_t buf; +int bufsiz; +char *eaddr; +{ + struct iovec iov[3]; +#ifdef PHASE2 + char header[5]; /* must be 5! */ +#endif PHASE2 + int cc; + +#ifdef PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP but */ + iov[1].iov_len = sizeof(header); /* make room for our fake LAP header */ + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; +#else PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; +#endif PHASE2 + +#ifdef PHASE2 + if ((cc = readv(fd, iov, 3)) < 0) { +#else PHASE2 + if ((cc = readv(fd, iov, 2)) < 0) { +#endif PHASE2 + perror("abread"); + return(cc); + } +#ifdef PHASE2 + /* make a fake LAP header to fool the higher levels */ + buf[0] = buf[11]; /* destination node ID */ + buf[1] = buf[12]; /* source node ID */ + buf[2] = 0x02; /* always long DDP */ + return(cc - 14 - sizeof(header)); +#else PHASE2 + return(cc - 14); +#endif PHASE2 +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct ephandle *eph; + struct ether_header *eh; + struct strbuf pbuf, dbuf; + struct sockaddr sa; +#ifdef PHASE2 + char *q; +#endif PHASE2 + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + sa.sa_family = AF_UNSPEC; /* by definition */ + eh = (struct ether_header *)sa.sa_data; /* make pointer */ + bcopy(eaddr, &eh->ether_dhost, sizeof(eh->ether_dhost)); +#ifdef PHASE2 + eh->ether_type = htons(buflen); + /* + * Fill in the remainder of the 802.2 and SNAP header bytes. + * Clients have to leave 8 bytes free at the start of buf as + * NIT won't let us send any more than 14 bytes of header :-( + */ + q = (char *) buf; + *q++ = 0xaa; /* destination SAP */ + *q++ = 0xaa; /* source SAP */ + *q++ = 0x03; /* control byte */ + *q++ = (eph->protocol == 0x809b) ? 0x08 : 0x00; + *q++ = 0x00; /* always zero */ + *q++ = (eph->protocol == 0x809b) ? 0x07 : 0x00; + *q++ = (eph->protocol >> 8) & 0xff; + *q++ = (eph->protocol & 0xff); +#else PHASE2 + eh->ether_type = htons(eph->protocol); +#endif PHASE2 + pbuf.len = sizeof(sa); + pbuf.buf = (char *)&sa; + dbuf.len = buflen; + dbuf.buf = (caddr_t)buf; + + if (putmsg(eph->fd, &pbuf, &dbuf, 0) < 0) { + return(-1); + } + return(buflen); +} + +private char ebuf[2000]; /* big enough */ + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + +#ifdef PHASE2 /* leave room for rest of ELAP hdr */ + for (len = 8, p = ebuf+8, i = 0 ; i < iovlen ; i++) +#else PHASE2 + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) +#endif PHASE2 + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} diff --git a/support/ethertalk/spfiltp.c b/support/ethertalk/spfiltp.c new file mode 100644 index 0000000..ce1cb85 --- /dev/null +++ b/support/ethertalk/spfiltp.c @@ -0,0 +1,660 @@ +static char rcsid[] = "$Author: djh $ $Date: 1996/04/25 00:49:30 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/ethertalk/RCS/spfiltp.c,v 2.6 1996/04/25 00:49:30 djh Rel djh $"; +static char revision[] = "$Revision: 2.6 $"; + +/* + * spfiltp.c - Simple "protocol" level interface to Ultrix packetfilter + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * April 1991 Jeffrey Mogul @ DECWRL (created from senetp.c) + * June 1991 David Hornsby, add Phase 2 support + * June 1995 Conrad Huang, add FDDI support + * + */ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef pyr +#include +extern int errno; +#else pyr +#include +#endif pyr +#include +#ifdef pyr +#include +#else pyr +#include +#endif pyr +#ifdef USING_FDDI_NET +#include +#include +#endif USING_FDDI_NET +#include +#include + +#include +#include "../uab/proto_intf.h" + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int fd; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ + int socket; /* ddp socket */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +private int header_size = 14; /* assume Ethernet for now */ + +#ifdef USING_FDDI_NET +export int source_offset = 6; /* assume Ethernet for now */ +export int using_fddi = 0; /* assume not FDDI for now */ +#endif USING_FDDI_NET + +extern char interface[50]; + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, socket, dev, devno) +int protocol; +int socket; +char *dev; +int devno; +{ + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "%s%d",dev,devno); + strncpy(eph->ifr.ifr_name, devnamebuf, sizeof eph->ifr.ifr_name); + + if ((s = init_nit(1024, protocol, socket, &eph->ifr)) < 0) { + return(-1); + } + + eph->inuse = TRUE; + eph->fd = s; + eph->protocol = protocol; + eph->socket = socket; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(TRUE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].fd); /* toss listener */ + close(ephlist[edx-1].fd); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize nit on a particular protocol type + * + * Runs in promiscous mode for now. + * + * Return: socket if no error, < 0 o.w. +*/ +private int +init_nit(chunksize, protocol, socket, ifr) +u_long chunksize; +u_short protocol; +int socket; +struct ifreq *ifr; +{ + u_long if_flags; + int s; + char device[64]; + + if ((s = pfopen(interface, O_RDWR)) < 0) { + sprintf(device, "open: %s", interface); + perror(device); + return(-1); + } + + if (setup_pf(s, protocol, socket) < 0) + return(-1); + +#define NOBUF +#ifndef NOBUF + setup_buf(s, chunksize); +#endif NOBUF + + /* flush read queue */ + ioctl(s, EIOCFLUSH, 0); + return(s); +} + +#ifdef PHASE2 +/* + * add a multicast address to the interface +*/ +int +pi_addmulti(multi, ifr) +char *multi; +struct ifreq *ifr; +{ + int sock; + + if ((sock = pfopen(interface, O_RDWR)) < 0) { + perror("pfopen()"); + return(-1); + } + /* + * get the real interface name (interface may be generic "pf0") + * + */ + if (ioctl(sock, EIOCIFNAME, ifr) < 0) { + perror("EIOCIFNAME"); + return(-1); + } + close(sock); + + ifr->ifr_addr.sa_family = AF_UNSPEC; + bcopy(multi, ifr->ifr_addr.sa_data, EHRD); + /* + * open a socket, temporarily, to use for SIOC* ioctls + * + */ + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return(-1); + } + + if (ioctl(sock, SIOCADDMULTI, (struct ifreq *)ifr) < 0) { + perror("SIOCADDMULTI"); + close(sock); + return(-1); + } + + close(sock); + return(0); +} +#endif PHASE2 + +#ifndef NOBUF +/* + * setup buffering (not wanted) + * +*/ +setup_buf(s, chunksize) +int s; +u_long chunksize; +{ + struct timeval timeout; + + /* Push and configure the buffering module. */ + if (ioctl(s, I_PUSH, "nbuf") < 0) { + perror("ioctl: nbuf"); + } + timeout.tv_sec = 0; + timeout.tv_usec = 200; + si.ic_cmd = NIOCSTIME; + si.ic_timout = 10; + si.ic_len = sizeof timeout; + si.ic_dp = (char *)&timeout; + if (ioctl(s, I_STR, (char *)&si) < 0) { + perror("ioctl: timeout"); + return(-1); + } + + si.ic_cmd = NIOCSCHUNK; + + si.ic_len = sizeof chunksize; + si.ic_dp = (char *)&chunksize; + if (ioctl(s, I_STR, (char *)&si)) { + perror("ioctl: chunk size"); + return(-1); + } +} +#endif NOBUF + +/* + * establish protocol filter + * +*/ +private int +setup_pf(s, prot, sock) +int s; +u_short prot; +int sock; +{ + u_short offset; + unsigned queuelen; + u_int dlt; + struct enfilter pf; + register u_short *fwp = pf.enf_Filter; + extern int errno; + + + queuelen = 8; + if (ioctl(s, EIOCSETW, &queuelen) < 0) { + perror("ioctl: set recv queue length"); + return(-1); + } + +#ifdef USING_FDDI_NET + if (ioctl(s, BIOCGDLT, &dlt) < 0) { + perror("ioctl: get data link type"); + return(-1); + } + if (dlt == DLT_FDDI) { + header_size = 16; + source_offset = 10; + using_fddi = 1; + } +#endif USING_FDDI_NET + + pf.enf_Priority = 128; + +#ifdef USING_FDDI_NET + if (using_fddi) + offset = (header_size - 2) / sizeof (ushort); + else +#endif USING_FDDI_NET +#define s_offset(structp, element) (&(((structp)0)->element)) + offset = ((int)s_offset(struct ether_header *, ether_type))/sizeof(u_short); +#ifdef PHASE2 +#ifdef ULT42PFBUG + if (sock >= 0) +#endif ULT42PFBUG + offset += 4; /* shorts: 2 bytes length + 6 bytes of 802.2 and SNAP */ +#endif PHASE2 + *fwp++ = ENF_PUSHWORD + offset; + *fwp++ = ENF_PUSHLIT | (sock >= 0 ? ENF_CAND : ENF_EQ); + *fwp++ = htons(prot); + pf.enf_FilterLen = 3; + if (sock >= 0) { +#ifdef PHASE2 + *fwp++ = ENF_PUSHWORD + offset + 6; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have dest socket */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((sock & 0xff) << 8); +/* if not wanted, fail it */ + *fwp++ = ENF_PUSHLIT; + *fwp++ = 0; + pf.enf_FilterLen += 7; +#else PHASE2 +/* short form */ + *fwp++ = ENF_PUSHWORD + offset + 2; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have lap type in LH */ + *fwp++ = ENF_PUSHWORD + offset + 3; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0x00ff); /* now have dest in RH */ + *fwp++ = ENF_NOPUSH | ENF_OR; /* now have lap,,dest */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((1 << 8) | (sock & 0xff)); +/* long form */ + *fwp++ = ENF_PUSHWORD + offset + 2; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0xff00); /* now have lap type in LH */ + *fwp++ = ENF_PUSHWORD + offset + 7; + *fwp++ = ENF_PUSHLIT | ENF_AND; + *fwp++ = htons(0x00ff); /* now have dest in RH */ + *fwp++ = ENF_NOPUSH | ENF_OR; /* now have lap,,dest */ + *fwp++ = ENF_PUSHLIT | ENF_COR; + *fwp++ = htons((2 << 8) | (sock & 0xff)); +/* if neither, fail it */ + *fwp++ = ENF_PUSHLIT ; + *fwp++ = 0; + pf.enf_FilterLen += 20; +#endif PHASE2 + } + + if (ioctl(s, EIOCSETF, &pf) < 0) { + perror("ioctl: protocol filter"); + return(-1); + } + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct endevp endev; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + if (ioctl(eph->fd, EIOCDEVP, &endev) < 0) { + perror("Ioctl: SIOCGIFADDR"); + return(-1); + } + bcopy(endev.end_addr, ea, 6); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg, edx); +} + +export +pi_listener_2(edx, listener, arg1, arg2) +int edx; +int (*listener)(); +caddr_t arg1; +int arg2; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].fd, listener, arg1, arg2); +} + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = readv(eph->fd, iov, iovlen)) < 0) { + perror("abread"); + return(cc); + } + return(cc); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[3]; + struct ethernet_addresses ea; +#ifdef USING_FDDI_NET + struct fddi_header fh; +#endif USING_FDDI_NET +#ifdef PHASE2 + char header[8]; +#endif PHASE2 + int cc; + +#ifdef PHASE2 +#ifdef USING_FDDI_NET + if (using_fddi) + iov[0].iov_base = (caddr_t)&fh; + else +#endif USING_FDDI_NET + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = header_size; + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP */ +#ifdef ULT42PFBUG + iov[1].iov_len = 0; +#else ULT42PFBUG + iov[1].iov_len = sizeof(header); +#endif ULT42PFBUG + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; + cc = pi_readv(edx, iov, 3); +#ifdef USING_FDDI_NET + if (using_fddi) + return(cc - header_size - sizeof (header)); + else +#endif USING_FDDI_NET +#ifdef ULT42PFBUG + return(cc - sizeof(ea)); +#else ULT42PFBUG + return(cc - sizeof(ea) - sizeof(header)); +#endif ULT42PFBUG +#else PHASE2 + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +#endif PHASE2 +} + +pi_reada(fd, buf, bufsiz, eaddr) +int fd; +caddr_t buf; +int bufsiz; +char *eaddr; +{ + struct iovec iov[3]; +#ifdef PHASE2 + char header[5]; /* must be 5! */ +#endif PHASE2 + int cc; + +#ifdef PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = header_size; + iov[1].iov_base = (caddr_t)header; /* consume 802.2 hdr & SNAP but */ + iov[1].iov_len = sizeof(header); /* make room for our fake LAP header */ + iov[2].iov_base = (caddr_t)buf; + iov[2].iov_len = bufsiz; +#else PHASE2 + iov[0].iov_base = (caddr_t)eaddr; + iov[0].iov_len = header_size; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; +#endif PHASE2 + +#ifdef PHASE2 + if ((cc = readv(fd, iov, 3)) < 0) { +#else PHASE2 + if ((cc = readv(fd, iov, 2)) < 0) { +#endif PHASE2 + perror("abread"); + return(cc); + } +#ifdef PHASE2 + /* make a fake LAP header to fool the higher levels */ + buf[0] = buf[11]; /* destination node ID */ + buf[1] = buf[12]; /* source node ID */ + buf[2] = 0x02; /* always long DDP */ + return(cc - header_size - 5); +#else PHASE2 + return(cc - header_size); +#endif PHASE2 +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct ephandle *eph; + struct ether_header eh; +#ifdef USING_FDDI_NET + struct fddi_header fh; +#endif USING_FDDI_NET + struct sockaddr sa; + struct iovec iov[2]; +#ifdef PHASE2 + char *q; +#endif PHASE2 + +#ifdef USING_FDDI_NET + if (using_fddi) + iov[0].iov_base = (caddr_t)&fh; + else +#endif USING_FDDI_NET + iov[0].iov_base = (caddr_t)&eh; + iov[0].iov_len = header_size; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = buflen; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + +#ifdef USING_FDDI_NET + if (using_fddi) { + bcopy(eaddr, fh.fddi_dhost, 6); + fh.fddi_fc = FDDIFC_LLC_ASYNC; + fh.fddi_ph[0] = 0; + fh.fddi_ph[1] = 0; + fh.fddi_ph[2] = 0; + } else +#endif USING_FDDI_NET + bcopy(eaddr, eh.ether_dhost, 6); +#ifdef PHASE2 +#ifdef USING_FDDI_NET + if (!using_fddi) +#endif USING_FDDI_NET + eh.ether_type = htons(buflen); + /* + * Fill in the remainder of the 802.2 and SNAP header bytes. + * Clients have to leave 8 bytes free at the start of buf as + * NIT won't let us send any more than 14 bytes of header :-( + */ + q = (char *) buf; + *q++ = 0xaa; /* destination SAP */ + *q++ = 0xaa; /* source SAP */ + *q++ = 0x03; /* control byte */ + *q++ = (eph->protocol == 0x809b) ? 0x08 : 0x00; + *q++ = 0x00; /* always zero */ + *q++ = (eph->protocol == 0x809b) ? 0x07 : 0x00; + *q++ = (eph->protocol >> 8) & 0xff; + *q++ = (eph->protocol & 0xff); +#else PHASE2 + eh.ether_type = htons(eph->protocol); +#endif PHASE2 + + if (writev(eph->fd, iov, 2) < 0) { + return(-1); + } + return(buflen); +} + +private char ebuf[2000]; /* big enough */ + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + +#ifdef PHASE2 + for (len = 8, p = ebuf+8, i = 0 ; i < iovlen ; i++) +#else PHASE2 + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) +#endif PHASE2 + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} diff --git a/support/uab/Makefile.m4 b/support/uab/Makefile.m4 new file mode 100644 index 0000000..a4bbb2c --- /dev/null +++ b/support/uab/Makefile.m4 @@ -0,0 +1,61 @@ +CFLAGS=-DDEBUG cflags() specialcflags() +DESTDIR=capsrvrdestdir() +PROGS=uabprogs() +POBJS=uabpobjs() +CAPLIB=libcap() +LFLAGS= + +SRCS=aarp.c kip_mpx.c rtmp.c ethertalk.c ddprouter.c ddpsvcs.c ddpport.c \ + hash.c asyncatalk.c +OBJS=aarp.o kip_mpx.o rtmp.o ethertalk.o ddprouter.o ddpsvcs.o ddpport.o \ + hash.o asyncatalk.o + +all: ${PROGS} + +uab: uab.o ${OBJS} ${POBJS} + ${CC} ${LFLAGS} -o uab uab.o ${OBJS} ${POBJS} ${CAPLIB} + +install: ${PROGS}.install + +.install: + +uab.install: uab + -strip uab + ifdef([sysvinstall],[install -f $(DESTDIR) uab], + [${INSTALLER} uab ${DESTDIR}]) + +kip_mpx.o: kip_mpx.c mpxddp.h gw.h node.h ddpport.h + ${CC} -c ${CFLAGS} -DTAB=etalklocal() -DMTAB=etalklocal() kip_mpx.c + +uab.o: uab.c mpxddp.h gw.h node.h ddpport.h if_desc.h + ${CC} -c ${CFLAGS} -DUAB_PIDFILE=uabpidfile() \ + -DBRIDGE_DESC=uabbrdescr() uab.c + +lint: + lint -h uab.c ${SRCS} + +clean: + rm -f *.o uab + +spotless: + rm -f *.o *.orig uab Makefile makefile + +# ddpport.h: mpxddp.h node.h +# gw.h: node.h ddport.h (mpxddp.h) +# if_desc.h: mpxddp.h + +ddprouter.o: ddprouter.c gw.h node.h ddpport.h mpxddp.h +rtmp.o: rtmp.c gw.h node.h ddpport.h mpxddp.h + +ethertalk.o: ethertalk.c proto_intf.h ethertalk.h node.h \ + ddpport.h if_desc.h mpxddp.h +aarp.o: aarp.c proto_intf.h ethertalk.h aarp_defs.h aarp.h + +ddpport.o: ddpport.c ddpport.h node.h mpxddp.h + +dlip.o: dlip.c proto_intf.h +snitp.o: snitp.c proto_intf.h + +hash.o: hash.c hash.h + +asyncatalk.o: asyncatalk.c diff --git a/support/uab/README b/support/uab/README new file mode 100644 index 0000000..f52b27e --- /dev/null +++ b/support/uab/README @@ -0,0 +1,65 @@ +UAB - Unix AppleTalk Bridge + +AppleTalk Bridge For Unix + +written by Charlie C. Kim + Academic Computing and Communications Group + Center For Computing Activities + Columbia University +August 1988 + +Copyright (c) 1988 by The Trustees of Columbia University + in the City of New York. + +Portions Copyright (c) 1990 The University of Melbourne. + +Permission is granted to any individual or institution to use, +copy, or redistribute this software so long as it is not sold for +profit, provided that this notice and the original copyright +notices are retained. Columbia University nor the author make no +representations about the suitability of this software for any +purpose. It is provided "as is" without express or implied +warranty. + +----- + +See whatiswhat for a general description of modules. + +See desc.ms for general overview. + +See UAB.8 for info on running uab. + +----- + +Installation + +This version of UAB has been incorporated into CAP. Run Configure in the +CAP top level directory and answer 'yes' to the using UAB question. The CAP +libraries will be set up for UAB use and UAB will be compiled and installed +automatically (in /usr/local/cap by default). + +YOU MUST STILL MANUALLY EDIT AND INSTALL THE FILE "bridge_desc" (by default +this file is in /usr/local/lib/cap, unless the 'caplibdestdir' variable is +modified [for example, to "/etc/"] in m4.setup). + +The file /etc/etalk.local is written by UAB when it discovers sufficient +information about the connected networks. CAP programs will use this +file to obtain network and bridge details. + +Installation on Sony NEWS + +1. make /dev/ether? + +UAB for the Sony NEWS uses the rawether interface. To make the device file, + + % cd /dev; sh MAKEDEV ether0 + +2. run Configure + +Ensure that Configure recognizes that it is running under NEWS. It will do +this automatically if the directory '/usr/sony/bin' exists, otherwise type +"newsos" for the Operating System type. Answer 'y' to the UAB question. + +3. add the appropriate entry to the bridge_desc file ... EG: + +ohio [ELAP,/dev/ether:0] mkip [138.115,Mac-net] diff --git a/support/uab/STILL_TO_DO b/support/uab/STILL_TO_DO new file mode 100644 index 0000000..324b852 --- /dev/null +++ b/support/uab/STILL_TO_DO @@ -0,0 +1,11 @@ +UAB +--- + +*** still to be done as of 15th February 1991: + +UAB should work OK with the 'enet' interface (via senetp.c) +on SUNs, this is untested. + +*** already done: + +The NIT interface (snitp.c) on SUNs and DLI (dlip.c) under ULTRIX seem fine. diff --git a/support/uab/aarp.c b/support/uab/aarp.c new file mode 100644 index 0000000..1f38b49 --- /dev/null +++ b/support/uab/aarp.c @@ -0,0 +1,1116 @@ +static char rcsid[] = "$Author: djh $ $Date: 1993/09/28 08:24:19 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/aarp.c,v 2.6 1993/09/28 08:24:19 djh Rel djh $"; +static char revision[] = "$Revision: 2.6 $"; + +/* + * aarp.c - AppleTalk Address Resolution Protocol handler + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * April 3, 1988 CCKim Created + * Aug 26, 1988 Moved into separate module from ethertalk + * April 28,1991 djh Added Phase 2 support + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef pyr +#include +#else pyr +#include +#endif pyr + +#include +#include "proto_intf.h" +#include "ethertalk.h" +#include "aarp_defs.h" +#include "aarp.h" /* double checks aarp.h */ +#include "hash.h" +#include "log.h" + +#define LOG_LOG 0 +#define LOG_BASE 1 +#define LOG_LOTS 9 + +private int aarptab_compare(); +private u_int aarp_compress(); +private caddr_t aarptab_new(); +private AARP_ENTRY *aarptab_get(); +private int aarptab_delete(); + +export caddr_t aarptab_init(); +export AARP_ENTRY *aarptab_find(); +export caddr_t aarp_init(); +export int aarp_get_host_addr(); +#ifdef PHASE2 +export int aarp_set_host_addr(); +#endif PHASE2 +export int aarp_resolve(); +export int aarp_insert(); +export int aarp_acquire_etalk_node(); +#ifdef sony_news +int aarp_listener(); +#else sony_news +private aarp_listener(); +#endif sony_news +export int aarp_inited = 0; + +private AARP_ENTRY *aarp_find_free_etalk_node(); +private probe_and_request_driver(); +private void aarp_probed(); +private void aarp_reply(); +private void aarp_request(); +private struct ai_host_node *host_node(); + +/* pointers to host ethernet and broadcast addresess */ +#ifdef PHASE2 +private u_char b_eaddr[EHRD] = {0x09, 0x00, 0x07, 0xff, 0xff, 0xff}; +#else PHASE2 +private u_char b_eaddr[EHRD] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +#endif PHASE2 + +/* list of all the node ids: DUMP ONLY */ +private AARP_ENTRY *aarptab_node_list; + +private AARP_ENTRY *aarptab_free_list; + + +private int m_aarptab = 0; +private int m_probe = 0; +private int f_probe = 0; + + +/* compare two ethertalk node addresses */ +private int +aarptab_compare(k,node) +struct ethertalkaddr *k; +AARP_ENTRY *node; +{ +#ifdef PHASE2 + if (node) + return(memcmp(k, &node->aae_pa, ETPL)); + else + return -1; +#else PHASE2 + return(k->node - node->aae_pa.node); +#endif PHASE2 +} + +/* compress an ethertalk node addresses */ +private u_int +aarptab_compress(k) +struct ethertalkaddr *k; +{ +#ifdef PHASE2 + /* ZZ */ +#endif PHASE2 + return(k->node); +} + +/* create a new arp table entry based upon the ethertalk node */ +private caddr_t +aarptab_new(k) +struct ethertalkaddr *k; +{ + AARP_ENTRY *en; + + if (aarptab_free_list) { + en = aarptab_free_list; /* get first node */ + aarptab_free_list = en->aae_next; /* and unlink from free list */ + /* if dump, don't need to link into dump table, already there */ + } else { + if ((en = (AARP_ENTRY *)malloc(sizeof(AARP_ENTRY))) == NULL) + return(NULL); + m_aarptab++; + } + en->aae_next = aarptab_node_list; + aarptab_node_list = en; + en->aae_flags = 0; /* clear flags */ + en->aae_used = 0; /* clear used flag */ + en->aae_ttl = 0; /* clear ttl */ + bcopy(k, &en->aae_pa, sizeof(*k)); /* copy in node address */ + return((caddr_t)en); +} + +/* initial hash table for aarp table */ +export caddr_t +aarptab_init() +{ + static caddr_t hashtab = NULL; + + if (aarp_inited) + return(hashtab); + + hashtab = h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE, + AARP_TAB_SIZE, + aarptab_compare, aarptab_new, aarptab_compress, + NULL, NULL, NULL); + return(hashtab); +} + +/* see if we need to try to speed up hash lookups */ +aarptab_speedup(aih) +AI_HANDLE *aih; +{ + struct hash_statistics *s; + int nbkts; + + aih->ai_accesses = 0; /* clear */ + s = h_statistics(aih->ai_aarptab); /* get stats */ + if (s->hs_davg < AI_AARPTAB_REHASH_POINT) + return; /* nothing to do */ + logit(LOG_BASE, "aarp: %d buckets, %d entries", + s->hs_buckets, s->hs_used); + logit(LOG_BASE, "aarp: arp hash table average lookup distance %d.%02d", + s->hs_davg / 100, s->hs_davg % 100); + /* possibly delete old entries */ + if ((s->hs_buckets/2) > s->hs_used) { + logit(LOG_BASE, "aarp: buckets half filled, reconsider hash function"); + return; + } else { + nbkts = s->hs_buckets*2; + } + logit(LOG_BASE, "aarp: rehashing table from %d buckets to %d buckets", + s->hs_buckets, nbkts); + h_rehash(aih->ai_aarptab, nbkts); +} + +/* find an aarp table entry given an ethertalk node address */ +export AARP_ENTRY * +aarptab_find(aih, pa) +AI_HANDLE *aih; +struct ethertalkaddr *pa; +{ + AARP_ENTRY *r; + if ((r = (AARP_ENTRY *)h_member(aih->ai_aarptab, pa))) { +#ifdef AARP_DUMP_TABLE + r->aae_used++; /* used once more */ +#endif + if (aih->ai_accesses++ > AI_AARPTAB_EVAL_POINT) + aarptab_speedup(aih); + } + return(r); +} + +/* find an aarp table entry given an ethertalk node address */ +/* if not found, return a new entry */ +private AARP_ENTRY * +aarptab_get(aih, pa) +AI_HANDLE *aih; +struct ethertalkaddr *pa; +{ + AARP_ENTRY *r; + int d, b; + + r = (AARP_ENTRY *)h_operation(HASH_OP_INSERT,aih->ai_aarptab,pa,-1,-1,&d,&b); + if (r == NULL) + return(NULL); + if (r->aae_flags == 0) { +#ifdef PHASE2 + logit(LOG_LOTS, "New AARP mapping for node %d/%d.%d", + r->aae_pa.node, r->aae_pa.dummy[1], r->aae_pa.dummy[2]); +#else PHASE2 + logit(LOG_LOTS, "New AARP mapping for node %d", r->aae_pa.node); +#endif PHASE2 + r->aae_aarptab = aih->ai_aarptab; /* remember */ + } else { + if (aih->ai_accesses++ > AI_AARPTAB_EVAL_POINT) + aarptab_speedup(aih); + } +#ifdef AARP_DUMP_TABLE + r->aae_used++; /* bump */ +#endif + return(r); +} + +#ifdef notdef +/* delete an aarp table entry */ +private int +aarptab_delete(aih, aa) +AI_HANDLE *aih; +AARP_ENTRY *aa; +{ + + aa->aae_flags = 0; /* not okay */ +#ifdef PHASE2 + logit(LOG_LOTS, "deleting arp entry: node %d/%d.%d", + aa->aae_pa.node, aa->aae_pa.dummy[1], aa->aae_pa.dummy[2]); +#else PHASE2 + logit(LOG_LOTS, "deleting arp entry: node %d", aa->aae_pa.node); +#endif PHASE2 + en = (AARP_ENTRY *)h_delete(aih->ai_aarptab, aa->aae_pa.node); + if (en != aa) { + logit(LOG_BASE, "when deleting arp entry, freed entry was %x, wanted %x", + en, aa); + } + aa->aae_next = aarptab_free_list; + aarptab_free_list = aa; /* mark */ +} +#endif notdef + +/* scan arp table and see if anything needs going */ +aarptab_scan() +{ + struct timeval tv; + AARP_ENTRY *n, *np, *en, *t; + + np = NULL, n = aarptab_node_list; + while (n) { + if (n->aae_ttl <= 0) { +#ifdef PHASE2 + logit(LOG_LOTS, "deleting arp entry: node %d/%d.%d", + n->aae_pa.node, n->aae_pa.dummy[1], n->aae_pa.dummy[2]); +#else PHASE2 + logit(LOG_LOTS, "deleting arp entry: node %d", n->aae_pa.node); +#endif PHASE2 + en = (AARP_ENTRY *)h_delete(n->aae_aarptab, &n->aae_pa); + if (en != n) { + logit(LOG_BASE,"when deleting arp entry, freed entry was %x, wanted %x", + en, n); + } + if (np) + np->aae_next = n->aae_next; /* unlink */ + else + aarptab_node_list = n->aae_next; /* at head of list */ + /* link onto free list */ + t = n->aae_next; /* remember next */ + n->aae_next = aarptab_free_list; + aarptab_free_list = n; /* mark */ /* delete it */ + n = t; /* advance n */ + continue; /* and skip regular advance */ + } else + n->aae_ttl--; /* dec. ttl */ + np = n; /* remeber previous */ + n = n->aae_next; /* advance n */ + } + tv.tv_sec = AARP_SCAN_TIME; + tv.tv_usec = 0; + relTimeout(aarptab_scan, 0, &tv, TRUE); +} + +/* + * initialize AARP module + * + * arguments are: dev, devno of the interface + * maxnodes specfies the maximum number of nodes wanted on this + * interface (usually one) + * + * returns pointer to a AI_HANDLE - as caddr_t + * +*/ +caddr_t +aarp_init(dev, devno, maxnodes) +char *dev; +int devno; +int maxnodes; +{ + int aarp_listener(); + AI_HANDLE *aih = NULL; + static int here_before = 0; + int ph = -1; + + if (!here_before) { + struct timeval tv; + + here_before = 1; + aarptab_node_list = NULL; /* clear */ + aarptab_free_list = NULL; + tv.tv_sec = AARP_SCAN_TIME; + tv.tv_usec = 0; + relTimeout(aarptab_scan, 0, &tv, TRUE); + } + +#ifdef sony_news + ph = devno; +#else sony_news +#ifdef AARPD + if ((ph = pi_open(ETHERTYPE_AARP, -1, dev, devno)) < 0) { +#else AARPD + if ((ph = pi_open(ETHERTYPE_AARP, dev, devno)) < 0) { +#endif AARPD + logit(L_UERR|LOG_LOG,"pi_open: aarp_init"); + return(NULL); + } +#endif sony_news + + if ((aih = (AI_HANDLE *)malloc(sizeof(AI_HANDLE))) == NULL) + goto giveup; + if ((aih->ai_nodes = (struct ai_host_node *) + malloc(sizeof(struct ai_host_node)*maxnodes)) == NULL) + goto giveup; + + /* clear host node table */ + bzero(aih->ai_nodes, sizeof(struct ai_host_node)*maxnodes); + aih->ai_flags = 0; /* clear flags for interface */ + aih->ai_numnode = 0; /* no nodes yet */ + aih->ai_maxnode = maxnodes; /* max # of nodes (usually 1) */ + aih->ai_ph = ph; + /* get ethernet address */ + if ((pi_get_ethernet_address(ph, aih->ai_eaddr)) < 0) + goto giveup; + +#ifdef sony_news + logit(LOG_BASE,"Ethernet address is %02x:%02x:%02x:%02x:%02x:%02x", +#else sony_news + logit(LOG_BASE,"Ethernet address for %s%d is %02x:%02x:%02x:%02x:%02x:%02x", + dev, devno, +#endif sony_news + aih->ai_eaddr[0], aih->ai_eaddr[1], aih->ai_eaddr[2], + aih->ai_eaddr[3], aih->ai_eaddr[4], aih->ai_eaddr[5]); + + /* setup arp table */ + if ((aih->ai_aarptab = aarptab_init()) == NULL) + goto giveup; + aih->ai_accesses = 0; /* no access to table yet */ + +#ifndef sony_news + /* establish listener now */ + pi_listener(aih->ai_ph, aarp_listener, (caddr_t)aih); +#endif sony_news + + aarp_inited = 1; + + return((caddr_t)aih); + giveup: + if (ph >= 0) + pi_close(ph); + if (aih) { + if (aih->ai_nodes) + free(aih->ai_nodes); + free(aih); + } + return(NULL); +} + +/* + * get a host node address + * +*/ +export int +aarp_get_host_addr(aih, pa, which) +AI_HANDLE *aih; +struct ethertalkaddr *pa; +int which; +{ + if (aih->ai_numnode > which && + aih->ai_nodes[which].aihn_state == AI_NODE_OKAY) { + *pa = aih->ai_nodes[which].aihn_pa; /* copy it */ + /* should we count pa node address? */ +#ifdef PHASE2 + return(4); +#else PHASE2 + return(1); +#endif PHASE2 + } + return(-1); +} + +#ifdef PHASE2 +/* + * set a host node address + * +*/ +export int +aarp_set_host_addr(aih, pa) +AI_HANDLE *aih; +struct ethertalkaddr *pa; +{ + int i = aih->ai_numnode; + struct ai_host_node *an = aih->ai_nodes; + + for ( ; i ; an++, i--) + if (an->aihn_state == AI_NODE_OKAY) + an->aihn_pa = *pa; + + if (i == 0) + return(0); + + return(-1); +} +#endif PHASE2 + +/* + * returns -1: can't be resolved + * 0: resolving, come back later + * 1: resolved + * + * Resolves the ethernet address of the specified destination. + * Special cases are: + * dstnode == 0xff -> broadcast + * srcnode != ourselves - error + * dstnode == ourselves - our hw addr (maybe should be error if we + * can't send to ourselves) + * In general, algorithm is: + * check internal table. If entry is valid and if the ttl is valid + * return the address from the table + * If not in the table or ttl is bad, then do a request -- however, + * the current packet WILL drop since the aarp request will not block + * +*/ +export int +aarp_resolve(aih, tpa, wantbr, eaddr) +AI_HANDLE *aih; +struct ethertalkaddr *tpa; +int wantbr; +u_char **eaddr; +{ + struct timeval *tv; + AARP_ENTRY *aa; + + if (wantbr) { + *eaddr = b_eaddr; /* want to broadcast */ + return(1); + } + + /* allow target to be ourselves */ + if (host_node(aih, tpa, AI_NODE_OKAY)) { + *eaddr = aih->ai_eaddr; + return(1); + } + + /* find the node in the table */ + if ((aa = aarptab_find(aih, tpa)) != NULL && aa->aae_flags & AARP_OKAY) { + if (aa->aae_flags & (AARP_PERM)) { + *eaddr = aa->aae_eaddr; + return(1); + } + if (aa->aae_ttl > 0) { + *eaddr = aa->aae_eaddr; /* yes, return valid */ + return(1); + } + } + /* query on interface */ + aarp_request(aih, tpa); + return(0); +} + +/* + * call with hardware address, protocol address + * flag is TRUE if insert is "hard" FALSE o.w. + * -- A HARD insert will smash what is there + * -- A SOFT insert will return FALSE if the ha's don't match and + * invalidate the cache entry + * If the insert succeeds, then the ttl is bumped to the normal level + * + * Note: aarp_insert performs the gleaning functions of the aarp spec. +*/ +export int +aarp_insert(aih, ha, pa, hard) +AI_HANDLE *aih; +u_char *ha; +struct ethertalkaddr *pa; +int hard; +{ + AARP_ENTRY *aa; + struct timeval *tv; + + /* check to make sure ha isn't broadcast (or multicast)! */ + if (bcmp(ha, b_eaddr, EHRD) == 0) + return(FALSE); +#ifdef PHASE2 + if (ha[0] == 0x09 && ha[1] == 0x00 && ha[2] == 0x07 && ha[3] == 0x00) + return(FALSE); /* zone multicast address */ + logit(LOG_LOTS,"Got mapping for %d/%d.%d", + pa->node, pa->dummy[1], pa->dummy[2]); +#else PHASE2 + logit(LOG_LOTS, "Got mapping for %d", pa->node); +#endif PHASE2 + dumpether(LOG_LOTS, " Address", ha); + + aa = aarptab_get(aih, pa); /* find it or get a new one */ + if (aa->aae_flags & AARP_OKAY) { + if (aa->aae_flags & (AARP_PERM)) /* don't reset these */ + return(FALSE); + if (hard) +#ifdef PHASE2 + logit(LOG_LOTS, "Resetting an old mapping for node %d/%d.%d", + pa->node, pa->dummy[1], pa->dummy[2]); +#else PHASE2 + logit(LOG_LOTS, "Resetting an old mapping for node %d", pa->node); +#endif PHASE2 + } + /* if arp entry is in cache and we aren't doing a hard update */ + /* and the hardware addresses don't match, don't do an update */ + if ((aa->aae_flags & AARP_OKAY) && !hard) { + if (bcmp((caddr_t)aa->aae_eaddr, (caddr_t)ha, EHRD) != 0) { + logit(LOG_BASE, "AARP RESET - ethernet address mismatch"); +#ifdef PHASE2 + logit(LOG_BASE,"node number is %d/%d.%d", + pa->node, pa->dummy[1], pa->dummy[2]); +#else PHASE2 + logit(LOG_BASE,"node number is %d", pa->node); +#endif PHASE2 + dumpether(LOG_BASE, "incoming address", ha); + dumpether(LOG_BASE, "cached address",aa->aae_eaddr); + aa->aae_flags &= ~AARP_OKAY; /* there we go */ + return(FALSE); + } + } else { + bcopy((caddr_t)pa, (caddr_t)&aa->aae_pa, sizeof(aa->aae_pa)); + bcopy((caddr_t)ha, (caddr_t)aa->aae_eaddr, EHRD); /* copy in mapping */ + aa->aae_flags = AARP_OKAY; /* reset resolving flag if set too */ + } + aa->aae_ttl = AARP_TTL; /* reset */ + return(TRUE); +} + +/*ARGSUSED*/ +#ifdef sony_news +aarp_listener(fd, aih, proth, buf, len) +#else sony_news +private +aarp_listener(fd, aih, proth) +#endif sony_news +int fd; /* dummy */ +AI_HANDLE *aih; +int proth; +#ifdef sony_news +u_char *buf; +int len; +#endif sony_news +{ + struct ethertalkaddr *dpa, *spa; + byte *sha; + struct ether_arp arp; + + struct ai_host_node *hn; + + /* note, we use read protocol because we cannot trust incoming etalk */ + /* addresses from DLI on Ultrix if sent to broadcast */ +#ifdef sony_news + bcopy(buf, &arp, sizeof(arp)); +#else sony_news + if (pi_read(aih->ai_ph, &arp, sizeof(arp)) < 0) + return; +#endif sony_news + + if (ntohs(arp.arp_hrd) != ARPHRD_ETHER || /* not ethernet? */ + ntohs(arp.arp_pro) != ETHERTYPE_APPLETALK || /* not appletalk? */ + arp.arp_hln != EHRD || /* wrong hrdwr length */ + arp.arp_pln != ETPL) { /* wrong protocol length? */ + logit(5, "AARP drop!: %d %d %d %d", + arp.arp_hrd, arp.arp_pro, arp.arp_hln, arp.arp_pln); + return; + } + + dpa = (struct ethertalkaddr *)arp.arp_tpa; /* reformat */ + spa = (struct ethertalkaddr *)arp.arp_spa; /* reformat */ + /* copy these because sunos4.0 they are struct's rather than arrays */ + /* and we need to take the address and hate the way the warning */ + /* messages come up, besides saves us a bit on dereferncing */ +#if defined(sony_news) || defined(__osf__) + sha = (byte *)&(arp.arp_sha[0]); +#else sony_news + sha = (byte *)&arp.arp_sha; +#endif sony_news + + /* check dummy bytes? */ + /* this is the first one of these, so ... the reason we have an & */ + /* in front of an array is that under sunos4.0, arp_sha, etc. are */ + /* now structs */ + if (bcmp((caddr_t)sha, (caddr_t)aih->ai_eaddr, EHRD) == 0) { + logit(LOG_LOTS, "Packet from self"); + return; + } + if (host_node(aih, spa, AI_NODE_OKAY)) { +#ifdef PHASE2 + logit(LOG_BASE, "Address conflict %d/%d.%d\n", + spa->node, spa->dummy[1], spa->dummy[2]); +#else PHASE2 + logit(LOG_BASE, "Address conflict %d\n", spa->node); +#endif PHASE2 + } + + switch (ntohs(arp.arp_op)) { + case ARPOP_PROBE: + if ((hn = host_node(aih, spa, 0))) { + switch (hn->aihn_state) { + case AI_NODE_OKAY: + aarp_reply(aih, &hn->aihn_pa, &arp); /* defend! */ + break; + case AI_NODE_PROBE: + hn->aihn_state = AI_NODE_UNUSED; + break; + default: + break; + } + break; + } + aarp_probed(aih, sha, spa, arp); /* check over entry since we got probe */ + break; + case ARPOP_REQUEST: + if (host_node(aih, spa, 0)) /* drop if request from one of */ + break; /* our address */ + /* probably should be more discriminating here */ + /* insert new entry */ + aarp_insert(aih, sha, (struct ethertalkaddr *)arp.arp_spa, TRUE); + if ((hn = host_node(aih, dpa, AI_NODE_OKAY))) + aarp_reply(aih, &hn->aihn_pa, &arp); + break; + case ARPOP_REPLY: + /* check to see that dpa isn't one we are probing */ + /* if it is, then things are bad for that potential addr */ + + if ((hn = host_node(aih, dpa, AI_NODE_PROBE))) { + hn->aihn_state = AI_NODE_UNUSED; + break; + } + /* drop if source was self (whether ready or not) */ + /* don't need check before here because replies are not broadcast, */ + /* so why would we get it :-) */ + if (host_node(aih, spa, 0)) + break; + /* mark in our tables */ + aarp_insert(aih, sha, spa, TRUE); + break; + } +} + +/* AARP PROBE: some hooks in the listener too */ +/* + * initialize and start AARP probe process + * +*/ +struct aarp_aphandle { + AI_HANDLE *aaph_aih; /* protocol handle */ + AARP_ENTRY *aaph_ae; /* request: point to node handle */ + struct ai_host_node *aaph_node; /* probe: which node # probing */ + struct ether_arp aaph_arp; /* arp */ + int (*aaph_callback)(); /* callback on success on request/probe */ + caddr_t aaph_callback_arg; /* and initial argument */ + int aaph_callback_result; /* this is the result */ + int aaph_tries; /* tries to do */ +}; + +/* + * acquire an ethertalk node + * doesn't try new node address + * callback is (*callback)(callback_arg, result) + * where result < 0 if couldn't + * result >= 0 ==> index of host node address for get host addr + * + * return: > 0 - bad node + * = 0 - okay + * < 0 - internal error + * +*/ + +/* probe state */ +export int +aarp_acquire_etalk_node(aih, initial_node, callback, callback_arg) +AI_HANDLE *aih; +struct ethertalkaddr *initial_node; +int (*callback)(); /* callback routine */ +caddr_t callback_arg; /* arg for callback */ +{ + struct aarp_aphandle *probe; + struct ai_host_node *ahn; + int whichnode = -1; + int i; + + /* bad node */ + if (initial_node->node == 0 || initial_node->node == 255) + return(1); +#ifdef PHASE2 + if (initial_node->node == 254) /* reserved */ + return(1); +#endif PHASE2 + if (aarptab_find(aih, initial_node) || host_node(aih, initial_node, 0)) { + /* callback ? */ + return(1); + } + + /* get probe structure */ + probe = (struct aarp_aphandle *)malloc(sizeof(struct aarp_aphandle)); + if (probe == NULL) + return(-1); + m_probe++; + for (ahn = aih->ai_nodes, i = 0; i < aih->ai_numnode; i++, ahn++) { + if (ahn->aihn_state) /* node in use or in probe */ + continue; + whichnode = i; /* got a good one, ahn set */ + break; + } + if (whichnode < 0) { /* no node allocate? */ + if (aih->ai_numnode >= aih->ai_maxnode) /* can't */ + return(-1); + whichnode = aih->ai_numnode++; /* next node */ + ahn = &aih->ai_nodes[whichnode]; /* get node */ + } + + /* initialize probe arp packet */ + bzero(&probe->aaph_arp, sizeof(probe->aaph_arp)); + probe->aaph_arp.arp_hrd = htons(ARPHRD_ETHER); + probe->aaph_arp.arp_pro = htons(ETHERTYPE_APPLETALK); + probe->aaph_arp.arp_op = htons(ARPOP_PROBE); + probe->aaph_arp.arp_hln = EHRD; + probe->aaph_arp.arp_pln = ETPL; +#if defined(sony_news) || defined(__osf__) + bcopy((caddr_t)aih->ai_eaddr, (caddr_t)&(probe->aaph_arp.arp_sha[0]), EHRD); +#else sony_news + bcopy((caddr_t)aih->ai_eaddr, (caddr_t)&probe->aaph_arp.arp_sha, EHRD); +#endif sony_news + + bcopy((caddr_t)initial_node, (caddr_t)probe->aaph_arp.arp_spa, ETPL); + bcopy((caddr_t)initial_node, (caddr_t)probe->aaph_arp.arp_tpa, ETPL); + + probe->aaph_tries = AARP_PROBE_TRY; + + probe->aaph_callback = callback; + probe->aaph_callback_arg = callback_arg; + + probe->aaph_aih = aih; /* handle for interface */ + probe->aaph_ae = NULL; /* mark as none */ + + probe->aaph_callback_result = whichnode; /* mark */ + probe->aaph_node = ahn; /* remember it */ + ahn->aihn_state = AI_NODE_PROBE; /* mark in probe state */ + ahn->aihn_pa = *initial_node; /* copy pa */ + + /* do first probe */ + probe_and_request_driver(probe); + return(0); /* okay, we are trying! */ +} + +/* main probe and request driver */ +private +probe_and_request_driver(aphandle) +struct aarp_aphandle *aphandle; +{ + register AARP_ENTRY *enp; + + struct timeval tv; + struct ai_host_node *ahn; + AI_HANDLE *aih = aphandle->aaph_aih; +#ifdef PHASE2 + char abuf[sizeof(struct ether_arp)+8]; +#endif PHASE2 + + /* either of these can be running or both - both really doesn't */ + /* make much sense though */ + enp = aphandle->aaph_ae; /* get pointer to node handle */ + ahn = aphandle->aaph_node; + + if (enp && ahn) { + logit(LOG_LOG, "internal error: probe and request set in aarp driver\n"); + logit(LOG_LOG|L_EXIT, "fatal: please report\n"); + } + + if (enp && (enp->aae_flags & AARP_RESOLVING) == 0) + goto dofree; + if (ahn && ahn->aihn_state == AI_NODE_UNUSED) { + /* tell caller that probe for the node address failed */ + if (aphandle->aaph_callback) + (*aphandle->aaph_callback)(aphandle->aaph_callback_arg, -1); + goto dofree; + } + if (aphandle->aaph_tries <= 0) { /* nothing left to do? */ + /* if we are out of tries, then node has been acquired, hurrah! */ + if (ahn) { + ahn->aihn_state = AI_NODE_OKAY; + } + if (enp) { + enp->aae_flags &= ~AARP_RESOLVING; + } + if (aphandle->aaph_callback) + (*aphandle->aaph_callback)(aphandle->aaph_callback_arg, + aphandle->aaph_callback_result); + goto dofree; + } + /* set timeout to next try */ + aphandle->aaph_tries--; + if (enp) { + /* should decay based on number of tries */ + tv.tv_sec = AARP_REQUEST_TIMEOUT_SEC; + tv.tv_usec = AARP_REQUEST_TIMEOUT_USEC; + } + if (ahn) { + /* set timeout to next try */ + tv.tv_sec = AARP_PROBE_TIMEOUT_SEC; + tv.tv_usec = AARP_PROBE_TIMEOUT_USEC; + } +#ifdef PHASE2 + /* make room for the rest of the ELAP header */ + bcopy(&aphandle->aaph_arp, abuf+8, sizeof(struct ether_arp)); + if (pi_write(aih->ai_ph, abuf, sizeof(abuf), b_eaddr) < 0) +#else PHASE2 +#ifdef sony_news + if (pi_write(aih->ai_ph,&aphandle->aaph_arp, sizeof(struct ether_arp), + b_eaddr, ETHERTYPE_AARP) < 0) +#else sony_news + if (pi_write(aih->ai_ph,&aphandle->aaph_arp, sizeof(struct ether_arp), + b_eaddr) < 0) +#endif sony_news +#endif PHASE2 + logit(LOG_BASE|L_UERR, "pi_write failed: aarp driver"); + /* setup timeout to next (relative timeout) */ + relTimeout(probe_and_request_driver, aphandle, &tv, TRUE); + return; +dofree: + while (remTimeout(probe_and_request_driver, aphandle)) + /* remove while some on queue: NULL STATEMENT */; + /* address was resolving, nothing left */ + free((caddr_t)aphandle); + f_probe++; +} + + +/* + * got a probe on the specified ha, pa pair and pa is not one of ours + * + * mark the mapping pair as suspect. If the specified ha is + * different, then mark it as very suspect. In both cases, the + * "suspect" flag is moving down the ttl of the mapping. + * +*/ +private void +aarp_probed(aih, ha, pa, arp) +AI_HANDLE *aih; +u_char *ha; +struct ethertalkaddr *pa; +struct ether_arp *arp; +{ + AARP_ENTRY *aa; + struct timeval *tv; + int nttl; + + if ((aa = aarptab_find(aih, pa)) == NULL) /* find entry */ + return; /* nothing */ + if ((aa->aae_flags & AARP_OKAY) == 0) /* nothing to do? */ + return; /* ;right */ + /* get reduction factor on ttl */ + nttl = (bcmp((caddr_t)ha, (caddr_t)aa->aae_eaddr, EHRD) == 0) ? + AARP_PROBED_SAME : AARP_PROBED_DIFF; + /* update ttl to lessor of new or old */ + if (nttl < aa->aae_ttl) + aa->aae_ttl = nttl; +} + +/* + * reply to an arp request packet + * + * note, smashes the arp packet + * +*/ +private void +aarp_reply(aih, pa, arp) +AI_HANDLE *aih; +struct ethertalkaddr *pa; +struct ether_arp *arp; +{ + caddr_t sha, tha; +#ifdef PHASE2 + char abuf[sizeof(struct ether_arp)+8]; +#endif PHASE2 + +#if defined(sony_news) || defined(__osf__) + sha = (caddr_t)&(arp->arp_sha[0]); /* need & because can be struct */ + tha = (caddr_t)&(arp->arp_tha[0]); +#else sony_news + sha = (caddr_t)&arp->arp_sha; /* need & because can be struct */ + tha = (caddr_t)&arp->arp_tha; +#endif sony_news + + bcopy(sha, tha, EHRD); + bcopy((caddr_t)arp->arp_spa, (caddr_t)arp->arp_tpa, ETPL); + arp->arp_op = htons(ARPOP_REPLY); + bcopy((caddr_t)aih->ai_eaddr, sha, EHRD); + /* copy in our node */ + bcopy((caddr_t)pa, (caddr_t)arp->arp_spa, ETPL); +#ifdef PHASE2 + bcopy(arp, abuf+8, sizeof(struct ether_arp)); + if (pi_write(aih->ai_ph, abuf, sizeof(abuf), tha) < 0) { +#else PHASE2 +#ifdef sony_news + if (pi_write(aih->ai_ph, arp, sizeof(*arp), tha, ETHERTYPE_AARP) < 0) { +#else sony_news + if (pi_write(aih->ai_ph, arp, sizeof(*arp), tha) < 0) { +#endif sony_news +#endif PHASE2 + logit(LOG_LOG|L_UERR, "etsend"); + } +} + +/* + * do an aarp request + * - doesn't check that target is ourselves +*/ +private void +aarp_request(aih, tpa) +AI_HANDLE *aih; +struct ethertalkaddr *tpa; +{ + AARP_ENTRY *aa; + struct aarp_aphandle *ap; +#ifdef PHASE2 + int forcePROBE = tpa->dummy[0]; + /* can you say 'hack' */ + tpa->dummy[0] = 0; +#endif PHASE2 + + if ((aa = aarptab_get(aih, tpa)) == NULL) /* get new */ + return; + if (aa->aae_flags & (AARP_RESOLVING)) + return; + /* bad condition */ + if (aa->aae_flags & (AARP_PERM)) + return; + ap = (struct aarp_aphandle *)malloc(sizeof(struct aarp_aphandle)); + m_probe++; + aa->aae_pa = *tpa; + aa->aae_flags |= AARP_RESOLVING; + aa->aae_flags &= ~AARP_OKAY; /* make sure! (can be) */ + + /* establish arp packet */ + bzero(&ap->aaph_arp, sizeof(ap->aaph_arp)); +#ifdef PHASE2 + ap->aaph_arp.arp_op = htons((forcePROBE) ? ARPOP_PROBE : ARPOP_REQUEST); +#else PHASE2 + ap->aaph_arp.arp_op = htons(ARPOP_REQUEST); +#endif PHASE2 + ap->aaph_arp.arp_hrd = htons(ARPHRD_ETHER); + ap->aaph_arp.arp_pro = htons(ETHERTYPE_APPLETALK); + ap->aaph_arp.arp_hln = EHRD; + ap->aaph_arp.arp_pln = ETPL; +#ifdef notdef + bcopy((caddr_t)snode->aae_eaddr, (caddr_t)&ap->aaph_arp.arp_sha, EHRD); + bcopy((caddr_t)&snode->aae_pa, (caddr_t)ap->aaph_arp.arp_spa, + sizeof(struct ethertalkaddr)); +#else +#if defined(sony_news) || defined(__osf__) + bcopy((caddr_t)aih->ai_eaddr, (caddr_t)&(ap->aaph_arp.arp_sha[0]), EHRD); +#else sony_news + bcopy((caddr_t)aih->ai_eaddr, (caddr_t)&ap->aaph_arp.arp_sha, EHRD); +#endif sony_news + /* grab any source protocol address (on right interface already!) */ + { int i = aih->ai_numnode; + struct ai_host_node *an = aih->ai_nodes; + + for ( ; i ; an++, i--) + if (an->aihn_state == AI_NODE_OKAY) { + bcopy((caddr_t)&an->aihn_pa, (caddr_t)ap->aaph_arp.arp_spa, + sizeof(struct ethertalkaddr)); + break; + } + if (i == 0) /* no host node! drop request for now */ + return; + } +#endif + bcopy((caddr_t)tpa, (caddr_t)ap->aaph_arp.arp_tpa, sizeof(*tpa)); +#ifdef PHASE2 + if (forcePROBE) + bcopy((caddr_t)tpa, (caddr_t)ap->aaph_arp.arp_spa, sizeof(*tpa)); +#endif PHASE2 + + ap->aaph_ae = aa; /* remember this for later */ + ap->aaph_tries = AARP_REQUEST_TRY; + ap->aaph_node = NULL; /* clear this */ + ap->aaph_aih = aih; + ap->aaph_callback = NULL; + + probe_and_request_driver(ap); +} + + +/* + * See if the specified ethertalk address "n" is a host address + * return true if it is, false o.w. + * + * if flag is set, then it indicates that we only want to check against + * host nodes in the state flag is set to. + * +*/ +private struct ai_host_node * +host_node(aih, n, flag) +AI_HANDLE *aih; +struct ethertalkaddr *n; +{ + register int i = aih->ai_numnode; + register struct ai_host_node *ai_nodes = aih->ai_nodes; + + for ( ; i ; ai_nodes++, i--) + if (ai_nodes->aihn_state) { + if (flag && ai_nodes->aihn_state != flag) + continue; +#ifdef PHASE2 + if (ai_nodes->aihn_pa.node + && ai_nodes->aihn_pa.dummy[1] == n->dummy[1] + && ai_nodes->aihn_pa.dummy[2] == n->dummy[2] + && ai_nodes->aihn_pa.node == n->node) + return(ai_nodes); +#else PHASE2 + if (ai_nodes->aihn_pa.node && ai_nodes->aihn_pa.node == n->node) + return(ai_nodes); +#endif PHASE2 + } + return(NULL); +} + + +export +aarp_dump_stats(fd, ah) +FILE *fd; +AI_HANDLE *ah; +{ + struct hash_statistics *s = h_statistics(ah->ai_aarptab); + + fprintf(fd, " aarp hash table statistics, %d nodes in use\n", + s->hs_used); + fprintf(fd, "\t%d lookups since last rehash, average distance %.02f\n", + s->hs_lnum, s->hs_lnum ? ((float)s->hs_lsum / s->hs_lnum) : 0.0); + fprintf(fd, "\t%d lookups total, average distance %.02f\n", + s->hs_clnum, s->hs_clnum ? ((float)s->hs_clsum / s->hs_clnum) : 0.0); + putc('\n', fd); + fprintf(fd, " %d aarptab entries allocated\n", m_aarptab); + fprintf(fd, " %d probe/request handles, %d freed\n", m_probe, f_probe); + putc('\n', fd); +} + +export +aarp_dump_tables(fd, aih) +FILE *fd; +AI_HANDLE *aih; +{ + AARP_ENTRY *n; + +#ifdef AARP_DUMP_TABLE + fprintf(fd," ARP table dump for interface\n"); + for (n = aarptab_node_list; n ; n = n->aae_next) { + if (aih->ai_aarptab != n->aae_aarptab || n->aae_flags == 0) + continue; + fprintf(fd, + " node %d, %02x:%02x:%02x:%02x:%02x:%02x, used %d flags %s%s%sttl %d\n", + n->aae_pa.node, + n->aae_eaddr[0], n->aae_eaddr[1], n->aae_eaddr[2], + n->aae_eaddr[3], n->aae_eaddr[4], n->aae_eaddr[5], + n->aae_used, + AARP_FNAME_OKAY(n->aae_flags), + AARP_FNAME_RESOLVING(n->aae_flags), + AARP_FNAME_PERM(n->aae_flags), n->aae_ttl); + } + putc('\n', fd); +#endif +} diff --git a/support/uab/aarp.h b/support/uab/aarp.h new file mode 100644 index 0000000..c576670 --- /dev/null +++ b/support/uab/aarp.h @@ -0,0 +1,72 @@ +/* + * $Author: djh $ $Date: 91/02/15 23:07:10 $ + * $Header: aarp.h,v 2.1 91/02/15 23:07:10 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * aarp.h Apple Address Resolution Protocol interface + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * September 1988 CCKim Created + * +*/ + +/* + * intializes aarp for this interface: upto maxnode nodes for this interface +*/ +caddr_t aarp_init( /* char *dev, int devno, int maxnode */); + +/* + * returns a node address for this interface. index specifies which + * one (can have multiple) +*/ +int aarp_get_host_addr(/*struct ethertalkaddr *pa, int index*/); + + +/* + * resolves a ethertalk node address into an ethertalk address + * returns pointer to address in eaddr + * args: caddr_t ah, struct ethertalkaddr *pa, + * boolean wantbr, u_char **eaddr + * +*/ +int aarp_resolve(); + +/* + * inserts the a mapping into the aarp table + * + * if flag is "true" then will override any mappings already there + * [used by ethertalk for gleaning] + * + * args: caddr_t ah, u_char ha[6], struct ethertalkaddr *pa, int flag + * +*/ +int aarp_insert(); + +/* + * acquire an ethertalk node address + * tries to acquire the ethertalk node address specfied in node. + * calls back with the host address index: + * (*callback)(callback_arg, index) + * + * args: caddr_t ah, struct ethertalkaddr *node, int (*callback)(), + * caddr_t callback_arg + * +*/ +int aarp_acquire_etalk_node(); + diff --git a/support/uab/aarp_defs.h b/support/uab/aarp_defs.h new file mode 100644 index 0000000..c7f36eb --- /dev/null +++ b/support/uab/aarp_defs.h @@ -0,0 +1,128 @@ +/* + * $Author: djh $ $Date: 1994/10/10 08:56:13 $ + * $Header: /mac/src/cap60/support/uab/RCS/aarp_defs.h,v 2.2 1994/10/10 08:56:13 djh Rel djh $ + * $Revision: 2.2 $ +*/ + +/* + * aarp_defs.h Apple Address Resolution Protocol definitions + * + * Meant only for the aarp module! + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * August 1988 CCKim Created + * +*/ + +#define AARP_DUMP_TABLE "yes" /* turn this on */ + +/* + * AARP Ethernet type + * +*/ +#ifndef ETHERTYPE_AARP +# define ETHERTYPE_AARP 0x80f3 +#endif + +/* probe op, define if not defined */ +#ifndef ARPOP_PROBE +# define ARPOP_PROBE 3 +#endif + +/* + * AARP table management + * +*/ + +/* An ethertalk node address (should probably merge with aarptab) */ +typedef struct aarp_entry { + struct ethertalkaddr aae_pa; /* protocol address */ + int aae_flags; /* flags */ +#define AARP_OKAY 0x1 /* resolved */ +#define AARP_RESOLVING 0x2 /* trying to resolve this */ +#define AARP_PERM 0x4 /* permanent (not used) */ +#define AARP_FNAME_OKAY(f) ((f&AARP_OKAY) ? "okay " : "") +#define AARP_FNAME_RESOLVING(f) ((f&AARP_RESOLVING) ? "resolving " : "") +#define AARP_FNAME_PERM(f) ((f&AARP_PERM) ? "perm " : "") + u_char aae_eaddr[EHRD]; /* hardware address */ + int aae_ttl; /* time to live */ + int aae_used; /* # of times used */ + struct aarp_entry *aae_next; /* next in list of all */ + caddr_t aae_aarptab; /* back pointer */ +} AARP_ENTRY; + + +/* time to live */ +#define AARP_TTL 5 /* 4*AARP_SCAN_TIME */ +#define AARP_SCAN_TIME (60) /* scan every minute */ +/* we adjust ttls if an incoming probe comes in */ +/* if the ha is the same, ttl gets dropped. if the ha */ +/* is different, the ttl is smacked dead */ +#define AARP_PROBED_SAME 1 /* set time to live to AARP_SCAN_TIME */ +#define AARP_PROBED_DIFF 0 /* set time to live to zero */ + +/* + * AARP probe/request managment + * +*/ +/* number of times to probe an address */ +#define AARP_PROBE_TRY 15 +/* timeout between probes */ +#define AARP_PROBE_TIMEOUT_SEC 0 +#define AARP_PROBE_TIMEOUT_USEC 100000 /* 1/10 second */ +/* same for requests */ +#define AARP_REQUEST_TRY 10 +#define AARP_REQUEST_TIMEOUT_SEC 0 +#define AARP_REQUEST_TIMEOUT_USEC 250000 /* 1/4th second */ + +/* + * AARP table/interface management + * +*/ +/* number of buckets in the aarp hash table */ +#define AARP_TAB_SIZE 15 + +/* + * one of these are created for every "interface" open + * +*/ +typedef struct aarp_interface_handle { + int ai_ph; /* protocol handle */ + caddr_t ai_aarptab; /* aarp table */ + int ai_accesses; /* count of access to arp tabl */ +#define AI_AARPTAB_EVAL_POINT 2000 +#define AI_AARPTAB_REHASH_POINT 200 /* more than avg. of 2 access */ + struct ai_host_node { /* host's node table */ + struct ethertalkaddr aihn_pa; /* our protocol address */ + int aihn_state; +#define AI_NODE_UNUSED 0 /* node not in use */ +#define AI_NODE_PROBE -1 /* node in probe state */ +#define AI_NODE_OKAY 1 /* node okay */ + } *ai_nodes; + int ai_maxnode; + int ai_numnode; /* number of nodes in use */ + int ai_flags; /* interface flags */ + u_char ai_eaddr[EHRD]; /* enet addr */ + /* probably should return this integer instead of the handle */ +} AI_HANDLE; + + +/* exported procedure definitions */ +export caddr_t aarp_init(); +export int aarp_resolve(); +export int aarp_insert(); +export int aarp_acquire_etalk_node(); + diff --git a/support/uab/asyncatalk.c b/support/uab/asyncatalk.c new file mode 100644 index 0000000..fb2cc78 --- /dev/null +++ b/support/uab/asyncatalk.c @@ -0,0 +1,483 @@ +/* + * asyncatalk.c - local UNIX async appletalk interface + * + * Async AppleTalk for UNIX: + * + * We allocate a complete (non-extended) network (async_net) for use by + * asynchronous appletalk clients on this UNIX host. Packets destined for async + * nodes on async_net are sent to the async node client process at the special + * UDP port (nodeNum+PortOffset). If the packets are broadcast, they are sent + * to the UDP port (aaBroad) for redirection. + * + * aaBroad is monitored by our async_listener(). Packets are only resent to + * preregistered nodes. This cuts down on the amount of extra traffic sent for + * broadcasts. This function is normally performed by the asyncad daemon if + * async appletalk is being used in conjunction with a hardware gateway + * (Webster MultiPort Gateway or FastPath). + * + * With UAB, we extend the protocol slightly by using aaBroad to receive + * packets from the async client for gatewaying onto other nets or for local + * processing (zone lists etc). This protocol extension is only valid when + * the async node client process and the bridge IP address are the same host. + * Normally, we send to the bridge on a range of UDP ports (cf sockets). + * This is uneconomical for a UNIX KIP bridge as we have to open too many ports. + * + * Note that the "async bridge" exists only on 'bridge_net' as 'bridge_node'. + * We treat async_net as a remote net, not directly connected to us (we + * don't have a node number on the net). + * + * Currently: + * PortOffset 0xaa00 + * aaBroad 750 Unofficial! + * + * Edit History: + * + * December 1, 1990 djh@munnari.OZ.AU created + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "if_desc.h" /* describes "if" */ +#include "ddpport.h" /* describes a ddp port to "lap" */ +#include "log.h" + +/* some logging ideas */ +#define LOG_LOG 0 +#define LOG_PRIMARY 1 +#define LOG_LOTS 9 + +private int asyncatalk_init(); +private int asyncatalk_getnode(); +private int asyncatalk_listener(); +private int asyncatalk_send_ddp(); +private int asyncatalk_stats(); +private int asyncatalk_tables(); +private NODE *asyncatalk_ddpnode_to_node(); + +/* describe our interface to the world */ +private char *asyncatalk_lap_keywords[] = { + "asyncatalk", + "async", + NULL + }; + +export struct lap_description asyncatalk_lap_description = { + "Asynchronous AppleTalk Link Access Protocol", + asyncatalk_lap_keywords, + TRUE, /* need more than just key */ + asyncatalk_init, /* init routine */ + asyncatalk_stats, /* stats routine */ + asyncatalk_tables /* tables */ +}; + +extern word async_net; /* import from atalkdbm.c */ +extern char async_zone[34]; /* import from atalkdbm.c */ +extern word bridge_net; /* import from atalkdbm.c */ +extern byte bridge_node; /* import from atalkdbm.c */ + +private char *astat_names[] = { +#define ES_PKT_INPUT 0 /* packets input */ + "packets input", +#define ES_PKT_ACCEPTED 1 /* accepted input packets */ + "packets accepted", +#define ES_PKT_BROADCAST 2 /* packets for rebroadcast */ + "packets for rebroadcast", +#define ES_PKT_NOTFORME 3 /* packets not for me */ + "packets not for me", +#define ES_PKT_BAD 4 /* bad packets */ + "bad packets", +#define ES_BYTES_INPUT 5 /* accepted bytes */ + "bytes input", +#define ES_ERR_INPUT 6 /* number of input errors */ + "input errors", +#define ES_PKT_NOHANDLER 7 /* no handler */ + "packets without handlers", +#define ES_PKT_OUTPUT 8 /* packets output */ + "packets output", +#define ES_BYTES_OUTPUT 9 /* bytes output */ + "bytes output", +#define ES_ERR_OUTPUT 10 /* output errors */ + "output errors", +#define ES_RESOLVE_ERR_OUTPUT 11 /* could not resolve */ + "output resolve error" +#define ES_NUM_COUNTERS 12 +}; + +typedef struct asyncatalk_handle { + int ah_state; /* this connection state */ +#define AALAP_WAITING -1 /* almost ready */ +#define AALAP_BAD 0 /* error */ +#define AALAP_READY 1 /* okay */ + int ah_skt; /* asyncatalk socket */ + PORT_T ah_port; /* interface port */ + NODE ah_enode; /* host node id info */ + IDESC_TYPE *ah_id; /* interface description */ + int ah_stats[ES_NUM_COUNTERS]; /* statistics */ +} A_HANDLE; + +struct ap { /* DDP AppleTalk inside UDP */ + u_char ldst; /* even */ + u_char lsrc; /* odd */ + u_char ltype; /* even */ + u_char dd[8]; /* hop&len,cksum,dstnet,srcnet */ + u_char dstnode; + u_char srcnode; + u_char dstskt; + u_char srcskt; + u_char dtype; + u_char data[1024]; +}; + +#define aaBroad 750 /* for UAB really aaReBroad */ +#define PortOffset 0xaa00 /* start of range for nodes */ +#define MAXNODE 0x7f /* easier if only 2^n-1 */ + +#define UP 1 +#define DOWN 0 +short node_is_up[MAXNODE]; /* list of nodes and states */ + +/* + * async set-up, call with network number, interface name and number + * (the async flag is currently ignored - nothing to wait for ...) + * + */ + +private int +asyncatalk_init(id, async) +IDESC_TYPE *id; +int async; +{ + long len; + A_HANDLE *ah; + int i, flags; /* some more port description */ + struct sockaddr_in sin; + void start_listener(); + private int async_listener(); + + if (async_net != 0) { + logit(LOG_PRIMARY, "only allowed one async network per host!"); + return(FALSE); + } + + for(i = 0; i < MAXNODE ; i++) + node_is_up[i] = DOWN; + + if ((ah = (A_HANDLE *)malloc(sizeof(A_HANDLE))) == NULL) { + logit(LOG_PRIMARY, "can't get memory for port details"); + return(FALSE); + } + + /* link in both directions */ + id->id_ifuse = (caddr_t) ah; /* mark */ + ah->ah_id = id; /* remember this */ + + ah->ah_state = AALAP_WAITING; /* mark not quite ready */ + ah->ah_enode.n_size = 8; /* 8 bits */ + ah->ah_enode.n_bytes = 1; /* 1 byte */ + ah->ah_enode.n_id[0] = 0; /* this is it, and it's zero */ + /* the gateway doesn't have a node on the async net, it just forwards */ + + async_net = id->id_network; + if(id->id_zone != NULL) + strncpy(async_zone, id->id_zone+1, *id->id_zone); + +#if (defined(__386BSD__) || defined(__FreeBSD__)) + if((ah->ah_skt = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { +#else /* __386BSD__ */ + if((ah->ah_skt = socket(AF_INET, SOCK_DGRAM, 0, 0)) < 0) { +#endif /* __386BSD__ */ + logit(LOG_PRIMARY, "asyncatalk: Couldn't open output UDP socket"); + return(FALSE); + } + + flags = PORT_WANTSLONGDDP; /* only send out long DDP */ + flags &= ~PORT_FULLRTMP; /* don't need RTMP advertising */ + flags &= ~PORT_NEEDSBROADCAST; /* we don't want broadcasts */ + + /* establish port */ + ah->ah_port = port_create(id->id_network, ah->ah_enode.n_id[0], id->id_zone, + &ah->ah_enode, /* just junk with async */ + flags, /* details, details */ + (caddr_t) ah, /* useful storage */ + asyncatalk_send_ddp, /* send interface */ + asyncatalk_ddpnode_to_node, /* map from ddp */ + NULL, /* map to ddp */ + id->id_local); /* demuxer */ + + if (ah->ah_port) { + start_listener(ah); + ah->ah_state = AALAP_READY; + logit(LOG_PRIMARY, "port %d valid on interface %s%d", + ah->ah_port, id->id_intf, id->id_intfno); + } else { + ah->ah_state = AALAP_BAD; + logit(LOG_PRIMARY,"interface %s%d: no space for port", + id->id_intf, id->id_intfno); + return(FALSE); + } + return(TRUE); +} + +/* + * start our gateway listener on UDP port 750 (aaBroad) + * (this is really a KIP listener for bridge_net & bridge_node) + * + */ + +void +start_listener(ah) +A_HANDLE *ah; +{ + int s; + long len; + private int async_listener(); + struct sockaddr_in sin; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + logit(LOG_LOG|L_UERR, "socket: async listener init"); + return; + } + len = 6 * 1024; /* should be enough ?? */ + if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&len, sizeof(long)) != 0) + logit(LOG_PRIMARY, "setsockopt: Couldn't set socket recv buffer size"); + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons(aaBroad); + if (bind(s, (caddr_t) &sin, sizeof(sin)) < 0) { + logit(LOG_LOG|L_UERR, "bind: async listener init"); + close(s); + return; + } + init_fdlistening(); + fdlistener(s, async_listener, (caddr_t) ah, 0); + return; +} + +/* + * resolve a ddp node number to a node address on the specified port + * (note: do we need more information in some cases?) +*/ + +private NODE * +asyncatalk_ddpnode_to_node(port, ddpnet, ddpnode) +PORT_T port; +word ddpnet; +byte ddpnode; +{ + static NODE node = { 1, 8 }; /* initialize */ + int myddpnet = PORT_DDPNET(port); + + if (ddpnet != 0 && myddpnet != ddpnet) /* only allow this net! */ + return(NULL); + node.n_id[0] = ddpnode; /* make node */ + return(&node); +} + +/* + * listener gets called by low-level select when + * a packet is ready to be read from the network + * + */ + +private int +async_listener(fd, ah, intarg) +int fd; +A_HANDLE *ah; +int intarg; +{ + DDP hdr; + struct ap ap; + struct sockaddr_in fsin; + int fsinlen, n, i, ddpnode; + int *stats = ah->ah_stats; + word dstnet; + + if(bridge_node == 0) { /* not ready yet */ + logit(LOG_PRIMARY,"async_listener: received packet, gateway not ready"); + stats[ES_PKT_NOHANDLER]++; + return(1); + } + + fsinlen = sizeof(fsin); + if((n = recvfrom(fd, (caddr_t) &ap, sizeof(ap), + 0, (caddr_t) &fsin, &fsinlen)) < 0) { + logit(LOG_PRIMARY, "async_listener: recvfrom failed"); + stats[ES_ERR_INPUT]++; + return(1); + } + + if(ap.ldst == 0xC0 && ap.lsrc == 0xDE) { + /* an async node is registering with us */ + i = ap.srcskt & MAXNODE; /* who from */ + node_is_up[i] = ap.ltype; /* up or down */ + logit(LOG_PRIMARY,"async node %d is %s", i, (ap.ltype) ? "UP" : "DOWN"); + return(0); + } + + if(ap.ldst != 0xFA || ap.lsrc != 0xCE || ap.ltype != 2) { + logit(LOG_PRIMARY, "async_listener: found a bad packet"); + stats[ES_PKT_BAD]++; + return(1); /* not a valid lap header */ + } + + stats[ES_PKT_INPUT]++; + stats[ES_BYTES_INPUT] += n; + n -= (ddpSize+3); /* remove header bytes */ + + bcopy(ap.dd, &hdr, sizeof(hdr)); + dstnet = ntohs(hdr.dstNet); + + /* if not a broadcast on our net, route it internally */ + + if(!(dstnet == async_net && hdr.dstNode == 0xff)) { + ddp_route(ah->ah_port, &hdr, ap.data, n, (hdr.dstNode == 0xff)); + stats[ES_PKT_ACCEPTED]++; + return(0); + } + + stats[ES_PKT_BROADCAST]++; + + /* here we have a valid broadcast, redirect it to nodes */ + + fsin.sin_family = AF_INET; + fsin.sin_addr.s_addr = inet_addr("127.0.0.1"); + + for(i = 1 ; i < MAXNODE ; i++) { /* note: 0 is invalid node # */ + if(node_is_up[i]) { + fsin.sin_port = htons((short)i + PortOffset); + if(sendto(fd, (caddr_t) &ap, n, 0, &fsin, sizeof(fsin)) != n) { + logit(LOG_PRIMARY, "rebroadcast: sendto failed"); + stats[ES_ERR_OUTPUT]++; /* count and ignore */ + } + } + } + return(0); +} + +/* + * send a ddp packet on async appletalk (take long DDP only) + * + * port = port to send on + * dstnode == destination async node + * laptype == laptype of packet (header) + * header = packet header (for laptype) + * hsize = packet header length + * data = data + * dlen = datalength + * + * Should we be doing the broadcast multiplexing in here to save time ? + */ + +private +asyncatalk_send_ddp(port, dstnode, laptype, header, hsize, data, dlen) +PORT_T port; +NODE *dstnode; +int laptype; +byte *header; +int hsize; +u_char *data; +int dlen; +{ + DDP *ddp; + word length; + struct ap ap; + struct sockaddr_in sin; + A_HANDLE *ah = PORT_GETLOCAL(port, A_HANDLE *); + int *stats = ah->ah_stats; + + if (bridge_node == 0) { /* not ready, drop it */ + stats[ES_RESOLVE_ERR_OUTPUT]++; + return(-1); + } + + if (hsize != ddpSize) { /* drop it */ + stats[ES_ERR_OUTPUT]++; + return(-1); + } + + if (ah->ah_state != AALAP_READY) { /* drop it */ + stats[ES_ERR_OUTPUT]++; + return(-1); + } + + if (dstnode == NULL) { /* drop it */ + stats[ES_ERR_OUTPUT]++; + return(-1); + } + + if (dstnode->n_size != ah->ah_enode.n_size) { /* drop it */ + stats[ES_ERR_OUTPUT]++; + return(-1); + } + + ddp = (DDP *) header; + + ap.ldst = 0xFA; /* magic */ + ap.lsrc = 0xCE; /* magic */ + ap.ltype = lapDDP; /* long DDP only on KIP */ + if(ddp->srcNet == htons(async_net)) { /* not really connected */ + ddp->srcNet = htons(bridge_net); + ddp->srcNode = bridge_node; + } + bcopy(header, ap.dd, sizeof(ap.dd)); /* copy, as words are on odd addr */ + ap.dstnode = ddp->dstNode; + ap.srcnode = ddp->srcNode; + ap.dstskt = ddp->dstSkt; + ap.srcskt = ddp->srcSkt; + ap.dtype = ddp->type; + bcopy(data, ap.data, dlen); + sin.sin_family = AF_INET; + sin.sin_port = htons((ap.dstnode==0xff) ? aaBroad : (ap.dstnode+PortOffset)); + sin.sin_addr.s_addr = inet_addr("127.0.0.1"); + + length = dlen + ddpSize + 3; + + if(sendto(ah->ah_skt, (caddr_t)&ap, length, 0, &sin, sizeof(sin)) != length) { + logit(LOG_PRIMARY, "Couldn't write to the asyncatalk client"); + stats[ES_ERR_OUTPUT]++; + return(-1); /* drop it */ + } + stats[ES_PKT_OUTPUT]++; + stats[ES_BYTES_OUTPUT] += length; + return(length); +} + +private int +asyncatalk_stats(fd, id) +FILE *fd; +IDESC_TYPE *id; +{ + int i; + A_HANDLE *ah = (A_HANDLE *)id->id_ifuse; /* get handle */ + + fprintf(fd, "Interface %s%d statisitics\n", id->id_intf, + id->id_intfno); + fprintf(fd, " Interface counters\n"); + for (i = 0; i < ES_NUM_COUNTERS; i++) { + fprintf(fd, " %8d\t%s\n", ah->ah_stats[i], astat_names[i]); + } + putc('\n', fd); /* carriage return */ +} + +private int +asyncatalk_tables(fd, id) +FILE *fd; +IDESC_TYPE *id; +{ + A_HANDLE *ah = (A_HANDLE *)id->id_ifuse; /* get handle */ + + fprintf(fd, "Interface dump for %s%d\n",id->id_intf, id->id_intfno); + putc('\n', fd); /* carriage return */ +} diff --git a/support/uab/bpfiltp.c b/support/uab/bpfiltp.c new file mode 100644 index 0000000..26b4ba1 --- /dev/null +++ b/support/uab/bpfiltp.c @@ -0,0 +1,450 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/05/29 10:45:45 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/bpfiltp.c,v 2.2 1995/05/29 10:45:45 djh Rel djh $"; +static char revision[] = "$Revision: 2.2 $"; + +/* + * bpfiltp.c - Simple "protocol" level interface to BPF + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * July 1988 CCKim Created + * Oct 1993 David Matthews. Modified for Berkley Packet Filter + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "proto_intf.h" +#include "../enet/enet.h" + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int socket; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +static u_int pf_bufsize; +static char *read_buf; + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, dev, devno) +int protocol; +char *dev; +int devno; +{ + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "%s%d",dev,devno); + strncpy(eph->ifr.ifr_name, devnamebuf, sizeof eph->ifr.ifr_name); + eph->ifr.ifr_name[sizeof eph->ifr.ifr_name - 1] = ' '; + + if ((s = init_nit(1024, protocol, &eph->ifr)) < 0) { + return(-1); + } + + eph->inuse = TRUE; + eph->socket = s; + eph->protocol = protocol; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(TRUE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].socket); /* toss listener */ + close(ephlist[edx-1].socket); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize nit on a particular protocol type + * Return: socket if no error, < 0 o.w. +*/ +private int +init_nit(chunksize, protocol, ifr) +u_long chunksize; +u_short protocol; +struct ifreq *ifr; +{ + u_long if_flags; + int s; + char device[64]; + u_int imm; + struct bpf_version vers; + + sprintf(device, "/dev/%s", "bpf"); + + { + register int i; + register int failed = 0; + char enetd[256]; + + /* + * try all ethernet minors from (devname)0 on up. + * (e.g., /dev/enet0 .... ) + * + * Algorithm: assumption is that names start at 0 + * and run up as decimal numbers without gaps. We search + * until we get an error that is not EBUSY (i.e., probably + * either ENXIO or ENOENT), or until we sucessfully open one. + */ + + for (i = 0; !failed ; i++) { + sprintf (enetd, "%s%d", device, i); + if ((s = open (enetd, O_RDWR)) >= 0) + break; + /* if we get past the break, we got an error */ + if (errno != EBUSY) failed++; + } + + if (failed) { + perror("open: /dev/bpfXX"); + return(-1); + } + } + + /* Check the version number. */ + if ((ioctl(s, BIOCVERSION, &vers)) < 0) { + perror("ioctl: get version"); + return(-1); + } + + if (vers.bv_major != BPF_MAJOR_VERSION || + vers.bv_minor < BPF_MINOR_VERSION) { + fprintf(stderr, "Incompatible packet filter version\n"); + return -1; + } + + if (setup_pf(s, protocol) < 0) + return(-1); + + /* We have to read EXACTLY the buffer size. */ + + if ((ioctl(s, BIOCGBLEN, &pf_bufsize)) < 0) { + perror("ioctl: get buffer length "); + return(-1); + } + + read_buf = malloc(pf_bufsize); + + if (ioctl(s, BIOCSETIF, ifr) < 0) { + perror("ioctl: set interface"); + return(-1); + } + + imm = 1; + if (ioctl(s, BIOCIMMEDIATE, &imm) < 0) { + perror("ioctl: set immediate mode"); + return(-1); + } + + return(s); +} + + +/* + * establish protocol filter + * +*/ +setup_pf(s, prot, sock) +int s; +u_short prot; +int sock; +{ + u_short offset; + struct ether_header eh; + struct bpf_program pf; +#define PROG_SIZE 4 + struct bpf_insn pf_insns[PROG_SIZE]; + int ppf=0; + extern int errno; + +#define s_offset(structp, element) (&(((structp)0)->element)) + offset = ((int)s_offset(struct ether_header *, ether_type)); + + pf_insns[ppf].code = BPF_LD+BPF_H+BPF_ABS; + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = offset; + ppf++; + pf_insns[ppf].code = BPF_JMP+BPF_JEQ+BPF_K; + pf_insns[ppf].jt = 1; + pf_insns[ppf].jf = 0; + pf_insns[ppf].k = prot; + ppf++; + pf_insns[ppf].code = BPF_RET+BPF_K; /* Fail. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = 0; + ppf++; + pf_insns[ppf].code = BPF_RET+BPF_K; /* Success. */ + pf_insns[ppf].jt = pf_insns[ppf].jf = 0; + pf_insns[ppf].k = (u_int)-1; + ppf++; + + pf.bf_len = ppf; + pf.bf_insns = pf_insns; + if (ioctl(s, BIOCSETF, &pf) < 0) { + perror("ioctl: protocol filter"); + return(-1); + } + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + if (ioctl(eph->socket, SIOCGIFADDR, &eph->ifr) < 0) { + perror("Ioctl: SIOCGIFADDR"); + return(-1); + } + sa = (struct sockaddr *)&eph->ifr.ifr_data; + bcopy(sa->sa_data, ea, 6); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].socket, listener, arg, edx); +} + +static int +bp_readv(fd, iov, iovlen) +int fd; +struct iovec *iov; +int iovlen; +{ + int cc; + struct bpf_hdr *bp; + char *p; + int i, size; + /* Must read exactly the buffer size. */ + if ((cc = read(fd, read_buf, pf_bufsize)) < 0) { + return(cc); + } + /* The data begins with a header. */ + bp = (struct bpf_hdr*) read_buf; + p = read_buf + bp->bh_hdrlen; + size = bp->bh_caplen; +/* + printf("Packet size %d dest %x:%x:%x:%x:%x:%x from source %x:%x:%x:%x:%x:%x\n", + size, + p[0]&0xff, p[1]&0xff, p[2]&0xff, p[3]&0xff, p[4]&0xff, p[5]&0xff, + p[6]&0xff, p[7]&0xff, p[8]&0xff, p[9]&0xff, p[10]&0xff, p[11]&0xff); +*/ + for (i=0; i 0; i++) { + if (size < iov[i].iov_len) bcopy(p, iov[i].iov_base, size); + else bcopy(p, iov[i].iov_base, iov[i].iov_len); + p += iov[i].iov_len; + size -= iov[i].iov_len; + } + if (size > 0) return (bp->bh_caplen-size); + else return bp->bh_caplen; +} + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = bp_readv(eph->socket, iov, iovlen)) < 0) { + perror("abread"); + } + return(cc); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[2]; + struct ethernet_addresses ea; + int cc; + + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct ephandle *eph; + struct ether_header eh; + struct sockaddr sa; + struct iovec iov[2]; + + iov[0].iov_base = (caddr_t)&eh; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = buflen; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + bcopy(eaddr, &eh.ether_dhost, 6); +#ifdef __FreeBSD__ + /* This should really be fixed in the kernel packet filter code. */ + eh.ether_type = eph->protocol; +#else + eh.ether_type = htons(eph->protocol); +#endif + + if (writev(eph->socket, iov, 2) < 0) { + return(-1); + } + return(buflen); +} + +private char ebuf[2000]; /* big enough */ + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} diff --git a/support/uab/bridge_desc b/support/uab/bridge_desc new file mode 100644 index 0000000..a1efb9a --- /dev/null +++ b/support/uab/bridge_desc @@ -0,0 +1,89 @@ +# +# The bridge description file is a list of the valid ports for a +# particular Unix AppleTalk Bridge (UAB). +# +# In order to minimize the maintaince headache, one may use the same +# file on a multitude of hosts with the host name as a selector. +# +# Each port description is entered on a single line. +# +# The first item in a line is the host selector field. It can be the +# host name (as returned by gethostname). In addition, you can use % to +# match any character. "*" can be used to match any host. Finally, you +# can use "*" at the end of a string to complete a match. (Allowing "*" +# at both the beginning and end or at an arbritrary location is a pain +# because it is an unanchored search -- would have to use a regular +# expression matcher to do this -- ugh). +# [MUST BE SPECIFIED] +# +# The second field contains a tuple that specifies the interface's +# link access protocol (LAP) and any device specific data required. +# +# Valid LAP method specifications: +# ELAP - EtherTalk Link Access Protocol +# EtherTalk - same as above +# ASYNC - Asynchronous AppleTalk for UNIX +# AsyncATalk - same as above +# [MUST BE SPECIFIED] +# +# The device specific data consists of a "device name" followed by an +# colon and a "device number". If the colon is omitted, the device +# number is assumed to be zero. +# +# For Ethertalk, this should be interpreted as a ethernet "tap" device +# (SunOS, Ultrix). For example, "ie:1" for ethertalk on interface ie1. +# +# For Asynchronous Appletalk, this is just a label. Suggested use as:0 +# +# (Note, this is subject to change) +# +# The third field specifies the local demultiplexing delivery +# mechanism for delivery of DDP packets not destined for the bridge +# process. Currently defined mechanisms are: "none" which says there +# will be no other client processes; "mkip" - modified version of kip +# style udp encapsulation using a different udp port range. +# Asynchronous AppleTalk has a built-in delivery mechanism, specify 'none'. +# [MUST BE SPECIFIED] +# +# The fourth and last field specifies two items paired in a +# [,] format. The first is the DDP network that should +# be associated with this port. If you specify zero, the ddp network +# number will be acquired via RTMP if there are other bridges that +# know the network number. Note that only a single network is allowed +# at this point. The network number may be specified as or +# .. In both cases, the number may be decimal +# (no leading zero), octal (leading zero), or hexadecimal (leading 0x +# or 0X). +# +# The second item specifies the zone name associated with +# the ddp network associated with this port. If it is not specified, +# it will be acquired via ZIP if there are other bridges on the +# network that know the zone name. Note: you may omit the comma if +# you do not wish to specify a zone. +# +# For Asynchronous Appletalk, the net number and Zone name MUST BE SPECIFIED +# as there is no associated ZIP mechanism (and we are the only gateway ...) +# +# note, \ can be used to quote a character (like a space in the zone +# name :-) A "\" at the end of a line continues the line. Carriage +# return and line feed both terminate a line. +# +# You should order the file from more specific to less specific (in +# terms of host name matches. Once a match has been found, then only +# matches with the exactly same pattern will succeed! + +#hostname [type,data] local [network,zone] + +munnari.OZ.AU [elap,ie:1] mkip [0] +munnari.OZ.AU [elap,ie:0] mkip [83.1,unimelb-CompSci] +munnari.OZ.AU [async,as:0] none [170.170,unimelb-Async] + +# In this example, munnari.OZ.AU has three interfaces, two using modified KIP +# for local host delivery (EG CAP). The third has no local form of delivery. +# We expect ie1 to find out its Network Number and Zone from another gateway +# using RTMP and ZIP. We seed interface ie0 with the values we want. The end +# result is that munnari.OZ.AU acts as an EtherTalk gateway ... +# +# munnari.OZ.AU also provides an async appletalk network 170.170 in the zone +# unimelb-Async. It is strongly suggested that you provide a separate network +# for async clients to avoid swamping them in lookups. diff --git a/support/uab/ddpport.c b/support/uab/ddpport.c new file mode 100644 index 0000000..7f072ae --- /dev/null +++ b/support/uab/ddpport.c @@ -0,0 +1,227 @@ +static char rcsid[] = "$Author: djh $ $Date: 91/03/13 20:38:01 $"; +static char rcsident[] = "$Header: ddpport.c,v 2.2 91/03/13 20:38:01 djh Exp $"; +static char revision[] = "$Revision: 2.2 $"; + +/* + * ddpport.c - ddp port manager + * + * Handles the ddp port which sits on the interface boundary between + * DDP and LAP. In addition, it carries the "local delivery" or + * demuxing information necessary to pass data to individual modules + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * Sept 4, 1988 CCKim Created + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#ifndef _TYPES +# include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include +#include "ddpport.h" +#include "log.h" + +private PORT *port_list = NULL; + +#define LOG_HIGH 1 +#define LOG_STD 2 + +/* + * Establish an open port. + * ddp_net, ddp_node - ddp network/node to associate with this port + * (0 if not seeded) + * zone - zone if any (null if not) + * node - node id of this port (required) + * local_handle - local data handle for caller + * sendrtn - send data routine + * map_fromddp - map ddp net, node to lap node + * map_laptoddp - map inverse (not used) + * demuxer - demux structure that describes the local delivery mechanism + * +*/ +export PORT_T +port_create(ddp_net, ddp_node, zone, node, flags, local_handle, + sendrtn, map_fromddp, map_laptoddp, demuxer) +word ddp_net; +word ddp_node; +byte *zone; +NODE *node; +int flags; +caddr_t local_handle; +int (*sendrtn)(); +NODE *(*map_fromddp)(); +int (*map_laptoddp)(); +struct mpxddp_module *demuxer; +{ + struct route_entry *re; + PORT *port; + + if ((port = (PORT *)malloc(sizeof(PORT))) == NULL) { + logit(LOG_HIGH|L_UERR, "malloc for port failed"); + return(NULL); + } + port->p_next = NULL; + port->p_flags = flags|PORT_ISCONNECTED; /* mark here */ + port->p_ddp_node = ddp_node; /* mark ddp node */ + port->p_ddp_net = 0; /* mark as unknown for now */ + port->p_zonep = NULL; /* mark as unknown for now */ + port->p_id = node; + port->p_local_data = local_handle; + port->p_send_if = sendrtn; + port->p_map_lap_if = map_fromddp; + port->p_map_ddp_if = map_laptoddp; + port->p_mpx = NULL; + if (ddp_net) { + port_net_ready(port, ddp_net, NULL); /* this will set ddp_net properly */ + if (route_add_host_entry(port, ddp_net, zone) < 0) { + logit(LOG_HIGH,"***couldn't add host route for port %d, net %d.%d", + port, nkipnetnumber(ddp_net), nkipnetnumber(ddp_net)); + } + if (zone) /* if zone, mark that too */ + port_zone_known(port, zone); + } + /* wait until after we set zone, etc if we did */ + if (demuxer) { + if (!port_setdemuxer(port, demuxer)) + logit(LOG_HIGH, "couldn't initialize local divery via %s on port %d\n", + demuxer->mpx_name,port); + } + /* may need to call a lower level grab routine (e.g. if working with */ + /* kernel ddp) */ + return(port); +} + +/* + * set and initialize local delivery: can be called later if not set + * in port create. + * returns true,false + * +*/ +export +port_setdemuxer(port, mpx) +PORT_T port; +struct mpxddp_module *mpx; +{ + int i; + + if (mpx == NULL) + return(TRUE); + + if (!mpx->mpx_init || (i = mpx->mpx_init()) < 0) /* init multiplexor */ + return(FALSE); + if (mpx->mpx_grab) /* if socket grabber */ + ddp_reopen(mpx->mpx_grab, i, NULL); /* then call it */ + port->p_mpx_hdl = i; /* remember handle */ + /* port is ready already! */ + if (mpx->mpx_havenode) /* always mark node (must be known) */ + (*mpx->mpx_havenode)(i, port->p_ddp_node); + /* if netready, send down net */ + if ((port->p_flags & PORT_NETREADY) && mpx->mpx_havenet) + (*mpx->mpx_havenet)(i, port->p_ddp_net, port->p_ddp_node); + /* if portready then send down zone (==> netready) */ + if ((port->p_flags & PORT_ISREADY) && mpx->mpx_havezone) + (*mpx->mpx_havezone)(i, port->p_zonep); + port->p_mpx = mpx; + return(TRUE); +} + +/* + * get the head of the "lap" interface port list + * +*/ +export PORT_T +port_list_start() +{ + return(port_list); +} + +/* + * port's net is ready, set vars: return TRUE if was ready + * + * (Is this really sufficient?) + * +*/ +export boolean +port_net_ready(port, net, sid, zone) +PORT_T port; /* port */ +word net; /* ddp network for port */ +NODE *sid; /* node the information came from */ +byte *zone; /* zone */ +{ + if (port == NULL) { /* how did this happen? */ + logit(LOG_HIGH, "port null in port_net_ready"); + return(FALSE); + } + if (port->p_ddp_net == 0) { + port->p_ddp_net = net; + port->p_next = port_list; /* link into list */ + port_list = port; + port->p_flags |= PORT_NETREADY; /* mark sure marked ready */ + /* set net & bridge info (bridge is ourselves) */ + if (port->p_mpx && port->p_mpx->mpx_havenet) + (*port->p_mpx->mpx_havenet)(port->p_mpx_hdl, net, port->p_ddp_node); + return(FALSE); + } + if (port->p_ddp_net != net) { + logit(LOG_STD, "***net mismatch: port %d's net is %d.%d, received %d.%d", + port, nkipnetnumber(port->p_ddp_net), + nkipsubnetnumber(port->p_ddp_net), + nkipnetnumber(net), nkipsubnetnumber(net)); + if (sid) + logit(LOG_STD, "offending node: %s", + node_format(sid)); + } + return(TRUE); +} + +/* + * zone acquired, returns TRUE, FALSE + * +*/ +export boolean +port_zone_known(port,zone) +PORT_T port; +byte *zone; +{ + if (port == NULL) { /* how did this happen? */ + logit(LOG_HIGH, "port null in port_zone_ready"); + return(FALSE); + } + if ((port->p_flags & PORT_NETREADY) == 0) + return(FALSE); + port->p_flags |= PORT_ISREADY; /* mark sure marked ready */ + /* zone storage is kept */ + port->p_zonep = zone; /* remember it */ + if (port->p_mpx && port->p_mpx->mpx_havezone) + (*port->p_mpx->mpx_havezone)(port->p_mpx_hdl, zone); + return(TRUE); +} diff --git a/support/uab/ddpport.h b/support/uab/ddpport.h new file mode 100644 index 0000000..189111d --- /dev/null +++ b/support/uab/ddpport.h @@ -0,0 +1,119 @@ +/* + * $Author: djh $ $Date: 91/02/15 23:07:20 $ + * $Header: ddpport.h,v 2.1 91/02/15 23:07:20 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * ddpport.c - ddp port manager + * + * Handles the ddp port which sits on the interface boundary between + * DDP and LAP. In addition, it carries the "local delivery" or + * demuxing information necessary to pass data to individual modules + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * Sept 4, 1988 CCKim Created + * +*/ + +#ifndef _DDP_PORT_INCLUDED +#define _DDP_PORT_INCLUDED "yes" + +#include "mpxddp.h" +#include "node.h" + +/* this struct should really be hidden totally */ +/* + * PORT, struct port + * + * First, it describes the "lap" level parameters for a particular + * interface. Second, it describes the ddp network attached to that + * interface. ("lap" could be alap, elap, etc). + * + * Note: no routine should access port directly + * +*/ +typedef struct port { + struct port *p_next; /* pointer to next struct */ + int p_flags; /* if any */ +#define PORT_ISCONNECTED 0x1 +/* could do at lower level, more efficient here? */ +#define PORT_WANTSLONGDDP 0x2 /* wants to output long ddp */ +#define PORT_NEEDSBROADCAST 0x4 /* needs broadcasts sent back */ +#define PORT_FULLRTMP 0x8 /* full rtmp (advertise bridge) */ +#define PORT_NETREADY 0x10 /* we know the net of the port */ +#define PORT_ISREADY 0x20 /* port info complete (net,node,zone) */ + word p_ddp_net; /* primary ddp network of port */ + byte p_ddp_node; /* primary ddp node of port */ + byte *p_zonep; /* remember our zone */ + NODE *p_id; /* lap level id */ + caddr_t p_local_data; /* local data for port manager */ + int (*p_send_if)(); /* send routine for that port */ + NODE *(*p_map_lap_if)(); /* ddp node to "lap" node mapper*/ + int (*p_map_ddp_if)(); /* map "lap" node to ddp node */ + /* other */ + struct mpxddp_module *p_mpx; /* remember demuxer */ + int p_mpx_hdl; /* mpx handle, in case for each port */ +} PORT; + +/* PORT_T may change to an int */ +typedef struct port *PORT_T; + +#define PORT_BAD(p) ((p) == NULL) +#define PORT_GOOD(p) ((p) != NULL) +#define PORT_NEXT(p) ((p)->p_next) +#define PORT_LIST_START() ((PORT_T)port_list_start()) +#define PORT_FLAGS(p) ((p)->p_flags) +/* port is open */ +# define PORT_CONNECTED(p) ((p)->p_flags & PORT_ISCONNECTED) +/* network defined */ +# define PORT_READY(p) ((p)->p_flags & PORT_ISREADY) +# define PORT_WANTS_LONG_DDP(p) ((p)->p_flags & PORT_WANTSLONGDDP) +# define PORT_NEEDS_BROADCAST(p) ((p)->p_flags & PORT_NEEDSBROADCAST) +# define PORT_ISBRIDGING(p) ((p)->p_flags & PORT_FULLRTMP) +#define PORT_DDPNET(p) ((p)->p_ddp_net) +#define PORT_DDPNODE(p) ((p)->p_ddp_node) +#define PORT_GETLOCAL(p, type) ((type)(p)->p_local_data) +#define PORT_NODEID(p) ((p)->p_id) +/* send if possible */ +#define PORT_SEND(p,dst,laptype,hdr,hdrlen,data,datalen) \ + (((p)->p_send_if) ? \ + ((*(p)->p_send_if)((p),(dst),(laptype),(hdr),(hdrlen),(data),(datalen))) : \ + -1) +#define PORT_MAKENODE(p,net,node) \ + (((p)->p_map_lap_if) ? \ + ((*(p)->p_map_lap_if)((p),(net),(node))) : \ + NULL) +#define PORT_DEMUX(p, ddp, data, datalen) \ + (((p)->p_mpx && (p)->p_mpx->mpx_send_ddp) ? \ + ((*(p)->p_mpx->mpx_send_ddp)((p)->p_mpx_hdl, (ddp), (data), (datalen))) : \ + -1) +/* set the port demuxer */ +#define PORT_SETDEMUXER(p, desc) (port_setdemuxer((p), (desc))) +#define PORT_NET_READY(p, net, sid) (port_net_ready((p),(net),(sid))) +#define PORT_ZONE_KNOWN(p, zone) (port_zone_known((p),(zone))) +#define PORT_GRAB(p, skt) (((p)->p_mpx && (p)->p_mpx->mpx_grab) ? \ + ((*(p)->p_mpx->mpx_grab)(skt, NULL)) : -1) +export PORT_T port_create(); +export boolean port_set_demuxer(); +export PORT_T port_list_start(); +export boolean port_net_ready(); +export boolean port_zone_known(); + +#endif /* INCLUDE THIS FILE */ + + diff --git a/support/uab/ddprouter.c b/support/uab/ddprouter.c new file mode 100644 index 0000000..d0e92b8 --- /dev/null +++ b/support/uab/ddprouter.c @@ -0,0 +1,492 @@ +static char rcsid[] = "$Author: djh $ $Date: 91/02/15 23:07:21 $"; +static char rcsident[] = "$Header: ddprouter.c,v 2.1 91/02/15 23:07:21 djh Rel $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * ddprouter.c - simple ddp router + * + * Follows specification set in "Inside Appletalk" by Gursharan Sidhu, + * Richard F. Andrews, and Alan B. Oppenheimer, Apple Computer, Inc. + * June 1986. + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * August, 1988 CCKim Created + * November, 1990 djh@munnari.OZ.AU no short DDP, fix hop increment + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "gw.h" + +export void ddp_route_init(); +export void ddp_route_start(); +export void sddp_route(); +export void ddp_route(); +export int ddp_open(); /* local ddp routines */ +export int ddp_reopen(); +private void ddp_input(); +export void ddp_output(); +private int ddp_matchournode(); +private void dump_types(); +export void ddp_dump_stats(); + +#define NUMDDPTYPES 8 +char *ddp_type_names[NUMDDPTYPES] = { + "unknown", "RTMP", "NBP", "ATP", "ECHO", "RTMP Request", "ZIP", "ADSP" +}; + +#define valid_type(d) (((d)->type>=ddpRTMP&&(d)->type<=ddpADSP)?(d)->type:0) + + +struct ddp_stats { + int short_ddp_routed; /* short ddp sent to router */ + int long_ddp_routed; /* long ddp sent to router */ + int ddp_forwarded; /* long ddp forwarded */ + int ddp_types_forwarded[NUMDDPTYPES]; /* breakdown by type */ + int drop_noroute; /* no routes */ + int drop_hops; /* dropped because of hops */ + int long_ddp_output; /* long ddp output */ + int long_ddp_types_output[NUMDDPTYPES]; /* breakdown by type */ + int short_ddp_output; /* short ddp output */ + int short_ddp_types_output[NUMDDPTYPES]; + int ddp_input; /* ddp packets input */ + int ddp_input_bad; /* bad type packets input ... */ + int ddp_input_dropped; /* broadcast from us, drop it */ + int ddp_types_input[NUMDDPTYPES]; +}; + +private struct ddp_stats ddp_stats; + +/* note: open for all ports */ +private boolean (*ddp_handlers[ddpMaxSkt+1])(); /* define internal sockets */ + +/* + * start ddp routing - need address of an internal router though to allow + * mpx and aux. services (takes long ddp header, data and datalength ) + * + * beabridge set to true tells us to tell rtmp to output rtmp packets + * and to answer rtmp request packets + * +*/ +export void +ddp_route_start() +{ + rtmp_start(); + ddp_svcs_start(); /* start if needed (says we know net) */ +} + +/* + * give us a chance to initialize. + * +*/ +export void +ddp_route_init() +{ + int i; + + for (i = 1 ; i < ddpMaxSkt; i++) + ddp_handlers[i] = NULL; + rtmp_init(); + ddp_svcs_init(); /* init other ddp services */ +} + +/* + * route a short ddp packet coming in from an interface + * + * basically, make the short header into a long ddp header and deliver + * internally + * + * ddps is aligned okay, cannot assume for data +*/ +export void +sddp_route(port, srcnode, ddps, data, datalen) +PORT_T port; +byte srcnode; +ShortDDP *ddps; +byte *data; +int datalen; +{ + DDP ddp; + int i; + + /* get indicated data length */ + i = (ddpLengthMask & ntohs(ddps->length)) - ddpSSize; + if (i < datalen) /* reduce in case caller is enet, etc. */ + datalen = i; + ddp.length = htons(datalen + (ddpSize-ddpSSize)); + ddp.checksum = 0; + ddp.srcNet = ddp.dstNet = PORT_DDPNET(port); + ddp.srcNode = srcnode; + ddp.dstNode = PORT_DDPNODE(port); + ddp.srcSkt = ddps->srcSkt; + ddp.dstSkt = ddps->dstSkt; + ddp.type = ddps->type; + ddp_stats.short_ddp_routed++; + ddp_input(port, &ddp, data, datalen); +} + +/* + * route a long ddp packet. + * + * + * ddp is aligned okay, cannot assume for data +*/ +export void +ddp_route(port, ddp, data, datalen, isbroadcast) +PORT_T port; +DDP *ddp; +byte *data; +int datalen; +int isbroadcast; /* if port okay, then came in as broadcast */ +{ + struct route_entry *re; + NODE *dstnode; + int i; + + /* get indicated data length */ + i = (ddpLengthMask & ntohs(ddp->length)) - ddpSize; + if (i < datalen) /* reduce in case caller is enet, etc. */ + datalen = i ; + + ddp_stats.long_ddp_routed++; + /* don't do certain things if the packet comes internally (flagged */ + /* by the fact that the port is null) */ + if (port) { + /* if from self, then drop */ + if (ddp_matchournode(ddp->srcNet, ddp->srcNode, FALSE)) { + return; + } + /* forward packets for self to self */ + /* don't forward broadcasts 'cept to self*/ + if (isbroadcast || + (ddp->dstNet==PORT_DDPNET(port) && ddp->dstNode==PORT_DDPNODE(port))) { + ddp_input(port, ddp, data, datalen); + return; + } + } + + /* get route to send the packet on */ + /* drop if no route or bad port */ + if ((re = route_find(ddp->dstNet)) == NULL) { + ddp_stats.drop_noroute++; + return; /* drop the packet */ + } + if (PORT_BAD(re->re_port) || !PORT_CONNECTED(re->re_port)) { + ddp_stats.drop_noroute++; + return; + } + if (re->re_dist == 0) { + if (ddp->dstNode == PORT_DDPNODE(re->re_port)) { + ddp_input(re->re_port, ddp, data, datalen); + return; + } + if (ddp->dstNode == DDP_BROADCAST_NODE) /* deliver ourselves a copy */ + ddp_input(re->re_port, ddp, data, datalen); + if (re->re_ddp_net == 0) /* drop if no net established yet */ + return; + dstnode = PORT_MAKENODE(re->re_port, ddp->dstNet, ddp->dstNode); + ddp_stats.long_ddp_output++; + ddp_stats.long_ddp_types_output[valid_type(ddp)]++; + } else { + /* move over into msg and skip first two bits, but only keep 4 bits */ + int hops = ntohs(ddp->length) >> (8+2) & 0xf; + + if (re->re_ddp_net == 0) /* drop if no net established yet */ + return; +#define DDP_MAXHOPS 15 +#define ddpHopShift 10 /* 10 bits to the left for hops */ + hops++; /* forwarding, *HAVE* to increment hops */ + if (hops >= DDP_MAXHOPS) { + ddp_stats.drop_hops++; + return; /* drop it */ + } + ddp->length = htons((ntohs(ddp->length)&ddpLengthMask)|hops<re_bridgeid_p; + ddp_stats.ddp_forwarded++; + ddp_stats.ddp_types_forwarded[valid_type(ddp)]++; + } + /* wait until here in case for ourselves (okay) */ + if (re->re_ddp_net == 0) { /* drop if no net established yet */ + return; + } + /* send it out */ + PORT_SEND(re->re_port, dstnode, lapDDP, ddp, ddpSize, data, datalen); +} + +/* + * Internal DDP input/output routines + * +*/ + +/* + * ddp open - open socket for processing - maybe allow socket + * to be open on a single port only, but not necessary for now + * + * Generally, should only be for a very small number of priviledged + * sockets +*/ +export int +ddp_open(ddpskt, handler) +int ddpskt; +boolean (*handler)(); +{ + register PORT_T port; + + if (ddpskt > 0 && ddpskt < ddpMaxSkt) { + /* need to open mpx socket - remember to callback in mpx open too */ + /* how to do ? */ + ddp_handlers[ddpskt] = handler; + for (port = PORT_LIST_START(); port ; port = PORT_NEXT(port)) + PORT_GRAB(port, ddpskt); + return(TRUE); + } + return(FALSE); +} + +/* smash through open socket list and call grab to grab the socket if */ +/* necessary (generally for mpx) */ +export int +ddp_reopen(grab, arg, carg) +int (*grab)(); +int arg; +caddr_t carg; +{ + int i; + + for (i = 1; i < ddpMaxSkt; i++) + if (ddp_handlers[i]) + (*grab)(i, arg, carg); +} + +/* + * ddp input. Processes an packet destined for internal use. + * +*/ +private void +ddp_input(port, ddp, data, datalen) +PORT_T port; +DDP *ddp; +byte *data; +int datalen; +{ + int ds; /* destination socket */ + + ddp_stats.ddp_input++; + + /* should we checksum? - probably */ + if (ddp->dstSkt == 0 || ddp->dstSkt == ddpMaxSkt) { + /* count ddp dropped bad type */ + ddp_stats.ddp_input_bad++; + return; + } + + /* reject any packet from us (already know from us) */ + /* that is a broadcast and whose srcSkt == dstSkt */ + if (ddp_matchournode(ddp->srcNet, ddp->srcNode, FALSE) && + ddp->dstNode == DDP_BROADCAST_NODE && + ddp->dstSkt == ddp->srcSkt) { + /* RECORD */ + ddp_stats.ddp_input_dropped++; + return; + } + ds = ddp->dstSkt; + if (ddp_handlers[ds]) { + /* they return TRUE if they handle, false if we should try to */ + /* forward it to an internal node */ + if ((*(ddp_handlers[ds]))(port, ddp, data, datalen)) { + ddp_stats.ddp_types_input[valid_type(ddp)]++; + return; + } + } + + /* send to process if naught else */ + if (port) { + ddp_stats.ddp_types_input[valid_type(ddp)]++; + PORT_DEMUX(port, ddp, data, datalen); + } +} + +/* + * ddp output sends out a packet + * + * Special processing: + * ddp output will output long ddp only if the appropriate flag is + * set and sufficient input is given. + * ddp output will also send back a broadcast packet to the input + * routines if the "lap" layer is not capable of receiving its own + * broadcasts + * + * Expect these ddp fields + * dstNet + * dstNode + * dstSkt + * srcSkt + * type + * Fills in srcNet, srcNode, length, checksum with appropriate values + * + * +*/ +export void +ddp_output(dstnode, ddp, data, size) +NODE *dstnode; +DDP *ddp; +byte *data; +int size; +{ + ShortDDP ddps; + struct route_entry *re; + PORT_T port; + byte pddpnode; + + /* make sure we can route to destination */ + if ((re = route_find(ddp->dstNet)) == NULL) + return; + port = re->re_port; + pddpnode = PORT_DDPNODE(re->re_port); + +/* + * short ddp? + * + * "Apple recommends, although currently does not require, that any DDP + * packet sent through EtherTalk use the extended DDP header format. All + * EtherTalk implementations must accept extended headers for any incoming + * DDP packet. For compatability with third-party implementations of + * AppleTalk that use EtherNet, EtherTalk implementations currently + * also accept short DDP headers. The use of short DDP headers on EtherNet + * is strongly discouraged by Apple and is expected to be phased out" + * + * - Inside AppleTalk, 1989 + * + * In light of the above, and the fact that an extra 8 bytes hardly makes + * a difference with EtherNet packets (minimum 60 bytes anyway), lets back + * out of the lapShortDDP PORT_SEND() below ... + * + * (NB: sddp_route() converts to long DDP format for internal delivery). + * + */ + + /* yes, if dstNet is same as outgoing port */ + /* unless port wants long ddp and node was given */ + /* have "special" case for no node in case it is not possible to map */ + /* from "NODE *" type to a ddp node */ + if (ddp->dstNet == PORT_DDPNET(port) && + (dstnode || !PORT_WANTS_LONG_DDP(port))) { + /* yes */ + /* set the length, as a side effect, zero the hop count */ + ddps.length = htons(ddpSSize + size); + ddps.dstSkt = ddp->dstSkt; + ddps.srcSkt = ddp->srcSkt; + ddps.type = ddp->type; + if (ddp->dstNode == DDP_BROADCAST_NODE && PORT_NEEDS_BROADCAST(port)) { + sddp_route(port, pddpnode, &ddps, data, size); + } else if (ddp->dstNode == pddpnode) { + sddp_route(port, pddpnode, &ddps, data, size); + return; + } +#ifdef REALLY_WANT_TO_SEND_SHORT_DDP_PACKETS + if (dstnode || (dstnode = PORT_MAKENODE(port, 0, ddp->dstNode))) { + ddp_stats.short_ddp_output++; + ddp_stats.short_ddp_types_output[valid_type(ddp)]++; + PORT_SEND(port, dstnode, lapShortDDP, &ddps, ddpSSize, data, size); + } + return; +#else REALLY_WANT_TO_SEND_SHORT_DDP_PACKETS + if (!(dstnode || (dstnode = PORT_MAKENODE(port, 0, ddp->dstNode)))) + return; +#endif REALLY_WANT_TO_SEND_SHORT_DDP_PACKETS + } + /* definitely should put checksum here !!!! */ + ddp->checksum = 0; /* should we put the checksum in? probably */ + /* set the length, as a side effect, zero the hop count */ + ddp->length = htons(size+ddpSize); /* length */ + ddp->srcNet = PORT_DDPNET(port); + ddp->srcNode = pddpnode; + ddp_route(NULL, ddp, (byte *)data, size, FALSE); +} + + +/* + * Given a ddp network and node number, ddp matchournode will return true + * if it corresponds to the ddp network/node number of any port. + * +*/ +private int +ddp_matchournode(net, node, br) +register word net; +register byte node; +int br; +{ + register PORT_T port; + + for (port = PORT_LIST_START() ; port ; port = PORT_NEXT(port)) + if ((net == 0 || PORT_DDPNET(port) == net) && + ((br && node == DDP_BROADCAST_NODE) || PORT_DDPNODE(port) == node)) + return(TRUE); + return(FALSE); +} + + +private void +dump_types(fd, table) +FILE *fd; +int table[]; +{ + int i; + + fprintf(fd, " by type\n"); + for (i = 1; i < NUMDDPTYPES; i++) + fprintf(fd, "\t%d %s\n", table[i], ddp_type_names[i]); + /* output "unknown last */ + fprintf(fd, "\t%d %s\n", table[0], ddp_type_names[0]); +} + +export void +ddp_dump_stats(fd) +FILE *fd; +{ + fprintf(fd, "\nddp route\n"); + fprintf(fd, " short ddp received: %d\n", ddp_stats.short_ddp_routed); + + fprintf(fd, " long ddp received: %d\n", ddp_stats.long_ddp_routed); + fprintf(fd, " forwarded to another bridge: %d\n", ddp_stats.ddp_forwarded); + dump_types(fd, ddp_stats.ddp_types_forwarded); + fprintf(fd, " output: %d\n", ddp_stats.long_ddp_output); + dump_types(fd, ddp_stats.long_ddp_types_output); + + fprintf(fd, " ddp packets input for node: %d\n", ddp_stats.ddp_input); + dump_types(fd, ddp_stats.ddp_types_input); + fprintf(fd, " dropped: bad type %d\n", ddp_stats.ddp_input_bad); + fprintf(fd, "\t self broadcast %d\n", ddp_stats.ddp_input_dropped); + + fprintf(fd, " short ddp output: %d\n", ddp_stats.short_ddp_output); + dump_types(fd, ddp_stats.short_ddp_types_output); + fprintf(fd, " dropped: no route %d\n", ddp_stats.drop_noroute); + fprintf(fd, "\t too many hops %d\n", ddp_stats.drop_hops); +} + diff --git a/support/uab/ddpsvcs.c b/support/uab/ddpsvcs.c new file mode 100644 index 0000000..eaa599f --- /dev/null +++ b/support/uab/ddpsvcs.c @@ -0,0 +1,303 @@ +static char rcsid[] = "$Author: djh $ $Date: 1992/07/30 09:50:38 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/ddpsvcs.c,v 2.3 1992/07/30 09:50:38 djh Rel djh $"; +static char revision[] = "$Revision: 2.3 $"; + +/* + * ddpsvcs.c - simple ddp router + * + * Some ddp services + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * September, 1988 CCKim Created + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include + +#include "gw.h" + +export void ddp_svcs_init(); +export void ddp_svcs_start(); +private int echo_handler(); +private int nbp_handler(); +private int nbp_ptrs(); +private void nbp_bridge_request_handler(); +#ifdef dontdothisrightnow +private void nbp_lookup_handler(); +private void nbp_lookup_reply_handler(); +export int nbp_lookup(); +export int nbp_register(); +export int nbp_delete(); +export int nbp_tickle(); +#endif + +/* + * glue + * +*/ + +export void +ddp_svcs_start() +{ +} + +export void +ddp_svcs_init() +{ + ddp_open(echoSkt, echo_handler); + ddp_open(nbpNIS, nbp_handler); +} + +/* + * echo service + * +*/ +private boolean +echo_handler(port, ddp, data, datalen) +PORT_T port; +DDP *ddp; +byte *data; +int datalen; +{ + byte e_cmd; /* echo command */ + DDP rddp; /* reply ddp header */ + + if (ddp->type != ddpECHO) /* dump it */ + return(TRUE); + + e_cmd = *data; /* first byte is command */ + switch (e_cmd) { + case echoRequest: + break; + case echoReply: + return(FALSE); /* still needs forwarding */ + } + *data = echoReply; /* establish as reply */ + rddp.dstNet = ddp->srcNet; /* respond to sender */ + rddp.dstNode = ddp->srcNode; + rddp.dstSkt = ddp->srcSkt; + rddp.srcSkt = echoSkt; /* set socket */ + rddp.type = ddpECHO; /* and type */ + ddp_output(NULL, &rddp, data, datalen); + return(TRUE); /* we handled it */ +} + +/* + * handle incoming nbp packet + * + * +*/ +private boolean +nbp_handler(port, ddp, data, datalen) +PORT_T port; +DDP *ddp; +byte *data; +int datalen; +{ + word control; + + if (ddp->type != ddpNBP) /* dump it */ + return(TRUE); + + if (datalen < nbpMinSize) + return(TRUE); /* say we handled it! */ + control = *data >> 4; /* get nbp control part */ + switch (control) { + case nbpBrRq: /* bridge request for lookup */ + nbp_bridge_request_handler(port, ddp, data, datalen); + return(TRUE); + case nbpLkUp: /* nbp lookup */ + break; + case nbpLkUpReply: /* nbp lookup reply */ + return(FALSE); /* for now*/ + /* nbp extended codes */ + case nbpRegister: /* register a name */ + case nbpDelete: /* delete a name */ + case nbpTickle: /* let them know socket is alive and well */ + break; + case nbpStatusReply: /* status on register/delete */ + case nbpLookAside: /* Lookup via NIS */ + return(FALSE); /* for now */ + } + return(FALSE); /* for now */ +} + +/* + * given a pointer to an NBP packet, return pointers to the three + * elements of the nbp entity. assumes there is only one there. + * + * returns the valid length of nbp packet +*/ +private int +nbp_ptrs(nbp, obj, typ, zon) +NBP *nbp; +byte **obj; +byte **typ; +byte **zon; +{ + byte *o, *t, *z; + int ol, tl, zl; + o = nbp->tuple[0].name; + ol = *o; + if (ol >= ENTITYSIZE) + return(0); + t = o+ol+1; /* get ptr to type */ + tl = *t; /* get type length */ + if (tl >= ENTITYSIZE) + return(0); + z = t+tl+1; /* get ptr to zone */ + zl = *z; + if (zl >= ENTITYSIZE) + return(0); + *obj = o, *typ = t, *zon = z; + /* add 3 for lengths, add 1 for enum, and addtrblock */ + return(ol+tl+zl+nbpMinSize); +} + +/* + * take an nbp packet and handle it if it is a nbp BrRq + * +*/ +private void +nbp_bridge_request_handler(port, ddp, data, datalen) +PORT_T port; /* incoming port */ +DDP *ddp; +byte *data; +int datalen; +{ + struct route_entry *re; + int tuplelen; + byte *obj, *typ, *zon; /* pointers to parts of entity name */ + byte *zonep; /* nbp zone to lookup */ + NBP nbp; + DDP sddp; + + bcopy((caddr_t)data,(caddr_t)&nbp,sizeof(NBP)>datalen?datalen:sizeof(NBP)); + nbp.control = nbpLkUp; + /* if bad tuplelen, then drop */ + if ((tuplelen = nbp_ptrs(&nbp, &obj, &typ, &zon)) <= 0) + return; + /* zone of "=" is invalid */ + if (zon[0] == 1 && zon[1] == nbpEquals) + return; + /* 0 length zone or "*" qualifies as this zone */ + if (zon[0] == 0 || (zon[0] == 1 && zon[1] == nbpStar)) { + int t; + + t = zon[0]; + re = route_find(ddp->srcNet == 0 ? PORT_DDPNET(port) : ddp->srcNet); + if (re == NULL) /* can't rewrite zone name */ + return; + if (re->re_zonep == NULL) /* bad zone? */ + return; + zonep = re->re_zonep; + /* copy it in, plenty of space */ + bcopy((caddr_t)zonep, (caddr_t)zon, (int)(1+(*zonep))); + tuplelen += (*zonep- t); /* fix length */ + } else { + if ((zonep = zone_find(zon, FALSE)) == NULL) + return; + } + /* fixup and figure out zone */ + sddp = *ddp; + sddp.checksum = 0; + sddp.length = htons(tuplelen + ddpSize); + for (re = route_list_start(); re; re = route_next(re)) { + if (!re->re_state || re->re_zonep != zonep) + continue; + sddp.dstNode = DDP_BROADCAST_NODE; + sddp.dstNet = re->re_ddp_net; + logit(LOG_LOTS, "nbp lkup net %d.%d for zone %s", + nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net), + zonep+1); + ddp_output(NULL, &sddp, (byte *)&nbp, tuplelen); + } + return; +} + +#ifdef dontdothisrightnow +/* + * handle an incoming nbp lookup request + * +*/ +private void +nbp_lookup_handler(port, ddp, data, datalen) +PORT_T port; +DDP *ddp; +byte *data; +int datalen; +{ + int tuplelen; + byte *obj, *typ, *zon; + NPB nbp; + + bcopy((caddr_t)data,(caddr_t)&nbp,sizeof(NBP)>datalen?datalen:sizeof(NBP)); + /* if bad tuplelen, then drop */ + if ((tuplelen = nbp_ptrs(&nbp, &obj, &typ, &zon)) <= 0) + return; + /* zone specified is not myzone */ + if (!(zon[0] == 0 || (zon[0] == 1 && zon[1] == '*'))) { + /* check to see if zone specified is same as port's zone */ + byte *zp; + + if ((zp = zone_find(zon, FALSE)) == NULL) /* find it */ + return; /* wasn't there */ + if (zp != port->p_zonep) /* check the canonical zone name */ + return; /* bad, return */ + } + /* check our tables and rspond */ + { + EntityName *en; + } +} + +private void +nbp_lookup_reply_handler() +{ +} + +export int +nbp_lookup() +{ +} + +export int +nbp_register() +{ + /* check by lookup first? */ + /* insert into our tables */ +} + +export int +nbp_delete() +{ + /* delete from our tables */ +} + +export int +nbp_tickle() +{ + /* reset timer */ +} + +#endif /* NOTDONE */ diff --git a/support/uab/dlip.c b/support/uab/dlip.c new file mode 100644 index 0000000..5918905 --- /dev/null +++ b/support/uab/dlip.c @@ -0,0 +1,276 @@ +static char rcsid[] = "$Author: djh $ $Date: 91/02/15 23:07:26 $"; +static char rcsident[] = "$Header: dlip.c,v 2.1 91/02/15 23:07:26 djh Rel $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * dlip.c - Simple "protocol" level interface to DLI + * + * Provides ability to read/write packets at ethernet level + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * April 3, 1988 CCKim Created + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "proto_intf.h" + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int socket; /* file descriptor of socket */ + int protocol; /* ethernet protocol */ + struct sockaddr_dl sdli; /* dli interface: to send with */ + struct sockaddr_dl rdli; /* dli interface: to receive with */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +/* + * setup for particular device devno + * all pi_open's will go this device + * +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return 0 for okay +*/ +export int +pi_open(protocol, dev, devno) +int protocol; +char *dev; +int devno; +{ + struct ephandle *eph; + struct sockaddr_dl *dl; + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + dl = &eph->sdli; /* point */ + dl->dli_family = AF_DLI; + strcpy(dl->dli_device.dli_devname, dev); + dl->dli_device.dli_devnumber = devno; + dl->dli_substructype = DLI_ETHERNET; + /* update these */ + dl->choose_addr.dli_eaddr.dli_ioctlflg = DLI_EXCLUSIVE; + dl->choose_addr.dli_eaddr.dli_protype = protocol; + + if ((s = socket(AF_DLI, SOCK_DGRAM, DLPROTO_DLI)) < 0) + return(-1); + if (bind(s, dl, sizeof(struct sockaddr_dl)) < 0) { + close(s); + return(-1); + } + bcopy(dl, &eph->rdli, sizeof(struct sockaddr_dl)); + + eph->inuse = TRUE; + eph->socket = s; + eph->protocol = protocol; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(TRUE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].socket); /* toss listener */ + close(ephlist[edx-1].socket); + ephlist[edx-1].inuse = 0; + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct ifdevea buf; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; /* find handle */ + sprintf(buf.ifr_name, "%s%d",eph->sdli.dli_device.dli_devname, + eph->sdli.dli_device.dli_devnumber); + if (ioctl(eph->socket,SIOCRPHYSADDR, &buf) < 0) { + perror("iotcl"); + return(-1); + } + bcopy(buf.current_pa, ea, DLI_EADDRSIZE); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].socket, listener, arg, edx); + return(0); +} + + +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct msghdr msg; + int cc; + struct ephandle *eph ; + struct ethernet_addresses *ea; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + msg.msg_iov = iov+1; + msg.msg_iovlen = iovlen-1; + msg.msg_name = (caddr_t)&eph->rdli; + msg.msg_namelen = sizeof(eph->rdli); + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; + if ((cc = recvmsg(eph->socket, &msg, 0)) < 0) { + perror("recvmsg"); + return(cc); + } + ea = (struct ethernet_addresses *)iov[0].iov_base; + ea->etype = eph->protocol; + /* check length -- naw */ + bcopy(eph->rdli.choose_addr.dli_eaddr.dli_target, ea->saddr, EHRD); + bcopy(eph->rdli.choose_addr.dli_eaddr.dli_dest, ea->daddr, EHRD); + return(cc+iov[0].iov_len); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[2]; + struct ethernet_addresses ea; + int cc; + + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct iovec iov[1]; + + iov[0].iov_base = buf; + iov[0].iov_len = buflen; + return(pi_writev(edx, iov, 1, eaddr)); +} + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +char *eaddr; +{ + struct ephandle *eph; + struct msghdr msg; + int cc; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + bcopy(eaddr, eph->sdli.choose_addr.dli_eaddr.dli_target, DLI_EADDRSIZE); + msg.msg_name = (caddr_t)&eph->sdli; + msg.msg_namelen = sizeof(eph->sdli); + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; + cc = sendmsg(eph->socket, &msg, 0); + return(cc); +} diff --git a/support/uab/ethertalk.c b/support/uab/ethertalk.c new file mode 100644 index 0000000..ab171bc --- /dev/null +++ b/support/uab/ethertalk.c @@ -0,0 +1,536 @@ +static char rcsid[] = "$Author: djh $ $Date: 1991/09/01 06:13:59 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/ethertalk.c,v 2.3 1991/09/01 06:13:59 djh Rel djh $"; +static char revision[] = "$Revision: 2.3 $"; + +/* + * ethertalk.c - ethertalk interface + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * April 3, 1988 CCKim Created + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef sony_news +#include +#endif sony_news + +#include + +#include "proto_intf.h" /* iso: level 0 */ +#include "ethertalk.h" /* iso: level 1 */ +#include "aarp.h" /* iso: level 1 */ + +#include "if_desc.h" /* describes "if" */ +#include "ddpport.h" /* describes a ddp port to "lap" */ +#include "log.h" + +#ifdef sony_news +#include "aarp_defs.h" +#endif sony_news + +/* some logging ideas */ +#define LOG_LOG 0 +#define LOG_PRIMARY 1 +#define LOG_LOTS 9 + +/* + Ethertalk packet format: + :: destination node :: + :: source node :: + + +*/ + +private int etalk_init(); +private int etalk_getnode(); /* basis */ +private etalk_initfinish(); + +private int etalk_send_ddp(); +private int etalk_listener(); +private NODE *etalk_ddpnode_to_node(); +private int etalk_stats(); +private int etalk_tables(); + +/* describe our interface to the world */ +private char *ethertalk_lap_keywords[] = { + "ethertalk", + "elap", + NULL + }; + +export struct lap_description ethertalk_lap_description = { + "EtherTalk Link Access Protocol", + ethertalk_lap_keywords, + TRUE, /* need more than just key */ + etalk_init, /* init routine */ + etalk_stats, /* stats routine */ + etalk_tables /* tables */ +}; + + +/* interface statistics */ + +private char *estat_names[] = { +#define ES_PKT_INPUT 0 /* packets input */ + "packets input", +#define ES_PKT_ACCEPTED 1 /* accepted input packets */ + "packets accepted", +#define ES_PKT_BAD 2 /* bad packets */ + "bad packets", +#define ES_PKT_NOTFORME 3 /* not for me packets */ + "packets not for me", +#define ES_BYTES_INPUT 4 /* accepted bytes */ + "bytes input", +#define ES_ERR_INPUT 5 /* number of input errors */ + "input errors", +#define ES_PKT_NOHANDLER 6 /* no handler */ + "packets without handlers", +#define ES_PKT_OUTPUT 7 /* packets output */ + "packets output", +#define ES_BYTES_OUTPUT 8 /* bytes output */ + "bytes output", +#define ES_ERR_OUTPUT 9 /* output errors */ + "output errors", +#define ES_RESOLVE_ERR_OUTPUT 10 /* could not resolvve */ + "output resolve error" +#define ES_NUM_COUNTERS 11 +}; + +typedef struct ethertalk_handle { + int eh_state; /* this connection state */ +#define ELAP_WAITING -1 +#define ELAP_BAD 0 /* error */ +#define ELAP_READY 1 /* okay */ + PORT_T eh_port; /* ethertalk port */ + int eh_ph; /* ethertalk protocol handle */ + caddr_t eh_ah; /* aarp module handle */ + NODE eh_enode; /* host node id */ + IDESC_TYPE *eh_id; /* interface description */ + int eh_stats[ES_NUM_COUNTERS]; /* statistics */ +} E_HANDLE; + + +/* + * call with provisional network number, interface name and number + * + * provisional number should be 0 if not seeding + * +*/ +private int +etalk_init(id, async) +IDESC_TYPE *id; +int async; +{ + int etph; + E_HANDLE *eh; + int hostid; + + + if ((eh = (E_HANDLE *)malloc(sizeof(E_HANDLE))) == NULL) + return(NULL); + + pi_setup(); + + if ((etph = pi_open(ETHERTYPE_APPLETALK, id->id_intf, id->id_intfno)) < 0) { + logit(LOG_LOG|L_UERR,"pi_open"); + free(eh); + return(NULL); + } + eh->eh_ph = etph; + + /* init for a single node */ +#ifdef sony_news + eh->eh_ah = (caddr_t)aarp_init("", etph, 1); +#else sony_news + eh->eh_ah = (caddr_t)aarp_init(id->id_intf, id->id_intfno, 1); +#endif sony_news + if (eh->eh_ah == NULL) { + logit(LOG_LOG|L_UERR, "aarp_init"); + pi_close(etph); + free(eh); + return(NULL); + } + /* link in both directions */ + id->id_ifuse = (caddr_t)eh; /* mark */ + eh->eh_id = id; /* remember this */ + + eh->eh_state = ELAP_WAITING; /* mark waiting */ + + /* acquire node address */ + hostid = 0xff & gethostid(); /* use last byte of hostid as hint */ + if (etalk_getnode(eh, hostid, etalk_initfinish) < 0) { + pi_close(etph); + free(eh); + } + + if (async) /* async means to stop early */ + return(TRUE); + /* wait for node acquisition? */ + while (eh->eh_state == ELAP_WAITING) + abSleep(10, TRUE); + return(eh->eh_state == ELAP_READY); /* true if okay, 0 o.w. */ +} + +/* + * try to acquire an ethertalk host node address using hint as the basis + * callback to who (cbarg is E_HANDLE, result where -1 if address in use + * host node address index o.w.) + * +*/ +private int +etalk_getnode(eh, hint, who) +E_HANDLE *eh; +int hint; +int (*who)(); +{ + struct ethertalkaddr pa; + int n; + + pa.dummy[0] = pa.dummy[1] = pa.dummy[2] = 0; + /* EtherTalk II fixup */ + pa.node = hint; /* use fourth byte of eaddr as guess */ + while ((n=aarp_acquire_etalk_node(eh->eh_ah, &pa, who, eh)) != 0) { + if (n < 0) { + /* error */ + /* clean up */ + return(-1); + } + pa.node++; /* try next */ + } + return(0); +} + +/* + * finish off the init + * +*/ +private +etalk_initfinish(eh, result) +E_HANDLE *eh; +int result; +{ + PORT_T eh_port; /* ethertalk port */ + struct ethertalkaddr pa; + int flags; + int nodesize; + IDESC_TYPE *id = eh->eh_id; /* get interface description */ + + if (result < 0) { + if ((result = etalk_getnode(eh,(rand()%254)+1, etalk_initfinish)) < 0) { + logit(LOG_LOG, "could not acquire node on interface %s%d\n", + id->id_intf, id->id_intfno); + eh->eh_state = ELAP_BAD; + } + return; + } + + if ((nodesize = aarp_get_host_addr(eh->eh_ah, &pa, result)) < 0) { + logit(LOG_PRIMARY, "aarp get host node address failed for %d", result); + logit(LOG_PRIMARY, "interface %s%d can't be intialized", + id->id_intf, id->id_intfno); + eh->eh_state = ELAP_BAD; /* mark bad */ + return; + } + eh->eh_enode.n_size = 8*nodesize; /* 8 bits */ + eh->eh_enode.n_bytes = nodesize; /* 1 byte */ + /* EtherTalk II fixup */ + eh->eh_enode.n_id[0] = pa.node; /* this is it */ + + flags = PORT_WANTSLONGDDP; + if (!pi_delivers_self_broadcasts()) + flags |= PORT_NEEDSBROADCAST; + if (id->id_isabridge) + flags |= PORT_FULLRTMP; + + /* establish port */ + /* EtherTalk II fixup */ + eh_port = port_create(id->id_network, pa.node, id->id_zone, + &eh->eh_enode, flags, (caddr_t)eh, + etalk_send_ddp, /* send interface */ + etalk_ddpnode_to_node, /* map from ddp */ + NULL, /* map node to ddp node, net */ + id->id_local); /* demuxer */ + if (eh_port) { + /* go to ethertalk level */ + pi_listener(eh->eh_ph, etalk_listener, (caddr_t)eh_port); + eh->eh_state = ELAP_READY; + logit(LOG_PRIMARY, "port %d acquired node %d on interface %s%d", + eh_port, pa.node, id->id_intf, id->id_intfno); + } else { + eh->eh_state = ELAP_BAD; + logit(LOG_PRIMARY,"acquired node %d on interface %s%d, but no space for port", + pa.node, id->id_intf, id->id_intfno); + } + /* phew */ +} + +#ifdef sony_news +u_char recv_buf[ETHERMTU]; +#endif sony_news + +/* + * listen to incoming ethertalk packets and handle them + * +*/ +/*ARGSUSED*/ +private +etalk_listener(fd, port, etph) +int fd; /* dummy */ +PORT_T port; +int etph; +{ + static LAP lap; + /* room for packet and then some */ + static byte rbuf[ddpMaxData+ddpSize+lapSize+100]; + int cc; + struct iovec iov[3]; + struct ethertalkaddr spa; + struct ethernet_addresses ea; + struct ethertalk_handle *eh = PORT_GETLOCAL(port, struct ethertalk_handle *); + int *stats = eh->eh_stats; + int ddpnode; +#ifdef sony_news + int idx = 0; +#endif sony_news + + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); +#ifdef sony_news + iov[1].iov_base = (caddr_t)recv_buf; + iov[1].iov_len = ETHERMTU; + if ((cc = pi_readv(etph, iov, 2)) < 0) { +#else sony_news + iov[1].iov_base = (caddr_t)⪅ + iov[1].iov_len = lapSize; + iov[2].iov_base = (caddr_t)rbuf; + iov[2].iov_len = sizeof(rbuf); + if ((cc = pi_readv(etph, iov, 3)) < 0) { +#endif sony_news + logit(LOG_LOG|L_UERR, "pi_readv: ethertalk_listener"); + stats[ES_ERR_INPUT]++; /* input error */ + return(cc); + } +#ifdef sony_news + if (ea.etype == ETHERTYPE_AARP) + return(aarp_listener(fd, ((E_HANDLE *)(port->p_local_data))->eh_ah, + etph, recv_buf, cc)); + if (ea.etype != ETHERTYPE_APPLETALK) + return(-1); + idx = 0; + bcopy(&(recv_buf[idx]), &lap, lapSize); + idx += lapSize; + bcopy(&(recv_buf[idx]), rbuf, cc-idx); +#endif sony_news + /* eat the packet and drop it */ + if (eh->eh_state != ELAP_READY) /* drop */ + return(cc); + /* handle packet */ + cc -= (lapSize+sizeof(ea)); + + if (lap.src == 0xff) { /* bad, bad, bad */ + stats[ES_PKT_BAD]++; + return(-1); + } + /* lap dest isn't right */ + /* fixup point */ + ddpnode = PORT_DDPNODE(port); + if (lap.dst != 0xff && lap.dst != ddpnode) { + stats[ES_PKT_NOTFORME]++; + return(-1); + } + + stats[ES_PKT_INPUT]++; + stats[ES_BYTES_INPUT] += cc; + /* pick out source for aarp table management if not self */ + /* EtherTalk II fixup */ + if (lap.src != ddpnode) { + spa.dummy[0] = spa.dummy[1] = spa.dummy[2] = 0; + spa.node = lap.src; + if (!aarp_insert(eh->eh_ah, ea.saddr, &spa, FALSE)) /* drop it */ + return(-1); /* enet address change */ + } + + switch (lap.type) { + case lapDDP: + ddp_route(port, rbuf, rbuf+ddpSize, cc, lap.dst == 0xff); + break; + case lapShortDDP: /* don't allow short ddp for now */ + /*munge short ddp to ddp */ + sddp_route(port, lap.src, rbuf, rbuf+ddpSSize, cc); + break; + default: + stats[ES_PKT_NOHANDLER]++; + return(-1); + } + return(0); +} + +/* + * resolve a ddp node number to a node address on the specified port + * (note: do we need more information in some cases?) +*/ +private NODE * +etalk_ddpnode_to_node(port, ddpnet, ddpnode) +PORT_T port; +word ddpnet; +byte ddpnode; +{ + /* EtherTalk II fixup */ + /* think this is okay */ + static NODE node = { 1, 8 }; /* initialize */ + int myddpnet = PORT_DDPNET(port); + + if (ddpnet != 0 && myddpnet != ddpnet) /* only allow this net! */ + return(NULL); + node.n_id[0] = ddpnode; /* make node */ + return(&node); +} + +/* resolve a node to a ddp node (do we want this?) */ +/* think we will need it + one that resolves it to a net in the future ? */ +private byte +etalk_node_to_ddpnode(port, node) +PORT_T port; +NODE *node; +{ + /* EtherTalk II fixup */ + if (node->n_size == 8) /* 8 bits? */ + return(node->n_id[0]); + return(0); +} + + +/* + * send a ddp packet on ethertalk + * (should we convert short to long ddp?) + * + * port = port to send on + * dstnode == destination ethertalk node + * laptype == laptype of packet (header) + * header = packet header (for laptype) + * hsize = packet header length + * data = data + * dlen = datalength +*/ +private +etalk_send_ddp(port, dstnode, laptype, header, hsize, data, dlen) +PORT_T port; +NODE *dstnode; +int laptype; +byte *header; +int hsize; +u_char *data; +int dlen; +{ + struct iovec iov[3]; + u_char *eaddr; + LAP lap; + struct ethertalk_handle *eh = PORT_GETLOCAL(port, struct ethertalk_handle *); + struct ethertalkaddr tpa; + int *stats = eh->eh_stats; + int i; + + if (eh->eh_state != ELAP_READY) { /* drop */ + stats[ES_ERR_OUTPUT]++; + return(-1); + } + if (dstnode == NULL) { /* can't! */ + stats[ES_ERR_OUTPUT]++; /* can't */ + return(-1); + } + + /* should be higher? */ + if (dstnode->n_size != eh->eh_enode.n_size) { /* for now? */ + stats[ES_ERR_OUTPUT]++; /* can't */ + return(-1); + } + /* source is always us! */ + lap.src = eh->eh_enode.n_id[0]; /* get source node */ + lap.dst = dstnode->n_id[0]; /* get dest node */ + lap.type = laptype; + /* EtherTalk II fixup */ + tpa.dummy[0] = tpa.dummy[1] = tpa.dummy[2] = 0; + tpa.node = dstnode->n_id[0]; + + if (aarp_resolve(eh->eh_ah, &tpa, lap.dst == 0xff, &eaddr) <= 0) { + stats[ES_RESOLVE_ERR_OUTPUT]++; + return(-1); + } + iov[0].iov_len = lapSize; + iov[0].iov_base = (caddr_t)⪅ + iov[1].iov_len = hsize; + iov[1].iov_base = (caddr_t)header; + iov[2].iov_len = dlen; + iov[2].iov_base = (caddr_t)data; +#ifdef sony_news + if ((i = pi_writev(eh->eh_ph, iov, (dlen == 0) ? 2 : 3, eaddr, + ETHERTYPE_APPLETALK)) < 0) { +#else sony_news + if ((i = pi_writev(eh->eh_ph, iov, (dlen == 0) ? 2 : 3, eaddr)) < 0) { +#endif sony_news + stats[ES_ERR_OUTPUT]++; + return(i); + } + stats[ES_PKT_OUTPUT]++; + stats[ES_BYTES_OUTPUT] += i; + return(i); +} + + +private int +etalk_stats(fd, id) +FILE *fd; +IDESC_TYPE *id; +{ + E_HANDLE *eh = (E_HANDLE *)id->id_ifuse; /* get handle */ + int i; + + fprintf(fd, "Interface %s%d statisitics\n", id->id_intf, + id->id_intfno); + fprintf(fd, " Interface counters\n"); + for (i = 0; i < ES_NUM_COUNTERS; i++) { + fprintf(fd, " %8d\t%s\n", eh->eh_stats[i], estat_names[i]); + } + putc('\n', fd); /* carriage return */ + /* call up aarp too */ + aarp_dump_stats(fd, eh->eh_ah); + putc('\n', fd); /* finish */ +} + +private int +etalk_tables(fd, id) +FILE *fd; +IDESC_TYPE *id; +{ + E_HANDLE *eh = (E_HANDLE *)id->id_ifuse; /* get handle */ + + fprintf(fd, "Interface dump for %s%d\n",id->id_intf, id->id_intfno); + aarp_dump_tables(fd, eh->eh_ah); + putc('\n', fd); +} diff --git a/support/uab/ethertalk.h b/support/uab/ethertalk.h new file mode 100644 index 0000000..7cce3ce --- /dev/null +++ b/support/uab/ethertalk.h @@ -0,0 +1,39 @@ +/* + * $Author: djh $ $Date: 1991/05/29 12:48:21 $ + * $Header: /mac/src/cap60/support/uab/RCS/ethertalk.h,v 2.2 1991/05/29 12:48:21 djh Rel djh $ + * $Revision: 2.2 $ +*/ + +/* + * Ethertalk definitions + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * August 1988 CCKim Created + * April 1991 djh Added Phase 2 support + * +*/ + +/* format of an ethertalk appletalk address */ +struct ethertalkaddr { + byte dummy[3]; /* should be "network" */ + byte node; /* appletalk node # */ +}; + +#ifndef ETHERTYPE_APPLETALK +# define ETHERTYPE_APPLETALK 0x809b +#endif + +#define ETPL 4 /* ethertalk protocol address length */ + diff --git a/support/uab/gw.h b/support/uab/gw.h new file mode 100644 index 0000000..541616a --- /dev/null +++ b/support/uab/gw.h @@ -0,0 +1,96 @@ +/* + * $Author: djh $ $Date: 91/02/15 23:07:31 $ + * $Header: gw.h,v 2.1 91/02/15 23:07:31 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * gw.h - common definitions for our bridge + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * August, 1988 CCKim Created + * +*/ + +#include "node.h" +#include "ddpport.h" + +/* probably shouldn't be defined here */ +#ifndef DDP_BROADCAST_NODE +# define DDP_BROADCAST_NODE 0xff /* broadcast ddp node */ +#endif + +/* + * a route entry describes a routing to a ddp network + * + * Most of the items are taken pretty much from inside appletalk. + * + * The bridge "lap" id is stored as a pointer to a node address to + * minimize the comparision overhead (though it produces a consistency + * and lookup efficiency problem)--with a pointer we need only compare + * two addreses. + * +*/ +struct route_entry { + struct route_entry *re_next; /* next route entry */ + PORT_T re_port; /* port network is attached to */ + NODE *re_bridgeid_p; /* bridge (lap level id) pointer */ + word re_ddp_net; /* network we are routing */ + int re_dist; /* distance (hops for now) */ + int re_state; /* state of network */ + byte *re_zonep; /* zone pascal string (NULL means none) */ + int re_zip_taken; /* zip takedown in effect */ + int re_zip_helper; /* just a helper var */ +}; + +/* log levels */ +#define LOG_LOG 0 /* always */ +#define LOG_PRIMARY 1 /* primary information */ +#define LOG_BASE 4 /* base info */ +#define LOG_LOTS 6 /* lots of good info */ +#define LOG_JUNK 8 /* starting to get into junk here */ + +/* function declarations */ +/* rtmp.c */ +void rtmp_init(); +void rtmp_start(); + +struct route_entry *route_find(); +struct route_entry *route_list_start(); +struct route_entry *route_next(); +int route_add_host_entry(); + +char *node_format(); + +byte *zone_find(); + +void rtmp_dump_stats(); +void rtmp_dump_table(); + + +/* ethertalk.c */ +PORT_T ethertalk_init(); + +/* ddproute.c */ +void ddp_route_init(); +void ddp_route_start(); +void sddp_route(); +void ddp_route(); +int ddp_open(); /* local ddp routines (input via listener) */ +void ddp_output(); +void ddp_dump_stats(); + diff --git a/support/uab/hash.3 b/support/uab/hash.3 new file mode 100644 index 0000000..362d796 --- /dev/null +++ b/support/uab/hash.3 @@ -0,0 +1,669 @@ +.TH hash 3 +.SH NAME +h_new, h_operation, h_free, h_redefine\- manage hash tables +.SH SYNTAX +.B #include +.br +.B #include +.PP +.B "caddr_t h_new(policy, htype, M, compare, allocate, compress, \ +hash, shash, chainops)" +.br +.B int policy; +.br +.B int htype; +.br +.B int M; +.br +.B int (\(**compare)(); +.br +.B caddr_t (\(**allocate)(); +.br +.B u_int (\(**compress)(); +.br +.B u_int (\(**hash)(); +.br +.B u_int (\(**shash)(); +.br +.B struct hash_bucket_list_ops \(**chainops; +.PP +.B "int (\(**compare)(key, data)" +.br +.B caddr_t key; +.br +.B caddr_t data; +.PP +.B "caddr_t (\(**allocate)(p)" +.br +.B caddr_t p; +.PP +.B u_int (\(**hash)(M, logM, item); +.br +.B int M; +.br +.B int logM; +.br +.B caddr_t item; +.PP +.B u_int (\(**shash)(M, logM, hidx, item); +.br +.B int M; +.br +.B int logM; +.br +.B int hidx; +.br +.B caddr_t item; +.PP +.B u_int (\(**compress)(item); +.br +.B caddr_t item; +.PP +.B "caddr_t h_operation(operation, hth, key, bkt, dadvance, distance, bucket)" +.br +.B int operation; +.br +.B caddr_t hth; +.br +.B caddr_t key; +.br +.B int bkt; +.br +.B int dadvance; +.br +.B int \(**distance; +.br +.B int \(**bucket; +.PP +.B MACRO on h_operation: +.br +.B h_member(hth,key) +.br +.B caddr_t hth; +.br +.B caddr_t key; +.PP +.B MACRO on h_operation: +.br +.B h_insert(hth, key) +.br +.B caddr_t hth; +.br +.B caddr_t key; +.PP +.B MACRO on h_operation: +.br +.B h_delete(hth,key) +.br +.B caddr_t hth; +.br +.B caddr_t key; +.PP +.B "caddr_t h_redefine(hth, policy, htype, M, compare, allocate, \ +hash, shash, compress, chainops)" +.br +.B caddr_t hth; +.br +.B int policy; +.br +.B int htype; +.br +.B int M; +.br +.B int (\(**compare)(); +.br +.B caddr_t (\(**allocate)(); +.br +.B caddr_t (\(**compress)(); +.br +.B u_int (\(**hash)(); +.br +.B u_int (\(**shash)(); +.br +.B struct hash_bucket_list_ops \(**chainops; +.PP +.B MACRO on h_redefine: +.br +.B h_rehash(hth,M) +.br +.B caddr_t hth; +.br +.B int M; +.PP +.B "void h_free(hth, free_func)" +.br +.B caddr_t hth; +.br +.B int (\(**free_func)(); +.PP +.B int (\(**free_func)(data); +.br +.B caddr_t data +.PP +.B "struct hash_statistics *h_statistics(hth)" +.br +.B caddr_t hth; +.SH DESCRIPTION +.I h_new, +.I h_redefine, +.I h_free, +and +.I h_operation +define a general purpose hash table manager that is capable of +handling collision resolution via chaining and open hashing with +linear probing and double probing. +.PP +.I h_new +is used to create and define the parameters for a hash table. +.I h_redefine +allows you to redefine the hash table parameters. The +associated macro +.I h_rehash +allows you redefine the size of the table. +.I h_free +is used to free a hash table. +.PP +.I h_operation +provides "member", "insert", and "delete" functions for a hash table. +h_operation provides a high degree of control to the user. There are +three associated macros +.I h_insert, +.I h_delete, +and +.I h_member +that act as "wrappers" to +.I h_operation +for +simple operation. +.SH Creating hash tables +.I h_new +creates a new hash table and returns a handle that is used to +reference it. The various arguments to h_new define the hash table +definition (e.g. chaining, open hash, etc) and define some general +functions necessary for the hashing operations (insert, delete, find). +.PP +.I policy +defines how collisions are to be resolved. +.I HASH_POLICY_CHAIN +says that we should chain off the bucket on a collision. +.I HASH_POLICY_LINEAR_PROBE +resolves collisions with linear probes (e.g. by searching for the next +empty hash bucket). +.I HASH_POLICY_DOUBLE_HASH +is like linear probe, but searches in increments given by a +secondary hash function. +Note that the performance of the non-chain methods degrade severely as +the number of elements in the hash table approach the hash table size. +.PP +.I M +defines the minimum hash table size. For some hash function types, M may be +increased to some prime number or power of 2 larger than the passed +value. +.PP +.I htype +defines the hashing function. There are a few internally defined +hashing functions that may be specified. +.TP 10 +\fBHASH_TYPE_OWN +says that you will be supplying a +.I hash +function and possibly a +.I shash +function. M will be taken as given. See the discussion of +.I hash +and +.I shash +below for more information. +.TP 10 +\fBHASH_TYPE_DIVISION +is the simplest method. The bucket is choosen on the basis of "key +modulo M". +.I hash_new +resizes the supplied M upwards until it is relatively prime to +2,3,5,7,11,13,17,19. It would be best if M was prime such that M does +not divide (size of character set)^b plus/minus a where b and a are +small numbers; however, choosing M to be relatively prime to the prime +factors less than 20 should still give decent results. +The secondary hash for +HASH_TYPE_DIVISION +assumes +that M is prime and uses the function 1 + (K modulo (M - 2)). Things +will work best if M and M-2 are twin primes like 1021 and 1019. In +general, this will not be true and you should evaluate the +effectiveness on your data. +(See Knuth, Volume 3 for a full discussion). +.TP 10 +\fBHASH_TYPE_MULTIPLICATIVE +forces up the passed M so that it is a power of 2 (call it 2^r). +The hash function used is AK>>(number of bits in a word - r) where A +is an integer constant relatively prime to 2^32 (for a 32 bit +machine). +A has been chosen to attempt fibonacci +hashing (whether this holds or not is debatable--futher research +required). See A_MULTIPLIER in hash.h. +The secondary hash function takes r higher bits in the product defined +above and oring in a one (e.g. right shifts number of bits - 2*r). +(See Knuth, Volume 3, for a full discussion). +.PP +The +.I compare +function is a required user specified routine to compare a key (key) to a +stored data item (data). +It should return negative, zero, or positive if the comparision is +less than, equal to, or greater than respectively. +.PP +The +.I allocate +function allows one to insert data through +.I h_operation +without allocating it before hand. +If +.I allocate +is not given, it assume that the key is the data. +.PP +.I hash, +if non-null, defines the primary hash function that is used to compute +the bucket corresponding to the key. +.I shash, +if non-null, defines the secondary hash function used to obtain a +"movement" value for collision resolution for the open hash policies. +It is worth noting +that +.I shash, +if specified, will be used by linear probing. +Specifying linear +probing versus double probing matters when no secondary hash function +is given. +The arguments to +.I hash +are the size of the hash +table, the log base 2 of the size of the hash table (not the floor +log2(M), but 2^r s.t. 2^r >= M), and the key K. +.I shash +also takes as a parameter (hidx) the value return by +.I hash. +Specification of the hash functions will override any specified by the +hash type argument; however, the passed value of M will still be +resized according to the passed hash type (e.g. for multiplicative, +M will be bumped until it is a power of 2). +.PP +.I compress +is used to compress a coerce a key to an unsigned +integer for the hash functions and to dereference the data pointed to by +key (which usually is a pointer). +It is generally required +for internal hashing +functions are used and optional otherwise (though your hash function +would have to do the compression if you don't supply this routine). +An example of a compress function for an +string would be: +.nf + compress(s) unsigned char *s; + { + unsigned int j = 0; + while (*s) j += *s++; + } +.fi +In this case, it is important to note that a simpler function like an +xor across the +data will make the range too small (unless the table is very small) +because you would only be making use of 8 bits for a maximum hash +range of 256. +(Note: this +compression function is only so-so, it would be better if it rotated +the data on every turn to ensure that all the bits come fully into +play--however, this is highly dependent upon the data the hashing +type). Note, if you don't supply a compression function (e.g. specify +as NULL), then the key will be used directly. +This will cause +problems if sizeof(caddr_t) != sizeof(unsigned int), so consider this +carefully (i.e. don't do it -- pass a pointer to a variable containing +the key and write a dummy compress function that just returns the value). +.PP +.I chainops +will be describe in a later section in full detail. Essentially, it +allows one to chain off the buckets in an arbritrary fashion (perhaps +with another hash table). By default, it is done with an ordered linked +list. Of course, it is only meanful when the policy selected is chain. +.PP +.I h_redefine +takes the hash table handle as an argument in addition to all the +other arguments of +.I h_new. +.I h_redefine +will reformat the hash table +according to the passed arguments. It will rehash if the hash table +is valid (so it should not be called lightly). +.I WARNING: +If you want to use h_redefine, it is important that the "key" as +passed to the +.I h_operation +routines is the same as the data stored in the buckets! +This is necessary because +.I h_redefine +operates by calling +.I h_insert +with the items in the buckets as the key. +.PP +.I policy +and +.I type +can be +specified as +.I HASH_POLICY_OLD +and +.I HASH_TYPE_OLD respectively to retain +the old policy and type. For +.I compare, +.I allocate, +.I hash, +.I shash, +.I compress, and +.I chainops, +pass NULL unless you wish to change those functions. Set M +to be zero to retain the old table size (note, if a new hash type is +specified, the passed M may be resized). It is expected that the main use +.I h_redefine +will be to increase the hash table size: use the macro +.I h_rehash +for this. +.PP +.I h_free +will free a hash table. It calls +.I free_func +on every item inserted into the table so that data can be released if +necessary. If free_func is NULL, then it is assumed that the data +need not be released. +.SH Hash Operations +.I h_operation +provides insert, member, and delete operations on a hash table created +by h_new. A high degree of control over its operation is provided. +The macros +.I h_insert, +.I h_delete, +and +.I h_member +hide the less commonly used arguments. +.PP +.I operation +defines the operation to be performed. It best if +.I key +is a pointer to data instead of the actual key. +.TP 10 +\fB HASH_OP_MEMBER +finds an item based upon +.I key +and returns it. If the item is not +in the table, NULL is returned. +The comparision function defined in +.I h_new +is used to determine if the +item is in the table. +.TP 10 +\fBHASH_OP_INSERT +is like find, but the item is inserted if it wasn't already in the +table. +.I allocate, +if non-null, +as defined in +.I h_new +is called to get the data to be stored. If +.I allocate +is NULL, then it assumed that the key is the data. +NULL is returned if the item could not be inserted because all the +buckets were filled or a memory allocation failed. +.TP 10 +\fB HASH_OP_DELETE +will remove the specified item from the table and return it +if it was in the table. +NULL will be returned if the item was not in the +table. +.PP +.I hth +is the hash table handle as returned by +.I h_new. +.PP +.I key +is the used to match the data in the table. +Normally it is a pointer to some data item. +.PP +.I bkt, +and +.I dadvance +allow you to specify the hash bucket to use and the +hash advance (default is 1) to use in open hashing collision +resolution respectively. +If +these are specified as negative numbers, the hash functions +defined in +.I h_new +will be used. +.PP +.I bucket +should be a pointer to an integer into which the primary bucket will +be returned (e.g. the index returned by primary hash function). +.I distance +is set to the number of buckets examined (beyond the first one) before +the item as added. +.SH Chaining off buckets +The default action for chaining off a bucket is to use a linked list +ordered largest to smallest (as defined by the comparision function). +It is possible to define an arbitrary method by defining a set of +chain operations. The functions needed are defined below and should be put +in a struct hash_bucket_list_ops and passed upon a hash table create. +.nf + struct hash_bucket_list_ops { + caddr_t (*hlo_find)(); + caddr_t (*hlo_insert)(); + int (*hlo_delete)(); + caddr_t (*hlo_get)(); /* get any and remove */ + }; +.fi +.PP +In the following discussion, +.I bp +is where information about the "list" +is stored. "list" is used to mean your storage mechanism. It could +be linked list, hash table, array, etc. +.I bp +allows you to disambiguate which list--unless your hash table size is +one, you must support more than one list. An item in the following is +an abstract entity that can be compared against a key by the +.I compare +function provided in +.I h_new. +.I hlo_find, +.I hlo_insert, +and +.I hlo_delete +are matched functions. +.I hlo_find +is always called before +.I hlo_insert +or +.I hlo_delete +and the hash table functions will only call insert or delete if the +item (defined by the key) is not in the list +and in the list respectively. +.PP +.nf +caddr_t (*hlo_find)(bp, key, cmp, distance, hint, hint2) +caddr_t bp; +caddr_t key; +int (*cmp)(); +int *distance; +caddr_t *hint; +caddr_t *hint2; +.fi +.I hlo_find +is used to see if the specified item is in the list based upon the +key. It should return +the the item stored in the list if there and NULL +otherwise. If non-null, this is the value that will be returned by +.I h_operation. +If the return value will be non-null, then +.I distance +should be set to +some metric by this function (e.g. distance from head of list on +linked list). +.I cmp +is a comparision function to use (as passed in h_new). +.I hint, +and +.I hint2 +are places to store hints for +.I hlo_insert +and +.I hlo_delete. +.PP +.nf +caddr_t (*hlo_insert)(bp, key, allocate, distance, hint, hint2) +caddr_t *bp; +caddr_t key; +caddr_t (*allocate)(); +int *distance; +caddr_t hint1; +caddr_t hint2; +.fi +.I hlo_insert +should insert an item onto the list. It should call +.I allocate, +if defined, to create the item based upon the key. The distance should +be updated with respect to your metric set. +.I hint, +and +.I hint2 +are passed as set by the +.I hlo_find. +You should set the bucket pointer to point to your "list" if the list +was empty before (e.g. *bp = head_of_list, *bp = hash_table_handle, +etc.). +.I hlo_insert +should return the stored data. If it cannot insert the +item it may return NULL +.I hlo_insert's +value will be the value +returned by +.I hlo_operation. +.PP +.nf +int (*hlo_delete)(bp, key, distance, hint, hint2) +caddr_t *bp; +caddr_t key; +int *distance; +caddr_t hint; +caddr_t hint2; +.fi +.I hlo_delete +should remove the specified item from the list. It should return TRUE +on success and FALSE on failure. distance should be set to the +distance of the deleted item with respect to the arbritry metric +defined for your set of functions. The bucket pointer should be set +to NULL if there are no longer items in the list (e.g. *bp = NULL). +.I hint +and +.I hint2 +are passed as set by the last +.I hlo_find +operation. +.PP +.nf +caddr_t (*hlo_get)(bp) +caddr_t *bp; +.fi +.I hlo_get +is used by the +.I h_redefine +and +.I h_free +functions. +It should unlink an abritrary item from the list and return it. +.PP +The following simple set of functions define a hash table with no +collisions allowed: +.nf + none_find(bp, key, cmp, distance, hint, hint2) + caddr_t bp, key, *hint,*hint2; + int (*cmp)(), *distance; + { + *distance = 0; + if (bp == NULL) /* nothing in list */ + return(NULL); + if ((*cmp)(key,bp) == 0) + return(*bp); + } + + caddr_t none_insert(bp, key, allocate, distance, hint, hint2) + caddr_t *bp, key, *hint,*hint2; + caddr_t (*dup)(); + { + *distance = 0; + *bp = allocate ? (*allocate)(key) : key; + } + + int none_delete(bp, key, distance, hint, hint2) + caddr_t *bp, key, *hint,*hint2; + { + caddr_t v = *bp; + *distance = 0; + return(v != NULL); /* true if we deleted */ + } + + caddr_t none_get(bp) + caddr_t *bp; + { + caddr_t r = *bp; + *bp = NULL; + return(r); + } +.fi +.SH Statisitcs +.I h_statistics +returns a pointer to the following structure: +.nf + struct hash_statistics { + int hs_buckets; /* number of buckets in table */ + /* describes # of entries in chain */ + int hs_used; /* # of buckets filled */ + /* describes table (not accurate for chain policy) */ + int hs_davg; /* average distance from hash */ + int hs_dsum; /* sum of distances from hash */ + int hs_dmax; /* maximum distance from hash */ + /* describes lookup patterns (describes distance into */ + /* linear table if the policy is chain */ + int hs_lnum; /* remember number of lookups */ + int hs_lsum; /* sum of lookup distances */ + int hs_lavg; /* average lookup distance */ + /* cumulative for lookup patterns (describes overall */ + /* efficiency) */ + int hs_clnum; /* remember number of lookups */ + int hs_clsum; /* sum of lookup distances */ + }; +.fi +The averages are reported as a fixed point number with two decimal +digits of precision after the decimal point (e.g. avg/100.avg%100). +.PP +The lookup and table statistics are cleared on a +.I h_redefine +operation. +.SH NOTES +Some analysis of the hashing functions provided should be done to +determine how "good" they are. +.br +Allocate probably should have been called "get_item" in the above. +.br +Possibly some method for returning the "nth" or "next" item in the +hash table should be provided for times when it is necessary to access +the items in a linear fashion. However, it possible to do this +already using the "allocate" call to put the items on a linked list or +in an array. +.SH RESTRICTIONS +Perhaps more control over the hashing functions should be provided; +however, it is easy enough to replace them. +.SH REFERENCES +Searching and Sorting, The Art of Computer Programming, Volume 3, +Donald E. Knuth. +.SH "SEE ALSO" +bsearch(3), lsearch(3), string(3), tsearch(3), hsearch(3) + + diff --git a/support/uab/hash.c b/support/uab/hash.c new file mode 100644 index 0000000..491791d --- /dev/null +++ b/support/uab/hash.c @@ -0,0 +1,793 @@ +static char rcsid[] = "$Author: djh $ $Date: 1994/10/10 08:56:13 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/hash.c,v 2.4 1994/10/10 08:56:13 djh Rel djh $"; +static char revision[] = "$Revision: 2.4 $"; + +/* + * hash.h - external definitions for hash.c - generalized hashing function + * + * written by Charlie C. Kim + * Academic Networking, Communications and Systems Group + * Center For Computing Activities + * Columbia University + * September 1988 + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * Sept 5, 1988 CCKim Created + * Sept 6, 1988 CCKim Finished: level 0 + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include "hash.h" + +#ifndef FALSE +# define FALSE 0 +#endif +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef PRIVATE +# define PRIVATE static +#endif + + +/* + * holds and describes a hash table + * + * ht_policy: policy on collisions (cf hash.h) + * ht_cfunc: takes (key, data) and returns true if they are equal + * ht_afunc: takes a key and returns the item from higher up + * ht_cpfunc: should compress the key to a integer -- only required if + * no hash function has been provided + * ht_hfunc: takes (M, data) as arguments - returns hash index + * M is the sizeof(int)*8 - log of the table size if the hashing + * type is multiplicative + * ht_hfunc2: is the secondary hashing function for double hashing + * ht_chainops: chaining function other than linked list + * ht_stats: describes performance of hash table + * ht_type: hash function type + * ht_buckets: pointer to the hash table buckets + * +*/ +typedef struct htable { + int ht_M; /* # of hash table buckes */ + int ht_logM; /* M or log M (for certain hash types) */ + int ht_policy; /* hashing policies for collision */ + /* alway call with passed key first, data item second */ + int (*ht_cfunc)(); /* comparision function */ + caddr_t (*ht_afunc)(); /* allocate function */ + u_int (*ht_cpfunc)(); /* compression function */ + u_int (*ht_hfunc)(); /* hashing function */ + u_int (*ht_hfunc2)(); /* secondary hashing function */ + struct hash_bucket_list_ops *ht_chainops; /* chain functions */ + int ht_type; /* hash type */ + struct hash_statistics ht_stats; /* statisitics */ + caddr_t *ht_buckets; /* actual hash table */ +} HTABLE; + +/* some hash functions */ +PRIVATE u_int hash_multiplicative(); +PRIVATE u_int hash_division(); +PRIVATE u_int hash2_multiplicative(); +PRIVATE u_int hash2_division(); + +/* list operations */ +PRIVATE caddr_t list_find(); +PRIVATE caddr_t list_insert(); +PRIVATE int list_delete(); +PRIVATE caddr_t list_get(); + +/* basic hash bucket chaining with an ordered link list */ +PRIVATE struct hash_bucket_list_ops hash_chain_by_list = { + list_find, + list_insert, + list_delete, + list_get +}; + +/* table of primary hashfunctions */ +PRIVATE u_int (*hash_funcs[HASH_TYPE_NUM])() = { + NULL, + hash_division, + hash_multiplicative, +}; + +/* table of secondary hash functions */ +PRIVATE u_int (*hash_funcs2[HASH_TYPE_NUM])() = { + NULL, /* own */ + hash2_division, + hash2_multiplicative, +}; + +/* free a hash table - free_func gets called to free data */ +void +h_free(ht, free_func) +HTABLE *ht; +int (*free_func)(); +{ + caddr_t *bt; + caddr_t p; + int M, i, policy; + caddr_t (*get_func)(); + + M = ht->ht_M; /* get # of entries */ + bt = ht->ht_buckets; /* get buckets */ + ht->ht_buckets = NULL; /* just in case... */ + if (ht->ht_chainops) + get_func = ht->ht_chainops->hlo_get; + policy = ht->ht_policy; + if (bt == NULL) + return; + for (i = 0; i < M; i++) { + if (bt[i] == NULL) + continue; + switch (policy) { + case HASH_POLICY_CHAIN: + if (get_func == NULL) + break; + while ((p = (*get_func)(&bt[i]))) + if (free_func) + (*free_func)(p); + break; + default: + if (free_func) + (*free_func)(bt[i]); + } + } + free((caddr_t)bt); /* free old table */ + free((caddr_t)ht); +} + +/* setup a new hash table, returns handle for hash table */ +caddr_t +h_new(policy, hashtype, M, cfunc, afunc, cpfunc, hfunc, hfunc2, chainops) +int policy; +int hashtype; /* hash type */ +int M; +int (*cfunc)(); /* comparision function */ +caddr_t (*afunc)(); /* allocate function */ +u_int (*cpfunc)(); /* compression function */ +u_int (*hfunc)(); /* hash function */ +u_int (*hfunc2)(); /* secondary hash function */ +struct hash_bucket_list_ops *chainops; +{ + HTABLE *htable; + + if (cfunc == NULL) { /* required! */ + fprintf(stderr, "hash table create: no compare function\n"); + return(NULL); + } + if (!HASH_TYPE_VALID(hashtype)) { + fprintf(stderr, "hash table create: invalid type %d\n", hashtype); + return(NULL); + } + if (hashtype == HASH_TYPE_OWN && hfunc == NULL) { + fprintf(stderr, "hash table create: must give hash function when own set\n"); + return(NULL); + } + if (!HASH_POLICY_VALID(policy)) { + fprintf(stderr, "hash table create: invalid policy %d\n", policy); + return(NULL); + } + if (M <= 0) { + fprintf(stderr, "hash table create: invalid hash table size %d\n", M); + return(NULL); + } + if ((htable = (HTABLE *)malloc(sizeof(HTABLE))) == NULL) + return(NULL); + htable->ht_policy = policy; + htable->ht_cfunc = cfunc; + htable->ht_afunc = afunc; + htable->ht_hfunc = hash_funcs[hashtype]; + if (htable->ht_policy == HASH_POLICY_DOUBLE_HASH) + htable->ht_hfunc2 = hash_funcs2[hashtype]; + else + htable->ht_hfunc2 = NULL; + /* override std. hash functions if specified */ + if (hfunc) + htable->ht_hfunc = hfunc; + if (hfunc2) + htable->ht_hfunc2 = hfunc2; + htable->ht_cpfunc = cpfunc; + htable->ht_chainops = chainops ? chainops : &hash_chain_by_list; + htable->ht_type = hashtype; + bzero(&htable->ht_stats, sizeof(htable->ht_stats)); + htable->ht_stats.hs_buckets = M; + htable->ht_M = 0; /* assume these */ + htable->ht_buckets = NULL; + return((caddr_t)h_redefine(htable,HASH_POLICY_OLD,HASH_TYPE_OLD, M, + NULL, NULL,NULL,NULL, NULL, NULL)); +} + +/* redefine an existing hash table, will rehash by creating new set of */ +/* buckets and killing off old set */ +caddr_t +h_redefine(ht, policy, hashtype, M, cfunc, afunc, cpfunc, hfunc, hfunc2, + chainops) +HTABLE *ht; +int policy; /* hashing policy */ +int hashtype; /* hashing type */ +int M; /* size */ +int (*cfunc)(); /* comparision function */ +caddr_t (*afunc)(); /* allocate function */ +u_int (*hfunc)(); /* hash function */ +u_int (*hfunc2)(); /* secondary hash function */ +u_int (*cpfunc)(); /* compression function */ +struct hash_bucket_list_ops *chainops; +{ + int logM, oldM, i, oldPolicy; + struct hash_bucket_list_ops *oldChainOps; + caddr_t *bt, *nbt; + caddr_t p; + + if (!HASH_TYPE_VALID(hashtype) && hashtype != HASH_TYPE_OLD) { + fprintf(stderr, "hash table create: invalid type %d\n", hashtype); + return(NULL); + } + if (!HASH_POLICY_VALID(policy) && policy != HASH_POLICY_OLD) { + fprintf(stderr, "hash table create: invalid policy %d\n", policy); + return(NULL); + } + if (M <= 0) /* zero means base on old */ + M = ht->ht_M; + if (hashtype == HASH_TYPE_OLD) + hashtype = ht->ht_type; /* get old */ + logM = 0; + switch (hashtype) { + case HASH_TYPE_MULTIPLICATIVE: + i = M >> 1; + M = 1; + logM = 0; + while (i) { /* while M is still about */ + i >>= 1; /* divide by 2 */ + M <<= 1; /* multiply by 2 */ + logM++; + } + break; + case HASH_TYPE_DIVISION: + M += (1 - (M%2)); /* make odd */ + /* scale up M so it isn't relatively prime for these small primes */ + /* c.f. Fundamental of Data Structures, Horowitz and Sahni, pp. 461 */ + while (!((M%3) && (M%5) && (M%7) && (M%11) && (M%13) && (M%17)&&(M%19))) + M+=2; + break; + default: + break; + } + if (M <= ht->ht_M) /* nothing to do */ + return((caddr_t)ht); + if (logM == 0) { /* no logM? figure it */ + int t = M>>1; /* get M */ + do { + logM++; /* int log M to 1 */ + t >>= 1; /* divide by 2 */ + } while (t); + } + bt = ht->ht_buckets; /* get buckets */ + oldM = ht->ht_M; + oldPolicy = ht->ht_policy; + oldChainOps = ht->ht_chainops; + + if ((nbt = (caddr_t *)calloc((u_int)M, sizeof(caddr_t))) == NULL) { + fprintf(stderr, "hash table create: no memory for %d element table\n",M); + return(NULL); /* return */ + } + ht->ht_buckets = nbt; /* save new bucket table */ + ht->ht_M = M; /* assume these */ + ht->ht_logM = logM; + ht->ht_stats.hs_buckets = M; /* mark # of buckets */ + ht->ht_policy = (policy == HASH_POLICY_OLD) ? oldPolicy : policy; + if (afunc) + ht->ht_afunc = afunc; + if (cfunc) + ht->ht_cfunc = cfunc; + if (ht->ht_type != hashtype && hashtype != HASH_TYPE_OLD) { + ht->ht_hfunc = hash_funcs[hashtype]; + if (ht->ht_policy == HASH_POLICY_DOUBLE_HASH) + ht->ht_hfunc2 = hash_funcs2[hashtype]; + else + ht->ht_hfunc2 = NULL; + } + /* always reset if given */ + if (hfunc) + ht->ht_hfunc = hfunc; + if (hfunc2) + ht->ht_hfunc2 = hfunc2; + if (cpfunc) + ht->ht_cpfunc = cpfunc; + if (chainops) + ht->ht_chainops = chainops; + ht->ht_type = hashtype; + { + struct hash_statistics *s = &ht->ht_stats; + /* no longer valid */ + s->hs_used = s->hs_davg = s->hs_dsum = s->hs_dmax = 0; + /* no longer valid */ + s->hs_lnum = s->hs_lsum = s->hs_lavg = 0; + /* cum. statistics stay */ + } + /* rehash if new table */ + if (bt) { + afunc = ht->ht_afunc; /* save */ + ht->ht_afunc = NULL; /* turn off for a bit */ + for (i = 0; i < oldM; i++) { + if (bt[i]) { + switch (oldPolicy) { + case HASH_POLICY_CHAIN: + while ((p = (*oldChainOps->hlo_get)(&bt[i]))) + h_insert(ht, p); + break; + default: + h_insert(ht, bt[i]); + } + } + } + ht->ht_afunc = afunc; /* turn back on */ + free((caddr_t)bt); /* free old table */ + } + return((caddr_t)ht); +} + +/* update hash TABLE statistics: generally, these are off for chain */ +/* and when there are deletes done */ +PRIVATE int +update_hash_table_stats(s, distance, updown) +struct hash_statistics *s; +int distance; +int updown; +{ + if (distance > s->hs_dmax) /* new maximum distance */ + s->hs_dmax = distance; + s->hs_dsum += distance; /* bump sum of distances */ + s->hs_used += updown; + if (s->hs_used) + s->hs_davg = (100*s->hs_dsum) / s->hs_used; /* scale it */ + else + s->hs_davg = 0; + return(s->hs_davg); +} + +/* update lookup statisitics */ +PRIVATE int +update_hash_lookup_stats(s, distance) +struct hash_statistics *s; +int distance; +{ + s->hs_lsum += distance; /* bump sum of distances */ + s->hs_lnum++; /* bump number of distances */ + s->hs_clsum += distance; /* same for cum. */ + s->hs_clnum++; + s->hs_lavg = (100*s->hs_lsum) / s->hs_lnum; /* save 2 decimal points */ + return(s->hs_lavg); +} + +/* hash table operation: delete, insert, find */ +caddr_t +h_operation(what, ht, key, idx, idx2, d, b) +int what; +HTABLE *ht; +caddr_t key; +int idx; /* preliminary index (-1 if none) */ +int idx2; /* secondary index (-1 if none) */ +int *d; /* return distance ? */ +int *b; /* return bucket # */ +{ + int sidx, t; + int distance; + u_int cpkey; /* compress version of key */ + caddr_t *bp; /* bucket pointer */ + caddr_t *pbp = NULL; /* previous bucket pointer for delete */ + caddr_t data = NULL; + + /* blather */ + if (ht == NULL || HASH_OP_INVALID(what)) + return(NULL); + if (idx < 0) { + if (ht->ht_cpfunc) { + cpkey = (*ht->ht_cpfunc)(key); + idx = (*ht->ht_hfunc)(ht->ht_M, ht->ht_logM, cpkey); + } else + idx = (*ht->ht_hfunc)(ht->ht_M, ht->ht_logM, key); + } + sidx = idx; + if (ht->ht_buckets == NULL) { + fprintf(stderr, "No buckets for hash table! (Possibly using a freed \ + hash table handle)\n"); + return(NULL); + } + bp = &ht->ht_buckets[idx]; /* start */ + distance = 0; + if (b) + *b = sidx; + + if (ht->ht_policy == HASH_POLICY_CHAIN) { + caddr_t hint, hint2; + + /* distance should be updated */ + data = (*ht->ht_chainops->hlo_find)(*bp,key,ht->ht_cfunc, + &distance, &hint, &hint2); + switch (what) { + case HASH_OP_DELETE: + if (!data) + break; + /* key */ + /* ignore error (should not happen!) */ + (void)(*ht->ht_chainops->hlo_delete)(bp, key, &distance, hint, hint2); + update_hash_table_stats(&ht->ht_stats, -distance, -1); + break; + case HASH_OP_MEMBER: + if (data) + t = update_hash_lookup_stats(&ht->ht_stats, distance); + break; + case HASH_OP_INSERT: + if (data) { + t = update_hash_lookup_stats(&ht->ht_stats, distance); + break; + } + data= (*ht->ht_chainops->hlo_insert)(bp,key,ht->ht_afunc, + &distance, hint,hint2); + update_hash_table_stats(&ht->ht_stats, distance, 1); + break; + } + if (d) + *d = distance; + return(data); + } + + do { + if (*bp == NULL) { + switch (what) { + case HASH_OP_DELETE: /* finished delete */ + break; + case HASH_OP_MEMBER: + data = NULL; + break; + case HASH_OP_INSERT: + /* left with insert */ + data = ht->ht_afunc ? (*ht->ht_afunc)(key) : key; + *bp = data; + update_hash_table_stats(&ht->ht_stats, distance, 1); + break; + } + if (d) + *d = distance; + return(data); + } else { + switch (what) { + case HASH_OP_DELETE: + /* if we haven't found an key to delete, try to find it */ + if (!pbp) { + if ((*ht->ht_cfunc)(key, *bp) == 0) { + data = *bp; /* save return key */ + *bp = NULL; /* clear out this bucket */ + pbp = bp; /* remember this bucket */ + update_hash_table_stats(&ht->ht_stats, -distance, -1); + } + } else { + /* delete old distance */ + update_hash_table_stats(&ht->ht_stats, -distance, -1); + /* insert new distance */ + update_hash_table_stats(&ht->ht_stats, distance-1, 1); + *pbp = *bp; /* move bucket */ + *bp = NULL; /* clear out this bucket */ + pbp = bp; /* remember this bucket */ + } + default: + if ((*ht->ht_cfunc)(key, *bp) == 0) { + t = update_hash_lookup_stats(&ht->ht_stats, distance); + if (d) + *d = distance; + return(*bp); /* done */ + } + } + } + if (idx2 < 0 && ht->ht_hfunc2) + if (ht->ht_cpfunc) + idx2 = (*ht->ht_hfunc2)(ht->ht_M, ht->ht_logM, idx, cpkey); + else + idx2 = (*ht->ht_hfunc2)(ht->ht_M, ht->ht_logM, idx, key); + distance++; + idx += idx2 > 0 ? idx2 : 1; /* bump index */ + bp++; /* advance bucket pointer */ + if (idx >= ht->ht_M) { /* need to wrap around */ + idx %= ht->ht_M; /* push index about */ + bp = &ht->ht_buckets[idx]; /* and reset buckets */ + } + } while (sidx != idx); + return(NULL); +} + +/* return hash statistics */ +struct hash_statistics * +h_statistics(h) +HTABLE *h; +{ + return(&h->ht_stats); +} + + +/* for linked list */ +struct hash_chain_item { + struct hash_chain_item *hci_next; /* pointer to next item in chain */ + caddr_t hci_data; /* pointer to data */ +}; + +/* + * hint == previous(hint2) + * hint2 is the match node or node whose data is > than current + * +*/ +PRIVATE caddr_t +list_find(h, key, cmp, distance, hint, hint2) +struct hash_chain_item *h; +caddr_t key; +int (*cmp)(); +int *distance; +struct hash_chain_item **hint; +struct hash_chain_item **hint2; +{ + struct hash_chain_item *hp = NULL; + int d,c; + + *distance = 0; /* no distnace */ + *hint = NULL; /* mark no hint */ + *hint2 = NULL; + if (h == NULL) + return(NULL); + for (d = 0 ; h ; h = h->hci_next) { + if ((c = (*cmp)(key, h->hci_data)) >= 0) + break; + d++; + hp = h; + } + if (distance) + *distance = d; + if (hint2) + *hint2 = h; + if (hint) + *hint = hp; + return(c == 0 ? h->hci_data : NULL); +} + +/* + * insert item into chain. hint is from the lookup and helps us insert + * distance is from lookup too (we could choose to change) + * + * hint == previous(hint2) + * hint2 is the match node or node whose data is > than current + * return 0 on success, -1 on failure. + * + */ +/*ARGSUSED*/ +PRIVATE caddr_t +list_insert(head, key, alloc, distance, hint, hint2) +caddr_t *head; +caddr_t key; +caddr_t (*alloc)(); +int *distance; +struct hash_chain_item *hint; +struct hash_chain_item *hint2; +{ + struct hash_chain_item *h; + + h = (struct hash_chain_item *)malloc(sizeof(struct hash_chain_item)); + if (h == NULL) + return(NULL); + h->hci_data = alloc ? (*alloc)(key) : key; + h->hci_next = hint2; + if (hint) + hint->hci_next = h; + else + *head = (caddr_t)h; + return(h->hci_data); +} + +/* + * assumes a find has been done, hint is set by find and item exists + * in the list + * head - head of list + * item - data (unused) + * hint - previous node to one that contains item + * distance - distance to update (not done) (may be deleted) + * +*/ +/*ARGSUSED*/ +PRIVATE int +list_delete(head, key, distance, hint, hint2) +caddr_t *head; +caddr_t key; +int *distance; /* not used */ +struct hash_chain_item *hint; +struct hash_chain_item *hint2; +{ + /* trust our input: two things could be wrong, first */ + /* hint2 == NULL ==> nothing to delete */ + /* hint2 != "key" ==> item not in list */ + if (hint == NULL) { + *head = (caddr_t)hint2->hci_next; /* remove */ + free((caddr_t)hint2); + return(TRUE); + } + hint->hci_next = hint2->hci_next; /* unlink */ + free((caddr_t)hint2); /* get rid of node */ + return(TRUE); +} + +/* gets first item on list and returns data, freeing up node */ +PRIVATE caddr_t +list_get(h) +struct hash_chain_item **h; +{ + struct hash_chain_item *n; + caddr_t d; + + if (h == NULL || *h == NULL) + return(NULL); + n = *h; /* get item */ + *h = n->hci_next; /* and remove */ + d = n->hci_data; + free((caddr_t)n); + return(d); +} + +/* do hash division method */ +/*ARGSUSED*/ +PRIVATE u_int +hash_division(M, logM, idx) +int M; +int logM; +u_int idx; +{ + return(idx % M); +} + +/* will work will with M if M-2,M are twin primes */ +/*ARGSUSED*/ +PRIVATE u_int +hash2_division(M, logM, hidx, idx) +int M; +int logM; +u_int hidx; +u_int idx; +{ + return(1 + (idx % (M-2))); +} + +/* handle multiplicative method - hopefully the multiplier gives us */ +/* good range */ +/*ARGSUSED*/ +PRIVATE u_int +hash_multiplicative(M, logM, idx) +int M; +int logM; +u_int idx; +{ + return(((u_int)((u_int)idx*(u_int)A_MULTIPLIER)>>(8*sizeof(int)-logM))); +} + +/* the r more bits -- should be indepdent of the first r bits */ +/*ARGSUSED*/ +PRIVATE u_int +hash2_multiplicative(M, logM, hidx, idx) +int M; +int logM; +u_int hidx; +u_int idx; +{ + return(((u_int)((u_int)idx*(u_int)A_MULTIPLIER)>>(8*sizeof(int)-logM-logM)|1) ); +} + +#ifdef TESTIT +/* test program */ +u_int +docomp(data) +char *data; +{ + u_int j; + j = 0; + while (*data) + j = ((j + *data++) >> 1) | j<<31; + return(j); +} + +char * +alloc_func(p) +char *p; +{ + char *d = (caddr_t)malloc(strlen(p) + 1); + strcpy(d, p); + return(d); +} + +dumpstats(msg, s) +char *msg; +struct hash_statistics *s; +{ + printf("%s\n\t %d bkts used, avg dist = %d.%02d, max dist = %d\n", + msg, + s->hs_used, s->hs_davg/100, s->hs_davg % 100, + s->hs_dmax); +} + +main() +{ + HTABLE *hpc, *hplp, *hpdh; + extern strcmp(); + char buf[BUFSIZ]; + int b, d, op; + char *p; + +#define X 16 + + hpc = (HTABLE *)h_new(HASH_POLICY_CHAIN, HASH_TYPE_DIVISION, X, + strcmp, alloc_func, docomp, NULL, NULL, NULL); + hplp = (HTABLE *)h_new(HASH_POLICY_LINEAR_PROBE, + HASH_TYPE_MULTIPLICATIVE, X, strcmp, + alloc_func, docomp, NULL, NULL, NULL); + hpdh = (HTABLE *)h_new(HASH_POLICY_DOUBLE_HASH, + HASH_TYPE_MULTIPLICATIVE, X, strcmp, + alloc_func, docomp, NULL, NULL, NULL); + while (gets(buf) != NULL) { + p = buf+1; + switch (buf[0]) { + case '+': + printf("INSERT %s\n", buf+1); + op = HASH_OP_INSERT; + break; + case '-': + printf("DELETE %s\n", buf+1); + op = HASH_OP_DELETE; + break; + case ' ': + printf("FIND %s\n", buf+1); + op = HASH_OP_MEMBER; + break; + default: + op = HASH_OP_INSERT; + p = buf; + } + if ((h_operation(op, hpc, p, -1, -1, &d, &b))) + printf("chain: %s at distance %d from bucket %d\n", p, d,b); + else + printf("chain hash table overflow or item not in table\n"); + if ((h_operation(op, hplp, p, -1, -1, &d, &b))) + printf("linear probe: %s at distance %d from bucket %d\n", p, d,b); + else + printf("linear probe hash table overflow or item not in table\n"); + if ((h_operation(op, hpdh, p, -1, -1, &d, &b))) + printf("double hash: %s at distance %d from bucket %d\n", p, d,b); + else + printf("double hash table overflow or item not in table\n"); + } + dumpstats("double hash with multiplicative hash", h_statistics(hpdh)); + h_redefine(hpdh, HASH_POLICY_CHAIN,HASH_TYPE_DIVISION, X, NULL, + NULL, NULL, NULL,NULL,NULL); + dumpstats("redefine above as chain with division hash", h_statistics(hpdh)); + h_redefine(hpdh, HASH_POLICY_LINEAR_PROBE,HASH_TYPE_MULTIPLICATIVE, + X, NULL,NULL,NULL,NULL,NULL,NULL); + dumpstats("redefine above as linear probe with multiplicative hash", + h_statistics(hpdh)); + dumpstats("chain with division hash", h_statistics(hpc)); + dumpstats("linear probe with multiplicative hash", h_statistics(hplp)); + h_free(hpdh, free); +} +#endif diff --git a/support/uab/hash.h b/support/uab/hash.h new file mode 100644 index 0000000..bc0abb6 --- /dev/null +++ b/support/uab/hash.h @@ -0,0 +1,127 @@ +/* + * $Author: djh $ $Date: 91/02/15 23:07:37 $ + * $Header: hash.h,v 2.1 91/02/15 23:07:37 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * hash.h - external definitions for hash.c - generalized hashing function + * + * written by Charlie C. Kim + * Academic Networking, Communications and Systems Group + * Center For Computing Activities + * Columbia University + * September 1988 + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * Sept 5, 1988 CCKim Created + * Sept 6, 1988 CCKim Finished: level 0 + * +*/ + +#ifndef _HASH_HEADER_INCLUDED +#define _HASH_HEADER_INCLUDED "yes" + +/* hash table operations recognized */ +#define HASH_OP_DELETE 2 +#define HASH_OP_MEMBER 1 +#define HASH_OP_INSERT 0 +#define HASH_OP_NUM 3 + +#define HASH_OP_VALID(n) ((n) < HASH_OP_NUM && (n) >= HASH_OP_INSERT) +#define HASH_OP_INVALID(n) ((n) >= HASH_OP_NUM || (n) < HASH_OP_INSERT) + +/* hashing collision policies */ +#define HASH_POLICY_OLD -1 /* old for redefine */ +#define HASH_POLICY_CHAIN 0 /* chain on collisions */ +#define HASH_POLICY_LINEAR_PROBE 1 /* linear probes on collisions */ +#define HASH_POLICY_DOUBLE_HASH 2 /* double hash on collisions */ + +/* given a hash policy, returns true if it is a valid policy */ +#define HASH_POLICY_VALID(n) ((n) <= HASH_POLICY_DOUBLE_HASH && \ + (n) >= HASH_POLICY_CHAIN) +#define HASH_POLICY_LINKSNEEDED(n) ((n) == HASH_POLICY_CHAIN) + +/* hash function types */ +#define HASH_TYPE_OLD -1 /* old for redefine */ +#define HASH_TYPE_OWN 0 /* our own */ +#define HASH_TYPE_DIVISION 1 /* division method */ +#define HASH_TYPE_MULTIPLICATIVE 2 /* multiplicative method */ +/* for multiplicative mode: try for fibonacci */ +/* only valid for 32 bit machines! */ +#define A_MULTIPLIER 2630561063 /* gotta figure out a good one */ + +#define HASH_TYPE_NUM 3 + +#define HASH_TYPE_VALID(n) ((n) < HASH_TYPE_NUM && \ + (n) >= HASH_TYPE_OWN) + + +/* + * structure to allow operations other than a linear list off a hash + * bucket in the chain policy + * +*/ +struct hash_bucket_list_ops { + caddr_t (*hlo_find)(); /* find a member */ + caddr_t (*hlo_insert)(); /* insert a member (returns ptr to */ + /* data) */ + int (*hlo_delete)(); /* delete a member */ + caddr_t (*hlo_get)(); /* get any member and remove from list */ +}; + +/* averages are fixed decimal with 2 digits after the decimal */ +/* they are kept in case we want to do something when average */ +/* distances get too large */ +struct hash_statistics { + int hs_buckets; /* number of buckets in table */ + /* describes # of entries in chain */ + int hs_used; /* # of buckets filled */ + /* describes table (not accurate for chain policy) */ + int hs_davg; /* average distance from hash index */ + int hs_dsum; /* sum of distances from hash index */ + int hs_dmax; /* maximum distance from hash index */ + /* describes lookup patterns (describes distance into linear table */ + /* if the policy is chain */ + int hs_lnum; /* remember number of lookups */ + int hs_lsum; /* sum of lookup distances */ + int hs_lavg; /* average lookup distance */ + /* cumulative for lookup patterns (describes overall efficiency) */ + int hs_clnum; /* remember number of lookups */ + int hs_clsum; /* sum of lookup distances */ +}; + +/* function declarations */ +caddr_t h_new(); /* create new table */ +caddr_t h_operation(); /* hash operations */ +struct hash_statistics *h_statistics(); /* returns stats on a table */ +void h_free(); /* free a table */ +/* must specify policy and type */ +caddr_t h_redefine(); /* redefine operating parameters for a */ + /* hash table, forces a rehash */ +#define h_rehash(ht,M) (h_redefine((ht),HASH_POLICY_OLD,HASH_TYPE_OLD,\ + (M),NULL,NULL,NULL,NULL,NULL,NULL)) +/* call hash operation for these */ +#define h_member(ht,key) (h_operation(HASH_OP_MEMBER,(ht),(key),-1,-1,\ + NULL,NULL)) +#define h_insert(ht,key) (h_operation(HASH_OP_INSERT,(ht),(key),\ + -1,-1, NULL, NULL)) +#define h_delete(ht,key) (h_operation(HASH_OP_DELETE,(ht),(key),-1,-1,\ + NULL,NULL)) + + +#endif /* _HASH_HEADER_INCLUDED */ diff --git a/support/uab/if_desc.h b/support/uab/if_desc.h new file mode 100644 index 0000000..86a4986 --- /dev/null +++ b/support/uab/if_desc.h @@ -0,0 +1,64 @@ +/* + * $Author: djh $ $Date: 91/02/15 23:07:39 $ + * $Header: if_desc.h,v 2.1 91/02/15 23:07:39 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * if_desc.h - interface description + * + * describes parameters for a interface + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * Sept 8, 1988 CCKim Created + * +*/ + +#ifndef _IF_DESC_INCLUDED +#define _IF_DESC_INCLUDED "yes" +# include "mpxddp.h" +/* + * description of a supported LAP type + * +*/ +typedef struct lap_description { + char *ld_name; /* name of lap */ + char **ld_key; /* array of keywords to use for lap type */ + int ld_wants_data; /* needs more than key */ + int (*ld_init_routine)(); /* (id, async) */ + int (*ld_stats_routine)(); /* (fd, id) */ + int (*ld_dump_routine)(); /* (fd, id) */ +} LDESC_TYPE; + +/* + * an interface description (port description) + * + * Call ld_init_routine with this (+ async flag if you want to run + * async if possible) + * All fields except id_ifuse are not touched by ld_init_routine +*/ +typedef struct interface_description { + struct lap_description *id_ld; /* lap description */ + char *id_intf; /* interface name */ + int id_intfno; /* interface # */ + struct mpxddp_module *id_local; /* local delivery */ + int id_isabridge; /* flag */ + int id_network; /* network number (net order) */ + byte *id_zone; /* zone name (pstring) */ + struct interface_description *id_next; /* next in list */ + caddr_t id_ifuse; /* interface use */ +} IDESC_TYPE; +#endif diff --git a/support/uab/kip_mpx.c b/support/uab/kip_mpx.c new file mode 100644 index 0000000..dedcfba --- /dev/null +++ b/support/uab/kip_mpx.c @@ -0,0 +1,535 @@ +static char rcsid[] = "$Author: djh $ $Date: 1995/05/29 10:45:50 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/kip_mpx.c,v 2.6 1995/05/29 10:45:50 djh Rel djh $"; +static char revision[] = "$Revision: 2.6 $"; + +/* + * kip_mpx.c - talks to cap processes via udp + * + * demultiplexing communications point with various processes. + * Two versions in here: one version speaks directly to KIP module + * CAP clients (KIP). The other speaks on a modified range (MKIP) + * + * KIP writes etalk.local and cannot be used when the rebroadcaster + * is in use. NOTE: THIS DOES NOT (WILL NEVER) WORK CORRECTLY. + * MKIP writes etalk.local for use by CAP programs. + * + * This version really cheats and uses a different range of ports, + * but the same encapsulation used for kip. + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * April 3, 1988 CCKim Created + * November, 1990 djh@munnari.OZ.AU formalize etalk.local, check portrange + * +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "mpxddp.h" +#include "gw.h" +#include "log.h" + +#define LOG_STD 2 + +/* + ethertalk to kip - try 1: + o forward packets by sending to ddp udp port + o receive packets by having the remote send to "bridge" socket + o much like kip methods except different socket range and fewer + conditions in lower level code +*/ + +#ifndef TAB +# define TAB "/etc/etalk.local" +#endif +#ifndef MTAB +# define MTAB "/etc/etalk.local" +#endif + +/* handle kip fakeout */ +#define mkip_rebPort 903 +#define kip_rebPort 902 + +struct kipdef { + int s; /* socket */ + word net; + byte node; + byte bridge_node; + struct sockaddr_in sin; +}; + +extern word this_net, bridge_net, nis_net, async_net; +extern byte this_node, bridge_node, nis_node; +extern char this_zone[34], async_zone[34]; +extern struct in_addr bridge_addr; + +#define MKIP_HANDLE 0 +#define KIP_HANDLE 1 +#define NUM_KIP_HANDLE 2 + +#define VALID_HANDLE(h) ((h) >= 0 && (h) < NUM_KIP_HANDLE) +#define INVALID_HANDLE(h) ((h) >= NUM_KIP_HANDLE || (h) < 0) +private struct kipdef kipdefs[NUM_KIP_HANDLE]; + +#define KIP_PRIMARY 0 /* primary listening */ +#define KIP_SECONDARY 1 /* secondary: local to process */ + +/* destination */ +private struct in_addr desthost; + +private DDP kddp; +private LAP klap; +private char kbuf[ddpMaxData+ddpSize]; /* add in space for lap */ + +private word portrange; /* old or new ... */ + +private int kip_init(); +private int mkip_init(); +private int kip_grab(); +private int kips_ahoy(); +private int kip_sendddp(); +private int kip_havenode(); +private int kip_havenet(); +private int kip_havezone(); + +export struct mpxddp_module mkip_mpx = { + "modified KIP forwarding", + "MKIP", + mkip_init, + NULL, /* grab, don't do for now (ddpsrvc not done) */ + kip_sendddp, + kip_havenode, + kip_havenet, + kip_havezone +}; + +export struct mpxddp_module kip_mpx = { + "KIP forwarding", + "KIP", +#ifdef notdef + /* doesn't work right because of the way kip routing is done, sigh */ + kip_init, + NULL, /* grab, don't do for now (ddpsrvc not done) */ + kip_sendddp, + kip_havenode, + kip_havenet, + kip_havezone +#else + NULL, /* init */ + NULL, /* grab, don't do for now (ddpsrvc not done) */ + NULL, /* senddp */ + NULL, /* havenode */ + NULL, /* have net */ + NULL /* have zone */ +#endif +}; + +static int name_toipaddr(); + +private +kip_listener(fd, k, level) +int fd; +struct kipdef *k; +int level; +{ + struct msghdr msg; + struct iovec iov[3]; + int len; + struct sockaddr_in from_sin; + + /* should check k */ + iov[0].iov_base = (caddr_t)&klap; + iov[0].iov_len = lapSize; + iov[1].iov_base = (caddr_t)&kddp; + iov[1].iov_len = ddpSize; + iov[2].iov_base = kbuf; + iov[2].iov_len = ddpMaxData; + msg.msg_name = (caddr_t) &from_sin; + msg.msg_namelen = sizeof(from_sin); + msg.msg_iov = iov; + msg.msg_iovlen = 3; +#if (defined(__386BSD__) || defined(__FreeBSD__)) + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; +#else /* __386BSD__ */ + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; +#endif /* __386BSD__ */ + if ((len = recvmsg(fd,&msg,0)) < 0) { + logit(LOG_LOG|L_UERR, "recvmsg: kip listener"); + return(len); + } + /* could do rebroadcaster work here */ + if (len < (lapSize+ddpSize)) + return(-1); + len -= (lapSize + ddpSize); + if (klap.type != lapDDP) + return(-1); + switch (level) { + case KIP_PRIMARY: + if (klap.src != k->node) /* drop! must have wrong configuration */ + return(-1); + if (kddp.srcNode != k->node || (kddp.srcNet != k->net && kddp.srcNet != 0)) + return(-1); + break; + case KIP_SECONDARY: + /* always accept? */ + break; + } + ddp_output(NULL, &kddp, (byte *)kbuf, len); + return(0); +} + +/* + * initialize kip forwarding + * +*/ +private int +mkip_init() +{ + static int inited = FALSE; + int i; + + if (inited) + return(-1); + if ((i = kips_ahoy(MKIP_HANDLE)) >= 0) + inited = TRUE; + return(i); +} + +private int +kip_init() +{ + static int inited = FALSE; + int i; + + if (inited) + return(-1); + if ((i = kips_ahoy(KIP_HANDLE)) >= 0) + inited = TRUE; + return(i); +} + +private int +kips_ahoy(h) +{ + int cc; + struct kipdef *k; + word getPRange(); + + if (h != MKIP_HANDLE && h != KIP_HANDLE) + return(-1); + k = &kipdefs[h]; + k->net = 0; + k->node = 0; + k->bridge_node = 0; + portrange = getPRange(); + desthost.s_addr = inet_addr("127.0.0.1"); + if (desthost.s_addr == -1) + return(-1); + /* no need to bind since we don't recv on this socket, just send... */ + if ((k->s = socket(AF_INET,SOCK_DGRAM,0)) < 0) { + logit(LOG_LOG|L_UERR, "socket: kip init"); + return(-1); + } + { long len; + len = 6 * 1024; /* should be enough ?? */ + if(setsockopt(k->s,SOL_SOCKET,SO_RCVBUF,(char *)&len,sizeof(long))!=0){ + fprintf(stderr, "Couldn't set socket options\n"); + } + } + + + k->sin.sin_family = AF_INET; + switch (h) { + case MKIP_HANDLE: + k->sin.sin_addr.s_addr = desthost.s_addr; + k->sin.sin_port = htons(mkip_rebPort); + break; + case KIP_HANDLE: + k->sin.sin_addr.s_addr = INADDR_ANY; + k->sin.sin_port = htons(kip_rebPort); + break; + } + if ((cc = bind(k->s, (caddr_t)&k->sin, sizeof(k->sin))) < 0) { + close(k->s); + logit(LOG_LOG|L_UERR, "bind: kip init"); + return(cc); + } + fdlistener(k->s, kip_listener, k, KIP_PRIMARY); + return(h); +} + +private int +kip_grab(hdl, skt) +int hdl; +int skt; +{ + int fd, e; + struct sockaddr_in lsin; + word mddp2ipskt(); + word ddp2ipskt(); + + if (INVALID_HANDLE(hdl)) + return(-1); + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + logit(LOG_STD|L_UERR, "kip grab: socket for skt %d", skt); + return(fd); + } + { long len; + len = 6 * 1024; /* should be enough ?? */ + if(setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(char *)&len,sizeof(long))!=0){ + fprintf(stderr, "Couldn't set socket options\n"); + } + } + lsin.sin_family = AF_INET; + /* bind only for localhost if mkip */ + switch (hdl) { + case MKIP_HANDLE: + lsin.sin_addr.s_addr = desthost.s_addr; + lsin.sin_port = htons(mddp2ipskt(skt)); + break; + case KIP_HANDLE: + lsin.sin_addr.s_addr = INADDR_ANY; + lsin.sin_port = htons(ddp2ipskt(skt)); + break; + } + if (lsin.sin_port == 0) { /* same swapped or unswapped */ + close(fd); /* bad ddp socket */ + return(-1); + } + if ((e = bind(fd, (caddr_t)&lsin, sizeof(lsin))) < 0) { + logit(LOG_STD|L_UERR, "kip grab: bind for skt %d", skt); + return(fd); + } + fdlistener(kipdefs[hdl].s, kip_listener, KIP_SECONDARY); + return(fd); +} + +/* + * send along a ddp packet to the appropriate process + * +*/ +private int +kip_sendddp(hdl, ddp, data, cc) +int hdl; +DDP *ddp; +caddr_t data; +int cc; +{ + struct msghdr msg; + word destskt; + int err; + struct iovec iov[4]; + LAP lap; + int t; + struct kipdef *k; + word mddp2ipskt(); + word ddp2ipskt(); + + if (hdl != MKIP_HANDLE && hdl != KIP_HANDLE) + return(-1); + k = &kipdefs[hdl]; + + /* I THINK this is the right place for this -- this way, the demuxer */ + /* could possible handle multiple networks */ + if (ddp->dstNet != k->net && ddp->dstNode != k->node) /* drop */ + return(0); + + if (hdl == KIP_HANDLE) + destskt = htons(ddp2ipskt(ddp->dstSkt)+128); + else + destskt = htons(mddp2ipskt(ddp->dstSkt)); + /* establish a dummy lap header */ + lap.type = lapDDP; + lap.dst = k->node; + lap.src = ddp->srcNode; + iov[IOV_LAP_LVL].iov_base = (caddr_t) ⪅ /* LAP header */ + iov[IOV_LAP_LVL].iov_len = lapSize; /* size */ + iov[IOV_DDP_LVL].iov_base = (caddr_t) ddp; + iov[IOV_DDP_LVL].iov_len = ddpSize; + /* figure out what the data length should be */ + t = (ntohs(ddp->length)&ddpLengthMask) - ddpSize; + /* if data size passed down is too large, then truncate. (sunos */ + /* trailing bytes or packet less than 60 bytes */ + /* so, pass back min of cc, length */ + iov[2].iov_len = (cc > t) ? t : cc; + iov[2].iov_base = data; + /* send through */ + k->sin.sin_addr = desthost; + k->sin.sin_port = destskt; + msg.msg_name = (caddr_t) &k->sin; + msg.msg_namelen = sizeof(k->sin); + msg.msg_iov = iov; + msg.msg_iovlen = 3; +#if (defined(__386BSD__) || defined(__FreeBSD__)) + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; +#else /* __386BSD__ */ + msg.msg_accrights = 0; + msg.msg_accrightslen = 0; +#endif /* __386BSD__ */ + if ((err = sendmsg(k->s,&msg,0)) < 0) + logit(LOG_LOG|L_UERR, "sendmsg: kip write"); + return(err); +} + +/* + * have node now -- just remember + * +*/ +private int +kip_havenode(hdl, node) +int hdl; +byte node; +{ + if (hdl != MKIP_HANDLE && hdl != KIP_HANDLE) + return(FALSE); + kipdefs[hdl].node = node; + return(TRUE); +} + +/* + * have network now + * +*/ +private int +kip_havenet(hdl, net, node) +word net; +byte node; +{ + if (hdl != MKIP_HANDLE && hdl != KIP_HANDLE) + return(FALSE); + kipdefs[hdl].net = net; + kipdefs[hdl].bridge_node = node; + return(TRUE); +} + +/* + * have zone: ready to go + * +*/ +private int +kip_havezone(hdl, zone) +int hdl; +byte *zone; +{ + FILE *fp; + int i; + struct kipdef *k; + char *file; + word save_async_net; + char save_async_zone[34]; + + if (hdl != MKIP_HANDLE && hdl != KIP_HANDLE) + return(FALSE); + k = &kipdefs[hdl]; + file = (hdl == MKIP_HANDLE) ? MTAB : TAB; + + save_async_net = async_net; + strncpy(save_async_zone, async_zone, sizeof(save_async_zone)); + + /* set some defaults */ + openetalkdb(file); + this_net = k->net; + this_node = k->node; + /* zone is pascal string, this_zone is 'C' */ + cpyp2cstr(this_zone, zone); + /* + * if same net, leave node & addr unchanged + * - we could be using other bridge + */ + if (bridge_net != k->net) { + bridge_net = k->net; + bridge_node = k->bridge_node; + name_toipaddr("127.0.0.1", &bridge_addr); + } + /* + * if same net, leave node unchanged, atis running elsewhere + */ + if (nis_net != k->net) { + nis_net = k->net; + nis_node = k->node; + } + /* + * if async_net wasn't zero, must be using UAB internal, keep it. + */ + if (save_async_net != 0) { + async_net = save_async_net; + strncpy(async_zone, save_async_zone, sizeof(async_zone)); + } + + etalkdbupdate(file); /* write out the new stuff */ + + return(TRUE); +} + +static int +name_toipaddr(name, ipaddr) +char *name; +struct in_addr *ipaddr; +{ + struct hostent *host; + + if (isdigit(name[0])) { + if ((ipaddr->s_addr = inet_addr(name)) == -1) + return(-1); + return(0); + } + if ((host = gethostbyname(name)) == 0) + return(-1); + bcopy(host->h_addr, (caddr_t)&ipaddr->s_addr, sizeof(ipaddr->s_addr)); +} + +/* don't hardwire the port ranges, not nice */ + +private word +mddp2ipskt(skt) +word skt; +{ + return((skt&0x80) ? (skt+ddpNWKSUnix) : (skt+portrange)); +} + +/* fix this when (if) normal KIP works */ + +private word +ddp2ipskt(skt) +word skt; +{ + return((skt&0x80) ? (skt+ddpNWKSUnix) : (skt+portrange)); +} + +private word +getPRange() +{ + struct servent *getservbyname(); + + if(getservbyname("at-rtmp", "udp") == NULL) + return(ddpWKSUnix); /* 768 */ + else + return(ddpOWKSUnix); /* 200 */ +} diff --git a/support/uab/log.c b/support/uab/log.c new file mode 100644 index 0000000..e3e12c0 --- /dev/null +++ b/support/uab/log.c @@ -0,0 +1,177 @@ +static char rcsid[] = "$Author: djh $ $Date: 91/03/13 20:38:47 $"; +static char rcsident[] = "$Header: log.c,v 2.2 91/03/13 20:38:47 djh Exp $"; +static char revision[] = "$Revision: 2.2 $"; + +/* + * log.c - simple logging facility + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * Aug, 1988 CCKim created + * +*/ + +#include +#include +#include +#include +#include "log.h" + + +/* current debug level */ +static int dlevel; + +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + +/* + * set debug level + * +*/ +get_debug_level() +{ + return(dlevel); +} +set_debug_level(n) +int n; +{ + dlevel = n; +} + +/* + * print message - use vprintf whenever possible (solves the problem + * of using the varargs macros -- you must interpret the format). + * This is something all machine should, but don't have :-) + */ + +static FILE *lfp = stderr; + + +#ifdef NOVPRINTF +/* Bletch - gotta do it because pyramids don't work the other way */ +/* (using _doprnt and &args) and don't have vprintf */ +/* of course, there will be something that is just one arg larger :-) */ +/*VARARGS1*/ +logit(level, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af) +int level; +char *fmt; +#else +/*VARARGS*/ +logit(va_alist) +va_dcl +#endif +{ + long time(); + char *mytod(); +#ifndef NOVPRINTF + register char *fmt; + va_list args; + int level; +#endif + int saveerr; + extern int errno; + extern int sys_nerr; + extern char *sys_errlist[]; + + if (lfp == NULL) /* no logging? */ + return; + + saveerr = errno; +#ifndef NOVPRINTF + va_start(args); + level = va_arg(args, int); + fmt = va_arg(args, char *); +#endif + + if (dlevel < (level & L_LVL)) + return; +#ifdef AARPD + fprintf(lfp,"aarpd: %s ",mytod()); +#else AARPD + fprintf(lfp,"uab: %s ",mytod()); +#endif AARPD + +#ifndef NOVPRINTF + vfprintf(lfp, fmt, args); + va_end(args); +#else + fprintf(lfp, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af); +#endif + if (level & L_UERR) { + if (saveerr < sys_nerr) + fprintf(lfp, ": %s", sys_errlist[saveerr]); + else + fprintf(lfp, ": error %d\n", saveerr); + } + putc('\n', lfp); + fflush(lfp); + if (level & L_EXIT) + exit(1); +} + +islogitfile() +{ + return(lfp != NULL); +} + +logitfileis(filename, mode) +char *filename; +char *mode; +{ + FILE *fp; + + if ((fp = fopen(filename, mode)) != NULL) { + logit(0, "log file name %s", filename); + } else { + logit(0|L_UERR, "couldn't open logfile %s", filename); + } + lfp = fp; /* reset */ +} + +nologitfile() +{ + if (lfp && lfp != stderr) + fclose(lfp); + lfp = NULL; +} + +/* + * return pointer to formatted tod in static buffer + * +*/ +static char * +mytod() +{ + long tloc; + struct tm *tm, *localtime(); + static char buf[100]; /* should be large enough */ + + (void)time(&tloc); + tm = localtime(&tloc); + if (tm->tm_year > 99) + sprintf(buf, "%02d:%02d:%02d %02d/%02d/%04d ", + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_mon+1, tm->tm_mday, tm->tm_year+1900); + else + sprintf(buf, "%02d:%02d:%02d %02d/%02d/%02d ", + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_mon+1, tm->tm_mday, tm->tm_year); + return(buf); +} diff --git a/support/uab/log.h b/support/uab/log.h new file mode 100644 index 0000000..f136ddb --- /dev/null +++ b/support/uab/log.h @@ -0,0 +1,34 @@ +/* + * $Author: djh $ $Date: 91/02/15 23:07:44 $ + * $Header: log.h,v 2.1 91/02/15 23:07:44 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * log.h - simple logging facility header file + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * Aug, 1988 CCKim created + * +*/ + +/* logging flags */ +#define L_UERR 0x20 /* want unix error message */ +#define L_EXIT 0x10 /* exit after logging */ +#define L_LVL 0xf /* debug levels */ +#define L_LVLMAX 15 /* maximum level */ + diff --git a/support/uab/makefile b/support/uab/makefile new file mode 100644 index 0000000..1a6fc32 --- /dev/null +++ b/support/uab/makefile @@ -0,0 +1,68 @@ +# Makefile autoconfigured for ... +# SunOS system on Fri Feb 15 14:00:24 EST 1991 + + + +MFLAGS= +LFLAGS= +CC=cc +LD=ld +SHELL=/bin/sh +INSTALLER=cp +CFLAGS=-DDEBUG -O -DSHORT_NAMES -DMELBOURNE +DESTDIR=/usr/local/cap +PROGS= +POBJS= +CAPLIB=-lcap +LFLAGS= + +SRCS=aarp.c kip_mpx.c rtmp.c ethertalk.c ddprouter.c ddpsvcs.c ddpport.c \ + hash.c asyncatalk.c +OBJS=aarp.o kip_mpx.o rtmp.o ethertalk.o ddprouter.o ddpsvcs.o ddpport.o \ + hash.o asyncatalk.o + +all: ${PROGS} + +uab: uab.o ${OBJS} ${POBJS} + ${CC} ${LFLAGS} -o uab uab.o ${OBJS} ${POBJS} ${CAPLIB} + +install: ${PROGS}.install + +.install: + +uab.install: uab + -strip uab + ${INSTALLER} uab ${DESTDIR} + +kip_mpx.o: kip_mpx.c mpxddp.h gw.h node.h ddpport.h + cc -c ${CFLAGS} kip_mpx.c + +uab.o: uab.c mpxddp.h gw.h node.h ddpport.h if_desc.h + cc -c ${CFLAGS} -DUAB_PIDFILE=\"/usr/local/lib/cap/uab.pid\" \ + -DBRIDGE_DESC=\"/usr/local/lib/cap/bridge_desc\" uab.c + +lint: + lint -h uab.c ${SRCS} + +clean: + rm -f *.o uab + +# ddpport.h: mpxddp.h node.h +# gw.h: node.h ddport.h (mpxddp.h) +# if_desc.h: mpxddp.h + +ddprouter.o: ddprouter.c gw.h node.h ddpport.h mpxddp.h +rtmp.o: rtmp.c gw.h node.h ddpport.h mpxddp.h + +ethertalk.o: ethertalk.c proto_intf.h ethertalk.h node.h \ + ddpport.h if_desc.h mpxddp.h +aarp.o: aarp.c proto_intf.h ethertalk.h aarp_defs.h aarp.h + +ddpport.o: ddpport.c ddpport.h node.h mpxddp.h + +dlip.o: dlip.c proto_intf.h +snitp.o: snitp.c proto_intf.h + +hash.o: hash.c hash.h + +asyncatalk.o: asyncatalk.c diff --git a/support/uab/mpxddp.h b/support/uab/mpxddp.h new file mode 100644 index 0000000..7f970c1 --- /dev/null +++ b/support/uab/mpxddp.h @@ -0,0 +1,87 @@ +/* + * $Author: djh $ $Date: 91/02/15 23:07:47 $ + * $Header: mpxddp.h,v 2.1 91/02/15 23:07:47 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * demultipexor/multiplexor interface + * + * used to send packets to/from processes from a central process that + * handles incoming ddp packets from an interface that can only be + * attached by a single process + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * August 1988 CCKim Created + * +*/ + +#ifndef _MPX_DDP_INCLUDED +#define _MPX_DDP_INCLUDED "yes" +/* + * demuliplexing module interface point + * +*/ + +struct mpxddp_module { + char *mpx_name; /* name of module (usually transport type) */ + char *mpx_key; /* key for specification purposes */ + int (*mpx_init)(); /* init routine */ + int (*mpx_grab)(); /* used to grab ddp sockets */ + int (*mpx_send_ddp)(); /* send ddp routine */ + int (*mpx_havenode)(); /* mark node known */ + int (*mpx_havenet)(); /* network & bridge (if nec) known */ + int (*mpx_havezone)(); /* zone known */ +}; + +/* + * mpx_init + * + * initialization + * + * int (*mpx_init)() - returns handle (hdl) for later use + * +*/ + +/* + * mpx_grab + * + * mpx_grab should "grab" the specified ddp socket (in its own way) + * and forward packets received upon that ddp socket to (s)ddp_router + * + * int (*mpx_grab)(hdl, skt); +*/ + +/* + * mpx_send_ddp + * + * used by the mpx process to send ddp packets to clients + * + * (*mpx_send_ddp)(hdl, DDP *ddp, caddr_t data, int data_len) + */ + +/* + * used by multiplexing process to send back info on the current ddp world + * + * Ordering will always be: havenode, havenet, havezone + * + * (*mpx_havenode)(hdl,byte node) + * (*mpx_havenet)(hdl,word net, byte bridgenode) + * (*mpx_havezone)(hdl,pstr zone) + * +*/ + +#endif /* INCLUDE THIS FILE */ diff --git a/support/uab/node.h b/support/uab/node.h new file mode 100644 index 0000000..0877b39 --- /dev/null +++ b/support/uab/node.h @@ -0,0 +1,52 @@ +/* + * $Author: djh $ $Date: 91/02/15 23:07:48 $ + * $Header: node.h,v 2.1 91/02/15 23:07:48 djh Rel $ + * $Revision: 2.1 $ +*/ + +/* + * node.h - defines a "node" (as per RTMP id) + * + * will eventually be the header file for a node manager + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * Sept 4, 1988 CCKim Created + * +*/ + +#ifndef _NODE_INCLUDED +#define _NODE_INCLUDED "yes" +/* + * NODE, struct node_addr + * + * describes a node address which can be up to 256 bits in length + * (theoretically). This would be the "lap" level node id which need + * not bear any relationship to the ddp node. + * +*/ + +#define MAXNODEBYTE 8 +#define MAXNODEBIT 256 + +typedef struct node_addr { + int n_bytes; /* number of bytes */ + int n_size; /* size of node */ + byte n_id[MAXNODEBYTE]; /* node number on that port */ +} NODE; + +#endif /* FILE INCLUDED */ + diff --git a/support/uab/patches/bug.1 b/support/uab/patches/bug.1 new file mode 100644 index 0000000..3726b8b --- /dev/null +++ b/support/uab/patches/bug.1 @@ -0,0 +1,171 @@ +UAB EXPERIMENTAL RELEASE - BUG REPORT #1 + +Two problems listed here + +OS: any +Revision: n/a +Major local modifications: n/a +Machine Type: n/a +Date: 5/2/89 +Reported by: Charlie C. Kim, cck@cunixc.columbia.edu +Priority: high + +Problem: UAB may dereference a null pointer (and core dump if you are lucky) + +Diagnosis: The call to aarp_request in aarp_resolve was bogus. One of +the arguments to aarp_request was the aarp table entry of the source +node: unfortunately, this no longer made sense and could possibly be +NULL. Luckily, a call to aarp_request was relatively rare (most aarp +table entries were gleaned). + +Solution: Rewrite aarp_request. See patch(aarp.c). + + +OS: any +Revision: n/a +Major local modifications: n/a +Machine Type: n/a +Date: 5/2/89 +Reported by: Charlie C. Kim, cck@cunixc.columbia.edu +Priority: medium + +Problem: Under certain circumstances, uab ports set to auto-acquire +network numbers will not do so. + +Diagnosis: A filter in the routing portion of the code improperly +filtered out broadcasts if the network number did not match the ddp +port's network number. In general, this should not cause a problem, +except when the port's network number was not established (e.g. was +set to zero) and a long ddp packet was input. + +Solution: Loosen filter. See patch(ddprouter.c). + +PATCH: +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%START OF PATCH%%%%%%%%%%%%%%%%%%%%%% +*** /tmp/,RCSt1023127 Wed May 3 01:02:44 1989 +--- aarp.c Tue May 2 23:57:57 1989 +*************** +*** 413,419 + } + } + /* query on interface */ +! aarp_request(aih, aa, tpa); + return(0); + } + + +--- 413,419 ----- + } + } + /* query on interface */ +! aarp_request(aih, tpa); + return(0); + } + +*************** +*** 794,800 + * - doesn't check that target is ourselves + */ + private void +! aarp_request(aih, snode, tpa) + AI_HANDLE *aih; + AARP_ENTRY *snode; + struct ethertalkaddr *tpa; + +--- 794,800 ----- + * - doesn't check that target is ourselves + */ + private void +! aarp_request(aih, tpa) + AI_HANDLE *aih; + struct ethertalkaddr *tpa; + { +*************** +*** 796,802 + private void + aarp_request(aih, snode, tpa) + AI_HANDLE *aih; +- AARP_ENTRY *snode; + struct ethertalkaddr *tpa; + { + AARP_ENTRY *aa; + +--- 796,801 ----- + private void + aarp_request(aih, tpa) + AI_HANDLE *aih; + struct ethertalkaddr *tpa; + { + AARP_ENTRY *aa; +*************** +*** 822,827 + ap->aaph_arp.arp_pro = htons(ETHERTYPE_APPLETALK); + ap->aaph_arp.arp_hln = EHRD; + ap->aaph_arp.arp_pln = ETPL; + bcopy((caddr_t)snode->aae_eaddr, (caddr_t)&ap->aaph_arp.arp_sha, EHRD); + bcopy((caddr_t)&snode->aae_pa, (caddr_t)ap->aaph_arp.arp_spa, + sizeof(struct ethertalkaddr)); + +--- 821,827 ----- + ap->aaph_arp.arp_pro = htons(ETHERTYPE_APPLETALK); + ap->aaph_arp.arp_hln = EHRD; + ap->aaph_arp.arp_pln = ETPL; ++ #ifdef notdef + bcopy((caddr_t)snode->aae_eaddr, (caddr_t)&ap->aaph_arp.arp_sha, EHRD); + bcopy((caddr_t)&snode->aae_pa, (caddr_t)ap->aaph_arp.arp_spa, + sizeof(struct ethertalkaddr)); +*************** +*** 825,830 + bcopy((caddr_t)snode->aae_eaddr, (caddr_t)&ap->aaph_arp.arp_sha, EHRD); + bcopy((caddr_t)&snode->aae_pa, (caddr_t)ap->aaph_arp.arp_spa, + sizeof(struct ethertalkaddr)); + bcopy((caddr_t)tpa, (caddr_t)ap->aaph_arp.arp_tpa, sizeof(*tpa)); + + ap->aaph_ae = aa; /* remember this for later */ + +--- 825,846 ----- + bcopy((caddr_t)snode->aae_eaddr, (caddr_t)&ap->aaph_arp.arp_sha, EHRD); + bcopy((caddr_t)&snode->aae_pa, (caddr_t)ap->aaph_arp.arp_spa, + sizeof(struct ethertalkaddr)); ++ #else ++ bcopy((caddr_t)aih->ai_eaddr, (caddr_t)&ap->aaph_arp.arp_sha, EHRD); ++ /* grab any source protocol address (on right interface already!) */ ++ { int i = aih->ai_numnode; ++ struct ai_host_node *an = aih->ai_nodes; ++ ++ for ( ; i ; an++, i--) ++ if (an->aihn_state == AI_NODE_OKAY) { ++ bcopy((caddr_t)&an->aihn_pa, (caddr_t)ap->aaph_arp.arp_spa, ++ sizeof(struct ethertalkaddr)); ++ break; ++ } ++ if (i == 0) /* no host node! drop request for now */ ++ return; ++ } ++ #endif + bcopy((caddr_t)tpa, (caddr_t)ap->aaph_arp.arp_tpa, sizeof(*tpa)); + + ap->aaph_ae = aa; /* remember this for later */ +*** /tmp/,RCSt1023127 Wed May 3 01:02:46 1989 +--- ddprouter.c Wed May 3 00:41:47 1989 +*************** +*** 181,188 + } + /* forward packets for self to self */ + /* don't forward broadcasts 'cept to self*/ +! if (ddp->dstNet == PORT_DDPNET(port) && +! (isbroadcast || (ddp->dstNode == PORT_DDPNODE(port)))) { + ddp_input(port, ddp, data, datalen); + return; + } +Z +--- 181,188 ----- + } + /* forward packets for self to self */ + /* don't forward broadcasts 'cept to self*/ +! if (isbroadcast || +! (ddp->dstNet==PORT_DDPNET(port) && ddp->dstNode==PORT_DDPNODE(port))) { + ddp_input(port, ddp, data, datalen); + return; + } +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%END OF PATCH%%%%%%%%%%%%%%%%%%%%%% diff --git a/support/uab/patches/bug.2 b/support/uab/patches/bug.2 new file mode 100644 index 0000000..3da96d4 --- /dev/null +++ b/support/uab/patches/bug.2 @@ -0,0 +1,47 @@ +*** rtmp.c.old Sun Feb 4 11:30:32 1990 +--- rtmp.c Sun Feb 4 11:38:20 1990 +*************** +*** 540,546 **** + word net; + NODE id, *sid; + +! if (ddp->type == ddpRTMPRQ) /* is it a rtmp request? */ + return(rtmprq_handler(port,ddp)); /* yes, handle it */ + if (ddp->type != ddpRTMP) /* is it rtmp? */ + return(TRUE); /* no, dump it */ +--- 540,546 ---- + word net; + NODE id, *sid; + +! if (ddp->type == ddpRTMP_REQ) /* is it a rtmp request? */ + return(rtmprq_handler(port,ddp)); /* yes, handle it */ + if (ddp->type != ddpRTMP) /* is it rtmp? */ + return(TRUE); /* no, dump it */ +*************** +*** 902,909 **** + int count; + int mainnotknown = FALSE; + +! if (!zone_unknown) /* set whenever new route is created */ +! return; + /* initialize helper field, find first to query */ + for (first=NULL, re = routes, j = 0; re ; re = re->re_next) + if (re->re_state && !re->re_zonep) { +--- 902,908 ---- + int count; + int mainnotknown = FALSE; + +! if (zone_unknown) { /* set whenever new route is created */ + /* initialize helper field, find first to query */ + for (first=NULL, re = routes, j = 0; re ; re = re->re_next) + if (re->re_state && !re->re_zonep) { +*************** +*** 961,966 **** +--- 960,966 ---- + log(LOG_LOTS, "zipping %s for %d networks", node_format(curbridge), + count-1); + ddp_output(curbridge, &ddp, zip_buf, count*sizeof(word)); ++ } + } + /* restart query */ + { struct timeval tv; diff --git a/support/uab/patches/bug.3 b/support/uab/patches/bug.3 new file mode 100644 index 0000000..62ec2d6 --- /dev/null +++ b/support/uab/patches/bug.3 @@ -0,0 +1,25 @@ + Problem: UAB missing first byte from the received long DDP packet + + + + + +*** ethertalk.c.org Fri Feb 23 10:47:41 1990 +--- ethertalk.c Fri Feb 23 10:49:39 1990 +*************** +*** 304,310 **** + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)⪅ +! iov[1].iov_len = sizeof(lap); + iov[2].iov_base = (caddr_t)rbuf; + iov[2].iov_len = sizeof(rbuf); + if ((cc = pi_readv(etph, iov, 3)) < 0) { +--- 304,310 ---- + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)⪅ +! iov[1].iov_len = lapSize; + iov[2].iov_base = (caddr_t)rbuf; + iov[2].iov_len = sizeof(rbuf); + if ((cc = pi_readv(etph, iov, 3)) < 0) { diff --git a/support/uab/proto_intf.h b/support/uab/proto_intf.h new file mode 100644 index 0000000..0cbcd6d --- /dev/null +++ b/support/uab/proto_intf.h @@ -0,0 +1,66 @@ +/* + * $Author: djh $ $Date: 1995/08/30 14:11:46 $ + * $Header: /mac/src/cap60/support/uab/RCS/proto_intf.h,v 2.2 1995/08/30 14:11:46 djh Rel djh $ + * $Revision: 2.2 $ +*/ + +/* + * protocol interface header: + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * August 1988 CCKim Created + * +*/ + +/* call first (double call okay) */ +export int pi_setup(); +/* call with protocol (network order), device name (e.g. qe), device */ +/* unit number, return < 0 on error, > 0 protocol handle */ +export int pi_open(/* int protocol, char * dev, int devno */); +/* get ethernet address, ea is pointer to place to return address */ +export int pi_get_ethernet_address(/* int edx, u_char *ea */); +/* returns TRUE if interface tap can see its own broadcasts (or they */ +/* are delivered by system */ +export int pi_delivers_self_broadcasts(); +/* close a protocol handle */ +export int pi_close(/* int edx */); +/* establishes a listener to be called when data ready on */ +/* protocol,interface. (*listener)(socket, arg, eh) */ +export int pi_listener(/* int edx, int (*listener), caddr_t arg */); +/* like read */ +export int pi_read(/* int edx, caddr_t buf, int bufsize */); +/* like readv */ +export int pi_readv(/* int edx, struct iovec iov[], int iovlen */ ); +/* like write */ +export int pi_write(/* int idx, caddr_t buf, int bufsize */); +/* like writev */ +export int pi_writev(/* int edx, struct iovec iov[], int iovlen */ ); + +#define EHRD 6 /* ethernet hardware address length */ + +/* much like struct ether_header, but we know what is here -- can be */ +/* variable on systems */ +struct ethernet_addresses { + u_char daddr[EHRD]; + u_char saddr[EHRD]; + u_short etype; +}; + +#define MAXOPENPROT 30 /* arb. number */ + diff --git a/support/uab/rtmp.c b/support/uab/rtmp.c new file mode 100644 index 0000000..78bb5a7 --- /dev/null +++ b/support/uab/rtmp.c @@ -0,0 +1,1389 @@ +static char rcsid[] = "$Author: djh $ $Date: 1992/07/15 14:04:45 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/rtmp.c,v 2.4 1992/07/15 14:04:45 djh Rel djh $"; +static char revision[] = "$Revision: 2.4 $"; + +/* + * rtmp.c: RTMP, ZIP, and NBP gateway protocol modules + * + * dropped NBP here because it needs access to routing table + * + * Follows specification set in "Inside Appletalk" by Gursharan Sidhu, + * Richard F. Andrews, and Alan B. Oppenheimer, Apple Computer, Inc. + * June 1986. + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * + * Edit History: + * + * August, 1988 CCKim Created + * +*/ +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "gw.h" +#ifndef OWNHASH +#include "hash.h" +#endif + +/* stupid function so we can link together items */ +struct chain { + struct chain *c_next; /* next in list */ + caddr_t c_data; /* data */ +}; +/* should be in appletalk.h */ +#define ZIP_query 1 /* query type */ +#define ZIP_reply 2 /* reply type */ +#define ZIP_takedown 3 /* takedown (NYI) */ +#define ZIP_bringup 4 /* bringup (NYI) */ + +struct zipddp { /* ZIP packet */ + byte zip_cmd; + byte zip_netcount; +}; + +/* zip name as found in zip reply and bringup packets */ +struct zipname { + word zip_net; + byte zip_len; +}; +#define zipNameLen 3 + +/* route states */ +#define R_NONE 0 /* or false */ +#define R_GOOD 1 +#define R_BAD 2 +#define R_SUSPECT 3 + +/* messages describing route states (shouldn't change) */ +private char *rtmp_state_msg[4] = { + "deleted", + "good", + "bad", + "suspect" +}; + +#define MAXHOPS 15 /* maximum # of hops a route can have */ + +/* when to "age" route entries */ +#define RTMP_VALIDITY_TIMEOUT 20 +/* rtmp send timeout: initial is offset from zip timeout */ +#define RTMP_INITIAL_SEND_TIMEOUT 30 +#define RTMP_SEND_TIMEOUT 10 +/* initial zip is for "local" zones */ +#define ZIP_INITIAL_TIMEOUT 5 +#define ZIP_QUERY_TIMEOUT 10 + +/* maximum number of routes */ +#define NROUTES 500 + +private struct route_entry *routes; /* chain of routes */ +private caddr_t route_htable_handle; /* route table handle */ + + +/* bridge node handling stuff */ +/* for now (should be hash table or some such) */ +private caddr_t bridgenode_htable_handle; +#define NUMBRNODES 500 +struct bridge_node { /* bridge node entry */ + NODE id; + PORT_T port; +}; + +struct bridge_node_key { /* structure to pass a key in */ + NODE *idp; + PORT_T port; +}; + +private int zone_unknown = 0; /* a zone is unknown */ + +/* same as pstr */ +private caddr_t zone_hash_table; +#define NZONES 250 /* random, need not be too big */ +private struct chain *zonelist; /* chain of zones */ + +private int m_route = 0; +private int m_bnode = 0; +private int m_cnode = 0; +private int m_zone = 0; + +export void rtmp_init(); +export void rtmp_start(); +private int route_compare(); +private caddr_t route_alloc(); +private u_int route_compress(); +private void routes_init(); +export struct route_entry *route_find(); +private struct route_entry *route_create(); +private void route_delete(); +export struct route_entry *route_list_start(); +export struct route_entry *route_next(); +export int route_add_host_entry(); +export char *node_format(); +private int bstrcmp(); +private int bstrcmpci(); +private int bridgenode_compare(); +private caddr_t bridgenode_alloc(); +private u_int bridgenode_compress(); +private void bridgenode_init(); +private NODE *bridgenode_find(); +private boolean rtmp_handler(); +private void rtmp_dump_entry(); +private void rtmp_dump_entry_to_file(); +private int rtmp_send_timeout(); +private void rtmp_send(); +private int rtmp_validity_timeout(); +private void rtmp_replace_entry(); +private void rtmp_update_entry(); +private boolean rtmprq_handler(); +private boolean zip_handler(); +private int zip_query_timeout(); +private int zip_query_handler(); +private int zip_reply_handler(); +private int zip_atp_handler(); +/* private int zone_hash(); */ +private caddr_t zone_alloc(); +private int pstrc(); +private int zone_compare(); +private u_int zone_compress(); +private void zone_init(); +export byte *zone_find(); + +private void rtmp_format_hash_stats(); +export void rtmp_dump_stats(); +export void rtmp_dump_table(); + +/* + * initialize + * + * clear up vars +*/ +export void +rtmp_init() +{ + routes_init(); + bridgenode_init(); + zone_init(); +} + + +/* + * rtmp start - fires up the timers + * +*/ +export void +rtmp_start() +{ + struct timeval tv; + tv.tv_sec = RTMP_VALIDITY_TIMEOUT; /* 20 second validity timer */ + tv.tv_usec = 0; + relTimeout(rtmp_validity_timeout, 0, &tv, TRUE); + /* these last two could be combined */ + tv.tv_sec = RTMP_INITIAL_SEND_TIMEOUT; + tv.tv_usec = 0; + relTimeout(rtmp_send_timeout, 0, &tv, TRUE); + tv.tv_sec = ZIP_INITIAL_TIMEOUT; + tv.tv_usec = 0; + relTimeout(zip_query_timeout, 0, &tv, TRUE); +} + +/* routing table handler */ +/* + * compare key net to route entry + * +*/ +private int +route_compare(net,re) +word *net; +struct route_entry *re; +{ + return(((int)*net) - ((int)(re->re_ddp_net))); +} + +/* + * allocate data: create a route + * + * +*/ +private caddr_t +route_alloc(net) +word *net; +{ + struct route_entry *re; + + if ((re = (struct route_entry *)malloc(sizeof(struct route_entry))) == NULL) + return(NULL); + m_route++; + re->re_ddp_net = *net; /* copy in network */ + re->re_state = R_NONE; /* set state to none */ + re->re_next = routes; + routes = re; /* link to head */ + return((caddr_t)re); /* and return */ +} + +/* + * compress key to u_int + * +*/ +private u_int +route_compress(net) +word *net; +{ + return(*net); +} + +private void +routes_init() +{ + routes = NULL; /* no routes */ + route_htable_handle = + h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE, NROUTES, + route_compare, route_alloc, route_compress, NULL, NULL, NULL); + ddp_open(rtmpSkt, rtmp_handler); +} +/* + * find route for a particular network + * +*/ +export +struct route_entry * +route_find(net) +word net; +{ + struct route_entry *re; + re = (struct route_entry *)h_member(route_htable_handle, &net); + if (re && re->re_state) /* we don't really delete */ + return(re); + return(NULL); +} + +/* + * create a routing entry and initialize it. + * +*/ +private struct route_entry * +route_create(net) +word net; +{ + register struct route_entry *re; + int d, b; + + re = (struct route_entry *) + h_operation(HASH_OP_INSERT, route_htable_handle, &net, -1,-1,&d,&b); + + if (re == NULL) /* should not happen, but. */ + return(NULL); + logit(LOG_LOTS, "new route for net %d.%d at hash [bkt %d, d %d]\n", + nkipnetnumber(net),nkipsubnetnumber(net), + b,d); + zone_unknown++; /* new route, so set */ + re->re_dist = 0; /* assume zero distance */ + re->re_bridgeid_p = NULL; /* means self */ +/* re->re_ddp_net = net; */ /* done already */ + re->re_zip_taken = FALSE; /* make sure */ + re->re_zonep = NULL; /* make sure */ + return(re); +} + +/* delete route - for now just set state to none */ +/* may want to time it out at some point */ +private void +route_delete(re) +struct route_entry *re; +{ + re->re_state = R_NONE; +} + + +/* return route list start */ +export struct route_entry * +route_list_start() +{ + return(routes); +} + +/* get next in list: hidden in case we want to change way done */ +export struct route_entry * +route_next(re) +struct route_entry *re; +{ + return(re->re_next); +} + +/* + * establish a new port + * + * net in network format (8 bits - word) + * node as bytes (variable length, network order, zero padded in front) + * nodesize - number of bits valid in node + * ddp_node + * zone to set if any + * +*/ +export +route_add_host_entry(port, net, zone) +PORT_T port; +word net; +byte *zone; +{ + struct route_entry *re; + + if (net == 0) + return(-1); + /* if network given, then construct internal route */ + /* do a find in case route already acquired */ + if ((re = route_find(net)) == NULL) + if ((re = route_create(net)) == NULL) + return(-1); + /* reset or set */ + re->re_state = R_GOOD; + re->re_port = port; + re->re_dist = 0; + re->re_bridgeid_p = NULL; + logit(LOG_BASE, "port %d host route added for network %d.%d", + port, nkipnetnumber(net), nkipsubnetnumber(net)); + rtmp_dump_entry("host port", re); + if (zone == NULL) + return(0); + /* if zone given for net, then add it */ + re->re_zonep = zone_find(zone, TRUE); /* insert zone name */ + if (re->re_zonep && zone_unknown > 0) { + logit(LOG_BASE, "port %d zone name %s inserted", port, re->re_zonep+1); + zone_unknown--; + } + return(0); +} + + + +/* + * format node structure for printing + * +*/ +export char * +node_format(node) +NODE *node; +{ + static char tmpbuf[200]; + static char *fmtstr = "%x%x%x%x%x%x%x%x"; + byte *id; + int n; + + if (node == NULL) + return("self"); + id = node->n_id; + /* if less than 5 bytes, convert to network order int and print */ + switch (node->n_bytes) { + case 4: + n = ntohl((id[0]<<24)|(id[1]<<16)|(id[2]<<8)|id[3]); + break; + case 3: + n = ntohl((id[0]<<16)|(id[1]<<8)|id[2]); + break; + case 2: + n = ntohs(id[0]<<8|id[1]); + break; + case 1: + n = id[0]; + break; + default: + sprintf(tmpbuf, fmtstr+2*(MAXNODEBYTE-node->n_bytes), + node->n_id[0], node->n_id[1], + node->n_id[2], node->n_id[3], + node->n_id[4], node->n_id[5], + node->n_id[6], node->n_id[7]); + return(tmpbuf); + } + sprintf(tmpbuf, "%d", n); + return(tmpbuf); +} + +/* like strncmp, but allows "0" bytes */ +private int +bstrcmp(a,b,l) +register byte *a; +register byte *b; +register int l; +{ + register int c = 0; /* if zero length, then same */ + + while (l--) /* while data */ + if ((c = (*a++ - *b++))) /* compare and get difference */ + break; /* return c */ + return(c); /* return value */ +} +/* like bstrcmp, but case insensitive */ +private int +bstrcmpci(a,b,l) +register byte *a; +register byte *b; +register int l; +{ + register int c = 0; /* if zero length, then same */ + register byte aa, bb; + + while (l--) { /* while data */ + aa = *a++; + if ( isascii(aa) && isupper(aa) ) + aa = tolower(aa); + bb = *b++; + if ( isascii(bb) && isupper(bb) ) + bb = tolower(bb); + if ((c = (aa - bb))) /* compare and get difference */ + break; /* return c */ + } + return(c); /* return value */ +} + +/* bridge node table handler */ + +/* compare (port,node) to bridgenode */ +private int +bridgenode_compare(k, bn) +struct bridge_node_key *k; +struct bridge_node *bn; +{ + if (k->port != bn->port) + return((int)(k->port - bn->port)); + if (k->idp->n_size != bn->id.n_size) + return(k->idp->n_size - bn->id.n_size); + return(bstrcmp((caddr_t)k->idp->n_id, (caddr_t)bn->id.n_id, bn->id.n_bytes)); +} + +/* allocate space for a bridge node */ +private caddr_t +bridgenode_alloc(k) +struct bridge_node_key *k; +{ + struct bridge_node *bn; + + if ((bn = (struct bridge_node *)malloc(sizeof(struct bridge_node))) == NULL) + return(NULL); + m_bnode++; +#ifdef DEBUG + logit(0, "BRIDGE NODE CREATE"); + logit(0, "PORT %d", k->port); + logit(0, "ID len %d, byte 0 %d", k->idp->n_size, k->idp->n_id[0]); +#endif + bn->id = *k->idp; /* copy in */ + bn->port = k->port; + return((caddr_t)bn); +} + +/* compress key to an u_int */ +private u_int +bridgenode_compress(k) +struct bridge_node_key *k; +{ + u_int r = (u_int)k->port; + int i = k->idp->n_bytes; /* # of bytes */ + byte *p = k->idp->n_id; /* data */ + + /* add in p, but keep rotating r */ + r ^= k->idp->n_size; /* xor size in */ + while (i--) + r = ((r>>1)|(r<<31)) + *p++; + return(r); +} + + +/* initialize */ +/* should we limit the # of bridge nodes by using a open hash? */ +private void +bridgenode_init() +{ + bridgenode_htable_handle = + h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE, NUMBRNODES, + bridgenode_compare, bridgenode_alloc, + bridgenode_compress, NULL, NULL, NULL); +} + +/* find a bridge node based on port,node key */ +private NODE * +bridgenode_find(port, node) +PORT_T port; +NODE *node; +{ + struct bridge_node *bn; + struct bridge_node_key bk; + int d,b; + + bk.idp = node; + bk.port = port; + + bn = (struct bridge_node *) + h_operation(HASH_OP_INSERT, bridgenode_htable_handle, &bk, -1,-1,&d,&b); + + if (bn == NULL) + return(NULL); +#ifdef notdef + printf("look bridge node %s at hash [bkt %d, d %d]\n", + node_format(node), b,d); +#endif + return(&bn->id); /* return node id */ +} + +/* RTMP handling */ + +/* + * handle incoming rtmp packets + * +*/ +/*ARGSUSED*/ +private boolean +rtmp_handler(port, ddp, data, len) +PORT_T port; +DDP *ddp; /* not used */ +byte *data; +int len; +{ + struct route_entry *re; + RTMPtuple tuple; + word net; + NODE id, *sid; + + if (ddp->type == ddpRTMP_REQ) /* is it a rtmp request? */ + return(rtmprq_handler(port,ddp)); /* yes, handle it */ + if (ddp->type != ddpRTMP) /* is it rtmp? */ + return(TRUE); /* no, dump it */ + if (len < sizeof(net)) /* rtmpSize */ + return(TRUE); + /* get net out */ + bcopy((caddr_t)data, (caddr_t)&net, sizeof(net)); + len -= sizeof(net); + data += sizeof(net); + if (len < 1) /* id len */ + return(TRUE); + id.n_size = *data; /* get id length */ + id.n_bytes = (id.n_size + 7) / 8; /* make into bytes */ + if (len < (id.n_bytes+1)) /* id len + id */ + return; + bcopy((caddr_t)data+1, (caddr_t)id.n_id, id.n_bytes); /* copy id */ + len -= (id.n_bytes + 1); /* reduce */ + data += (id.n_bytes + 1); /* reduce */ + + sid = bridgenode_find(port, &id); /* canonicalize */ + if (sid == NULL) /* ourselves or no room */ + return(TRUE); + logit(LOG_BASE, "NEW RTMP: port %d, source %s", port, node_format(sid)); + + if (!PORT_NET_READY(port, net, sid)) + route_add_host_entry(port, net, NULL); /* zone isn't known yet! */ + + /* 15/06/92 */ + /* if non-extended network, first tuple is always 0.0[$82] */ + if (len < rtmpTupleSize) + return(TRUE); + bcopy((caddr_t)data, (caddr_t)&tuple, rtmpTupleSize); + if (tuple.net == 0 && tuple.hops == 0x82) { + logit(LOG_LOTS, "ignore_entry: non-extended network marker"); + data += rtmpTupleSize; + len -= rtmpTupleSize; + } + + /* use tuplesize because of byte alignment problems */ + while (len >= rtmpTupleSize) { + bcopy((caddr_t)data, (caddr_t)&tuple, rtmpTupleSize); + data += rtmpTupleSize, len -= rtmpTupleSize; + re = route_find(tuple.net); /* get entry if any */ + if (re) /* update */ + rtmp_update_entry(re, port, sid, &tuple); + else { /* create */ + re = route_create(tuple.net); + if (!re) + continue; + logit(LOG_LOTS, "create_entry: net %d.%d", + nkipnetnumber(tuple.net), + nkipsubnetnumber(tuple.net)); + rtmp_replace_entry(re, port, sid, &tuple, FALSE); + } + } + return(TRUE); +} + + +/* + * dump rtmp table entry nicely + * +*/ +private void +rtmp_dump_entry(msg, re) +char *msg; +struct route_entry *re; +{ + if (!re->re_state) + return; + /* fixup */ + logit(LOG_LOTS, "%s: net %d.%d, bridge %s, dist %d, port %d, state %s", + msg, nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net), + node_format(re->re_bridgeid_p), re->re_dist, re->re_port, + rtmp_state_msg[re->re_state]); +} + +private void +rtmp_dump_entry_to_file(fd, re) +FILE *fd; +struct route_entry *re; +{ + if (!re->re_state) + return; + /* fixup */ +fprintf(fd, " net %d.%d, bridge %s, dist %d, port %d, state %s, zone %d-%s\n", + nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net), + node_format(re->re_bridgeid_p), re->re_dist, re->re_port, + rtmp_state_msg[re->re_state], + (re->re_zonep ? *re->re_zonep : 0), + (re->re_zonep ? ((char *)re->re_zonep+1) : (char *)"")); +} + +/* + * timeout: rtmp send - broadcast rtmp's + * +*/ +private int +rtmp_send_timeout() +{ + PORT_T port; + register struct route_entry *re; + struct timeval tv; + RTMP *rtmp; + RTMPtuple tuple; + char buf[ddpMaxData]; + char *p; + int count, rest; + + rtmp = (RTMP *)buf; + re = routes; + /* load up rtmp entry */ + do { + p = buf + sizeof(RTMP); /* point into buf */ + rest = ddpMaxData - sizeof(RTMP); + /* while room in packet */ + for (count = 0; re && rest >= rtmpTupleSize; re = re->re_next) { + if (re->re_state != R_GOOD && re->re_state != R_SUSPECT) + continue; /* not good or suspect */ + if (re->re_zip_taken) /* in zip takedown */ + continue; + /* strict: don't send out updates for routes we don't know */ + /* the zone for -- this way bad data may go away */ + if (re->re_zonep == NULL) + continue; + if (!(re->re_dist < MAXHOPS)) + continue; + tuple.net = re->re_ddp_net; + tuple.hops = re->re_dist; + bcopy((caddr_t)&tuple, p, rtmpTupleSize); + count++; /* found another */ + p += rtmpTupleSize; + rest -= rtmpTupleSize; + } + for (port = PORT_LIST_START(); port != NULL; port = PORT_NEXT(port)) { + NODE *pid; + + if (!PORT_ISBRIDGING(port)) + continue; + if (!PORT_READY(port)) + continue; + rtmp->net = PORT_DDPNET(port); + pid = PORT_NODEID(port); + rtmp->idLen = pid->n_size; + bcopy((caddr_t)pid->n_id, (caddr_t)&rtmp->id, pid->n_bytes); + rtmp_send(rtmp, 3+pid->n_bytes, count, rtmp->net, DDP_BROADCAST_NODE); + } + } while (re); /* still routes */ + tv.tv_sec = RTMP_SEND_TIMEOUT; /* 10 second send timer */ + tv.tv_usec = 0; + relTimeout(rtmp_send_timeout, 0, &tv, TRUE); +} + +/* + * send the rtmp packet on the specified port + * +*/ +private void +rtmp_send(rtmp, rtmp_size, count, dstnet, dst) +RTMP *rtmp; +int rtmp_size; +int count; +word dstnet; +byte dst; +{ + DDP rddp; /* reply ddp header */ + int dlen = rtmp_size+rtmpTupleSize*count; + + rddp.srcSkt = rtmpSkt; + rddp.dstNet = dstnet; + rddp.dstNode = dst; + rddp.dstSkt = rtmpSkt; + rddp.type = ddpRTMP; + ddp_output(NULL, &rddp, rtmp, dlen); + logit(LOG_LOTS, "RTMP: sent %d length packet with %d tuples",dlen,count); + logit(LOG_LOTS, "\tto net %d, node %d", dstnet, dst); +} + +/* + * timeout: rtmp validity + * + * run timer on rtmp validity +*/ +private int +rtmp_validity_timeout() +{ + register struct route_entry *re; + struct timeval tv; + for (re = routes; re; re = re->re_next) { + switch (re->re_state) { + case R_GOOD: + if (re->re_dist != 0) + re->re_state = R_SUSPECT; + break; + case R_SUSPECT: + rtmp_dump_entry("route went bad", re); + re->re_state = R_BAD; + break; + case R_BAD: + rtmp_dump_entry("route deleted", re); + route_delete(re); + break; + } + } + tv.tv_sec = RTMP_VALIDITY_TIMEOUT; /* 20 second validity timer */ + tv.tv_usec = 0; + + relTimeout(rtmp_validity_timeout, 0, &tv, TRUE); +} + +/* + * rtmp_replace_entry: replace or add a route + * + * if istickler is set then this is a tickler packet that ensures + * route stays good + * +*/ +private void +rtmp_replace_entry(re, port, sid, tuple, istickler) +struct route_entry *re; +PORT_T port; /* source port */ +NODE *sid; /* source id */ +RTMPtuple *tuple; +int istickler; /* true if this replace should be */ + /* considered "a tickle" */ +{ + int rewasthere = re->re_state; + + /* dump won't do anything if no state */ + if (rewasthere && !istickler) + rtmp_dump_entry("replacing entry", re); + re->re_dist = tuple->hops + 1; + re->re_bridgeid_p = sid; + re->re_port = port; + re->re_state = R_GOOD; + if (!istickler) + rtmp_dump_entry(rewasthere ? "replaced entry" : "new" , re); +} + +/* + * rtmp_update_entry - figure out whether the route should be updated + * or not + * +*/ +private void +rtmp_update_entry(re, port, sid, tuple) +struct route_entry *re; +PORT_T port; /* source port */ +NODE *sid; /* source id */ +RTMPtuple *tuple; +{ + if (re->re_state == R_BAD && tuple->hops < MAXHOPS) { /* replace entry */ + logit(LOG_LOTS, "update_entry: net %d.%d, replacing because bad", + nkipnetnumber(tuple->net), + nkipsubnetnumber(tuple->net)); + rtmp_replace_entry(re, port, sid, tuple, FALSE); + return; + } + if (tuple->hops < MAXHOPS && re->re_dist > tuple->hops) { + int istickler; + istickler = (re->re_dist == (tuple->hops+1)); + if (!istickler) { + /* if not simple case of updating bad point */ + logit(LOG_LOTS, "update_entry: net %d.%d, replacing because better route", + nkipnetnumber(tuple->net), + nkipsubnetnumber(tuple->net)); + } + rtmp_replace_entry(re, port, sid, tuple, istickler); + return; + } + /* know we know that hops >= 15 or re->re_dist <= tuple->hops */ + /* if re's bridge matches the rmtp source bridge */ + /* and in on the same port, then the network is futher away... */ + if (re->re_bridgeid_p == sid && re->re_port == port) { + re->re_dist++; + if ((re->re_dist) <= MAXHOPS) { + re->re_state = R_GOOD; + rtmp_dump_entry("hop count increased", re); + } else { + rtmp_dump_entry("too many hops", re); + route_delete(re); + } + } +} + + +/* + * handle incoming rtmp request packet + * +*/ +private int +rtmprq_handler(port, ddp) +PORT_T port; +DDP *ddp; +{ + RTMP rtmp; + NODE *pid; + + if (!PORT_ISBRIDGING(port)) /* not full bridge */ + return(TRUE); /* so, don't advertise */ + if (!PORT_READY(port)) /* port isn't fully setup */ + return(TRUE); + /* respond with data about the port */ + rtmp.net = PORT_DDPNET(port); + pid = PORT_NODEID(port); + rtmp.idLen = pid->n_size; + bcopy((caddr_t)pid->n_id, (caddr_t)&rtmp.id, pid->n_bytes); + /* no tuples */ + rtmp_send(&rtmp, 3+pid->n_bytes, 0, ddp->srcNet, ddp->srcNode); + return(TRUE); +} + +/* + * handle incoming DDP zip packets +*/ +private boolean +zip_handler(port, ddp, data, datalen) +PORT_T port; +DDP *ddp; +byte *data; +int datalen; +{ + struct zipddp zd; + + if (ddp->type == ddpATP) /* atp? */ + return(zip_atp_handler(port, ddp, data, datalen)); /* yes */ + + if (ddp->type != ddpZIP) /* zip? */ + return(TRUE); /* no, dump it */ + + if (datalen < sizeof(zd)) + return(TRUE); + bcopy((caddr_t)data, (caddr_t)&zd, sizeof(zd)); /* get zip header */ + datalen -= sizeof(zd), data += sizeof(zd); + + switch (zd.zip_cmd) { + case ZIP_query: + zip_query_handler(zd.zip_netcount, port, ddp, data, datalen); + break; + case ZIP_reply: + zip_reply_handler(zd.zip_netcount, port, ddp, data, datalen); + break; + case ZIP_takedown: + break; + case ZIP_bringup: + break; + } + return(TRUE); +} + +/* + * ZIP timeout + * + * query routes with unkown zones + * + * algorithm: send zip query to bridge that sent us the rtmp. + * try to enclose as many networks as possible in the query + * +*/ +private int +zip_query_timeout() +{ + DDP ddp; + struct route_entry *re; + struct route_entry *first; + PORT_T port; + int first_idx; + int j; + NODE *curbridge; + struct zipddp *zd; + word zip_buf[ddpMaxData / sizeof(word)]; + int count; + int mainnotknown = FALSE; + + if (zone_unknown) { /* set whenever new route is created */ + /* initialize helper field, find first to query */ + for (first=NULL, re = routes, j = 0; re ; re = re->re_next) + if (re->re_state && !re->re_zonep) { + if (!first) { /* remember first */ + first = re; + } + if (re->re_bridgeid_p == NULL) { + if (!mainnotknown) { + first = re; /* reset first one to do */ + mainnotknown = TRUE; /* don't know zone of a main interface */ + } + } + re->re_zip_helper = 0; + j++; /* mark work */ + } + if (j == 0) + zone_unknown = FALSE; + + /* query the various bridges */ + while (j && first) { + curbridge = first->re_bridgeid_p; /* bridge id pointer */ + port = first->re_port; /* get port for this bridge */ + count = 1; /* skip first word */ + re = first; /* this is where to start */ + first = NULL; /* reset start point */ + for (; re ; re = re->re_next) { + /* skip if main interf. not known */ + if (mainnotknown && re->re_bridgeid_p) + continue; /* and not local interface */ + if (!re->re_state || re->re_zonep || re->re_zip_helper) /* ignore */ + continue; + if (re->re_bridgeid_p == curbridge && + (count < (ddpMaxData / sizeof(word)))) { + j--; + re->re_zip_helper = 1; /* mark */ + zip_buf[count++] = re->re_ddp_net; /* to get */ + logit(LOG_LOTS, "will zip %s for %d.%d", node_format(curbridge), + nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net)); + } else if (!first) { /* remember next in sequence */ + first = re; /* set first */ + } + } + if (count == 1) /* something weird happened */ + continue; + zd = (struct zipddp *)zip_buf; + zd->zip_cmd = ZIP_query; + zd->zip_netcount = count - 1; + ddp.dstNet = PORT_DDPNET(port); + /* this will break on extended networks */ + ddp.dstNode = curbridge ? curbridge->n_id[0] : DDP_BROADCAST_NODE; + ddp.dstSkt = zipZIS; + ddp.srcSkt = zipZIS; + ddp.type = ddpZIP; + logit(LOG_LOTS, "zipping %s for %d networks", node_format(curbridge), + count-1); + ddp_output(curbridge, &ddp, zip_buf, count*sizeof(word)); + } + } + /* restart query */ + { struct timeval tv; + tv.tv_sec = ZIP_QUERY_TIMEOUT; + tv.tv_usec = 0; + + relTimeout(zip_query_timeout, 0, &tv, TRUE); + } +} + + +/* + * zip_query_handler: handle an incoming zip query packet + * +*/ +private int +zip_query_handler(count, port, ddp, data, datalen) +int count; +PORT_T port; +DDP *ddp; +byte *data; +int datalen; +{ + struct route_entry *re; + DDP sddp; + byte buf[ddpMaxData]; + int slen, i, zl; + byte *p; + word net; + struct zipddp *zd; + + p = buf; + p += sizeof(struct zipddp); + slen = sizeof(struct zipddp); + zd = (struct zipddp *)buf; + zd->zip_cmd = ZIP_reply; /* set command */ + zd->zip_netcount = 0; /* zero nets in response as yet */ + /* best effort -- fit as many as possible, but don't bother with */ + /* multiple replies -- not clear remote would handle anyway */ + while (count--) { + if (datalen < sizeof(net)) /* any data left? */ + break; /* no, count is wrong, stop! */ + bcopy((caddr_t)data, (caddr_t)&net, sizeof(net)); + datalen -= sizeof(net), data += sizeof(net); + if ((re = route_find(net)) == NULL) /* no route skip */ + continue; + if (!re->re_zonep) + continue; + i = ((*re->re_zonep) + 1 + sizeof(word)); + if ((slen + i) > ddpMaxData) + break; + /* copy in response data */ + bcopy((caddr_t)&net, (caddr_t)p, sizeof(net)); + p += sizeof(net); + zl = *re->re_zonep + 1; /* get zone length */ + bcopy((caddr_t)re->re_zonep, (caddr_t)p, zl); /* copy zone name */ + p += zl; + zd->zip_netcount++; /* increment count */ + logit(LOG_JUNK, "query on net %d.%d yields zone %s", + nkipnetnumber(net), nkipsubnetnumber(net), re->re_zonep+1); + slen += i; + } + sddp.dstNet = ddp->srcNet; + sddp.dstNode = ddp->srcNode; + sddp.dstSkt = ddp->srcSkt; + sddp.srcSkt = zipZIS; + sddp.type = ddpZIP; + ddp_output(NULL, &sddp, buf, slen); +} + +/* + * handle incoming zip reply. basically insert zone names + * into the table if possible + * +*/ +private int +zip_reply_handler(count, port, ddp, data, datalen) +int count; +PORT_T port; +DDP *ddp; +byte *data; +int datalen; +{ + word net; + struct route_entry *re; + byte *p = data; + byte *pp, *zone; + int zonelen; + + while (count--) { + if (datalen < (1+sizeof(net))) + break; + bcopy((caddr_t)p, (caddr_t)&net, sizeof(net)); /* get zone information */ + p += sizeof(net); /* move to the name */ + datalen -= sizeof(net); + zonelen = 1 + *p; + /* now p points to a pstr */ + if (datalen < zonelen) /* no data left? */ + break; + zone = p; /* p now points to zone */ + p += zonelen; + datalen -= zonelen; + if ((re = route_find(net)) == NULL) { + continue; + } + pp = (byte *)zone_find(zone, TRUE); /* find or insert zone name */ + if (pp == NULL) { + logit(LOG_BASE, "ZIP: no room for insert for zone\n"); + continue; + } + if (re->re_zonep) { /* zone already known for net */ + if (pp && pp != re->re_zonep) { + logit(LOG_BASE, "zone name conflict for %d.%d, received %s had %s\n", + nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net), + pp+1, re->re_zonep+1); + } + continue; + } + /* must have zone for this route (which didn't have one before) */ + re->re_zonep = pp; /* mark zone */ + /* if zone is known for primary route, say so */ + if (re->re_bridgeid_p == NULL && re->re_ddp_net == PORT_DDPNET(port)) + PORT_ZONE_KNOWN(port, re->re_zonep); + logit(LOG_BASE, "ZIPPED: from %d.%d.%d network %d.%d for zone %s", + nkipnetnumber(ddp->srcNet), nkipsubnetnumber(ddp->srcNet), + ddp->srcNode, + nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net), + re->re_zonep+1); + } +} + +/* + * handle a zip atp command: Get Zone List or Get My Zone + * + * +*/ +/* these are only defined in abatp.h not appletalk.h (because nobody) */ +/* should need such fine control under normal cirucmstances */ +/* put the ifndefs around in case we decide to move them one day */ +#ifndef atpCodeMask +# define atpCodeMask 0xc0 +#endif +#ifndef atpReqCode +# define atpReqCode 0x40 +#endif +#ifndef atpRspCode +# define atpRspCode 0x80 +#endif +#ifndef atpEOM +# define atpEOM 0x10 +#endif + +private int +zip_atp_handler(port, ddp, data, datalen) +PORT_T port; +DDP *ddp; +byte *data; +int datalen; +{ + int paranoia = FALSE; + DDP sddp; + ATP *atp; /* pointer to atp header */ + zipUserBytes *zub; /* pointer to zip user byte */ + char data_buf[ddpMaxData]; /* data buffer */ + char *p; /* pointer to data */ + int ps = ddpMaxData; /* room left in buffer */ + int sidx; /* for getzonelist */ + int count, t, i; + struct chain *zp; + + if (datalen < sizeof(ATP)) + return; + bcopy((caddr_t)data, (caddr_t)data_buf, sizeof(ATP)); /* get bytes */ + atp = (ATP *)data_buf; /* should be aligned */ + p = data_buf + sizeof(ATP); /* point to data */ + ps -= sizeof(ATP); + /* control must hold request and only request */ + if ((atp->control & atpCodeMask) != atpReqCode) + return; + /* bitmap should ask for at least one packet */ + if ((atp->bitmap & 0x1) == 0) + return; + zub = (zipUserBytes *)&atp->userData; + if (paranoia && zub->zip_zero != 0) + return; + zub->zip_zero = 0; /* ensure */ + switch (zub->zip_cmd) { + case zip_GetMyZone: + if (paranoia && ntohs(zub->zip_index) != 0) + return; + count = 1; + zub->zip_cmd = 0; /* zero because gmz */ + /* return zone of source network */ + /* if given network is 0 (mynet), use that of port */ + { struct route_entry *re; + if (ddp->srcNet == 0) { + if ((re = route_find(PORT_DDPNET(port))) == NULL) + return; + } else { + if ((re = route_find(ddp->srcNet)) == NULL) + return; + } + if (re->re_zonep == NULL) + return; + /* no way we could fill up buffer */ + { int zl; + zl = 1 + *re->re_zonep; + bcopy((caddr_t)re->re_zonep, (caddr_t)p, zl); + ps -= zl; + } + } + break; + case zip_GetZoneList: + sidx = ntohs(zub->zip_index); + sidx--; /* 1 is start */ + /* move through zonelist, decrementing sidx as we find a filled slot */ + /* a zone name may be sent more than once if we get an incoming zone */ + /* between GZL commands */ + /* move through sidx items */ + for (zp = zonelist; zp && sidx ; zp = zp->c_next) + sidx--; + if (sidx) /* no more zones */ + break; + /* i already set, zp already set */ + /* assume LastFlag */ + zub->zip_cmd = 0xff; /* set every bit because not sure */ + /* which bit is lastflag */ + count = 0; + while (zp) { + byte *znp = (byte *)zp->c_data; /* get zone name */ + t = znp[0] + 1; /* get length of zone name */ + if ((ps - t) < 0) { + zub->zip_cmd = 0; /* clear lastflag: one remains */ + break; + } + bcopy((caddr_t)znp, (caddr_t)p, t); /* copy data */ + count++; /* bump count */ + ps -= t; /* reduce available data */ + p += t; /* move data pointer */ + zp = zp->c_next; /* move to next zone */ + } + zub->zip_index = count; /* set count */ + break; + default: /* bad type, this is NOT paranoia */ + return; + } + zub->zip_index = htons(count); /* set count */ + atp->control = atpRspCode|atpEOM; + atp->bitmap = 0; /* sequence 0 */ + /* tid already set */ + sddp.dstNet = ddp->srcNet; + sddp.dstNode = ddp->srcNode; + sddp.dstSkt = ddp->srcSkt; + sddp.srcSkt = ddp->dstSkt; + sddp.type = ddpATP; + ddp_output(NULL, &sddp, (byte *)data_buf, ddpMaxData - ps); +} + +/* keep zone name in a linked list */ + +/* take a zone pstring and duplicate it -- make sure null terminated */ +private caddr_t +zone_alloc(p) +byte *p; +{ + int len = (int)*p; /* get length */ + struct chain *cnode; + byte *r; + + if ((cnode = (struct chain *)malloc(sizeof(struct chain))) == NULL) + return(NULL); + m_cnode++; + if (p == NULL) /* translate NULL string */ + p = '\0'; + r = (byte *)malloc(len+2); /* one for null, one for lenth */ + if (r == NULL) { + free(cnode); + return(NULL); + } + m_zone++; + bcopy(p, r, len+1); /* copy in data */ + r[len+1] = '\0'; /* make sure tied off */ + cnode->c_next = zonelist; /* link next to head */ + zonelist = cnode; /* link link to this */ + cnode->c_data = (caddr_t)r; /* copy in data */ + return((caddr_t)cnode); +} + +/* needs to be case insensitive */ +private int +pstrc(p,s) +byte *p, *s; +{ + int r = (*p - *s); + if (r) + return(r); + return(bstrcmpci(p+1, s+1, *p)); +} + +/* needs to be case insensitive */ +private int +zone_compare(s,cnode) +byte *s; +struct chain *cnode; +{ + return(pstrc(s, cnode->c_data)); +} + +/* needs to be case insensitive */ +private u_int +zone_compress(p) +byte *p; +{ + u_int r = 0; + int i = (int) *p++; + byte pp; + /* add in p, but keep rotating r */ + while (i--) { + pp = *p++; + if ( isascii(pp) && isupper(pp) ) + pp = tolower(pp); + r = ((r>>1)|(r<<31)) + pp; + } + return(r); +} + +private void +zone_init() +{ + zone_hash_table = h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE, + NZONES, zone_compare, zone_alloc, zone_compress, + NULL, NULL, NULL); + zonelist = NULL; + ddp_open(zipZIS, zip_handler); +} + +export byte * +zone_find(name, insert) +byte *name; +int insert; +{ + int b, d; + struct chain *cnode; + cnode = (struct chain *)h_operation(insert ? HASH_OP_INSERT : HASH_OP_MEMBER, + zone_hash_table, name, -1,-1,&d,&b); + if (cnode == NULL) + return(NULL); + logit(LOG_LOTS, "%s for %s [%d,%d]\n", + insert ? "insert" : "lookup", cnode->c_data+1, b,d); + return((byte *)cnode->c_data); /* return data */ +} + + +private void +rtmp_format_hash_stats(fd, s) +FILE *fd; +struct hash_statistics *s; +{ + fprintf(fd, "\t%d lookups since last rehash, average distance %.02f\n", + s->hs_lnum, s->hs_lnum ? ((float)s->hs_lsum / s->hs_lnum) : 0.0); + fprintf(fd, "\t%d lookups total, average distance %.02f\n", + s->hs_clnum, s->hs_clnum ? ((float)s->hs_clsum / s->hs_clnum) : 0.0); +} + +export void +rtmp_dump_stats(fd) +FILE *fd; +{ + putc('\n', fd); + fprintf(fd, "Hash table statistics for zone lookups\n"); + rtmp_format_hash_stats(fd, h_statistics(zone_hash_table)); + fprintf(fd, "\nHash table statistics for routing table lookups\n"); + rtmp_format_hash_stats(fd, h_statistics(route_htable_handle)); + putc('\n', fd); /* output cr */ + fprintf(fd,"%d routes, %d bridge nodes allocated\n", m_route, m_bnode); + fprintf(fd,"%d zones, %d zone chain nodes allocated\n", m_zone, m_cnode); + putc('\n', fd); /* output cr */ +} + +export void +rtmp_dump_table(fd) +FILE *fd; +{ + register struct route_entry *re; + + fprintf(fd, "Routing table dump\n"); + for (re = routes; re ; re = re->re_next) + if (re->re_state) + rtmp_dump_entry_to_file(fd, re); + putc('\n', fd); +} diff --git a/support/uab/senetp.c b/support/uab/senetp.c new file mode 100644 index 0000000..c9b6384 --- /dev/null +++ b/support/uab/senetp.c @@ -0,0 +1,400 @@ +static char rcsid[] = "$Author: djh $ $Date: 1991/05/18 07:57:59 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/senetp.c,v 2.2 1991/05/18 07:57:59 djh Exp djh $"; +static char revision[] = "$Revision: 2.2 $"; + +/* + * senetp.c - Simple "protocol" level interface to enet + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * July 1988 CCKim Created + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ +#include +#include +#include +#include + +#include +#include "proto_intf.h" +#include "../enet/enet.h" + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int socket; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, dev, devno) +int protocol; +char *dev; +int devno; +{ + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "%s%d",dev,devno); + strncpy(eph->ifr.ifr_name, devnamebuf, sizeof eph->ifr.ifr_name); + eph->ifr.ifr_name[sizeof eph->ifr.ifr_name - 1] = ' '; + + if ((s = init_nit(1024, protocol, &eph->ifr)) < 0) { + return(-1); + } + + eph->inuse = TRUE; + eph->socket = s; + eph->protocol = protocol; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(TRUE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].socket); /* toss listener */ + close(ephlist[edx-1].socket); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize nit on a particular protocol type + * + * Runs in promiscous mode for now. + * + * Return: socket if no error, < 0 o.w. +*/ +private int +init_nit(chunksize, protocol, ifr) +u_long chunksize; +u_short protocol; +struct ifreq *ifr; +{ + u_long if_flags; + int s; + char device[64]; + + strcpy(device, "/dev/"); + strcat(device, ifr->ifr_name); + + /* get clone */ + if ((s = open(device, O_RDWR)) < 0) { + perror("open: /dev/enetXX"); + return(-1); + } + + if (setup_pf(s, protocol) < 0) + return(-1); +#define NOBUF +#ifndef NOBUF + setup_buf(s, chunksize); +#endif + + /* flush read queue */ + ioctl(s, EIOCFLUSH, 0); + return(s); +} + +#ifndef NOBUF +/* + * setup buffering (not wanted) + * +*/ +setup_buf(s, chunksize) +int s; +u_long chunksize; +{ + struct timeval timeout; + + /* Push and configure the buffering module. */ + if (ioctl(s, I_PUSH, "nbuf") < 0) { + perror("ioctl: nbuf"); + } + timeout.tv_sec = 0; + timeout.tv_usec = 200; + si.ic_cmd = NIOCSTIME; + si.ic_timout = 10; + si.ic_len = sizeof timeout; + si.ic_dp = (char *)&timeout; + if (ioctl(s, I_STR, (char *)&si) < 0) { + perror("ioctl: timeout"); + return(-1); + } + + si.ic_cmd = NIOCSCHUNK; + + si.ic_len = sizeof chunksize; + si.ic_dp = (char *)&chunksize; + if (ioctl(s, I_STR, (char *)&si)) { + perror("ioctl: chunk size"); + return(-1); + } +} +#endif + +/* + * establish protocol filter + * +*/ +private int +setup_pf(s, prot) +int s; +u_short prot; +{ + u_short offset; + int ethert; + unsigned queuelen; + struct ether_header eh; + struct enfilter pf; + register u_short *fwp = pf.enf_Filter; + extern int errno; + + ethert = htons(prot); + if (ioctl(s, EIOCETHERT, ðert) < 0 && errno != EEXIST ) { + perror("ioctl: protocol filter"); + return(-1); + } + + queuelen = 8; + if (ioctl(s, EIOCSETW, &queuelen) < 0) { + perror("ioctl: set recv queue length"); + return(-1); + } + + pf.enf_Priority = 128; + +#define s_offset(structp, element) (&(((structp)0)->element)) + offset = ((int)s_offset(struct ether_header *, ether_type))/sizeof(u_short); +#ifdef undef + *fwp++ = ENF_PUSHLIT ; + *fwp++ = 1; + pf.enf_FilterLen = 2; +#endif + *fwp++ = ENF_PUSHWORD + offset; + *fwp++ = ENF_PUSHLIT | ENF_EQ; + *fwp++ = htons(prot); + pf.enf_FilterLen = 3; + + if (ioctl(s, EIOCSETF, &pf) < 0) { + perror("ioctl: protocol filter"); + return(-1); + } + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct endevp endev; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + if (ioctl(eph->socket, EIOCDEVP, &endev) < 0) { + perror("Ioctl: SIOCGIFADDR"); + return(-1); + } + bcopy(endev.end_addr, ea, 6); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].socket, listener, arg, edx); +} + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = readv(eph->socket, iov, iovlen)) < 0) { + perror("abread"); + return(cc); + } + return(cc); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[2]; + struct ethernet_addresses ea; + int cc; + + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct ephandle *eph; + struct ether_header eh; + struct sockaddr sa; + struct iovec iov[2]; + + iov[0].iov_base = (caddr_t)&eh; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = buflen; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + bcopy(eaddr, &eh.ether_dhost, 6); + eh.ether_type = htons(eph->protocol); + + if (writev(eph->socket, iov, 2) < 0) { + return(-1); + } + return(buflen); +} + +private char ebuf[2000]; /* big enough */ + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} diff --git a/support/uab/sllap.c b/support/uab/sllap.c new file mode 100644 index 0000000..c14421f --- /dev/null +++ b/support/uab/sllap.c @@ -0,0 +1,378 @@ +static char rcsid[] = "$Author: djh $ $Date: 1992/07/30 09:41:26 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/sllap.c,v 2.1 1992/07/30 09:41:26 djh Rel djh $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * sllap.c - Simple "protocol" level interface to HP Link Level Access + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * October 1991 djh@munnari.OZ.AU created + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "proto_intf.h" +#include + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int socket; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +/* + * simulate gethostid() + * + */ + +export int +gethostid() +{ + struct utsname xx; + + uname(&xx); + return(atoi(xx.idnumber)); +} + +/* + * setup for particular device devno + * all pi_open's will go this device + */ + +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay + */ + +export int +pi_open(protocol, dev, devno) +int protocol; +char *dev; +int devno; +{ + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "/dev/%s%d", dev, devno); + strncpy(eph->ifr.ifr_name, devnamebuf, sizeof eph->ifr.ifr_name); + + if ((s = init_nit(16, protocol, &eph->ifr)) < 0) + return(-1); + + eph->inuse = TRUE; + eph->socket = s; + eph->protocol = protocol; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(TRUE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].socket); /* toss listener */ + close(ephlist[edx-1].socket); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize LLA on a particular protocol type + * + * Return: socket if no error, < 0 o.w. + * + */ + +private int +init_nit(chunksize, protocol, ifr) +u_long chunksize; +u_short protocol; +struct ifreq *ifr; +{ + int s; + u_long if_flags; + char device[64]; + + if ((s = open(ifr->ifr_name, O_RDWR)) < 0) { + sprintf(device, "open: %s", ifr->ifr_name); + perror(device); + return(-1); + } + + if (setup_pf(s, protocol) < 0) + return(-1); + + setup_buf(s, chunksize); + + return(s); +} + +/* + * setup input packet queue length + * + */ + +setup_buf(s, chunksize) +int s; +u_long chunksize; +{ + struct fis fis; + + fis.reqtype = LOG_READ_CACHE; + fis.vtype = INTEGERTYPE; + fis.value.i = chunksize; + + ioctl(s, NETCTRL, &fis); + /* ignore error */ +} + +/* + * establish protocol filter + * + */ + +private int +setup_pf(s, prot) +int s; +u_short prot; +{ + struct fis fis; + + fis.reqtype = LOG_TYPE_FIELD; + fis.vtype = INTEGERTYPE; + fis.value.i = prot; /* htons() ?? */ + + if (ioctl(s, NETCTRL, &fis) < 0) + return (-1); + + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct ephandle *eph; + struct fis fis; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + eph = &ephlist[edx-1]; + + fis.reqtype = LOCAL_ADDRESS; + if (ioctl(eph->socket, NETSTAT, &fis) < 0) { + perror("Ioctl: LOCAL_ADDRESS"); + return(-1); + } + bcopy(fis.value.s, ea, 6); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].socket, listener, arg, edx); +} + +private char ebuf[2000]; /* big enough */ + +/* + * cheat - iov[0] == struct etherheader + * + */ + +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + struct fis fis; + int len, cc, i; + char *p; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + if ((cc = read(eph->socket, ebuf, sizeof(ebuf))) < 0) { + perror("abread"); + return(cc); + } + + fis.reqtype = FRAME_HEADER; + if (ioctl(eph->socket, NETSTAT, &fis) < 0) + return(-1); + + bcopy(fis.value.s, iov[0].iov_base, (len = iov[0].iov_len)); + + for (p = ebuf, i = 1 ; i < iovlen ; i++) { + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(p, iov[i].iov_base, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + } + return(len); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + int cc; + struct ephandle *eph ; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + cc = read(eph->socket, buf, bufsiz); + + return(cc); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct ephandle *eph; + struct fis fis; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + /* urk */ + fis.reqtype = LOG_DEST_ADDR; + fis.vtype = 6; + bcopy(eaddr, fis.value.s, 6); + if (ioctl(eph->socket, NETCTRL, &fis) < 0) + return(-1); + + if (write(eph->socket, buf, buflen) < 0) + return(-1); + + return(buflen); +} + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} diff --git a/support/uab/snitp.c b/support/uab/snitp.c new file mode 100644 index 0000000..84b79d9 --- /dev/null +++ b/support/uab/snitp.c @@ -0,0 +1,409 @@ +static char rcsid[] = "$Author: djh $ $Date: 1991/07/10 12:44:03 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/snitp.c,v 2.3 1991/07/10 12:44:03 djh Rel djh $"; +static char revision[] = "$Revision: 2.3 $"; + +/* + * snitp.c - Simple "protocol" level interface to Streams based NIT + * (SunOS 4.0) + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * July 1988 CCKim Created + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DEV_NIT +#define DEV_NIT "/dev/nit" +#endif DEV_NIT + +#include +#include "proto_intf.h" + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int socket; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, dev, devno) +int protocol; +char *dev; +int devno; +{ + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "%s%d",dev,devno); + strncpy(eph->ifr.ifr_name, devnamebuf, sizeof eph->ifr.ifr_name); + eph->ifr.ifr_name[sizeof eph->ifr.ifr_name - 1] = ' '; + + if ((s = init_nit(1024, protocol, &eph->ifr)) < 0) { + return(-1); + } + + eph->inuse = TRUE; + eph->socket = s; + eph->protocol = protocol; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(FALSE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].socket); /* toss listener */ + close(ephlist[edx-1].socket); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize nit on a particular protocol type + * + * Return: socket if no error, < 0 o.w. +*/ +private int +init_nit(chunksize, protocol, ifr) +u_long chunksize; +u_short protocol; +struct ifreq *ifr; +{ + u_long if_flags; + struct strioctl si; + int s; + + /* get clone */ + if ((s = open(DEV_NIT, O_RDWR)) < 0) { + perror(DEV_NIT); + return(-1); + } + + /* set up messages */ + if (ioctl(s, I_SRDOPT, (char *)RMSGD) < 0) { /* want messages */ + perror("ioctl: discretem"); + return(-1); + } + + si.ic_timout = INFTIM; + + if (setup_pf(s, protocol) < 0) + return(-1); +#define NOBUF +#ifndef NOBUF + setup_buf(s, chunksize); +#endif + /* bind */ + si.ic_cmd = NIOCBIND; /* bind */ + si.ic_timout = 10; + si.ic_len = sizeof(*ifr); + si.ic_dp = (caddr_t)ifr; + if (ioctl(s, I_STR, (caddr_t)&si) < 0) { + perror(ifr->ifr_name); + return(-1); + } + + /* flush read queue */ + ioctl(s, I_FLUSH, (char *)FLUSHR); + return(s); +} + +#ifndef NOBUF +/* + * setup buffering (not wanted) + * +*/ +setup_buf(s, chunksize) +int s; +u_long chunksize; +{ + struct strioctl si; + struct timeval timeout; + + /* Push and configure the buffering module. */ + if (ioctl(s, I_PUSH, "nbuf") < 0) { + perror("ioctl: nbuf"); + } + timeout.tv_sec = 0; + timeout.tv_usec = 200; + si.ic_cmd = NIOCSTIME; + si.ic_timout = 10; + si.ic_len = sizeof timeout; + si.ic_dp = (char *)&timeout; + if (ioctl(s, I_STR, (char *)&si) < 0) { + perror("ioctl: timeout"); + return(-1); + } + + si.ic_cmd = NIOCSCHUNK; + + si.ic_len = sizeof chunksize; + si.ic_dp = (char *)&chunksize; + if (ioctl(s, I_STR, (char *)&si)) { + perror("ioctl: chunk size"); + return(-1); + } +} +#endif + +/* + * establish protocol filter + * +*/ +private int +setup_pf(s, prot) +int s; +u_short prot; +{ + u_short offset; + struct ether_header eh; + struct packetfilt pf; + register u_short *fwp = pf.Pf_Filter; + struct strioctl si; +#define s_offset(structp, element) (&(((structp)0)->element)) + offset = ((int)s_offset(struct ether_header *, ether_type))/sizeof(u_short); + *fwp++ = ENF_PUSHWORD + offset; + *fwp++ = ENF_PUSHLIT | ENF_EQ; + *fwp++ = htons(prot); + pf.Pf_FilterLen = 3; + + si.ic_cmd = NIOCSETF; + si.ic_timout = 10; + si.ic_len = sizeof(pf); + si.ic_dp = (char *)&pf; + if (ioctl(s, I_PUSH, "pf") < 0) { + perror("ioctl: push protocol filter"); + return(-1); + } + if (ioctl(s, I_STR, (char *)&si) < 0) { + perror("ioctl: protocol filter"); + return(-1); + } + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + if (ioctl(eph->socket, SIOCGIFADDR, &eph->ifr) < 0) { + perror("ioctl: SIOCGIFADDR"); + return(-1); + } + sa = (struct sockaddr *)eph->ifr.ifr_data; + bcopy(sa->sa_data, ea, 6); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].socket, listener, arg, edx); +} + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = readv(eph->socket, iov, iovlen)) < 0) { + perror("abread"); + return(cc); + } + return(cc); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[2]; + struct ethernet_addresses ea; + int cc; + + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct ephandle *eph; + struct ether_header *eh; + struct strbuf pbuf, dbuf; + struct sockaddr sa; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + sa.sa_family = AF_UNSPEC; /* by def. */ + eh = (struct ether_header *)sa.sa_data; /* make pointer */ + bcopy(eaddr, &eh->ether_dhost, sizeof(eh->ether_dhost)); + eh->ether_type = htons(eph->protocol); + pbuf.len = sizeof(sa); + pbuf.buf = (char *)&sa; + dbuf.len = buflen; + dbuf.buf = (caddr_t)buf; + + if (putmsg(eph->socket, &pbuf, &dbuf, 0) < 0) { + return(-1); + } + return(buflen); +} + +private char ebuf[2000]; /* big enough */ + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} diff --git a/support/uab/snooppf.c b/support/uab/snooppf.c new file mode 100644 index 0000000..a824de9 --- /dev/null +++ b/support/uab/snooppf.c @@ -0,0 +1,360 @@ +static char rcsid[] = "$Author: djh $ $Date: 1991/08/31 15:26:11 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/snooppf.c,v 2.1 1991/08/31 15:26:11 djh Rel djh $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * snooppf.c - Simple "protocol" level interface to SGI SNOOP packetfilter + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * August 1991 David Hornsby djh@munnari.OZ.AU + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "proto_intf.h" +#include + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int socket; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, dev, devno) +int protocol; +char *dev; +int devno; +{ + int s, i; + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "%s%d",dev,devno); + strncpy(eph->ifr.ifr_name, devnamebuf, sizeof eph->ifr.ifr_name); + + if ((s = init_nit(32768, protocol, &eph->ifr)) < 0) { + return(-1); + } + + eph->inuse = TRUE; + eph->socket = s; + eph->protocol = protocol; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(FALSE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].socket); /* toss listener */ + close(ephlist[edx-1].socket); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize nit on a particular protocol type + * + * Return: socket if no error, < 0 o.w. + * +*/ +private int +init_nit(chunksize, protocol, ifr) +u_long chunksize; +u_short protocol; +struct ifreq *ifr; +{ + int s; + int on = 1; + struct sockaddr_raw sr; + + if ((s = socket(PF_RAW, SOCK_RAW, RAWPROTO_SNOOP)) < 0) { + perror("socket"); + return(-1); + } + + /* bind to interface */ + strncpy(sr.sr_ifname, ifr->ifr_name, sizeof(sr.sr_ifname)); + sr.sr_family = AF_RAW; + sr.sr_port = 0; + bind(s, &sr, sizeof(sr)); + + if (setup_pf(s, protocol) < 0) + return(-1); + +#ifndef NOBUF + setup_buf(s, chunksize); +#endif NOBUF + + /* start listening */ + ioctl(s, SIOCSNOOPING, &on); + return(s); +} + +#ifndef NOBUF +/* + * setup buffering + * +*/ +setup_buf(s, cc) +int s; +u_long cc; +{ + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &cc, sizeof(cc)) != 0) { + perror("setsockopt"); + return(-1); + } + return(0); +} +#endif + +/* + * establish protocol filter + * +*/ +private int +setup_pf(s, prot) +int s; +u_short prot; +{ + struct snoopfilter sf; + struct ether_header *eh; + + bzero((char *) &sf, sizeof(sf)); + eh = RAW_HDR(sf.sf_mask, struct ether_header); + eh->ether_type = 0xffff; + eh = RAW_HDR(sf.sf_match, struct ether_header); + eh->ether_type = htons(prot); + ioctl(s, SIOCADDSNOOP, &sf); + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + if (ioctl(eph->socket, SIOCGIFADDR, (caddr_t)&eph->ifr) < 0) { + perror("ioctl: SIOCGIFADDR"); + return(-1); + } + sa = (struct sockaddr *)&eph->ifr.ifr_data; + bcopy(sa->sa_data, ea, 6); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].socket, listener, arg, edx); +} + +private char ebuf[2000]; /* big enough */ + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int len, off, cc, i; + char *p; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = read(eph->socket, ebuf, sizeof(ebuf))) < 0) { + perror("abread"); + return(cc); + } + off = sizeof(struct snoopheader)+RAW_HDRPAD(sizeof(struct ether_header)); + for (len = 0, p = ebuf+off, i = 0 ; i < iovlen ; i++) { + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(p, iov[i].iov_base, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + } + return(len); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[2]; + struct ethernet_addresses ea; + int cc; + + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + int cc; + struct ephandle *eph; + struct ether_header eh; + struct sockaddr sa; + struct iovec iov[2]; + + iov[0].iov_base = (caddr_t)&eh; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = buflen; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + bcopy(eaddr, eh.ether_dhost, 6); + eh.ether_type = htons(eph->protocol); + + if ((cc = writev(eph->socket, iov, 2)) < 0) { + perror("writev"); + return(-1); + } + return(buflen); +} + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} diff --git a/support/uab/spfiltp.c b/support/uab/spfiltp.c new file mode 100644 index 0000000..4fed084 --- /dev/null +++ b/support/uab/spfiltp.c @@ -0,0 +1,390 @@ +static char rcsid[] = "$Author: djh $ $Date: 1991/07/01 06:42:11 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/spfiltp.c,v 2.1 1991/07/01 06:42:11 djh Rel djh $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * spfiltp.c - Simple "protocol" level interface to Ultrix packetfilter + * + * Provides ability to read/write packets at ethernet level + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * April 1991 Jeffrey Mogul @ DECWRL (created from senetp.c) + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ +#include +#include +#include +#include + +#include +#include "proto_intf.h" +#include + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int socket; /* file descriptor of socket */ + struct ifreq ifr; + int protocol; /* ethernet protocol */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, dev, devno) +int protocol; +char *dev; +int devno; +{ + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "%s%d",dev,devno); + strncpy(eph->ifr.ifr_name, devnamebuf, sizeof eph->ifr.ifr_name); + eph->ifr.ifr_name[sizeof eph->ifr.ifr_name - 1] = ' '; + + if ((s = init_nit(1024, protocol, &eph->ifr)) < 0) { + return(-1); + } + + eph->inuse = TRUE; + eph->socket = s; + eph->protocol = protocol; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(TRUE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].socket); /* toss listener */ + close(ephlist[edx-1].socket); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize nit on a particular protocol type + * + * Runs in promiscous mode for now. + * + * Return: socket if no error, < 0 o.w. +*/ +private int +init_nit(chunksize, protocol, ifr) +u_long chunksize; +u_short protocol; +struct ifreq *ifr; +{ + u_long if_flags; + int s; + char device[64]; + + if ((s = pfopen(ifr->ifr_name, O_RDWR)) < 0) { + sprintf(device, "open: %s", ifr->ifr_name); + perror(device); + return(-1); + } + + if (setup_pf(s, protocol) < 0) + return(-1); +#define NOBUF +#ifndef NOBUF + setup_buf(s, chunksize); +#endif + + /* flush read queue */ + ioctl(s, EIOCFLUSH, 0); + return(s); +} + +#ifndef NOBUF +/* + * setup buffering (not wanted) + * +*/ +setup_buf(s, chunksize) +int s; +u_long chunksize; +{ + struct timeval timeout; + + /* Push and configure the buffering module. */ + if (ioctl(s, I_PUSH, "nbuf") < 0) { + perror("ioctl: nbuf"); + } + timeout.tv_sec = 0; + timeout.tv_usec = 200; + si.ic_cmd = NIOCSTIME; + si.ic_timout = 10; + si.ic_len = sizeof timeout; + si.ic_dp = (char *)&timeout; + if (ioctl(s, I_STR, (char *)&si) < 0) { + perror("ioctl: timeout"); + return(-1); + } + + si.ic_cmd = NIOCSCHUNK; + + si.ic_len = sizeof chunksize; + si.ic_dp = (char *)&chunksize; + if (ioctl(s, I_STR, (char *)&si)) { + perror("ioctl: chunk size"); + return(-1); + } +} +#endif + +/* + * establish protocol filter + * +*/ +private int +setup_pf(s, prot) +int s; +u_short prot; +{ + u_short offset; + unsigned queuelen; + struct ether_header eh; + struct enfilter pf; + register u_short *fwp = pf.enf_Filter; + extern int errno; + + queuelen = 8; + if (ioctl(s, EIOCSETW, &queuelen) < 0) { + perror("ioctl: set recv queue length"); + return(-1); + } + + pf.enf_Priority = 128; + +#define s_offset(structp, element) (&(((structp)0)->element)) + offset = ((int)s_offset(struct ether_header *, ether_type))/sizeof(u_short); +#ifdef undef + *fwp++ = ENF_PUSHLIT ; + *fwp++ = 1; + pf.enf_FilterLen = 2; +#endif + *fwp++ = ENF_PUSHWORD + offset; + *fwp++ = ENF_PUSHLIT | ENF_EQ; + *fwp++ = htons(prot); + pf.enf_FilterLen = 3; + + if (ioctl(s, EIOCSETF, &pf) < 0) { + perror("ioctl: protocol filter"); + return(-1); + } + return(0); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct endevp endev; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + if (ioctl(eph->socket, EIOCDEVP, &endev) < 0) { + perror("Ioctl: SIOCGIFADDR"); + return(-1); + } + bcopy(endev.end_addr, ea, 6); + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].socket, listener, arg, edx); +} + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = readv(eph->socket, iov, iovlen)) < 0) { + perror("abread"); + return(cc); + } + return(cc); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[2]; + struct ethernet_addresses ea; + int cc; + + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +} + +export int +pi_write(edx, buf, buflen, eaddr) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +{ + struct ephandle *eph; + struct ether_header eh; + struct sockaddr sa; + struct iovec iov[2]; + + iov[0].iov_base = (caddr_t)&eh; + iov[0].iov_len = 14; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = buflen; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + bcopy(eaddr, &eh.ether_dhost, 6); + eh.ether_type = htons(eph->protocol); + + if (writev(eph->socket, iov, 2) < 0) { + return(-1); + } + return(buflen); +} + +private char ebuf[2000]; /* big enough */ + +export int +pi_writev(edx, iov, iovlen, eaddr) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr)); +} diff --git a/support/uab/srawetherp.c b/support/uab/srawetherp.c new file mode 100644 index 0000000..e17b5ac --- /dev/null +++ b/support/uab/srawetherp.c @@ -0,0 +1,282 @@ +static char rcsid[] = "$Author: djh $ $Date: 1991/09/01 06:24:45 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/srawetherp.c,v 2.1 1991/09/01 06:24:45 djh Rel djh $"; +static char revision[] = "$Revision: 2.1 $"; + +/* + * srawether.c - Simple "protocol" level interface to Rawether + * NEWS-OS 4.0 + * + * TAYA Shin'ichiro + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "proto_intf.h" + +typedef struct ephandle { /* ethernet protocol driver handle */ + int inuse; /* true if inuse */ + int socket; /* file descriptor of socket */ + int protocol; /* ethernet protocol */ +} EPHANDLE; + +private inited = FALSE; + +private EPHANDLE ephlist[MAXOPENPROT]; + +/* + * setup for particular device devno + * all pi_open's will go this device +*/ +export +pi_setup() +{ + int i; + + if (!inited) { + for (i = 0 ; i < MAXOPENPROT; i++) + ephlist[i].inuse = FALSE; + (void)init_fdlistening(); + inited = TRUE; /* don't forget now */ + } + return(TRUE); +} + +/* + * Open up a protocol handle: + * user level data: + * file descriptor + * protocol + * + * returns -1 and ephandle == NULL if memory allocation problems + * returns -1 for other errors + * return edx > 0 for okay +*/ +export int +pi_open(protocol, dev, devno) +int protocol; +char *dev; +int devno; +{ + struct ephandle *eph; + char devnamebuf[100]; /* room for device name */ + int s; + int i; + + for (i = 0; i < MAXOPENPROT; i++) { + if (!ephlist[i].inuse) + break; + } + if (i == MAXOPENPROT) + return(0); /* nothing */ + eph = &ephlist[i]; /* find handle */ + + sprintf(devnamebuf, "%s%d",dev,devno); + if ((s = init_rawether( devnamebuf, protocol)) < 0) { + return(-1); + } + + eph->inuse = TRUE; + eph->socket = s; + eph->protocol = protocol; + return(i+1); /* skip zero */ +} + +/* returns TRUE if machine will see own broadcasts */ +export int +pi_delivers_self_broadcasts() +{ + return(FALSE); +} + +export int +pi_close(edx) +int edx; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + fdunlisten(ephlist[edx-1].socket); /* toss listener */ + close(ephlist[edx-1].socket); + ephlist[edx-1].inuse = 0; + return(0); +} + +/* + * Initialize rawether on a particular protocol type + * + * Runs in promiscous mode for now. + * + * Return: socket if no error, < 0 o.w. +*/ +private int +init_rawether(name, protocol) +char *name; +u_short protocol; +{ + int s, type; + + /* get clone */ + if ((s = open(name, O_RDWR)) < 0) { + perror(name); + return(-1); + } + type = ETHER_ALL; + if(ioctl(s, ETHERIOCSTYPE, &type) < 0){ + perror("ioctl"); + return (-1); + } + + return(s); +} + +export int +pi_get_ethernet_address(edx,ea) +int edx; +u_char *ea; +{ + struct sockaddr *sa; + struct ephandle *eph; + + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + eph = &ephlist[edx-1]; + + if(ioctl(eph->socket, ETHERIOCGADDR, ea) != 0){ + perror("ioctl: SIOCGIFADDR"); + exit(1); + } + + return(0); +} + +export +pi_listener(edx, listener, arg) +int edx; +int (*listener)(); +caddr_t arg; +{ + if (edx < 1 || edx > MAXOPENPROT || !ephlist[edx-1].inuse) + return(-1); + + fdlistener(ephlist[edx-1].socket, listener, arg, edx); +} + +/* + * cheat - iov[0] == struct etherheader + * +*/ +export int +pi_readv(edx, iov, iovlen) +int edx; +struct iovec *iov; +int iovlen; +{ + struct ephandle *eph ; + int cc; + + if (edx < 1 || edx > MAXOPENPROT) + return(-1); + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + if ((cc = readv(eph->socket, iov, iovlen)) < 0) { + perror("abread"); + return(cc); + } + return(cc); +} + +export int +pi_read(edx, buf, bufsiz) +int edx; +caddr_t buf; +int bufsiz; +{ + struct iovec iov[2]; + struct ethernet_addresses ea; + int cc; + + iov[0].iov_base = (caddr_t)&ea; + iov[0].iov_len = sizeof(ea); + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = bufsiz; + cc = pi_readv(edx, iov, 2); + return(cc - sizeof(ea)); +} + +u_char pi_write_buf[ETHERMTU]; + +export int +pi_write(edx, buf, buflen, eaddr, protocol) +int edx; +caddr_t buf; +int buflen; +char *eaddr; +int protocol; +{ + struct ephandle *eph; + struct ether_header eh; + int l; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + + eph = &ephlist[edx-1]; + if (!eph->inuse) + return(-1); + + bcopy(eaddr, &(eh.ether_dhost[0]), sizeof(eh.ether_dhost)); + eh.ether_type =protocol; + bcopy(&eh, pi_write_buf, sizeof(struct ether_header)); + bcopy(buf, &pi_write_buf[sizeof(struct ether_header)], buflen); + + l = (buflen > ETHERMIN ? buflen : ETHERMIN) + sizeof(struct ether_header); + + if(write(eph->socket, pi_write_buf, l) < 0){ + return(-1); + } + return(buflen); +} + +private char ebuf[2000]; /* big enough */ + +export int +pi_writev(edx, iov, iovlen, eaddr, protocol) +int edx; +struct iovec *iov; +int iovlen; +unsigned char eaddr[6]; +int protocol; +{ + int i; + char *p; + int len; + + if (edx < 1 || edx > MAXOPENPROT || eaddr == NULL) + return(-1); + if (!ephlist[edx-1].inuse) + return(-1); + + for (len = 0, p = ebuf, i = 0 ; i < iovlen ; i++) + if (iov[i].iov_base && iov[i].iov_len >= 0) { + bcopy(iov[i].iov_base, p, iov[i].iov_len); + p += iov[i].iov_len; /* advance */ + len += iov[i].iov_len; /* advance */ + } + return(pi_write(edx, ebuf, len, eaddr, protocol)); +} diff --git a/support/uab/uab.c b/support/uab/uab.c new file mode 100644 index 0000000..674395e --- /dev/null +++ b/support/uab/uab.c @@ -0,0 +1,923 @@ +static char rcsid[] = "$Author: djh $ $Date: 1992/07/30 09:52:59 $"; +static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/uab.c,v 2.3 1992/07/30 09:52:59 djh Rel djh $"; +static char revision[] = "$Revision: 2.3 $"; + +/* + * UAB - Unix AppleTalk Bridge + * + * AppleTalk Bridge For Unix + * + * written by Charlie C. Kim + * Academic Computing and Communications Group + * Center For Computing Activities + * Columbia University + * August 1988 + * + * + * Copyright (c) 1988 by The Trustees of Columbia University + * in the City of New York. + * + * Permission is granted to any individual or institution to use, + * copy, or redistribute this software so long as it is not sold for + * profit, provided that this notice and the original copyright + * notices are retained. Columbia University nor the author make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * Edit History: + * + * April 3, 1988 CCKim Created + * December 2, 1990 djh@munnari.OZ.AU add async appletalk support + * +*/ + +static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \ +Columbia University in the City of New York"; + +#include +#include +#include +#include +#ifndef _TYPES +# include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include +#include "mpxddp.h" +#include "gw.h" +#include "if_desc.h" +#include "log.h" + +/* bridge description file */ +#ifndef BRIDGE_DESC +# define BRIDGE_DESC "bridge_desc" +#endif +private char *bd_file = BRIDGE_DESC; /* default bridge description */ + +private int debug = 0; +private int have_log = 0; + +/* import link access protocol descriptions */ +extern struct lap_description ethertalk_lap_description; +extern struct lap_description asyncatalk_lap_description; + +/* IMPORT: multiplex ddp descriptions */ +/* import mpx ddp module udpcap */ +extern struct mpxddp_module mkip_mpx; /* modified kip udp port range */ +extern struct mpxddp_module kip_mpx; /* no rebroadcaster allowed, */ + +private void disassociate(); +private int mark(); /* mark alive */ +private void usage(); +private char *doargs(); +export void dumpether(); + +/* these are currently only used internally */ +export boolean match_string(); +export char *gettoken(); +export char *getdelimitedstring(); +export char *mgets(); + +/* guts of start */ +private boolean parse_bd(); +private void pbd_err(); +private int parse_net(); + +private boolean connect_port(); + +/* control module */ +private void setup_signals(); /* MODULE:CONTROL */ +private void listsigactions(); /* MODULE:CONTROL */ +private int handlesigaction(); /* MODULE:CONTROL */ +private int runsignalactions(); /* MODULE:CONTROL */ +private void set_uab_pid(); /* MODULE:CONTROL */ +private int get_uab_pid(); /* MODULE:CONTROL */ + +/* + * give away terminal + * +*/ +private void +disassociate() +{ + int i; + /* disassociate */ + if (fork()) + _exit(0); /* kill parent */ + for (i=0; i < 3; i++) close(i); /* kill */ + (void)open("/",0); +#ifdef NODUP2 + (void)dup(0); /* slot 1 */ + (void)dup(0); /* slot 2 */ +#else + (void)dup2(0,1); + (void)dup2(0,2); +#endif +#ifdef TIOCNOTTY + if ((i = open("/dev/tty",2)) > 0) { + (void)ioctl(i, TIOCNOTTY, (caddr_t)0); + (void)close(i); + } +#endif TIOCNOTTY +#ifdef POSIX + (void) setsid(); +#endif POSIX +} + +private void +usage() +{ + fprintf(stderr,"Usage: uab -D -f \n"); + listsigactions(); + exit(1); +} + +private char * +doargs(argc, argv) +int argc; +char **argv; +{ + int c, pid; + extern char *optarg; + extern int optind; + + while ((c = getopt(argc, argv, "D:df:l:")) != EOF) { + switch (c) { + case 'f': + bd_file = optarg; + break; + case 'D': + debug = atoi(optarg); + break; + case 'd': + debug++; + break; + case 'l': + have_log = 1; + logitfileis(optarg, "w"); + break; + } + } + + if (optind == argc) + return; + + if ((pid = get_uab_pid()) < 0) + logit(L_EXIT|LOG_LOG, + "Couldn't get Unix AppleTalk Bridge pid, is it running?"); + + for (; optind < argc ; optind++) + if (handlesigaction(argv[optind], pid) < 0) + usage(); + exit(0); +} + +main(argc, argv) +char **argv; +int argc; +{ + int mark(); + + doargs(argc, argv); + set_debug_level(debug); + if (!debug) { + disassociate(); + if (!have_log) + nologitfile(); /* clear stderr as log file */ + } + setup_signals(); + ddp_route_init(); /* initialize */ + if (!parse_bd(bd_file)) + exit(1); + + ddp_route_start(); + set_uab_pid(); /* remember */ + /* mark every 30 minutes */ + Timeout(mark, (caddr_t)0, sectotick(60*30)); + do { + abSleep(sectotick(30), TRUE); + } while (runsignalactions()); +} + +private int +mark() +{ + logit(LOG_LOG, "uab running"); +} + +export void +dumpether(lvl,msg, ea) +int lvl; +char *msg; +byte *ea; +{ + logit(lvl, "%s: %x %x %x %x %x %x",msg, + ea[0], ea[1], ea[2], + ea[3], ea[4], ea[5]); +} + +/* + * start of guts + * parse the file + * +*/ + +/* list of known allowable lap descriptions */ +private struct lap_description *ld_list[] = { + ðertalk_lap_description, + &asyncatalk_lap_description, + NULL +}; + +/* list of valid interfaces */ +private struct interface_description *id_list; + +private struct mpxddp_module *mdm_list[] = { + &kip_mpx, + &mkip_mpx, + NULL +}; + +private struct mpxddp_module *check_local_delivery(); +private struct lap_description *check_lap_type(); + +/* + * check out local delivery methods (couldn't get the pointers right, + * so just hard coded the names :-) + * +*/ +private struct mpxddp_module * +check_local_delivery(local, invalid) +char *local; +int *invalid; +{ + struct mpxddp_module **m; + + *invalid = FALSE; + if (strcmpci(local, "none") == 0) + return(NULL); + + for (m = mdm_list; *m ; m++) { + if (strcmpci((*m)->mpx_key, local) == 0) + return(*m); + } + *invalid = TRUE; + return(NULL); +} + +/* + * check for a valid lap type + * +*/ +private struct lap_description * +check_lap_type(name) +char *name; +{ + struct lap_description **ld; + char **keys; + + for (ld = ld_list; *ld ; ld++) { + for (keys = (*ld)->ld_key; *keys; keys++) + if (strcmpci(*keys, name) == 0) + return(*ld); + } + return(NULL); +} + +private void +interface_dump_table(fd) +FILE *fd; +{ + IDESC_TYPE *id = id_list; + + while (id) { + if (id->id_ld && id->id_ld->ld_dump_routine) + (*id->id_ld->ld_dump_routine)(fd, id); + id = id->id_next; /* move to next in our list */ + } +} + +private void +interface_dump_stats(fd) +FILE *fd; +{ + IDESC_TYPE *id = id_list; + + while (id) { + if (id->id_ld && id->id_ld->ld_dump_routine) + (*id->id_ld->ld_stats_routine)(fd, id); + id = id->id_next; /* move to next in our list */ + } +} + +/* + * match base against pattern + * special chars are + * allow: % to match any char + * * (at start) to match anything (anything following * is ignored) + * * at end to match previous then anything + * +*/ +export boolean +match_string(base, pattern) +char *base; +char *pattern; +{ + char pc, bc; + + while ((pc = *pattern++)) { + if ((bc = *base) == '\0') + return(FALSE); + switch (pc) { + case '%': + break; + case '*': + return(TRUE); + default: + if (bc != pc) + return(FALSE); + break; + } + base++; + } + if (*base != '\0') /* end of string */ + return(FALSE); /* no, and pattern has ended! */ + return(TRUE); +} + +/* + * get a token. + * returns a pointer to a copy of the token (must be saved if you + * wish to keep across invocations of gettoken). + * + * We depend upon isspace to define white space (should be space, tab, + * carriage return, line feed and form feed + * + * "/" may be used to quote the characters. + * + * "#" at the start of a line (possibly with white space in front) + * is consider a comment. + * +*/ +private char tmpbuf[BUFSIZ]; /* temp for gettoken and getdel...string */ +#define TMPBUFENDP (tmpbuf+BUFSIZ-2) /* room for null */ +char * +gettoken(pp) +char **pp; +{ + char *p = *pp; + char *dp = tmpbuf; + char c; + boolean sawquote; + + /* no string or at the end */ + if (p == NULL || (c = *p) == '\0') + return(NULL); + + while ((c = *p) && isascii(c) && isspace(c)) /* skip over any spaces */ + p++; + + if (*p == '#') { /* is a comment */ + *pp = p; /* repoint */ + return(NULL); + } + + for (sawquote=FALSE, c = *p ; c != '\0' && dp < TMPBUFENDP; c = *p) { + if (sawquote) { /* in quote mode? */ + *dp++ = c; /* yes, move char in */ + sawquote = FALSE; /* and turn off quote mode */ + } else if (c == '\\') /* else, is the char a quote? */ + sawquote = TRUE; /* yes, turn quote flag on */ + else if (isascii(c) && isspace(c)) /* or is it a space? */ + break; /* yes, so stop tokenizing */ + else *dp++ = c; /* wasn't in quote, wasn't a quote */ + /* char and wasn't a space, so part of */ + /* token */ + p++; /* move past char */ + } + *dp = '\0'; /* tie off string */ + *pp = p; /* update pointer */ + if (tmpbuf == dp) /* nothing in string? */ + return(NULL); + return(tmpbuf); /* return pointer to token */ +} + +/* + * get a string deliminted by the characters start and end + * +*/ +export char * +getdelimited_string(pp, start, end) +char **pp; +char start; +char end; +{ + char *p = *pp; + char *dp = tmpbuf; + char c; + boolean sawquote; + + if (start) + while ((c = *p) && c != start) /* skip to start */ + p++; + *pp = p; + if (c == '\0') + return(NULL); + p++; /* skip begin char */ + for (sawquote=FALSE,c = *p; c != '\0' || dp < TMPBUFENDP; c = *p) { + if (sawquote) { /* in quote mode? */ + *dp++ = c; /* yes, move char in */ + sawquote = FALSE; /* and turn off quote mode */ + } else if (c == '\\') /* else, is the char a quote? */ + sawquote = TRUE; /* yes, turn quote flag on */ + else if (c == end) { /* or is it our end char */ + p++; /* yes, push past char */ + break; /* and stop tokenizing */ + } + else *dp++ = c; /* wasn't in quote, wasn't a quote */ + /* char and wasn't a end char, so part */ + /* of token */ + p++; /* move past char */ + } + *dp = '\0'; /* tie off string */ + *pp = p; /* update pointer */ + if (tmpbuf == dp) /* nothing in string? */ + return(NULL); + return(tmpbuf); /* return pointer to token */ +} + + +/* + * like gets, but accepts fd, will always null terminate, + * accepts "\" at the end of a line as a line continuation. + * +*/ +export char * +mgets(buf, size, fd) +char *buf; +int size; +FILE *fd; +{ + int c; + char lc = 0; + char *p = buf; + + if (size) /* make room for the null */ + size--; + while ((c = getc(fd)) != EOF && size) { + if (c == '\n' || c == '\r') { + if (lc == '\\') { + p--; /* backup pointer (toss the "\" */ + lc = 0; /* and clear lc */ + continue; + } + break; + } + *p++ = c; + size--; + lc = c; + } + *p = '\0'; /* tie off string */ + return(c == EOF ? NULL : buf); +} + + +/* + * parse a bridge description file + * +*/ +private boolean +parse_bd(file) +char *file; +{ + FILE *fd; + char hostname[BUFSIZ]; /* room for host name */ + char buf[BUFSIZ]; /* room for a lot */ + char *p, *bp, *t; + char *savedpat = NULL; + int i; + int lineno = 0; /* start line numbers */ + int connected_ports = 0; + int anymatches = TRUE; + LDESC_TYPE *ld; + IDESC_TYPE *id; + + if ((id = (IDESC_TYPE *)malloc(sizeof(IDESC_TYPE))) == NULL) + logit(L_EXIT|L_UERR|LOG_LOG, "out of memory in parse_pd"); + + if (gethostname(hostname, sizeof(hostname)) < 0) { + logit(L_UERR|LOG_LOG, "Can't get hostname: will only accept wildcards"); + hostname[0] = '\0'; + } + + if ((fd = fopen(file, "r")) == NULL) + logit(L_EXIT|L_UERR|LOG_LOG, "Can't open file %s", file); + + while (mgets(buf, sizeof(buf),fd)) { + lineno++; + p = buf; + /* get the tokens */ + if ((bp = gettoken(&p)) == NULL) /* comment or blank */ + continue; + if (savedpat && strcmp(bp, savedpat) == 0); + if (!savedpat) { + /* no saved pattern */ + if (!match_string(hostname, bp)) /* none of our concern */ + continue; + savedpat = (char *)strdup(bp); /* save the pattern */ + } else { + /* saved pattern */ + if (savedpat && strcmp(bp, savedpat) != 0) + continue; + } + logit(LOG_PRIMARY, "Port description match on %s", bp); + anymatches = TRUE; + if ((bp = getdelimited_string(&p, '[', ']')) == NULL) { + pbd_err(lineno, buf, "missing lap specification"); + continue; + } + if ((t = (char *)index(bp, ','))) { + *t = '\0'; /* divide */ + t++; /* and conquer*/ + } + if ((ld = check_lap_type(bp)) == NULL) { + pbd_err(lineno,buf, "invalid lap type"); + continue; + } + id->id_ld = ld; /* remember for later */ + id->id_intf = NULL; + id->id_intfno = 0; + bp = t; /* move to device dependent */ + if (bp) { + if ((t = (char *)index(bp, ':'))) { + id->id_intfno = atoi(t+1); /* convert to int */ + if (id->id_intfno == 0 && t[1] != '0') { + pbd_err(lineno,buf, + "interface specification, expected number after colon"); + continue; + } + *t = '\0'; /* kill of interface # */ + } + id->id_intf = (char *)strdup(bp); /* copy interface name */ + } else { + if (ld->ld_wants_data) { + pbd_err(lineno, buf, "this lap type requires additional data"); + continue; + } + } + if ((bp = gettoken(&p)) == NULL) { + pbd_err(lineno, buf, "missing local delivery"); + if (id->id_intf) + free(id->id_intf); + continue; + } + id->id_local = check_local_delivery(bp, &i); + if (i) { + pbd_err(lineno, buf, "invalid local delivery"); + if (id->id_intf) + free(id->id_intf); + continue; + } + id->id_zone = NULL; + if ((bp = getdelimited_string(&p, '[', ']')) != NULL) { + byte *z; + + id->id_isabridge = TRUE; + id->id_network = parse_net(bp, NULL); /* get network number */ + z = (byte *)index(bp, ','); + if (z) { + z = (byte *)strdup(z); /* duplicate string */ + *z = strlen(z+1); /* make pstr */ + id->id_zone = z; + } else id->id_zone = NULL; + id->id_zone = z ? ((byte *)strdup(z)) : NULL; + } else id->id_isabridge = FALSE; + logit(LOG_PRIMARY, "interface %s%d", id->id_intf, id->id_intfno); + if (id->id_local) + logit(LOG_PRIMARY, "\tlocal delivery is %s", id->id_local->mpx_name); + else + logit(LOG_PRIMARY, "\tno local delivery"); + if (id->id_isabridge) { + if (id->id_network) + logit(LOG_PRIMARY, "\tnetwork %d.%d", nkipnetnumber(id->id_network), + nkipsubnetnumber(id->id_network)); + else + logit(LOG_PRIMARY, "\tnetwork number from network"); + if (id->id_zone) + logit(LOG_PRIMARY, "\tzone %d-'%s'", *id->id_zone, id->id_zone+1); + } else + logit(LOG_PRIMARY, "\tnot routing on this interface"); + if ((*ld->ld_init_routine)(id, TRUE)) { + connected_ports++; + id->id_next = id_list; /* link into list */ + id_list = id; /* of active ld descriptions */ + /* create a new id */ + if ((id = (IDESC_TYPE *)malloc(sizeof(IDESC_TYPE))) == NULL) + logit(L_EXIT|L_UERR|LOG_LOG, "out of memory in parse_pd"); + } else { + logit(LOG_PRIMARY, "Couldn't establish the port"); + } + if (id->id_intf) + free(id->id_intf); + if (id->id_zone) + free(id->id_zone); + } + if (id) + free(id); + if (savedpat) + free(savedpat); + fclose(fd); + if (connected_ports == 0) { + logit(LOG_PRIMARY, "NO CONNECTED PORTS, ABORTING"); + return(FALSE); + } + return(TRUE); +} + +private void +pbd_err(lineno, line, msg) +{ + logit(LOG_LOG, "error in line %d - %s",lineno, msg); + logit(LOG_LOG, "line: %s", line); +} + +/* + * parse a network number in the format: + * number + * number.number (means high byte, low byte) + * where the number can be hex (0x or 0X preceeding) or octal (leading 0) + * (handled by strtol) + * + * set *rptr to point to last part of "s" used + * +*/ +private int +parse_net(s, rptr) +char *s; +char **rptr; +{ + char *e; + int p1, p2; + + p1 = strtol(s, &e, 0); /* convert */ + if (rptr) + *rptr = e; + if (*e != '.') + return(htons(p1)); + p2 = strtol(e+1, rptr, 0); + return(htons( ((p1&0xff) << 8) | (p2&0xff))); +} + + +/* MODULE: CONTROL */ + +/* pid file */ +#ifndef UAB_PIDFILE +# define UAB_PIDFILE "/etc/uab.pid" +#endif + +/* logging file */ +#ifndef UAB_RUNFILE +# define UAB_RUNFILE "/usr/tmp/uab.run" +#endif + +#ifndef UAB_STATSFILE +# define UAB_STATSFILE "/usr/tmp/uab.stats" +#endif + +#ifndef UAB_DUMPFILE +# define UAB_DUMPFILE "/usr/tmp/uab.dump" +#endif + +private char *uab_runfile = NULL; +private char *uab_pidfile = UAB_PIDFILE; +private char *uab_statsfile = UAB_STATSFILE; +private char *uab_dumpfile = UAB_DUMPFILE; + +private int uab_end(); +private int uab_undebug(); +private int uab_debuginc(); +private int table_dump(); +private int stats_dump(); + +/* + * do nice stuff + * +*/ + +private void +set_uab_pid() +{ + FILE *fd; + + if ((fd = fopen(uab_pidfile, "w")) != NULL) { + fprintf(fd, "%d\n",getpid()); + fclose(fd); + } +} + +private +get_uab_pid() +{ + FILE *fp; + int pid; + + if ((fp = fopen(uab_pidfile, "r")) == NULL) { + logit(L_UERR|LOG_LOG, "No pid file - maybe the daemon wasn't running?"); + return(-1); + } + if (fscanf(fp, "%d\n", &pid) != 1) { + logit(LOG_LOG, "pid file was bad"); + return(-1); + } + return(pid); +} + + +#define NSIGACT 6 + +struct sigtab { + char *s_name; + int s_signal; + char *s_action; +}; + +private struct sigtab sigtab[NSIGACT] = { +#define SIG_TDUMP SIGUSR1 + "tdump", SIG_TDUMP, "dump tables", +#define SIG_DEBUG SIGIOT + "debug", SIG_DEBUG, "increment debug level", +#define SIG_NODEBUG SIGEMT + "nodebug", SIG_NODEBUG, "clear debugging", +#define SIG_STAT SIGUSR2 + "statistics", SIG_STAT, "dump statistics", + "stat", SIG_STAT, "dump statistics", +#define SIG_EXIT SIGTERM + "exit", SIG_EXIT, "stop running uab" +}; + +private int signalactions; /* no signal actions to handle */ +#define SA_TABLE_DUMP 0x1 +#define SA_STATISTICS 0x2 +#define SA_UNDEBUG 0x4 /* turn off debugging */ +#define SA_DEBUG 0x8 /* turn on debugging */ +private void +setup_signals() +{ + signal(SIG_STAT, stats_dump); + signal(SIG_TDUMP, table_dump); + signal(SIG_EXIT, uab_end); + signal(SIG_DEBUG, uab_debuginc); + signal(SIG_NODEBUG, uab_undebug); + signalactions = 0; /* no signal actions to handle */ +} + +private void +listsigactions() +{ + int i; + struct sigtab *st; + + fprintf(stderr, " commands:\n"); + for (st = sigtab, i = 0; i < NSIGACT; i++, st++) + fprintf(stderr,"\t%s\t%s\n", st->s_name, st->s_action); +} + +private int +handlesigaction(s, pid) +char *s; +{ + int i; + struct sigtab *st; + + for (st = sigtab, i = 0; i < NSIGACT; i++, st++) { + if (strcmpci(s, st->s_name) != 0) + continue; + if (kill(pid, st->s_signal) < 0) + logit(LOG_LOG, "Couldn't send %s signal to daemon[%d] - is it running?", + st->s_action, pid); + else + logit(LOG_LOG, "Sent %s signal to daemon[%d]",st->s_action,pid); + return(0); + } + return(-1); +} + + +/* safe enough */ +private int +uab_end() +{ + logit(LOG_LOG, "Exiting - stopped by remote"); + unlink(uab_pidfile); + exit(0); +} + +/* nothing yet */ +/* unsafe (defer until later) */ +private int +table_dump() +{ + signalactions |= SA_TABLE_DUMP; + signal(SIG_TDUMP, table_dump); +} + +private int +stats_dump() +{ + signalactions |= SA_STATISTICS; + signal(SIG_STAT, stats_dump); +} + +private int +uab_undebug() +{ + debug = 0; + signalactions |= SA_UNDEBUG; + signal(SIG_NODEBUG, uab_undebug); +} + +private int +uab_debuginc() +{ + signalactions |= SA_DEBUG; + debug++; + signal(SIG_DEBUG, uab_debuginc); +} + +/* + * run deferred actions + * + * returns FALSE if everythings should stop +*/ +private int +runsignalactions() +{ + int done = 0; /* init to zero */ + + while (signalactions) { + /* always allow undebug and debug to run */ + if ((SA_UNDEBUG & signalactions) && debug == 0) { + set_debug_level(debug); + logit(LOG_LOG, "DEBUGGING OFF"); + if (uab_runfile) { + nologitfile(); + uab_runfile = NULL; /* reset this */ + } + signalactions &= ~SA_UNDEBUG; + } + if ((SA_DEBUG & signalactions) && debug != 0) { + if (!islogitfile()) { + uab_runfile = UAB_RUNFILE; + logitfileis(uab_runfile, "w+"); + } + set_debug_level(debug); + logit(LOG_LOG, "Debugging level is now %d", debug); + signalactions &= ~SA_DEBUG; + } + /* only run one of these at a time */ + if (SA_TABLE_DUMP & signalactions) { + FILE *fd; + long t; + + done = SA_TABLE_DUMP; + if ((fd = fopen(uab_dumpfile, "a+")) == NULL) { + logit(LOG_LOG|L_UERR, "can't open dump file %s", uab_dumpfile); + } else { + chmod(uab_dumpfile, 0774); + time(&t); + fprintf(fd, "Tables dump at %s", ctime(&t)); + interface_dump_table(fd); + rtmp_dump_table(fd); + fclose(fd); + } + } else if (SA_STATISTICS & signalactions) { + FILE *fd; + long t; + + done = SA_STATISTICS; + if ((fd = fopen(uab_statsfile, "a+")) == NULL) { + logit(LOG_LOG|L_UERR, "can't open statistics file %s", uab_statsfile); + } else { + chmod(uab_statsfile, 0774); + time(&t); + fprintf(fd, "Statistics dump at %s", ctime(&t)); + ddp_dump_stats(fd); + rtmp_dump_stats(fd); + interface_dump_stats(fd); + fclose(fd); + } + } + signalactions &= ~done; /* turn off run signals */ + } + return(TRUE); +} + diff --git a/support/uab/whatiswhat b/support/uab/whatiswhat new file mode 100644 index 0000000..e6f548d --- /dev/null +++ b/support/uab/whatiswhat @@ -0,0 +1,42 @@ +Unix AppleTalk Bridge + gw.h gateway definitions + uab.c gateway driver + bridge_desc bridge descriptions + desc.ms general description + +DDP/RTMP/ZIP + ddprouter.c ddp router + ddpsvcs.c ddp services + rtmp.c rtmp routines + +DDP PORT management + ddpport.c ddp port management + ddpport.h ddp port definitions + node.h lap node description + if_desc.h (lap) interface descriptions + +EtherTalk (ELAP) + aarp.c aarp routines + aarp_defs.h aarp specific header + aarp.h aarp general header + ethertalk.c ethertalk routines + ethertalk.h ethertalk definitions + +Async AppleTalk (ASYNC) + asyncatalk.c async routines + +Process (de)multiplexing + mpxddp.h ddp to local process (de)multiplexing + kip_mpx.c ddp to local process via modified kip scheme + +Protocol Interfaces + proto_intf.h protocol interface + dlip.c dli: protocol interface + snitp.c snit: protocol interface + +Auxillary routines: + hash.c hash routines + hash.h hash routine header + log.c log routines + log.h logging header +