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 +