| /********************************************************************* |
| Project : GUSI - Grand Unified Socket Interface |
| File : GUSIDispatch.cp- Dispatch calls to their correct recipient |
| Author : Matthias Neeracher |
| Language: MPW C/C++ |
| |
| $Log$ |
| Revision 1.1 2000/09/12 20:24:47 jack |
| Moved to Unsupported. |
| |
| Revision 1.1 1998/08/18 14:52:37 jack |
| Putting Python-specific GUSI modifications under CVS. |
| |
| Revision 1.4 1994/12/30 19:48:09 neeri |
| Remove (theoretical) support for pre-System 6 systems. |
| Remove built-in support for INETd. |
| Fix problems in connection with ROM PowerPC library. |
| Move open() to GUSIFileDispatch.cp. |
| Support AF_UNSPEC domains. |
| More work on spinning performance. |
| |
| Revision 1.3 1994/08/10 00:30:30 neeri |
| Sanitized for universal headers. |
| Prevent overly fast spinning. |
| |
| Revision 1.2 1994/05/01 23:47:34 neeri |
| Extend fflush() kludge. |
| Define _lastbuf for MPW 3.2 compatibility. |
| |
| Revision 1.1 1994/02/25 02:28:36 neeri |
| Initial revision |
| |
| Revision 0.27 1993/11/24 00:00:00 neeri |
| Flush stdio before closing |
| |
| Revision 0.26 1993/11/22 00:00:00 neeri |
| Extend two time loser for EBADF |
| |
| Revision 0.25 1993/11/12 00:00:00 neeri |
| Two time loser workaround for flush bug |
| |
| Revision 0.24 1993/06/27 00:00:00 neeri |
| {pre,post}_select |
| |
| Revision 0.23 1993/06/27 00:00:00 neeri |
| ftruncate |
| |
| Revision 0.22 1993/06/20 00:00:00 neeri |
| Further subtleties in console handling |
| |
| Revision 0.21 1993/05/21 00:00:00 neeri |
| Suffixes |
| |
| Revision 0.20 1993/05/15 00:00:00 neeri |
| Try to keep errno always set on error returns |
| |
| Revision 0.19 1993/05/13 00:00:00 neeri |
| Limit Search for configuration resource to application |
| |
| Revision 0.18 1993/01/31 00:00:00 neeri |
| Introducing daemons (pleased to meet you, hope you guess my name) |
| |
| Revision 0.17 1993/01/17 00:00:00 neeri |
| Be more careful about user aborts. |
| |
| Revision 0.16 1993/01/03 00:00:00 neeri |
| GUSIConfiguration |
| |
| Revision 0.15 1992/11/25 00:00:00 neeri |
| Still trying to get standard descriptors for standalone programs right. sigh. |
| |
| Revision 0.14 1992/10/05 00:00:00 neeri |
| Small fix in event dispatching |
| |
| Revision 0.13 1992/09/12 00:00:00 neeri |
| getdtablesize() |
| |
| Revision 0.12 1992/08/30 00:00:00 neeri |
| Move hasPPC to GUSIPPC.cp, AppleTalkIdentity |
| |
| Revision 0.11 1992/08/05 00:00:00 neeri |
| Change the way standard I/O channels are opened |
| |
| Revision 0.10 1992/08/03 00:00:00 neeri |
| Move Scatter/Gather to GUSIBuffer.cp |
| |
| Revision 0.9 1992/07/30 00:00:00 neeri |
| Features with initializers |
| |
| Revision 0.8 1992/07/13 00:00:00 neeri |
| hasProcessMgr |
| |
| Revision 0.7 1992/06/27 00:00:00 neeri |
| choose(), hasNewSF |
| |
| Revision 0.6 1992/06/06 00:00:00 neeri |
| Feature |
| |
| Revision 0.5 1992/04/19 00:00:00 neeri |
| C++ Rewrite |
| |
| Revision 0.4 1992/04/18 00:00:00 neeri |
| Changed read/write/send/recv dispatchers |
| |
| Revision 0.3 1992/04/17 00:00:00 neeri |
| Spin routines |
| |
| Revision 0.2 1992/04/16 00:00:00 neeri |
| User interrupt stuff |
| |
| Revision 0.1 1992/03/31 00:00:00 neeri |
| unix domain socket calls |
| |
| *********************************************************************/ |
| |
| #include "GUSIFile_P.h" |
| #include "GUSIMPW_P.h" |
| #include <SetJmp.h> |
| #include <Signal.h> |
| #include <CursorCtl.h> |
| #include <Resources.h> |
| #include <Events.h> |
| #include <Windows.h> |
| #include <Finder.h> |
| #include <Script.h> |
| #include <Events.h> |
| #include <Traps.h> |
| #include <CommResources.h> |
| #include <CTBUtilities.h> |
| #include <Connections.h> |
| #include <FileTransfers.h> |
| #include <Terminals.h> |
| #include <EPPC.h> |
| #include <PLStringFuncs.h> |
| #include <LowMem.h> |
| #include <Processes.h> |
| |
| #if GENERATINGCFM |
| #include <CodeFragments.h> |
| #endif |
| |
| #pragma segment GUSI |
| |
| /***************************** Globals ******************************/ |
| |
| GUSIConfiguration GUSIConfig; // Change the order of these declarations |
| SocketTable Sockets; // and you'll regret it (ARM ยง12.6.1) |
| GUSISpinFn GUSISpin = GUSIDefaultSpin; |
| GUSIExecFn GUSIExec = GUSIDefaultExec; |
| GUSIFTypeFn GUSIFType = (GUSIFTypeFn)0; |
| long gGUSISpeed = 1; |
| static GUSIEvtHandler * evtHandler = nil; |
| static short evtMask = 0; |
| static int errorSock = -1; |
| static int errorType = 0; |
| static int errorCount = 0; |
| const int errorMax = 3; |
| Boolean CatchStdIO = false; |
| |
| Feature hasMakeFSSpec( |
| gestaltFSAttr, |
| (1<<gestaltHasFSSpecCalls), |
| (1<<gestaltHasFSSpecCalls)); |
| Feature hasAlias( |
| gestaltAliasMgrAttr, |
| (1<<gestaltAliasMgrPresent), |
| (1<<gestaltAliasMgrPresent)); |
| Feature hasNewSF( |
| gestaltStandardFileAttr, |
| (1<<gestaltStandardFile58), |
| (1<<gestaltStandardFile58)); |
| Feature hasProcessMgr( |
| gestaltOSAttr, |
| (1<<gestaltLaunchControl), |
| (1<<gestaltLaunchControl)); |
| Feature hasCRM_P( |
| gestaltCRMAttr, |
| (1<<gestaltCRMPresent), |
| (1<<gestaltCRMPresent)); |
| Feature hasCRM(hasCRM_P, InitCRM); |
| Feature hasCTB(hasCRM, InitCTBUtilities); |
| Feature hasStdNBP_P( |
| gestaltStdNBPAttr, |
| (1<<gestaltStdNBPPresent), |
| (1<<gestaltStdNBPPresent)); |
| Feature hasStdNBP(hasCTB, hasStdNBP_P); |
| Feature hasAppleEvents( |
| gestaltAppleEventsAttr, |
| (1<<gestaltAppleEventsPresent), |
| (1<<gestaltAppleEventsPresent)); |
| Feature hasRevisedTimeMgr( |
| gestaltTimeMgrVersion, |
| 2L); |
| |
| /*********************** Error propagation ************************/ |
| |
| #ifdef GUSI_DISPATCH |
| inline |
| #endif |
| int GUSI_error(int err) |
| { |
| if (err) |
| errno = err; |
| |
| return -1; |
| } |
| |
| #ifdef GUSI_DISPATCH |
| inline |
| #endif |
| void * GUSI_error_nil(int err) |
| { |
| if (err) |
| errno = err; |
| |
| return nil; |
| } |
| |
| /*********************** GUSIConfiguration members ************************/ |
| |
| #ifndef GUSI_DISPATCH |
| |
| Boolean GUSIConfiguration::firstTime = false; |
| short GUSIConfiguration::we; |
| |
| void GUSIConfiguration::GUSILoadConfiguration(Handle h) |
| { |
| typedef GUSIConfigRsrc ** GUSIConfHdl; |
| GUSIConfHdl config = GUSIConfHdl(h); |
| long confSize = config ? GetHandleSize(Handle(config)) : 0; |
| |
| if (confSize < 4 || !(defaultType = (*config)->defaultType)) |
| defaultType = 'TEXT'; |
| if (confSize < 8 || !(defaultCreator = (*config)->defaultCreator)) |
| defaultCreator = 'MPS '; |
| if (confSize < 9) |
| autoSpin = 1; // do automatic spin on read/write |
| else |
| autoSpin = (*config)->autoSpin; |
| |
| if (confSize < 14) |
| version = '0102'; |
| else |
| version = (*config)->version; |
| |
| if (confSize < 10) { |
| noChdir = false; // Use chdir() |
| accurStat = false; // st_nlink = # of entries + 2 |
| hasConsole = false; |
| noAutoInitGraf = false; |
| sharedOpen = false; |
| sigPipe = false; |
| noAppleEvents = false; |
| delayConsole = false; |
| } else { |
| noChdir = ((*config)->flags & 0x80) != 0; |
| accurStat = ((*config)->flags & 0x40) != 0; |
| hasConsole = version >= '0150' && version <= '0180' && ((*config)->flags & 0x08) != 0; |
| delayConsole = version >= '0181' && ((*config)->flags & 0x20) != 0; |
| noAppleEvents = version >= '0181' && ((*config)->flags & 0x08) != 0; |
| noAutoInitGraf = version >= '0174' && ((*config)->flags & 0x04) != 0; |
| sharedOpen = version >= '0174' && ((*config)->flags & 0x02) != 0; |
| sigPipe = version >= '0174' && ((*config)->flags & 0x01) != 0; |
| } |
| |
| if (version < '0120' || confSize < 16) |
| numSuffices = 0; |
| else |
| numSuffices = (*config)->numSuffices; |
| |
| if (!numSuffices) |
| suffices = nil; |
| else if (suffices = new GUSISuffix[numSuffices]) { |
| HLock((Handle)config); |
| memcpy(suffices, (*config)->suffices, numSuffices*sizeof(GUSISuffix)); |
| for (int i=0; i<numSuffices; i++) |
| for (int j=0; j<4; j++) |
| if (((char *) (suffices+i))[j] == ' ') |
| ((char *) (suffices+i))[j] = 0; |
| } |
| } |
| |
| GUSIConfiguration::GUSIConfiguration() |
| { |
| short oldResFile = CurResFile(); |
| |
| if (!firstTime) |
| we = oldResFile; |
| else |
| UseResFile(we); |
| |
| Handle config = Get1Resource('GUย
I', GUSIRsrcID); |
| GUSILoadConfiguration(config); |
| if (!firstTime) { |
| firstTime = true; |
| |
| if (!noChdir) |
| chdir(":"); |
| } else |
| UseResFile(oldResFile); |
| |
| ReleaseResource((Handle)config); |
| } |
| |
| void GUSIConfiguration::SetDefaultFType(const TFileSpec & name) const |
| { |
| FInfo info; |
| |
| // |
| // Custom hook if existing |
| // |
| if (GUSIFType && GUSIFType(name)) |
| return; |
| |
| // |
| // Otherwise default behaviour |
| // |
| if (HGetFInfo(name.vRefNum, name.parID, name.name, &info)) |
| return; |
| |
| Ptr dot = PLstrrchr(name.name, '.'); |
| |
| if (dot && (name.name[0] - (dot-Ptr(name.name))) <= 4) { |
| char searchsuffix[5]; |
| |
| strncpy(searchsuffix, dot+1, name.name[0] - (dot-Ptr(name.name))); |
| |
| for (int i = 0; i<numSuffices; i++) |
| if (!strncmp(suffices[i].suffix, searchsuffix, 4)) { |
| info.fdType = suffices[i].suffType; |
| info.fdCreator = suffices[i].suffCreator; |
| |
| goto determined; |
| } |
| } |
| |
| info.fdType = defaultType; |
| info.fdCreator = defaultCreator; |
| info.fdFlags &= ~kHasBeenInited; |
| |
| determined: |
| HSetFInfo(name.vRefNum, name.parID, name.name, &info); |
| } |
| |
| void GUSIConfiguration::DoAutoInitGraf() const |
| { |
| if (*(GrafPtr **) LMGetCurrentA5() != &qd.thePort) |
| InitGraf(&qd.thePort); |
| const_cast<GUSIConfiguration *>(this)->noAutoInitGraf = true; |
| } |
| |
| #endif // GUSI_DISPATCH |
| |
| inline void GUSIConfiguration::DoAutoSpin() const |
| { |
| if (autoSpin) |
| SAFESPIN(0, SP_AUTO_SPIN, autoSpin); |
| } |
| |
| Boolean GUSIConfiguration::DelayConsole() const |
| { |
| return delayConsole; |
| } |
| |
| /************************ Handle nonstandard consoles *************************/ |
| |
| #ifndef GUSI_DISPATCH |
| |
| static void InitConsole() |
| { |
| if (MPWDomain::stdopen) { |
| for (int i = 0; i < 3; i++) { |
| Socket * sock = MPWDomain::stdopen(i); |
| |
| if (sock) |
| Sockets.Install(sock); |
| } |
| } else { |
| if (open("dev:console", O_RDONLY) < 0) |
| open("dev:null", O_RDONLY); |
| if (open("dev:console", O_WRONLY) < 0) |
| open("dev:null", O_WRONLY); |
| if (open("dev:console", O_WRONLY) < 0) |
| open("dev:null", O_WRONLY); |
| } |
| } |
| |
| void SocketTable::InitConsole() |
| { |
| if (needsConsole) { |
| needsConsole = false; |
| ::InitConsole(); |
| } |
| } |
| |
| #endif // GUSI_DISPATCH |
| |
| /************************ External routines *************************/ |
| |
| int getdtablesize() |
| { |
| return GUSI_MAX_FD; |
| } |
| |
| int socket(int domain, int type, int protocol) |
| { |
| SocketDomain * dom; |
| Socket * sock; |
| int fd; |
| |
| Sockets.InitConsole(); |
| |
| if (dom = SocketDomain::Domain(domain)) |
| if (sock = dom->socket(type, protocol)) |
| if ((fd = Sockets.Install(sock)) != -1) |
| return fd; |
| else |
| delete sock; |
| |
| if (!errno) |
| return GUSI_error(ENOMEM); |
| else |
| return -1; |
| } |
| |
| int socketpair(int domain, int type, int protocol, int * sv) |
| { |
| SocketDomain * dom; |
| Socket * sock[2]; |
| |
| Sockets.InitConsole(); |
| |
| if (dom = SocketDomain::Domain(domain)) |
| if (!dom->socketpair(type, protocol, sock)) |
| if ((sv[0] = Sockets.Install(sock[0])) != -1) |
| if ((sv[1] = Sockets.Install(sock[1])) != -1) |
| return 0; |
| else { |
| Sockets.Remove(sv[0]); |
| |
| goto failInstall; |
| } |
| else { |
| failInstall: |
| delete sock[0]; |
| delete sock[1]; |
| } |
| |
| if (!errno) |
| return GUSI_error(ENOMEM); |
| else |
| return -1; |
| } |
| |
| int pipe(int * fd) |
| { |
| GUSIwithUnixSockets(); |
| |
| if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) |
| return -1; |
| shutdown(fd[0], 1); |
| shutdown(fd[1], 0); |
| |
| return 0; |
| } |
| |
| int choose(int domain, int type, char * prompt, void * constraint, int flags, void * name, int * namelen) |
| { |
| SocketDomain * dom; |
| |
| if (dom = SocketDomain::Domain(domain)) |
| return dom->choose(type, prompt, constraint, flags, name, namelen); |
| |
| return -1; |
| } |
| |
| int bind(int s, const struct sockaddr *name, int namelen) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->bind((void *) name, namelen) : -1; |
| } |
| |
| int connect(int s, const struct sockaddr *addr, int addrlen) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->connect((void *) addr, addrlen) : -1; |
| } |
| |
| int listen(int s, int qlen) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->listen(qlen) : -1; |
| } |
| |
| int accept(int s, struct sockaddr *addr, int *addrlen) |
| { |
| Socket * sock = Sockets[s]; |
| |
| if (sock) |
| if (sock = sock->accept(addr, addrlen)) |
| if ((s = Sockets.Install(sock)) != -1) |
| return s; |
| else |
| delete sock; |
| |
| return -1; |
| } |
| |
| int close(int s) |
| { |
| errorSock = -1; |
| |
| return Sockets.Remove(s); |
| } |
| |
| #ifdef __MWERKS__ |
| int read(int s, char *buffer, int buflen) |
| #else |
| int read(int s, char *buffer, unsigned buflen) |
| #endif |
| { |
| GUSIConfig.DoAutoSpin(); |
| |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->read(buffer, (unsigned) buflen) : -1; |
| } |
| |
| int readv(int s, const struct iovec *iov, int count) |
| { |
| GUSIConfig.DoAutoSpin(); |
| |
| Socket * sock = Sockets[s]; |
| |
| if (sock) { |
| Scatterer scatt(iov, count); |
| |
| if (scatt) |
| return scatt.length(sock->read(scatt.buffer(), scatt.buflen())); |
| else |
| return GUSI_error(ENOMEM); |
| } else |
| return -1; |
| } |
| |
| int recv(int s, void *buffer, int buflen, int flags) |
| { |
| GUSIConfig.DoAutoSpin(); |
| |
| int fromlen = 0; |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->recvfrom(buffer, buflen, flags, nil, &fromlen) : -1; |
| } |
| |
| int recvfrom(int s, void *buffer, int buflen, int flags, struct sockaddr *from, int *fromlen) |
| { |
| GUSIConfig.DoAutoSpin(); |
| |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->recvfrom(buffer, buflen, flags, from, fromlen) : -1; |
| } |
| |
| int recvmsg(int s, struct msghdr *msg, int flags) |
| { |
| GUSIConfig.DoAutoSpin(); |
| |
| Socket * sock = Sockets[s]; |
| |
| if (sock) { |
| Scatterer scatt((struct iovec *)msg->msg_iov, msg->msg_iovlen); |
| |
| if (scatt) |
| return |
| scatt.length( |
| sock->recvfrom( |
| scatt.buffer(), |
| scatt.buflen(), |
| flags, |
| msg->msg_name, |
| (int *)&msg->msg_namelen)); |
| else |
| return GUSI_error(ENOMEM); |
| } else |
| return -1; |
| } |
| |
| #ifdef __MWERKS__ |
| int write(int s, const char *buffer, int buflen) |
| #else |
| int write(int s, const char *buffer, unsigned buflen) |
| #endif |
| { |
| /* fflush() in the MPW stdio library doesn't take no for an answer. |
| Our workaround is to treat a second subsequent ESHUTDOWN or EBADF as |
| an invitation to lie by pretending the write worked. |
| */ |
| |
| int len; |
| |
| GUSIConfig.DoAutoSpin(); |
| |
| Socket * sock = Sockets[s]; |
| |
| if (sock && (len = sock->write((char *) buffer, (unsigned) buflen)) != -1) |
| return len; |
| |
| switch (errno) { |
| case EINTR: |
| case EWOULDBLOCK: |
| case EINPROGRESS: |
| case EALREADY: |
| break; |
| default: |
| if (GUSIConfig.sigPipe) |
| raise(SIGPIPE); |
| if (errorSock == s && errorType == errno) { |
| if (++errorCount == errorMax) { |
| errorSock = -1; |
| |
| return buflen; |
| } |
| } else { |
| errorSock = s; |
| errorType = errno; |
| errorCount= 1; |
| } |
| } |
| return -1; |
| } |
| |
| static int HandleWriteErrors(int retval) |
| { |
| if (retval == -1) |
| switch (errno) { |
| case EINTR: |
| case EWOULDBLOCK: |
| case EINPROGRESS: |
| case EALREADY: |
| break; |
| default: |
| if (GUSIConfig.sigPipe) |
| raise(SIGPIPE); |
| break; |
| } |
| |
| return retval; |
| } |
| |
| int writev(int s, const struct iovec *iov, int count) |
| { |
| GUSIConfig.DoAutoSpin(); |
| |
| Socket * sock = Sockets[s]; |
| |
| if (sock) { |
| Gatherer gath(iov, count); |
| |
| if (gath) |
| return HandleWriteErrors(gath.length(sock->write(gath.buffer(), gath.buflen()))); |
| else |
| return GUSI_error(ENOMEM); |
| } else |
| return -1; |
| } |
| |
| int send(int s, const void *buffer, int buflen, int flags) |
| { |
| GUSIConfig.DoAutoSpin(); |
| |
| Socket * sock = Sockets[s]; |
| |
| return sock ? HandleWriteErrors(sock->sendto((void *)buffer, buflen, flags, nil, 0)) : -1; |
| } |
| |
| int sendto(int s, const void *buffer, int buflen, int flags, const struct sockaddr *to, int tolen) |
| { |
| GUSIConfig.DoAutoSpin(); |
| |
| Socket * sock = Sockets[s]; |
| |
| return sock ? HandleWriteErrors(sock->sendto((void *)buffer, buflen, flags, (void *) to, tolen)) : -1; |
| } |
| |
| int sendmsg(int s, const struct msghdr *msg, int flags) |
| { |
| GUSIConfig.DoAutoSpin(); |
| |
| Socket * sock = Sockets[s]; |
| |
| if (sock) { |
| Gatherer gath((struct iovec *) msg->msg_iov, msg->msg_iovlen); |
| |
| if (gath) |
| return |
| HandleWriteErrors(gath.length( |
| sock->sendto( |
| gath.buffer(), |
| gath.buflen(), |
| flags, |
| msg->msg_name, |
| msg->msg_namelen))); |
| else |
| return GUSI_error(ENOMEM); |
| } else |
| return -1; |
| } |
| |
| int select(int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) |
| { |
| Socket * sock; |
| long count; |
| int s; |
| long starttime, waittime; |
| fd_set rd, wd, ed; |
| Boolean r,w,e; |
| Boolean * canRead; |
| Boolean * canWrite; |
| Boolean * exception; |
| |
| count = 0; |
| FD_ZERO(&rd); |
| FD_ZERO(&wd); |
| FD_ZERO(&ed); |
| |
| if (timeout) |
| waittime = timeout->tv_sec*60 + timeout->tv_usec/16666; |
| else |
| waittime = 2000000000; // Slightly more than a year; close enough to "no timeout" |
| |
| starttime = LMGetTicks(); |
| |
| // Check files for kosherness |
| |
| for (s = 0; s < width ; ++s) |
| if ( (readfds && FD_ISSET(s,readfds)) |
| || (writefds && FD_ISSET(s,writefds)) |
| || (exceptfds && FD_ISSET(s,exceptfds)) |
| ) |
| if (!Sockets[s]) |
| return GUSI_error(EBADF); |
| |
| for (s = 0; s < width ; ++s) |
| if (sock = Sockets[s]) { |
| r = readfds && FD_ISSET(s,readfds); |
| w = writefds && FD_ISSET(s,writefds); |
| e = exceptfds && FD_ISSET(s,exceptfds); |
| |
| if (r || w || e) |
| sock->pre_select(r, w, e); |
| } |
| |
| do { |
| for (s = 0; s < width ; ++s) { |
| if (sock = Sockets[s]) { |
| r = false; |
| w = false; |
| e = false; |
| |
| canRead = (readfds && FD_ISSET(s,readfds)) ? &r : nil; |
| canWrite = (writefds && FD_ISSET(s,writefds)) ? &w : nil; |
| exception = (exceptfds && FD_ISSET(s,exceptfds)) ? &e : nil; |
| |
| if (canRead || canWrite || exception) { |
| count += sock->select(canRead, canWrite, exception); |
| |
| if (r) |
| FD_SET(s,&rd); |
| if (w) |
| FD_SET(s,&wd); |
| if (e) |
| FD_SET(s,&ed); |
| } |
| } |
| } |
| if (count) |
| break; |
| |
| SAVE_AND_CLEAR_ERRNO; |
| SAFESPIN(false, SP_SELECT, waittime); |
| |
| if (errno) { |
| count = -1; |
| |
| break; |
| } |
| } while (LMGetTicks() - starttime < waittime); |
| |
| for (s = 0; s < width ; ++s) |
| if (sock = Sockets[s]) { |
| r = readfds && FD_ISSET(s,readfds); |
| w = writefds && FD_ISSET(s,writefds); |
| e = exceptfds && FD_ISSET(s,exceptfds); |
| |
| if (r || w || e) |
| sock->post_select(r, w, e); |
| } |
| |
| if (count < 0) |
| return GUSI_error(EINTR); |
| |
| if (readfds) |
| *readfds = rd; |
| if (writefds) |
| *writefds = wd; |
| if (exceptfds) |
| *exceptfds = ed; |
| |
| return count; |
| } |
| |
| int getsockname(int s, struct sockaddr *name, int *namelen) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->getsockname(name, namelen) : -1; |
| } |
| |
| int getpeername(int s, struct sockaddr *name, int *namelen) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->getpeername(name, namelen) : -1; |
| } |
| |
| int shutdown(int s, int how) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->shutdown(how) : -1; |
| } |
| |
| int fcntl(int s, unsigned int cmd, int arg) |
| { |
| Socket * sock = Sockets[s]; |
| |
| if (sock) |
| return (cmd == F_DUPFD) ? Sockets.Install(sock, arg) : sock->fcntl(cmd, arg); |
| else |
| return -1; |
| } |
| |
| int dup(int s) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? Sockets.Install(sock) : -1; |
| } |
| |
| int dup2(int s, int s1) |
| { |
| Socket * sock = Sockets[s]; |
| |
| if (!sock) |
| return -1; |
| |
| if (Sockets[s1]) |
| Sockets.Remove(s1); |
| |
| return Sockets.Install(sock, s1); |
| } |
| |
| int ioctl(int s, unsigned int request, long *argp) |
| { |
| Socket * sock = Sockets[s]; |
| |
| if (!sock) |
| return -1; |
| |
| return sock->ioctl(request, argp); |
| } |
| |
| int getsockopt(int s, int level, int optname, void *optval, int * optlen) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->getsockopt(level, optname, optval, optlen) : -1; |
| } |
| |
| int setsockopt(int s, int level, int optname, const void *optval, int optlen) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->setsockopt(level, optname, (void *) optval, optlen) : -1; |
| } |
| |
| int fstat(int s, struct stat * buf) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->fstat(buf) : -1; |
| } |
| |
| long lseek(int s, long offset, int whence) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->lseek(offset, whence) : -1; |
| } |
| |
| int ftruncate(int s, long offset) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->ftruncate(offset) : -1; |
| } |
| |
| int isatty(int s) |
| { |
| Socket * sock = Sockets[s]; |
| |
| return sock ? sock->isatty() : -1; |
| } |
| |
| void GUSISetHook(GUSIHookCode code, GUSIHook hook) |
| { |
| switch (code) { |
| case GUSI_SpinHook: |
| GUSISpin = (GUSISpinFn) hook; |
| break; |
| case GUSI_ExecHook: |
| GUSIExec = (GUSIExecFn) hook; |
| break; |
| case GUSI_FTypeHook: |
| GUSIFType = (GUSIFTypeFn) hook; |
| break; |
| case GUSI_SpeedHook: |
| gGUSISpeed = (long) hook; |
| break; |
| } |
| } |
| |
| GUSIHook GUSIGetHook(GUSIHookCode code) |
| { |
| switch (code) { |
| case GUSI_SpinHook: |
| return (GUSIHook) GUSISpin; |
| case GUSI_ExecHook: |
| return (GUSIHook) GUSIExec; |
| case GUSI_FTypeHook: |
| return (GUSIHook) GUSIFType; |
| case GUSI_SpeedHook: |
| return (GUSIHook) gGUSISpeed; |
| break; |
| default: |
| return (GUSIHook) nil; |
| } |
| } |
| |
| int GUSISetEvents(GUSIEvtTable table) |
| { |
| short evt; |
| |
| evtHandler = table; |
| evtMask = 0; |
| |
| for (evt = 0; evt<16; ++evt) |
| if (evtHandler[evt]) |
| evtMask |= 1 << evt; |
| |
| return 0; |
| } |
| |
| GUSIEvtHandler * GUSIGetEvents(void) |
| { |
| return evtHandler; |
| } |
| |
| /*********************** SocketDomain members ***********************/ |
| |
| #ifndef GUSI_DISPATCH |
| |
| SocketDomain * SocketDomain::domains[GUSI_MAX_DOMAIN]; |
| ProcessSerialNumber SocketDomain::process; |
| |
| SocketDomain * SocketDomain::Domain(int domain) |
| { |
| if (domain < 0 || domain >= GUSI_MAX_DOMAIN || !domains[domain]) { |
| GUSI_error(EINVAL); |
| |
| return nil; |
| } else |
| return domains[domain]; |
| } |
| |
| void SocketDomain::Ready() |
| { |
| if (hasProcessMgr) |
| WakeUpProcess(&process); |
| } |
| |
| SocketDomain::SocketDomain(int domain) |
| { |
| #ifdef PREVENT_DUPLICATE_DOMAINS |
| if (domains[domain]) { |
| Str63 msg; |
| |
| sprintf((char *) msg+1, "Duplicate declaration for domain %d\n", domain); |
| msg[0] = (unsigned char)strlen((char *) msg+1); |
| |
| DebugStr(msg); |
| } |
| #endif |
| if (domain) // Ignore AF_UNSPEC domains |
| domains[domain] = this; |
| |
| if (hasProcessMgr && !process.highLongOfPSN && !process.lowLongOfPSN) |
| GetCurrentProcess(&process); |
| } |
| |
| SocketDomain::~SocketDomain() |
| { |
| } |
| |
| // Default implementations of socket() just returns an error |
| |
| Socket * SocketDomain::socket(int, short) |
| { |
| GUSI_error(EOPNOTSUPP); |
| |
| return nil; |
| } |
| |
| // Same with socketpair |
| |
| int SocketDomain::socketpair(int, short, Socket **) |
| { |
| return GUSI_error(EOPNOTSUPP); |
| } |
| |
| |
| int SocketDomain::choose(int, char *, void *, int, void *, int *) |
| { |
| return GUSI_error(EOPNOTSUPP); |
| } |
| |
| void SocketDomain::DontStrip() |
| { |
| } |
| |
| /*********************** SocketTable members ************************/ |
| |
| static void FlushStdio() |
| { |
| fwalk(fflush); |
| } |
| |
| SocketTable::SocketTable() |
| { |
| atexit(FlushStdio); |
| |
| needsConsole = true; |
| } |
| |
| int SocketTable::Install(Socket * sock, int start) |
| { |
| short fd; |
| |
| if (start<0 || start >= GUSI_MAX_FD) |
| return GUSI_error(EINVAL); |
| |
| for (fd=start; fd<GUSI_MAX_FD; ++fd) |
| if (!sockets[fd]) { |
| sockets[fd] = sock; |
| |
| ++sock->refCount; |
| return fd; |
| } |
| |
| return GUSI_error(EMFILE); |
| } |
| |
| int SocketTable::Remove(int fd) |
| { |
| Socket * sock; |
| |
| InitConsole(); |
| |
| if (fd<0 || fd >= GUSI_MAX_FD || !(sock = sockets[fd])) |
| return GUSI_error(EBADF); |
| |
| sockets[fd] = nil; |
| |
| if (!--sock->refCount) |
| delete sock; |
| |
| return 0; |
| } |
| |
| Socket * SocketTable::operator[](int fd) |
| { |
| Socket * sock; |
| |
| InitConsole(); |
| |
| if (fd<0 || fd >= GUSI_MAX_FD || !(sock = sockets[fd])) { |
| GUSI_error(EBADF); |
| |
| return nil; |
| } else |
| return sock; |
| } |
| |
| #ifndef powerc |
| #pragma far_code |
| #endif |
| |
| SocketTable::~SocketTable() |
| { |
| int i; |
| |
| // Flush stdio files (necessary to flush buffers) |
| |
| fwalk(fflush); |
| |
| // If we didn't need a console so far, we certainly don't need one now! |
| // Doing this further up would be dangerous for small write only apps |
| |
| needsConsole = false; |
| |
| // Now close stdio files, just to be sure |
| |
| fwalk(fclose); |
| |
| // Close all files |
| |
| for (i = 0; i<GUSI_MAX_FD; ++i) |
| if (sockets[i]) |
| close(i); |
| } |
| |
| #endif // GUSI_DISPATCH |
| |
| /********************** sleep()/alarm() ***********************/ |
| |
| static long GUSIAlarm = 0; |
| |
| int GUSICheckAlarm() |
| { |
| if (GUSIAlarm && LMGetTicks() > GUSIAlarm) { |
| GUSIAlarm = 0; |
| raise(SIGALRM); |
| |
| return 1; |
| } else |
| return 0; |
| } |
| |
| u_int alarm(u_int seconds) |
| { |
| long remaining = GUSIAlarm ? (LMGetTicks() - GUSIAlarm) / 60 : 0; |
| |
| GUSIAlarm = seconds ? LMGetTicks() + 60 * seconds : 0; |
| |
| return (remaining < 0) ? 0 : (u_int) remaining; |
| } |
| |
| static u_int DoSleep(long ticks) |
| { |
| long wakeup = LMGetTicks() + ticks; |
| |
| SAFESPIN(wakeup > LMGetTicks(), SP_SLEEP, wakeup - LMGetTicks()); |
| |
| long remaining = (LMGetTicks() - wakeup) / 60; |
| |
| return (remaining < 0) ? 0 : (u_int) remaining; |
| } |
| |
| u_int sleep(u_int seconds) |
| { |
| return DoSleep(seconds * 60); |
| } |
| |
| void usleep(u_int useconds) |
| { |
| DoSleep((useconds * 3) / 50000); |
| } |
| |
| /********************** Default spin function ***********************/ |
| |
| #ifndef GUSI_DISPATCH |
| |
| #ifndef powerc |
| #pragma smart_code |
| #endif |
| |
| /* Borrowed from tech note 263 */ |
| |
| #define kMaskModifiers 0xFE00 // we need the modifiers without the |
| // command key for KeyTrans |
| #define kMaskVirtualKey 0x0000FF00 // get virtual key from event message |
| // for KeyTrans |
| #define kUpKeyMask 0x0080 |
| #define kShiftWord 8 // we shift the virtual key to mask it |
| // into the keyCode for KeyTrans |
| #define kMaskASCII1 0x00FF0000 // get the key out of the ASCII1 byte |
| #define kMaskASCII2 0x000000FF // get the key out of the ASCII2 byte |
| #define kPeriod 0x2E // ascii for a period |
| |
| static Boolean CmdPeriod(EventRecord *theEvent) |
| { |
| Boolean fTimeToQuit; |
| short keyCode; |
| long virtualKey, keyInfo, lowChar, highChar, keyCId; |
| UInt32 state; |
| Handle hKCHR; |
| Ptr KCHRPtr; |
| |
| fTimeToQuit = false; |
| |
| if (((*theEvent).what == keyDown) || ((*theEvent).what == autoKey)) { |
| |
| // see if the command key is down. If it is, find out the ASCII |
| // equivalent for the accompanying key. |
| |
| if ((*theEvent).modifiers & cmdKey ) { |
| |
| virtualKey = ((*theEvent).message & kMaskVirtualKey) >> kShiftWord; |
| // And out the command key and Or in the virtualKey |
| keyCode = short(((*theEvent).modifiers & kMaskModifiers) | virtualKey); |
| state = 0; |
| |
| hKCHR = nil; /* set this to nil before starting */ |
| KCHRPtr = (Ptr)GetScriptManagerVariable(smKCHRCache); |
| |
| if ( !KCHRPtr ) { |
| keyCId = GetScriptVariable(short(GetScriptManagerVariable(smKeyScript)), smScriptKeys); |
| |
| hKCHR = GetResource('KCHR',short(keyCId)); |
| KCHRPtr = *hKCHR; |
| } |
| |
| if (KCHRPtr) { |
| keyInfo = KeyTrans(KCHRPtr, keyCode, &state); |
| if (hKCHR) |
| ReleaseResource(hKCHR); |
| } else |
| keyInfo = (*theEvent).message; |
| |
| lowChar = keyInfo & kMaskASCII2; |
| highChar = (keyInfo & kMaskASCII1) >> 16; |
| if (lowChar == kPeriod || highChar == kPeriod) |
| fTimeToQuit = true; |
| |
| } // end the command key is down |
| } // end key down event |
| |
| return( fTimeToQuit ); |
| } |
| |
| Boolean GUSIInterrupt() |
| { |
| EvQElPtr eventQ; |
| |
| for (eventQ = (EvQElPtr) LMGetEventQueue()->qHead; eventQ; ) |
| if (CmdPeriod((EventRecord *) &eventQ->evtQWhat)) |
| return true; |
| else |
| eventQ = (EvQElPtr)eventQ->qLink; |
| |
| return false; |
| } |
| |
| int StandAlone = 1; |
| long gGUSISpinControl = 0; |
| |
| int GUSIDefaultSpin(spin_msg msg, long arg) |
| { |
| static Boolean inForeground = true; |
| WindowPtr win; |
| EventRecord ev; |
| long sleepTime = 6; // 1/10 of a second by default |
| short mask = osMask|highLevelEventMask|mDownMask|evtMask; |
| |
| GUSIConfig.AutoInitGraf(); |
| |
| if (inForeground) { |
| register long contrib = (msg == SP_AUTO_SPIN) ? arg : gGUSISpeed; |
| gGUSISpinControl += contrib; |
| // Tweak when a spin point has been overshot |
| RotateCursor((gGUSISpinControl & 31) < contrib ? 32 : gGUSISpinControl); |
| } |
| |
| if (GUSIInterrupt()) |
| goto interrupt; |
| |
| if (!StandAlone && inForeground) // For MPW tools, SpinCursor already calls WNE |
| if (!GUSIConfig.noAppleEvents) // but it no longer reports AppleEvents |
| mask = highLevelEventMask|evtMask; |
| else |
| return 0; |
| |
| switch (msg) { |
| case SP_SLEEP: |
| case SP_SELECT: |
| if (arg >= sleepTime) // Only sleep if patience guaranteed |
| break; |
| // Otherwise, fall through |
| case SP_AUTO_SPIN: |
| sleepTime = 0; |
| break; |
| default: |
| break; |
| } |
| |
| if (WaitNextEvent(mask, &ev, sleepTime, nil)) |
| switch (ev.what) { |
| case mouseDown: |
| if (!evtHandler || !evtHandler[mouseDown]) |
| if (FindWindow(ev.where, &win) == inSysWindow) |
| SystemClick(&ev, win); |
| |
| break; |
| case osEvt: |
| if (ev.message & 1) |
| inForeground = true; |
| else |
| inForeground = false; |
| break; |
| case kHighLevelEvent: |
| if (!evtHandler || !evtHandler[kHighLevelEvent]) |
| if (hasAppleEvents) // actually pretty likely, if we get HL Events |
| AEProcessAppleEvent(&ev); // Ignore errors |
| break; |
| default: |
| break; |
| } |
| |
| if (ev.what >= 0 && ev.what < 24 && evtHandler && evtHandler[ev.what]) |
| evtHandler[ev.what](&ev); |
| |
| return 0; |
| |
| interrupt: |
| FlushEvents(-1, 0); |
| |
| return -1; |
| } |
| |
| /************************** Feature members **************************/ |
| |
| Feature::Feature(unsigned short trapNum, TrapType tTyp) |
| { |
| good = |
| NGetTrapAddress(trapNum, tTyp) != NGetTrapAddress(_Unimplemented, ToolTrap); |
| } |
| |
| Feature::Feature(OSType type, long value) |
| { |
| long attr; |
| |
| good = (!Gestalt(type, &attr) && (attr >= value)); |
| } |
| |
| Feature::Feature(OSType type, long mask, long value) |
| { |
| long attr; |
| |
| good = (!Gestalt(type, &attr) && ((attr & mask) == value)); |
| } |
| |
| Feature::Feature(const Feature & precondition, OSErrInitializer init) |
| { |
| good = precondition && !init(); |
| } |
| |
| Feature::Feature(OSErrInitializer init) |
| { |
| good = !init(); |
| } |
| |
| Feature::Feature(const Feature & precondition, voidInitializer init) |
| { |
| if (precondition) { |
| good = true; |
| init(); |
| } else |
| good = false; |
| } |
| |
| Feature::Feature(voidInitializer init) |
| { |
| good = true; |
| init(); |
| } |
| |
| Feature::Feature(const Feature & cond1, const Feature & cond2) |
| { |
| good = cond1 && cond2; |
| } |
| |
| OSErr AppleTalkIdentity(short & net, short & node) |
| { |
| static short mynet; |
| static short mynode; |
| static OSErr err = 1; |
| |
| if (err == 1) |
| if (!(err = MPPOpen())) |
| err = GetNodeAddress(&mynode, &mynet); |
| |
| |
| net = mynet; |
| node = mynode; |
| |
| return err; |
| } |
| |
| /************************** Setup suppport **************************/ |
| |
| /* Pray that the following function never inlines GUSISetup */ |
| |
| void GUSIDefaultSetup() |
| { |
| GUSISetup(GUSIwithAppleTalkSockets); |
| GUSISetup(GUSIwithInternetSockets); |
| GUSISetup(GUSIwithPAPSockets); |
| GUSISetup(GUSIwithPPCSockets); |
| GUSISetup(GUSIwithUnixSockets); |
| GUSISetup(GUSIwithSIOUXSockets); |
| } |
| |
| void GUSISetup(void (*proc)()) |
| { |
| proc(); |
| } |
| |
| void GUSILoadConfiguration(Handle hdl) |
| { |
| GUSIConfig.GUSILoadConfiguration(hdl); |
| } |
| |
| #endif // GUSI_DISPATCH |