This is Controller.m in view mode; [Download] [Up]
/* "Whois" mini example
* Eric P. Scott, San Francisco State University, October 1994
* (thanks to Arman Khalili for many good suggestions)
*/
/* Ideas for future enhancement:
* multiple sessions (split into Controller and Session classes)
* nonblocking gethostbyname(), connect()
*/
/* Generated by Interface Builder */
#import "Controller.h"
#import "SelectableText.h"
#ifdef NX_COMPILER_RELEASE_3_0
#include <bsd/libc.h>
#include <bsd/netdb.h>
#include <bsd/errno.h>
#include <bsd/sys/types.h>
#include <bsd/sys/socket.h>
#include <bsd/netinet/in.h>
#include <bsd/arpa/inet.h>
#else
#include <libc.h>
#include <netdb.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#import <appkit/Application.h>
#import <appkit/ButtonCell.h>
#import <appkit/Matrix.h>
#import <appkit/ScrollView.h>
#import <appkit/Text.h>
#import <appkit/TextFieldCell.h>
#import <appkit/TextField.h>
#import <appkit/View.h>
#import <appkit/Window.h>
#import <dpsclient/dpsclient.h>
@implementation Controller
#ifndef NX_COMPILER_RELEASE_3_0
- setEditMenu:anObject
{
editMenu = [anObject target]; // stupid IB
return self;
}
- setOut:anObject
{
out = [anObject docView]; // stupid IB
return self;
}
#endif
// these tables perform a "best effort" conversion between
// "ISO Latin 1" and "NEXTSTEP Encoding"
//
// 0..127 are not changed
// in the upper half, 32 means that no equivalent exists
const unsigned char iso1tonse[]={
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127,
32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32,
245, 193, 194, 195, 196, 197, 198, 199,
200, 32, 202, 203, 32, 205, 206, 207,
128, 161, 162, 163, 168, 165, 181, 167,
200, 160, 227, 171, 190, 177, 176, 197,
202, 209, 201, 204, 194, 157, 182, 180,
203, 192, 235, 187, 210, 211, 212, 191,
129, 130, 131, 132, 133, 134, 225, 135,
136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 158,
233, 151, 152, 153, 154, 155, 156, 251,
213, 214, 215, 216, 217, 218, 241, 219,
220, 221, 222, 223, 224, 226, 228, 229,
230, 231, 236, 237, 238, 239, 240, 159,
249, 242, 243, 244, 246, 247, 252, 253
};
const unsigned char nsetoiso1[]={
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127,
160, 192, 193, 194, 195, 196, 197, 199,
200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 217,
218, 219, 220, 221, 222, 181, 215, 247,
169, 161, 162, 163, 32, 165, 32, 167,
164, 32, 32, 171, 32, 32, 32, 32,
174, 173, 32, 32, 183, 166, 182, 32,
32, 32, 32, 187, 32, 32, 172, 191,
185, 145, 180, 147, 148, 175, 150, 151,
168, 178, 176, 184, 179, 157, 158, 159,
32, 177, 188, 189, 190, 224, 225, 226,
227, 228, 229, 231, 232, 233, 234, 235,
236, 198, 237, 170, 238, 239, 240, 241,
32, 216, 32, 186, 242, 243, 244, 245,
246, 230, 249, 250, 251, 144, 252, 253,
32, 248, 32, 223, 254, 255, 32, 32
};
static unsigned short stomp(unsigned short theChar, int flags,
unsigned short charSet)
{
if ((flags&NX_COMMANDMASK)==0) switch ([[[[NXApp keyWindow]
firstResponder] delegate] tag]) { // TextField's tag
default:
if (theChar==' ') return NX_TAB;
if (theChar>127) return NX_ILLEGAL;
break;
case 2:
if (theChar>127&&theChar<=255&&nsetoiso1[theChar]==32)
return NX_ILLEGAL;
break;
}
return NXFieldFilter(theChar, flags, charSet);
}
- appDidInit:sender
{
[query setNextText:out];
abortView=[abortButton superview];
[abortView getBounds:&oldBounds];
[abortButton removeFromSuperview];
fd= -1;
[query selectText:self];
[window makeKeyAndOrderFront:self];
[[window getFieldEditor:YES for:query] setCharFilter:stomp];
return self;
}
- windowWillClose:sender
{
[NXApp perform:@selector(terminate:) with:nil afterDelay:1
cancelPrevious:NO];
return self;
}
#ifndef NX_COMPILER_RELEASE_3_0
- windowWillResize:sender toSize:(NXSize *)frameSize
{
if (frameSize->width<590.0) frameSize->width=590.0;
if (frameSize->height<228.0) frameSize->height=228.0;
return self;
}
#endif
- windowDidResize:sender
{
if (![abortButton superview]) {
[sender disableDisplay];
[abortView addSubview:abortButton];
[abortButton superviewSizeChanged:&oldBounds.size];
[abortButton removeFromSuperview];
[sender reenableDisplay];
}
[abortView getBounds:&oldBounds];
return self;
}
- textDidChange:textObject
{
switch ([[textObject delegate] tag]) {
case 0:
if ([hostMatrix selectedRow]!=2) [hostMatrix selectCellAt:2:0];
break;
case 1:
if ([portMatrix selectedRow]!=2) [portMatrix selectCellAt:2:0];
break;
case 2:
if (state==0&&[out textLength]>0) [out setText:""];
break;
default:
break;
}
return self;
}
- quiet:sender
{
id f;
switch ([sender tag]) {
case 0:
f=[window firstResponder];
if (f&&[f delegate]==otherHost) [query selectText:self];
break;
case 1:
f=[window firstResponder];
if (f&&[f delegate]==otherPort) [query selectText:self];
break;
default:
break;
}
return self;
}
/* convert string to TCP port number, return in host order */
static unsigned short portfrom(const char *n, Text *t)
{
register struct servent *se;
int port, pos;
char buf[256];
pos=0;
if (sscanf(n, "%d%n", &port, &pos)==1&&(pos==0||n[pos]=='\0')&&
(unsigned)port<=65535) {
if (port==0) [t setText:"bad port\n"];
return port;
}
if (!(se=getservbyname((char *)n, "tcp"))) {
(void)sprintf(buf, "%s/tcp: unknown service\n", n);
[t setText:buf];
return 0;
}
return(ntohs(se->s_port));
}
static inline void append(Text *out, const char *buf)
{
register int e;
if ((e=[out textLength])<=0) [out setText:buf];
else [[out setSel:e:e] replaceSel:buf];
[out scrollSelToVisible];
}
// state
// 0 nothing open
// 1 awaiting first data
// 2 awaiting more data
// 3 last char was CR
void suck(int s, void *userData)
{
Controller *self;
register unsigned char *p, *q, *r;
register int c;
NXRect fr;
unsigned char buf[8192], buf2[8194];
self=(Controller *)userData;
/* read from socket, output anything we receive */
/* convert NVT end-of-line convention to UNIX newline */
c=read(s, buf, sizeof buf);
if (c<=0) {
if (self->state==3) {
buf2[0]='\r', buf2[1]='\0';
append(self->out, (const char *)buf2);
}
croak:
DPSRemoveFD(s);
(void)close(s);
if (self->state==1) [self->query selectText:self];
self->state=0;
[self->abortButton getFrame:&fr];
[self->abortButton removeFromSuperview];
[self->abortView displayFromOpaqueAncestor:&fr:1:NO];
return;
}
if (self->state==1) {
if (buf[0]==255&&(c==1||buf[1]>=236)) {
[self->out setTextGray:NX_DKGRAY];
[self->out setText:"Try telnet instead"];
goto croak;
}
[self->out setText:""];
self->state=2;
}
p=buf;
q=buf2;
r=p+c;
while (p<r) {
c= *p++;
if (c=='\r') {
if (p>=r) {
self->state=3;
break;
}
c= *p++;
if (c!='\n') *q++='\r';
if (c=='\0') continue;
}
if (c!=0) *q++=iso1tonse[c];
}
*q='\0';
append(self->out, (const char *)buf2);
[self->query selectText:self];
}
/* try to establish session with given host; return on failure */
BOOL try(Controller *self, Text *t,
struct sockaddr_in *sin, char *buf, int len)
{
int s;
char ebuf[256];
[t setTextGray:NX_DKGRAY];
(void)sprintf(ebuf, "Trying [%s]... ", inet_ntoa(sin->sin_addr));
[t setText:ebuf];
if ((s=socket(PF_INET, SOCK_STREAM, 0))<0) {
[t setText:strerror(errno)];
return NO;
}
self->fd=s;
if (connect(s, (struct sockaddr *)sin, sizeof(*sin))<0) {
[t setText:strerror(errno)];
(void)close(s);
return NO;
}
append(self->out, "OK\n");
/* send one-line message to server */
if (write(s, buf, len)<0) {
[t setText:strerror(errno)];
(void)close(s);
return NO;
}
[t setTextGray:NX_BLACK];
self->state=1;
DPSAddFD(s, suck, (void *)self, NX_BASETHRESHOLD);
[self->abortButton setEnabled:YES];
return YES;
}
- doit:sender
{
register struct hostent *h;
register int i;
register const char *p;
register char *q;
struct sockaddr_in sin;
NXRect fr;
char buf[1024];
p=[query stringValue];
q=buf;
while (*p) {
if (q>=&buf[sizeof buf-2]) break;
if (*p=='\n') {
if (q>=&buf[sizeof buf-3]) break;
*q++='\r';
}
*q++=(char)(nsetoiso1[*(unsigned char *)p++]);
}
*q++='\r';
*q++='\n';
if (state!=0) {
if (write(fd, buf, q-buf)<0) {
[out setTextGray:NX_DKGRAY];
[out setText:strerror(errno)];
}
return self;
}
[abortButton setEnabled:NO];
[abortView addSubview:abortButton];
[abortButton display];
DPSFlush();
bzero((char *)&sin, sizeof sin);
p=[[hostMatrix selectedCell] title];
if (p&&*p) p++; // titles have leading space
else p=[otherHost stringValue];
sin.sin_addr.s_addr=inet_addr((char *)p); /* try x.x.x.x format */
if (sin.sin_addr.s_addr!=0xffffffff) {
sin.sin_family=AF_INET;
p=[[portMatrix selectedCell] title];
if (p&&*p) p++; // titles have leading space
else p=[otherPort stringValue];
if ((sin.sin_port=htons(portfrom(p, out)))!=0)
(void)try(self, out, &sin, buf, q-buf);
else [otherPort selectText:self];
}
else {
[out setTextGray:NX_DKGRAY];
[out setText:"Resolving...\n"];
DPSFlush();
if (!(h=gethostbyname((char *)p))) { /* name lookup */
switch (h_errno) {
case 1:
[out setText:"Unknown host\n"];
break;
case 2:
[out setText:"Host name lookup failure\n"];
break;
case 3:
[out setText:"Unknown server error\n"];
break;
case 4:
[out setText:"No address associated with name\n"];
break;
default:
[out setText:"gethostbyname() failed\n"];
break;
}
[otherHost selectText:self];
}
else {
sin.sin_family=h->h_addrtype;
p=[[portMatrix selectedCell] title];
if (p&&*p) p++; // titles have leading space
else p=[otherPort stringValue];
if ((sin.sin_port=htons(portfrom(p, out)))!=0) {
i=0; do { /* some hosts have multiple IP addresses */
bcopy(h->h_addr_list[i], (char *)&sin.sin_addr,
h->h_length);
if (try(self, out, &sin, buf, q-buf)) break;
} while (h->h_addr_list[++i]);
}
else [otherPort selectText:self];
}
}
if (state==0) {
[abortButton getFrame:&fr];
[abortButton removeFromSuperview];
[abortView displayFromOpaqueAncestor:&fr:1:NO];
}
return self;
}
- undoit:sender
{
NXRect fr;
if (!sender) {
[abortButton getFrame:&fr];
[abortButton removeFromSuperview];
[abortView displayFromOpaqueAncestor:&fr:1:NO];
}
else if (state>0) {
DPSRemoveFD(fd);
(void)close(fd);
state=0;
[self perform:@selector(undoit:) with:nil afterDelay:1
cancelPrevious:NO];
[query selectText:self];
}
return self;
}
- reflectEditable:(BOOL)flag
{
[editMenu disableFlushWindow];
if (flag) {
if (![editCut isEnabled]) [editCut setEnabled:YES];
if (![editPaste isEnabled]) [editPaste setEnabled:YES];
if (![editDelete isEnabled]) [editDelete setEnabled:YES];
}
else {
if ([editCut isEnabled]) [editCut setEnabled:NO];
if ([editPaste isEnabled]) [editPaste setEnabled:NO];
if ([editDelete isEnabled]) [editDelete setEnabled:NO];
}
[[editMenu reenableFlushWindow] flushWindowIfNeeded];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.