/* This file is term0.c. It's written in C. */ #include #include #include /* for strcpy, memset */ #include /* for signal */ #ifdef _MSC_VER #define FAMILY AF_INET #include /* for Sleep */ #include /* or Winsock2.h */ static SOCKET s = 0; /* the socket */ #else #define FAMILY PF_INET #include #include #include /* for inet_pton */ #include /* for inet_pton */ #include /* for recv */ #include /* for napms */ #include #include /* for fcntl */ #include /* for EWOULDBLOCK */ int errno; static int s = 0; /* the socket */ #endif #include "term0.h" static size_t xmax = 0; /* Save and restore previous SIGPIPE handler. */ static void (*handler)(int) = NULL; #define CHECK(x) (0 <= (x) && (x) < xmax) static char *p; /* points to array of xmax chars */ #define P(x) (p[x]) /* For reading the initial xmax, and then the keystrokes from the server. Because of the O_NONBLOCK and FIONBIO in term0_construct, the recv will return -1 immediately instead of waiting if the user hasn't pressed a key. */ static unsigned char get_char(); static unsigned char get_char() { unsigned char c; if (recv(s, #ifdef _MSC_VER /* 2nd arg of recv is (void *) in Unix, (char *) in Microsoft */ (char *) #endif &c, 1, 0) == 1) { return c; } #ifdef _MSC_VER if (WSAGetLastError() == WSAEWOULDBLOCK) { return '\0'; /* No key was pressed. */ } fprintf(stderr, "Error after recv == %d\n", WSAGetLastError()); #else if (errno == EWOULDBLOCK) { return '\0'; /* No key was pressed. */ } perror("couldn't read a char"); #endif exit(1); } /* Return the key the user pressed. If no key was pressed, return '\0' immediately. */ char term0_key(void) { return get_char(); } void term0_construct(const char *ip, unsigned short port) { struct sockaddr_in term; size_t size; int i; /* for inet_pton */ char buffer[INET6_ADDRSTRLEN]; /* for inet_ntop */ #ifdef _MSC_VER unsigned long nonblock = ~(unsigned long)0; WSADATA data; #else int flags; /* returned by fcntl */ #endif #ifdef _MSC_VER /* http://msdn.microsoft.com/library/ default.asp?url=/library/en-us/winsock/wsapiref_2qr6.asp */ const int startup = WSAStartup(0x101, &data); if (startup != 0) { fprintf(stderr, "WSAStartup returned %d\n", startup); perror("unable to initialize WinSock library.\n"); exit(2); } if ((s = socket(FAMILY, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { fprintf(stderr, "Error after socket == %d\n", WSAGetLastError()); WSACleanup(); perror("couldn't create socket"); exit(3); } #else if ((s = socket(FAMILY, SOCK_STREAM, 0)) < 0) { perror("couldn't create socket"); exit(4); } #endif memset(&term, 0, sizeof term); /* fill with zeros */ term.sin_family = FAMILY; /* Internet address family */ term.sin_port = htons(port); #ifdef _MSC_VER term.sin_addr.S_un.S_addr = inet_addr(ip); if (term.sin_addr.S_un.S_addr == INADDR_NONE) { fprintf(stderr, "term0_construct: bad dotted string\n"); exit(5); } #else i = inet_pton(FAMILY, ip, &term.sin_addr); if (i == 0) { fprintf(stderr, "term0_construct: bad dotted string\n"); exit(6); } else if (i != 1) { perror("term0_construct"); exit(7); } #endif #ifdef _MSC_VER strcpy(buffer, inet_ntoa(&term.sin_addr)); #else if (inet_ntop(FAMILY, &term.sin_addr, buffer, sizeof buffer) == NULL) { perror("term0_construct"); exit(8); } #endif printf("Trying %s...\n", buffer); if (connect(s, (const struct sockaddr *)&term, (int)sizeof term) != 0) { fprintf(stderr, "errno == %d\n", errno); perror("Couldn't connect socket"); exit(9); } printf("Connected to %s.\n", buffer); xmax = get_char(); printf("term0 xmax == %u.\n", xmax); if ((p = #ifdef _MSC_VER /* needs cast to convert (void *) to (char *) */ (char *) #endif malloc(size = xmax)) == NULL) { fprintf(stderr, "term0_construct: couldn't allocate %d bytes.\n", size); exit(10); } for (i = 0; i < size; ++i) { p[i] = ' '; } /* All the subsequent input from the socket will be the user's keystrokes (if any). Make the input non-blocking. */ #ifdef _MSC_VER if (ioctlsocket(s, FIONBIO, &nonblock) == 0) { fprintf(stderr, "Error after ioctlsocket == %d\n", WSAGetLastError()); WSACleanup(); exit(11); } #else if ((flags = fcntl(s, F_GETFL, 0)) == -1) { perror ("fcntl(F_GETFL) failed"); exit(12); } if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) { perror ("fcntl(F_SETFL) failed"); exit(13); } #endif /* See note on send below. */ if ((handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) { perror("signal"); exit(14); } } void term0_destruct(void) /* Undo everything that term0_construct did. */ { if (signal(SIGPIPE, handler) == SIG_ERR) { perror("signal"); exit(15); } free(p); #ifdef _MSC_VER if (closesocket(s) != 0) { fprintf(stderr, "Error after closesocket == %d\n", WSAGetLastError()); WSACleanup(); exit(16); } #else if (shutdown(s, SHUT_RDWR) != 0) { perror("Can't shutdown the socket"); exit(17); } #endif printf("Connection closed.\n"); } unsigned term0_xmax(void) {return xmax;} char term0_get(unsigned x) { if (!CHECK(x)) { fprintf(stderr, "argument %u, %u, %u to term0_get " "out of range\n", x); exit(18); } return P(x); } void term0_put(unsigned x, char c) { char buffer[2]; if (!CHECK(x)) { fprintf(stderr, "argument %u, '%c' to term0_put " "out of range\n", x, c); exit(19); } P(x) = c; buffer[0] = x; buffer[1] = c; /* If we call send after the server has terminated, the operating system will send us the the SIGPIPE signal. We must ignore this signal if we want to survive until the perror. */ if (send(s, buffer, sizeof buffer, 0) != sizeof buffer) { #ifdef _MSC_VER fprintf(stderr, "Error after send == %d\n", WSAGetLastError()); #endif perror("couldn't write to the server"); exit(20); } } void term0_beep(void) { term0_put(0, '\a'); } void term0_puts(unsigned x, const char *str) { if (!CHECK(x)) { fprintf(stderr, "argument %u, \"%s\" to term0_puts " "out of range\n", x, str); exit(21); } for (; *str != '\0'; ++str) { term0_put(x, *str); ++x; x %= xmax; } } /* Wait a fraction of a second. 1000 milliseconds == 1 second. */ void term0_wait(int milliseconds) { #ifdef _MSC_VER Sleep(milliseconds); #else napms(milliseconds); #endif }