TweetFollow Us on Twitter

MacSockets
Volume Number:7
Issue Number:12
Column Tag:Com Workshop

Related Info: Device Manager

Library for UNIX-based Clients

By Frank Storbeck, The Netherlands

Introduction.

From the moment that Apple Computer, Inc. came with its UDP-TCP/IP driver for the Macintosh, there was suddenly an increased potential for use of client-server programs as developed in the UNIX-world. Alas, Apple came with its own calls for addressing their UDP-TCP/IP driver (“.IPP”). The calls are essentially the standard low level PBOpen, PBControl and PBClose calls from the DeviceManager. The software includes a file named “TCPPB.h”, in which one finds the definitions for the various DeviceManager calls for the .IPP-driver. However it would be easy if one could use the standard UNIX calls for socket-based communication instead. This gives you the opportunity to port the UNIX software to the Mac. So I started to write a small library with the .IPP equivalents for the client-functions close, connect, recv, recvfrom, recvmsg, send, sendto, sendmsg, shutdown, and socket. To use the Mac as a server, you also need the equivalents of the functions accept, bind, listen and select, but, as I only needed the client-software, I left them out of the project. There are also some other constraints. These will be addressed in the next paragraph. To use the MacTCP software you need a Mac-512E or better and the appropriate LocalTalk- or Ethernet-compatible hardware.

