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.