The MacTCP package is available as a single-user developer kit (APDA™ # M0230LL/D). It contains the MacTCP software, a programmers guide and an administrator guide. A licence is required to redistribute the software.

Sockets.

In the manual titled “Network Programming Guide” from SUN®-microsystems (part number 800-3850-10) you can find a good explanation of the in’s and out’s of “Socket-Based Interprocess Communication”. Here only a short summary will follow.

Normally some host sets up a particular service. A client that wants to communicate with this server has to know the address of the host and what is called the port-number of the service. For the standard UNIX-services these port-numbers are “well known” (here I quoted from the manual pages), but if you want to start to offer a new service there is a range of numbers from which you can choose. In the example given here for the use of the code I choose the value 31768 (in a UNIX-system these values for the port-numbers can be found in the file /etc/services).

A socket-pair is a “two way communication channel between two processes”. Each of these processes has a socket that can be used both for sending and receiving of data to and from the other process. The processes may reside on the same or different hosts, but they have to use the same address domains, the same communication method and the same protocol. As I had to use the software for a special project for communication on board of a research vessel, I allowed some more constraints to the software: only internet addressing mode (AF_INET), stream-sockets (SOCK_STREAM) and of course the TCP-IP protocol are supported.

Normally a server-program runs on a host for as long as possible (i.e. between start-up and shut-down). It then waits for the action of some client-program. The client tries to connect to the server, sends a message that it wants some service and then waits for the answer from the server. This can go on for as long as the service is needed. Then the connection is closed down and the server waits for the next client to be served. It is also possible that a server works for more than one client simultaneously. In this case it has a socket for each of the various clients to be served.

The client and server programs

This scheme shows up in the sample code for client.c and server.c that can be found in the listing. They are examples of how to use the calls to the functions for the socket-based communication. After the server has been activated, the client can be started, and it sends the following message to the server:

From: client

To: server

Mssg: Hello server!

Then the server replies with another message:

From: server

To: client

Mssg: Hello client!\n

The server has to be run on some SUN workstation (because of the call bcopy, replace it eventually with the ANSI-C call memcpy, but beware, the order of the parameters is different; also there is a function bzero to clear the memory locations over some range - if not present, deactivate it: the locations will be overwritten by the message anyhow!) The client has been compiled with THINK-C (4.0) compiler. The THINK-C project must also contain the ANSI, MacTraps and UNIX libraries.

The client-program can also be run on a SUN workstation. Just activate the SUN_C macro.

Of course you can change client and server as you like. You can maintain the connection until the client sends some bye-bye message, messages can be sent in chunks etc. etc. Do as you like.

Diagram 1: Sequence for communication between some server and one or more clients.

Some notes on the library

MacTCP provides the use of an asynchronous notification routine. This means that when some exception occurs, a user specified function can be activated. This is here implemented by the function SockNotify, which is hidden to the user. It calls the function DoAlert that draws an alert box on the screen displaying the appropriate massage for some time. The default showing time is 4 seconds, but can be changed with the function SetDialogShowTime. When the time is zero or negative, no DialogBox will be shown. The function DoAlert is also used by the function perror. In this case a stopIcon and an OK-button are added. The DialogBox shows the error message until the user clicks on the OK-button. As the function cannot be sure that a resource for the DialogBox and its DialogItemList is available, it is built up in memory. Note that to use it, you must initialize the DialogManager etc., otherwise your Mac behaves like the mother of all field-battles. All this can be switched off by deactivating the MAC_APPL macro. In this case the messages will be shown on stderr, using the console-library from THINK-C.

There are some extra routines added: close_MacSocket, exit_MacSocket, perror_MacSocket, and strerror_MacSocket,. They are used to prevent direct calls to their UNIX and ANSI-C equivalents close, exit, perror and strerror. In the file MacSocket.h there are macros that make sure that these calls will be diverted to their equivalents in the MacSocket library. e.g., if you call close, close_MacSocket first tries to close a socket-based connection. If the socket doesn’t exist, it calls the standard function close. This is a particular noteworthy example: you might have a file descriptor with number 5 and a socket with number 5. The only way to close the file with file-descriptor 5 while keeping the socket with number 5 in good shape is calling close directly:

/* 1 */

#undef close /* to dactivate a call to close_MacSocket */
close(5);
#define close close_MacSocket /* to reactivate it */

The program includes some files that are not provided here: “types.h”, “socket.h, “in.h”, “netdb.h” and “TCPPB.h”. The first four come from the SUNOS 4.1 operating system and are copyrighted by the Regents of the University of California I am not sure if I can redistribute them, so they are not included. But if you want to port software from a UNIX-platform to the Mac, you must have access to them anyhow. The file “TCPPB.h” is copyrighted by Apple Computer, Inc.

Last note: the program was written using SUNOS 4.0. Now we are using version 4.1. There they don’t speak about “address families” but about “address protocols”. Hence the macro AF_INET has been renamed to AP_INET, but AF_INET still works.

Listings server.c:
/***
 ***
 *** To send data between stream sockets (having communication 
 *** style SOCK_STREAM)
 *** Code based on an example taken from SUN® Network Program
 *** ming Guide,chapter 10, A Socket-Based Interprocess 
 *** Communications Tutorial,Revision A, of 27 March 1990, 
 *** Fig. 10-11, pp. 270-271.
 ***/
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/time.h>
#include<netinet/in.h>
#include<netdb.h>
#include<stdio.h>
#define TRUE(1)
#define DATA“\nFrom: server\nTo:   client\nMssg: Hello client!\n”
#define Port(31768)

main()
{
 int    sock, length;
 struct sockaddr_in server;
 int    msgsock;
 char buf[1024];
 fd_set ready;
 struct timeval  to;

 /* Create socket. */
 sock = socket(AF_INET, SOCK_STREAM, 0);
 if (sock < 0)
 {
 perror(“Server--> opening stream socket”);
 exit(1);
 }
 /* Name socket using wildcards. */
 server.sin_family = AF_INET;
 server.sin_addr.s_addr = INADDR_ANY;
 server.sin_port = Port;
 if (bind(sock, (struct sockadd *)&server, sizeof server) < 0)
 {
 perror(“Server--> getting socket name”);
 exit(1);
 }
 printf(“Server--> Socket port #%d\n”, ntohs(server.sin_port));

 /* Start accepting connections. */
 listen(sock, 5);
 do
 {
 FD_ZERO(&ready);
 FD_SET(sock, &ready);
 to.tv_sec = 5;
 if (select(sock + 1, &ready, (fd_set *)0, (fd_set *)0, &to) < 0)
 {
 perror(“Server--> select”);
 continue;
 }
 if (FD_ISSET(sock, &ready))
 {
 msgsock = accept(sock, (struct sockaddr *) 0, (int *) 0);
 if (msgsock == -1)
 {
 perror(“Server--> accept”);
 }
 else
 {
 bzero(buf, sizeof buf);
 if ((length = recv(msgsock, buf, 1024, 0)) < 0)
 {
 perror(“Server--> reading stream message”);
 }
 else
 {
 buf[length] = ‘\0’;
 printf(“Server-->%s\n”, buf);
 }
 if (send(msgsock, DATA, sizeof DATA, 0) < 0)
 {
 perror(“Server--> sending steam message”);
 }
 }
 close(msgsock);
 }
 else
 {
 /* Do something else. */
 }
 } while (TRUE);
 exit(0);
}
Listing:  socket.h
/***
 *** Copyright (c) 1990 Rijksinstituut voor Visserijonderzoek.
 *** All rights reserved.
 ***
 *** Redistribution and use in source and binary forms are 
 *** permitted provided that this notice is preserved and that 
 *** due credit is given to the Rijksinstituut voor Visserijonderzoek 
at IJmuiden.
 *** This software is provided “as is” without express or implied warranty.
 ***
 *** @(#)MacSocket.h 1.2 91/01/25
 ***/

#ifndef _MacSocket_H_
#define _MacSocket_H_

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unix.h>
#include<DeviceMgr.h>

#include“types.h”
#include“socket.h”
#include“in.h”
#include“netdb.h”
#include“TCPPB.h”

#ifdef  LAB_VIEW

/* to prevent the use of the malloc() call in a LabVIEW CIN */
#define SockElmnttheSockElmnt
#define SizeOf(x)x
#define malloc(x)&x

#else

#define SizeOf sizeof

#endif  LAB_VIEW

#ifndef NIL
#define NIL ((void*) 0L)
#endif  NIL
#ifndef InFrontOfAll
#define InFrontOfAll ((Ptr) -1L)
#endif  InFrontOfAll
#ifndef True
#define True(1)
#endif  True
#ifndef False
#define False    (0)
#endif  False
#define Abort    True
#define ReportIt False
#define Asynchronous True
#define SynchronousFalse
#define ConnectTimeout  (15)
#define SendTimeout(15)
#define RecvTimeout(15)
#define CloseTimeout (15)
#define SockBufSize(16384)
#define UserDataSize (128)

#define noTCPDriver(-1)
#define invalidArgument (2)
#define nonSOCK_STREAM  (13)
#define nonSocket(38)
#define nonAF_INET (43)
#define noBufSpace (55)
#define commandInProgress (1)

/* to force the application first to use the MacSocket versions */
#define perror   perror_MacSocket
#define exitexit_MacSocket
#define close    close_MacSocket
#define strerror strerror_MacSocket

/* Prototypes */
intaccept(int s, struct sockaddr *addr, int addrlen);
intbind(int s, struct sockaddr *name, int namelen);
intclose_MacSocket(int s);
intconnect(int s, struct sockaddr_in *name, int namelen);
void  exit_MacSocket(int status);
intlisten(int s, int backlog);
void  perror_MacSocket(char *s);
intrecv(int s, char *msg, int len, int flags);
intrecvfrom(int s, char *msg, int len, int flags, struct sockaddr_in 
*from,
 int *fromlen);
intrecvmsg(int s, struct msghdr *msg, int flags);
intsend(int s, char *msg, int len, int flags);
intsendto(int s, char *msg, int len, int flags, struct sockaddr_in *to,
 int tolen);
intsendmsg(int s, struct msghdr *msg, int flags);
intshutdown(int s, int how);
#ifdef  MAC_APPL
void  SetDialogShowTime(int sec);
#endif  MAC_APPL
intsocket(int domain, int type, int protocol);
char* strerror_MacSocket(int errnum);

#endif _MacSocket_H_
Listing:  client.c
/***
 ***
 *** To send data between stream sockets (having communication style
 *** SOCK_STREAM)
 *** Code based on an example taken from SUN® Network Programming Guide,
 *** chapter 10, A Socket-Based Interprocess Communications Tutorial,
 *** Revision A, of 27 March 1990, Fig. 10-9, pp. 266-267.
 ***
 ***/

#ifdef  SUN_C
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<stdio.h>
#else SUN_C
/*
#define MAC_APPL
*/
#include<console.h>
#include“MacSocket.h”
#define bcopy(s1, s2, n)  memcpy((s2), (s1), (n))
#endif  SUN_C

#define Port(31768)
#define DATA“\nFrom: client\nTo:   server\nMssg: Hello server!\n”

main()
{
 int    sock, mssgSize;
 struct sockaddr_in server;
 union
 {
 long   address;
 unsigned char domain[4];
 } host;
 char buf[1024];
 
#ifdef  MAC_APPL
 /* prevent showing informative MacTCP/IP messages */
 SetDialogShowTime(0);
#endif  MAC_APPL
 
 /* Create socket. */
 sock = socket(AF_INET, SOCK_STREAM, 0);
 if (sock < 0)
 {
 perror(“opening stream socket”);
 exit(1);
 }
 /* Connect socket using address 192.6.1.30 and port# */
 server.sin_family = AF_INET;
 host.domain[0] = 192;
 host.domain[1] = 6;
 host.domain[2] = 1;
 host.domain[3] = 30;
 bcopy((char*)&host.address, (char*)&server.sin_addr, 4);
 server.sin_port = Port;

 if (connect(sock, (struct sockaddr_in *)&server, sizeof server) < 0)
 {
 perror(“connecting stream socket”);
 exit(1);
 }
 if (send(sock, DATA, sizeof DATA, 0) < 0)
 {
 perror(“writing on stream socket”);
 exit(1);
 }
 if ((mssgSize = recv(sock, buf, sizeof buf, 0)) < 0)
 {
 perror(“reading on stream socket”);
 exit(1);
 }
 buf[mssgSize] = ‘\0’;
 printf(“Client--> %s\n”, buf);
 close(sock);
 exit(0);
}
Listing:  MacSocket.c
/***
 *** Copyright (c) 1990 Rijksinstituut voor Visserijonderzoek, IJmuiden.
 *** All rights reserved.
 ***
 *** Redistribution and use in source and binary forms are permitted
 *** provided that this notice is preserved and that due credit is given
 *** to the Rijksinstituut voor Visserijonderzoek at IJmuiden.
 *** This software is provided “as is” without express or implied warranty.
 ***
 *** @(#)MacSocket.c 1.7 91/04/04
 ***/

/*
#define MAC_APPL
*/
#include“MacSocket.h”

#define Connected(0x01)

typedef struct SockElmnt
{
 int    number;
 struct SockElmnt*next;
 TCPiopbthePB;
 char   sockBuf[SockBufSize];
} SockElmnt, *SockElmntPtr, **SockElmntHdl;

#ifdef  MAC_APPL

#define LineLength (350)
#define AMargin  (13)
#define BMargin  (23)
#define IconSize (32)
#define DlogTop  (66)
#define DlogLeft (50)
#define ButtonHeight (22)
#define ButtonLength (59)

typedef struct AlertDitl
{
 unsigned short  nItems;
 Handle textHandle;
 Rect   textRect;
 unsigned char text;
 unsigned char textSize;
 char   textString[8];
 Handle buttonHandle;
 Rect   buttonRect;
 unsigned char button;
 unsigned char buttonSize;
 char   buttonString[2];
 Handle iconHandle;
 Rect   iconRect;
 unsigned char icon;
 unsigned char iconSize;
 unsigned short  iconId;
 Handle outlineHandle;
 Rect   outlineRect;
 unsigned char outline;
 unsigned char outlineSize;
} AlertDitl, *AlertDitlPtr, **AlertDitlHdl;

#endif  MAC_APPL

#ifdef  close
#undef  close
#endif  close

#ifdef  exit
#undef  exit
#ifdef  MAC_APPL
#define exit(s)  ExitToShell()
#endif  MAC_APPL
#endif  exit

#ifdef  perror
#undef  perror
#endif  perror

#ifdef  strerror
#undef  strerror
#endif  strerror

/**
 **
 ** local prototypes
 **
 **/
static  voidDoAlert(int alertId, char* s1, char* s2, char* s3, char* 
s4);
static  SockElmntPtr CreateSocketStruct(void);
static  SockElmntPtr FindSocketStruct(int);
static  int OpenTCPDriver(void);
static  pascal voidOutlineProc(DialogPtr thePtr, int theItem);
static  voidPollTCPDriver(TCPiopb *thePB);
static  int RemoveSocketStruct(int);
static  pascal voidSockNotify(StreamPtr tcpStream,
 unsigned short eventCode,
 Ptr userDataPtr,
 unsigned short terminReason,
 struct ICMPReport *icmpMsg);

/**
 **
 ** local variables
 **
 **/
static  int errno_MacSocket = noErr;
static  int ioRefNumTCP;
static  charmacSocketErrorMssg[128] = “no error!”;
static  int showTime = 4;
static  int sockNumber = 4;
static  SockElmntPtr theHeadPtr = NIL;
static  charTCPDriverName[] = “\p.IPP”;
static  chartheUserData[UserDataSize];
static  charTCPDriverNotOpened = True;

#ifdef  LAB_VIEW

static  SockElmnttheSockElmnt;

#endif  LAB_VIEW

/***
 ***
 *** Mac versions the standard Socket-based internet communication protocol:
 ***
 ***/

/**
 **
 ** Local functions
 **
 **/

/*
 *
 * CreateSocketStruct - creates a data structure for a particular socket 
into a
 * linked list
 *
 */
static  SockElmntPtr CreateSocketStruct()
{
 register SockElmntPtr  theSockPtr;
 
 if ((theSockPtr = (SockElmntPtr)malloc(SizeOf(SockElmnt))) == NIL)
 {
 errno_MacSocket = noBufSpace;
 return (NIL);
 }
 theSockPtr->next = theHeadPtr;
 theHeadPtr = theSockPtr;
 theSockPtr->number = ++sockNumber;
 return (theSockPtr);
}

#ifdef  MAC_APPL
/*
 *
 * DoAlert - shows an alert message on the screen. When alertId is negative,
 * no icon and OK-button are displayed. The Alert message is then “showTime”
 * seconds visible.
 *
 */
static void DoAlert(int alertId, char* s1, char* s2, char* s3, char* 
s4)
{
 AlertDitlPtr  theAlertDitlPtr;
 AlertDitlHdl  theAlertDitlHdl;
 DialogRecord  dlogRecord;
 DialogPtrtheDlogPtr;
 Rect   theBounds;
 int    theItem;
 int    textHeight;
 int    nChars;
 FontInfo theFont;
 
 if ((alertId < 0) && (showTime <= 0))
 {
 return;
 }
 
 theAlertDitlHdl = (AlertDitlHdl)NewHandle(sizeof(AlertDitl));
 if (theAlertDitlHdl != NIL)
 {
 HLock(theAlertDitlHdl);
 theAlertDitlPtr = * theAlertDitlHdl;
 
 nChars = (int)((unsigned char)*s1) +
 (int)((unsigned char)*s2) +
 (int)((unsigned char)*s3) +
 (int)((unsigned char)*s4);
 
 GetFontInfo(&theFont);
 textHeight = ((nChars * theFont.widMax) / LineLength + 1) *
 (theFont.ascent + theFont.descent + theFont.leading);
 if ((textHeight < IconSize) && (alertId >= 0))
 {
 textHeight = IconSize;
 }
 
 theAlertDitlPtr->nItems = (alertId >= 0) ? 3 : 0;
 
 theAlertDitlPtr->textHandle = ((void*) 0L);
 theAlertDitlPtr->textRect.top = AMargin - 3;
 theAlertDitlPtr->textRect.left = BMargin - 3;
 if (alertId >= 0)
 {
 theAlertDitlPtr->textRect.left += IconSize + BMargin;
 }
 theAlertDitlPtr->textRect.bottom =
 theAlertDitlPtr->textRect.top + textHeight;
 theAlertDitlPtr->textRect.right =
 theAlertDitlPtr->textRect.left + LineLength;
 theAlertDitlPtr->text = statText + itemDisable;
 theAlertDitlPtr->textSize = 8;
 theAlertDitlPtr->textString[0] = ‘^’;
 theAlertDitlPtr->textString[1] = ‘0’;
 theAlertDitlPtr->textString[2] = ‘^’;
 theAlertDitlPtr->textString[3] = ‘1’;
 theAlertDitlPtr->textString[4] = ‘^’;
 theAlertDitlPtr->textString[5] = ‘2’;
 theAlertDitlPtr->textString[6] = ‘^’;
 theAlertDitlPtr->textString[7] = ‘3’;
 
 if (alertId >= 0)
 {
 theAlertDitlPtr->buttonHandle = ((void*) 0L);
 theAlertDitlPtr->buttonRect.top =
 theAlertDitlPtr->textRect.bottom + AMargin;
 theAlertDitlPtr->buttonRect.right =
 theAlertDitlPtr->textRect.right;
 theAlertDitlPtr->buttonRect.bottom =
 theAlertDitlPtr->buttonRect.top + ButtonHeight;
 theAlertDitlPtr->buttonRect.left =
 theAlertDitlPtr->buttonRect.right - ButtonLength;
 theAlertDitlPtr->button = ctrlItem + btnCtrl;
 theAlertDitlPtr->buttonSize = 2;
 theAlertDitlPtr->buttonString[0] = ‘O’;
 theAlertDitlPtr->buttonString[1] = ‘K’;
 
 theAlertDitlPtr->iconHandle = ((void*) 0L);
 theAlertDitlPtr->iconRect.top = theAlertDitlPtr->textRect.top;
 theAlertDitlPtr->iconRect.left = BMargin - 3;
 theAlertDitlPtr->iconRect.bottom =
 theAlertDitlPtr->iconRect.top + IconSize;
 theAlertDitlPtr->iconRect.right =
 theAlertDitlPtr->iconRect.left + IconSize;
 theAlertDitlPtr->icon = iconItem + itemDisable;
 theAlertDitlPtr->iconSize = 2;
 theAlertDitlPtr->iconId = alertId;
 
 theAlertDitlPtr->outlineHandle = (Handle)&OutlineProc;
 theAlertDitlPtr->outlineRect.top = theAlertDitlPtr->buttonRect.top;
 theAlertDitlPtr->outlineRect.left = theAlertDitlPtr->buttonRect.left;
 theAlertDitlPtr->outlineRect.bottom =
 theAlertDitlPtr->buttonRect.bottom;
 theAlertDitlPtr->outlineRect.right = theAlertDitlPtr->buttonRect.right;
 theAlertDitlPtr->outline = userItem + itemDisable;
 theAlertDitlPtr->outlineSize = 0;
 }
 
 theBounds.top = DlogTop;
 theBounds.left = DlogLeft;
 theBounds.bottom = DlogTop + AMargin - 3;
 theBounds.bottom += (alertId >= 0) ?
 theAlertDitlPtr->buttonRect.bottom :
 theAlertDitlPtr->textRect.bottom;
 theBounds.right = DlogLeft + theAlertDitlPtr->textRect.right + AMargin 
- 3;
 
 HUnlock(theAlertDitlHdl);
 
 ParamText(s1, s2, s3, s4);
 theDlogPtr = NewDialog(&dlogRecord, &theBounds, “\p”, True, dBoxProc,
 InFrontOfAll, False, 0L, theAlertDitlHdl);
 if (alertId >= 0)
 {
 ModalDialog(NIL, &theItem);
 }
 else
 {
 DrawDialog(theDlogPtr);
 sleep(showTime);
 }
 CloseDialog(theDlogPtr);
 DisposHandle(theAlertDitlHdl);
 }
};
#endif MAC_APPL

/*
 *
 * FindSocketStruct - finds a data structure for a particular socket 
in a linked list
 *
 */
static SockElmntPtrFindSocketStruct(int sock)
{
 register SockElmntPtr  theSockPtr;

 for (theSockPtr = theHeadPtr; theSockPtr != NIL;
   theSockPtr = (SockElmntPtr)theSockPtr->next)
 {
 if (theSockPtr->number == sock)
 {
 errno_MacSocket = noErr;
 return (theSockPtr);
 }
 }
 errno_MacSocket = nonSocket;
 return (NIL);
}

/*
 *
 * OpenTCPDriver - loads and opens UDP-TCP/IP driver (“.IPP”)
 *
 */
static  int OpenTCPDriver()
{
 ParamBlockRec thePB;
 
 thePB.ioParam.ioCompletion = NIL;
 thePB.ioParam.ioNamePtr = (StringPtr)TCPDriverName;
 thePB.ioParam.ioPermssn = fsCurPerm;
 PBOpen(&thePB, Asynchronous);
 PollTCPDriver((TCPiopb *)&thePB);
 if (errno_MacSocket == noErr)
 {
 TCPDriverNotOpened = False;
 ioRefNumTCP = thePB.ioParam.ioRefNum;
 return(1);
 }
 else
 {
 errno_MacSocket = noTCPDriver;
 return(0);
 }
}

#ifdef MAC_APPL
/*
 *
 * OutlineProc - Outlines an item in a dialogbox
 *
 */
static pascal void OutlineProc(WindowPtr thePtr, int theItem)
{
 int    theItemType;
 GrafPtrcurrentPort;
 Handle theItemHdl;
 Rect theBox;

 GetDItem (thePtr, theItem, &theItemType, &theItemHdl, &theBox);
 GetPort(&currentPort);
 SetPort(thePtr);
 PenSize(3, 3);
 InsetRect(&theBox, -4, -4);
 FrameRoundRect (&theBox, 16, 16);
 PenNormal();
 SetPort(currentPort);
};
#endif  MAC_APPL

/*
 *
 * PollTCPDriver - polls the UDP-TCP/IP driver until an action is completed
 *
 */
static  voidPollTCPDriver(TCPiopb *theTCPPtr)
{
 while((errno_MacSocket = theTCPPtr->ioResult) > 0)
 {
 SystemTask();
 }
}

/*
 *
 * RemoveSocketStruct - removes a data structure for a particular socket 
from a
 * linked list
 *
 */
static intRemoveSocketStruct(int sock)
{
 register SockElmntPtr  theSockPtr, thePrevOne = NIL;

 for (theSockPtr = theHeadPtr; theSockPtr != NIL;
   thePrevOne = theSockPtr, theSockPtr = (SockElmntPtr)theSockPtr->next)
 {
 if (theSockPtr->number == sock)
 {
 if (thePrevOne == NIL)
 {
 theHeadPtr = theSockPtr->next;
 }
 else
 {
 thePrevOne->next = theSockPtr->next;
 }
 free(theSockPtr);
 errno_MacSocket = noErr;
 return (1);
 }
 }
 errno_MacSocket = nonSocket;
 return(0);
}

/*
 *
 * SockNotify - prints notification messages for the UDP-TCP/IP driver
 *
 */
static pascal void SockNotify(StreamPtr tcpStream, unsigned short eventCode,
  Ptr userDataPtr, unsigned short terminReason, struct ICMPReport *icmpMsg)
{
 char theErrorMsg[128];
 char *errStrPtr1 = “\0”, *errStrPtr2 = “\0”;
#ifdef  MAC_APPL
 char errNoStr[32];
#endif  MAC_APPL
 
 strcpy(theErrorMsg, “MacSocket: “);
 switch (eventCode)
 {
 case TCPClosing:
 errStrPtr1 = “all data has been received and delivered”;
 break;
 case TCPULPTimeout:
 errStrPtr1 = “time out”;
 break;
 case TCPTerminate:
 errStrPtr1 = “connection terminated, “;
 switch(terminReason)
 {
 case TCPRemoteAbort:
 errStrPtr2 = “connection reset by peer”;
 break;
 case TCPNetworkFailure:
 errStrPtr2 = “network failure”;
 break;
 case TCPSecPrecMismatch:
 errStrPtr2 = “invallid security option or precedence level”;
 break;
 case TCPULPTimeoutTerminate:
 errStrPtr2 = “ULP time out”;
 break;
 case TCPULPAbort:
 errStrPtr2 = “connection aborted”;
 break;
 case TCPULPClose:
 errStrPtr2 = “connection closed gracefully”;
 break;
 case TCPServiceError:
 errStrPtr2 = “unexpected connection initiation segment read”;
 break;
 default:
 errStrPtr2 = “unknown reason”;
 break;
 }
 break;
 case TCPDataArrival:
 errStrPtr1 = “data arrived, no receive outstanding”;
 break;
 case TCPUrgent:
 errStrPtr1 = “urgent data arrived”;
 break;
 case TCPICMPReceived:
 errStrPtr1 = “Internet Control Message arrived”;
 /* still to be printed */
 break;
 default:
 errStrPtr1 = “unknown eventcode”;
 break;
 }
 strcat(theErrorMsg, errStrPtr1);
 strcat(theErrorMsg, errStrPtr2);
#ifndef MAC_APPL
 fprintf(stderr, “%s (%d).\n”, theErrorMsg, eventCode);
#else MAC_APPL
 NumToString(eventCode, errNoStr);
 DoAlert(-1, CtoPstr(theErrorMsg), “\p(“, errNoStr, “\p)”);
#endif  MAC_APPL
};

/**
 **
 ** Global functions
 **
 **/

/**
 **
 ** Functions that substitute relevant UNIX-system calls on the Mac
 **
 **/

/*
 *
 * close_MacSocket - delete a (socket) descriptor if present, else do 
close(int s)
 *
 */
int close_MacSocket(int s)
{
 TCPiopb*theTCPPtr;
 SockElmntPtr  theSockPtr, thePrevPtr = NIL;

 if ((theSockPtr = FindSocketStruct(s)) == NIL)
 {
 return(close(s));
 }
 
 theTCPPtr = &(theSockPtr->thePB);
 theTCPPtr->csCode = TCPClose;
 theTCPPtr->csParam.close.ulpTimeoutValue = CloseTimeout;
 theTCPPtr->csParam.close.ulpTimeoutAction = Abort;
 theTCPPtr->csParam.close.validityFlags = (byte)(timeoutValue | timeoutAction);
 theTCPPtr->csParam.close.userDataPtr = (Ptr)&theUserData;
 PBControl((ParmBlkPtr)theTCPPtr, Asynchronous);
 PollTCPDriver(theTCPPtr);
 theSockPtr->number = -1;
 if (errno_MacSocket == noErr)
 {
 return(0);
 }
 else
 {
 return (-1);
 }
};

/*
 *
 * exit_MacSocket - substitute for exit(int status)
 *
 */
void exit_MacSocket(int status)
{
 ParamBlockRec thePB;

 while (theHeadPtr != NIL)
 {
 shutdown(theHeadPtr->number, 2);
 }
 
 if (!TCPDriverNotOpened)
 {
 thePB.ioParam.ioCompletion = NIL;
 thePB.ioParam.ioRefNum = ioRefNumTCP;
 PBClose(&thePB, Asynchronous);
 /*
 PollTCPDriver(&thePB);
 */
 }
 exit(status);
}

/*
 *
 * perror_MacSocket - substitute for void perror(char *s)
 *
 */
void perror_MacSocket(char *s)
{
 char theErrorMsg[128];
#ifdef  MAC_APPL
 char errNoStr[32];
#endif  MAC_APPL
 
 strcpy(theErrorMsg, s);
 strcat(theErrorMsg, “: “);
 strcat(theErrorMsg, strerror_MacSocket(errno_MacSocket));
#ifndef MAC_APPL
 fprintf(stderr, “%s (%d).\n”, theErrorMsg, errno_MacSocket);
#else MAC_APPL
 NumToString(errno_MacSocket, errNoStr);
 DoAlert(stopIcon, CtoPstr(theErrorMsg), “\p (“, errNoStr, “\p).”);
#endif  MAC_APPL
}

/*
 *
 * strerror_MacSocket - substitute for strerror(int errnum)
 *
 */
char* strerror_MacSocket(int errnum)
{
 char *errStrPtr;
 
 switch (errnum)
 {
 case noTCPDriver:
 errStrPtr = “UDP-TCP/IP driver (\”.IPP\”) not available”;
 break;
 case invalidArgument:
 errStrPtr = “invalid address argument”;
 break;
 case nonSOCK_STREAM:
 errStrPtr = “socket type not supported”;
 break;
 case nonSocket:
 errStrPtr = “invalid socket descriptor”;
 break;
 case nonAF_INET:
 errStrPtr = “domain not supported”;
 break;
 case noBufSpace:
 errStrPtr = “no memory for buffers available”;
 break;
 case ipBadLapErr:
 errStrPtr = “bad network configuration”;
 break;
 case ipBadCnfgErr:
 errStrPtr = “bad IP configuration error”;
 break;
 case ipNoCnfgErr:
 errStrPtr = “missing IP or LAP configuration error”;
 break;
 case ipLoadErr:
 errStrPtr = “error in MacTCP load”;
 break;
 case ipBadAddr:
 errStrPtr = “error in getting address”;
 break;
 case streamAlreadyOpen:
 errStrPtr = “an open stream is already using this receive buffer area”;
 break;
 case invalidLength:
 errStrPtr = “the buffer is too small or too large”;
 break;
 case invalidBufPtr:
 errStrPtr = “illegal buffer pointer”;
 break;
 case insufficientResources:
 errStrPtr = “there are already 64 TCP streams open”;
 break;
 case invalidStreamPtr:
 errStrPtr = “the specified TCP stream is not open”;
 break;
 case connectionExists:
 errStrPtr = “this TCP stream has an open connection”;
 break;
 case duplicateSocket:
 errStrPtr = “a connection already exists on this link”;
 break;
 case commandTimeout:
 errStrPtr =
 “no connection attempt was received in the specified time-out period”;
 break;
 case openFailed:
 errStrPtr = “the connection came halfway up and then failed”;
 break;
 case connectionDoesntExist:
 errStrPtr = “there is no open connection on this stream”;
 break;
 case connectionClosing:
 errStrPtr = “a TCP close command was already issued”;
 break;
 case connectionTerminated:
 errStrPtr = “the connection went down”;
 break;
 case invalidRDS:
 errStrPtr = “trying to release wrong buffer”;
 break;
 default:
 errStrPtr = strerror(errnum);;
 break;
 }
 strcpy(macSocketErrorMssg, errStrPtr);
 return(macSocketErrorMssg);
}

/**
 **
 ** MacSocket calls
 **
 **/

/*
 *
 * connect - initiate a connection on a socket
 *
 */
int connect(int s, struct sockaddr_in *name, int namelen)
{
 TCPiopb*theTCPPtr;
 SockElmntPtr  theSockPtr;
 
 if (namelen != sizeof(struct sockaddr_in))
 {
 errno_MacSocket = invalidArgument;
 return (-1);
 }

 if ((theSockPtr = FindSocketStruct(s)) == NIL)
 {
 return (-1);
 }
 
 theTCPPtr = &(theSockPtr->thePB);
 theTCPPtr->csCode = TCPActiveOpen;
 theTCPPtr->csParam.open.ulpTimeoutValue = ConnectTimeout;
 theTCPPtr->csParam.open.ulpTimeoutAction = Abort;
 theTCPPtr->csParam.open.validityFlags =
 (byte)(timeoutValue | timeoutAction | typeOfService | precedence);
 memcpy((void *)&theTCPPtr->csParam.open.remoteHost, (void *)&name->sin_addr,
 sizeof(name->sin_addr));
 theTCPPtr->csParam.open.remotePort = name->sin_port;
 theTCPPtr->csParam.open.localPort = 0;
 theTCPPtr->csParam.open.tosFlags = (byte)(reliability | lowDelay);
 theTCPPtr->csParam.open.precedence = 0;
 theTCPPtr->csParam.open.dontFrag = False;
 theTCPPtr->csParam.open.timeToLive = (byte)60;
 theTCPPtr->csParam.open.security = False;
 theTCPPtr->csParam.open.optionCnt = (byte)0;
 theTCPPtr->csParam.open.userDataPtr = (Ptr)&theUserData;
 PBControl((ParmBlkPtr)theTCPPtr, Asynchronous);
 PollTCPDriver(theTCPPtr);
 if (errno_MacSocket == noErr)
 {
 return (0);
 }
 else
 {
 return (-1);
 }
};

/*
 *
 * recv - receive a message from a socket
 *
 */
int recv(int s, char *msg, int len, int flags)
{
 TCPiopb*theTCPPtr;
 SockElmntPtr  theSockPtr;
 
 if ((theSockPtr = FindSocketStruct(s)) == NIL)
 {
 return (-1);
 }
 
 theTCPPtr = &(theSockPtr->thePB);
 theTCPPtr->csCode = TCPRcv;
 theTCPPtr->csParam.receive.commandTimeoutValue = RecvTimeout;
 theTCPPtr->csParam.receive.rcvBuff = msg;
 theTCPPtr->csParam.receive.rcvBuffLen = len;
 theTCPPtr->csParam.receive.userDataPtr = (Ptr)&theUserData;
 PBControl((ParmBlkPtr)theTCPPtr, Asynchronous);
 PollTCPDriver(theTCPPtr);
 if (errno_MacSocket == noErr)
 {
 return (theTCPPtr->csParam.receive.rcvBuffLen);
 }
 else
 {
 return (-1);
 }
};

/*
 *
 * recvfrom - receive a message from a socket
 *
 */
int recvfrom(int s, char *msg, int len, int flags, struct sockaddr_in 
*from, int *fromlen)
{
 SockElmntPtr  theSockPtr;
 
 if ((theSockPtr = FindSocketStruct(s)) == NIL)
 {
 errno_MacSocket = invalidStreamPtr;
 return (-1);
 }
 
 if (from != NULL)
 {
 *fromlen = sizeof(from);
 if (!theSockPtr->state & Connected)
 {
 if (!connect(s, from, *fromlen))
 {
 return(-1);
 }
 }
 }
 return(recv(s, msg, len, flags));
};

/*
 *
 * recvmsg - receive a message from a socket
 *
 */
int recvmsg(int s, struct msghdr *msg, int flags)
{
 SockElmntPtr  theSockPtr;
 
 if ((theSockPtr = FindSocketStruct(s)) == NIL)
 {
 errno_MacSocket = invalidStreamPtr;
 return (-1);
 }
 
 if (msg->msg_name != NULL)
 {
 if (!theSockPtr->state & Connected)
 {
 if (!connect(s, (struct sockaddr_in *)msg->msg_name, msg->msg_namelen))
 {
 return(-1);
 }
 }
 }
 return(recv(s, (char *)msg->msg_iov, msg->msg_iovlen, flags));
};

/*
 *
 * send - send a message from a socket
 *
 */
int send(int s, char *msg, int len, int flags)
{
 TCPiopb*theTCPPtr;
 SockElmntPtr  theSockPtr;

 struct theWDS
 {
 short  length;
 Ptr  theDataPtr;
 short  end;
 } theWDS;
 

 if ((theSockPtr = FindSocketStruct(s)) == NIL)
 {
 return (-1);
 }
 theTCPPtr = &(theSockPtr->thePB);
 theTCPPtr->csCode = TCPSend;
 theTCPPtr->csParam.send.ulpTimeoutValue = SendTimeout;
 theTCPPtr->csParam.send.ulpTimeoutAction = True;
 theTCPPtr->csParam.send.validityFlags = (byte)(timeoutValue | timeoutAction);
 theTCPPtr->csParam.send.pushFlag = True; /* May be false ?? */
 theTCPPtr->csParam.send.urgentFlag = False;
 theWDS.length = len;
 theWDS.theDataPtr = msg;
 theWDS.end = 0;
 theTCPPtr->csParam.send.wdsPtr = (Ptr)&theWDS;
 theTCPPtr->csParam.send.userDataPtr = (Ptr)&theUserData;
 PBControl((ParmBlkPtr)theTCPPtr, Asynchronous);
 PollTCPDriver(theTCPPtr);
 if (errno_MacSocket == noErr)
 {
 return(len);
 }
 else
 {
 return (-1);
 }
};

/*
 *
 * sendto - send a message to a socket
 *
 */
int sendto(int s, char *msg, int len, int flags, struct sockaddr_in *to, 
int *tolen)
{
 SockElmntPtr  theSockPtr;
 
 if ((theSockPtr = FindSocketStruct(s)) == NIL)
 {
 errno_MacSocket = invalidStreamPtr;
 return (-1);
 }
 
 if (to != NULL)
 {
 *tolen = sizeof(struct sockaddr_in);
 if (!theSockPtr->state & Connected)
 {
 if (!connect(s, to, *tolen))
 {
 return(-1);
 }
 }
 }
 return(send(s, msg, len, flags));
};

/*
 * sendmsg - send a message to a socket
 */
int sendmsg(int s, struct msghdr *msg, int flags)
{
 SockElmntPtr  theSockPtr;
 
 if ((theSockPtr = FindSocketStruct(s)) == NIL)
 {
 errno_MacSocket = invalidStreamPtr;
 return (-1);
 }
 
 if (msg->msg_name != NULL)
 {
 if (!theSockPtr->state & Connected)
 {
 if (!connect(s, (struct sockaddr_in *)msg->msg_name, msg->msg_namelen))
 {
 return(-1);
 }
 }
 }
 return(send(s, (char *)msg->msg_iov, msg->msg_iovlen, flags));
};

#ifdef  MAC_APPL
/*
 *
 * SetDialogShowTime - sets the time that an MacTCP/IP info DialogBox 
is vissible.
 * when sec <= 0, no message will be displayed.
 *
 */
void SetDialogShowTime(int sec)
{
 showTime = sec;
}
#endif  MAC_APPL

/*
 *
 * shutdown - shut down part of a full-duplex connection
 *
 */
int shutdown(int s, int how)
{
 TCPiopb*theTCPPtr;
 SockElmntPtr  theSockPtr;

 switch (how)
 {
 case 1: /* further sends disalowed */
 return (close_MacSocket(s));
 break;
 case 2: /* further sends and receives disalowed */
 if ((theSockPtr = FindSocketStruct(s)) == NIL)
 {
 return(-1);
 }
 theTCPPtr = &(theSockPtr->thePB);
 theTCPPtr->csCode = TCPRelease;
 PBControl((ParmBlkPtr)theTCPPtr, Asynchronous);
 PollTCPDriver(theTCPPtr);
 return (RemoveSocketStruct(s));
 break;
 case 0: /* further receives disalowed */
 default:
 errno_MacSocket = invalidArgument;
 return(-1);
 break;
 }
};

/*
 * socket - create an endpoint for communication
 */
intsocket(int domain, int type, int protocol)
{
 TCPiopb*theTCPPtr;
 SockElmntPtr  theSockPtr;
 
 if (domain != AF_INET)
 {
 errno_MacSocket = nonAF_INET;
 return (-1);
 }
 if (protocol != 0)
 {
 errno_MacSocket = invalidArgument;
 return (-1);
 }
 if (type != SOCK_STREAM)
 {
 errno_MacSocket = nonSOCK_STREAM;
 return (-1);
 }
 if ((theSockPtr = (SockElmntPtr)CreateSocketStruct()) == NIL)
 {
 errno_MacSocket = noBufSpace;
 return (-1);
 }

 if (TCPDriverNotOpened)
 {
 if (!OpenTCPDriver())
 {
 return(-1);;
 }
 }
 theTCPPtr = &(theSockPtr->thePB);
 theTCPPtr->ioCompletion = NIL;
 theTCPPtr->ioRefNum = ioRefNumTCP;
 theTCPPtr->csCode = TCPCreate;
 theTCPPtr->csParam.create.rcvBuff = theSockPtr->sockBuf;
 theTCPPtr->csParam.create.rcvBuffLen = SockBufSize;
 theTCPPtr->csParam.create.notifyProc = SockNotify;
 theTCPPtr->csParam.create.userDataPtr = (Ptr)&theUserData;
 PBControl((ParmBlkPtr)theTCPPtr, Asynchronous);
 PollTCPDriver(theTCPPtr);
 if (errno_MacSocket == noErr)
 {
 return (theSockPtr->number);
 }
 else
 {
 RemoveSocketStruct(theSockPtr->number);
 return (-1);
 };
}
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Whitethorn Games combines two completely...
If you have ever gone fishing then you know that it is a lesson in patience, sitting around waiting for a bite that may never come. Well, that's because you have been doing it wrong, since as Whitehorn Games now demonstrates in new release Skate... | Read more »
Call of Duty Warzone is a Waiting Simula...
It's always fun when a splashy multiplayer game comes to mobile because they are few and far between, so I was excited to see the notification about Call of Duty: Warzone Mobile (finally) launching last week and wanted to try it out. As someone who... | Read more »
Albion Online introduces some massive ne...
Sandbox Interactive has announced an upcoming update to its flagship MMORPG Albion Online, containing massive updates to its existing guild Vs guild systems. Someone clearly rewatched the Helms Deep battle in Lord of the Rings and spent the next... | Read more »
Chucklefish announces launch date of the...
Chucklefish, the indie London-based team we probably all know from developing Terraria or their stint publishing Stardew Valley, has revealed the mobile release date for roguelike deck-builder Wildfrost. Developed by Gaziter and Deadpan Games, the... | Read more »
Netmarble opens pre-registration for act...
It has been close to three years since Netmarble announced they would be adapting the smash series Solo Leveling into a video game, and at last, they have announced the opening of pre-orders for Solo Leveling: Arise. [Read more] | Read more »
PUBG Mobile celebrates sixth anniversary...
For the past six years, PUBG Mobile has been one of the most popular shooters you can play in the palm of your hand, and Krafton is celebrating this milestone and many years of ups by teaming up with hit music man JVKE to create a special song for... | Read more »
ASTRA: Knights of Veda refuse to pump th...
In perhaps the most recent example of being incredibly eager, ASTRA: Knights of Veda has dropped its second collaboration with South Korean boyband Seventeen, named so as it consists of exactly thirteen members and a video collaboration with Lee... | Read more »
Collect all your cats and caterpillars a...
If you are growing tired of trying to build a town with your phone by using it as a tiny, ineffectual shover then fear no longer, as Independent Arts Software has announced the upcoming release of Construction Simulator 4, from the critically... | Read more »
Backbone complete its lineup of 2nd Gene...
With all the ports of big AAA games that have been coming to mobile, it is becoming more convenient than ever to own a good controller, and to help with this Backbone has announced the completion of their 2nd generation product lineup with their... | Read more »
Zenless Zone Zero opens entries for its...
miHoYo, aka HoYoverse, has become such a big name in mobile gaming that it's hard to believe that arguably their flagship title, Genshin Impact, is only three and a half years old. Now, they continue the road to the next title in their world, with... | Read more »

Price Scanner via MacPrices.net

B&H has Apple’s 13-inch M2 MacBook Airs o...
B&H Photo has 13″ MacBook Airs with M2 CPUs and 256GB of storage in stock and on sale for up to $150 off Apple’s new MSRP, starting at only $849. Free 1-2 day delivery is available to most US... Read more
M2 Mac minis on sale for $100-$200 off MSRP,...
B&H Photo has Apple’s M2-powered Mac minis back in stock and on sale today for $100-$200 off MSRP. Free 1-2 day shipping is available for most US addresses: – Mac mini M2/256GB SSD: $499, save $... Read more
Mac Studios with M2 Max and M2 Ultra CPUs on...
B&H Photo has standard-configuration Mac Studios with Apple’s M2 Max & Ultra CPUs in stock today and on Easter sale for $200 off MSRP. Their prices are the lowest available for these models... Read more
Deal Alert! B&H Photo has Apple’s 14-inch...
B&H Photo has new Gray and Black 14″ M3, M3 Pro, and M3 Max MacBook Pros on sale for $200-$300 off MSRP, starting at only $1399. B&H offers free 1-2 day delivery to most US addresses: – 14″ 8... Read more
Department Of Justice Sets Sights On Apple In...
NEWS – The ball has finally dropped on the big Apple. The ball (metaphorically speaking) — an antitrust lawsuit filed in the U.S. on March 21 by the Department of Justice (DOJ) — came down following... Read more
New 13-inch M3 MacBook Air on sale for $999,...
Amazon has Apple’s new 13″ M3 MacBook Air on sale for $100 off MSRP for the first time, now just $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD/Space Gray): $999 $100 off MSRP... Read more
Amazon has Apple’s 9th-generation WiFi iPads...
Amazon has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80-$100 off MSRP, starting only $249. Their prices are the lowest available for new iPads anywhere: – 10″ 64GB WiFi iPad (Space Gray or... Read more
Discounted 14-inch M3 MacBook Pros with 16GB...
Apple retailer Expercom has 14″ MacBook Pros with M3 CPUs and 16GB of standard memory discounted by up to $120 off Apple’s MSRP: – 14″ M3 MacBook Pro (16GB RAM/256GB SSD): $1691.06 $108 off MSRP – 14... Read more
Clearance 15-inch M2 MacBook Airs on sale for...
B&H Photo has Apple’s 15″ MacBook Airs with M2 CPUs (8GB RAM/256GB SSD) in stock today and on clearance sale for $999 in all four colors. Free 1-2 delivery is available to most US addresses.... Read more
Clearance 13-inch M1 MacBook Airs drop to onl...
B&H has Apple’s base 13″ M1 MacBook Air (Space Gray, Silver, & Gold) in stock and on clearance sale today for $300 off MSRP, only $699. Free 1-2 day shipping is available to most addresses in... Read more

Jobs Board

Medical Assistant - Surgical Oncology- *Apple...
Medical Assistant - Surgical Oncology- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Business Analyst | *Apple* Pay - Banco Popu...
Business Analyst | Apple PayApply now " Apply now + Apply Now + Start applying with LinkedIn Start + Please wait Date:Mar 19, 2024 Location: San Juan-Cupey, PR Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.