This is GopherClient.m in view mode; [Download] [Up]
#import "GopherClient.h" #import "StuartSpeaker.h" #import "GopherDispatcher.h" #import <appkit/Listener.h> #import <appkit/Speaker.h> #import "PhSpeaker.h" #include <stdio.h> // Declared in GopherDispatcher.m extern char defaultHost[256]; extern int defaultPort; static int isIndexedText = NO; static char lastIndexWord[256]; @implementation GopherClient /////////////////////////////////////////////////// // - (char *)currentCellName { id CurrentCell; CurrentCell = [[MainScroller matrixInColumn:[MainScroller selectedColumn]] selectedCell]; return (char *)[CurrentCell stringValue]; } ////////////////////////////////////////////////// // Compute a new location for a window. static void newLocation(NXPoint *p) { static count = 0; p->x += (20.0 * count); p->y -= (25.0 * count); count = (count > 10)? 0 : count+1; } ////////////////////////////////////////////////// // Automatically called by the system when an instance is created. - init { NXRect theFrame; [super init]; strncpy(currenthostname, defaultHost, 250); strcpy(currentfilename, ""); currentport = defaultPort; [NXApp loadNibSection:"GopherClient.nib" owner:self]; [MainScroller setCellClass:[GopherBrowserCell class]]; [MainScroller setDelegate:self]; [MainScroller useScrollButtons:YES]; [MainScroller loadColumnZero]; //[myWindow setDelegate:self]; [myWindow getFrame:&theFrame]; newLocation(&theFrame.origin); [myWindow moveTo:theFrame.origin.x :theFrame.origin.y]; [myWindow makeKeyAndOrderFront:nil]; [[MainText docView] setAutodisplay:YES]; theText = (char *)malloc(MAXTEXTLENGTH); return self; } ///////////////////////////////////////////////// // Close the window. - close { [myWindow performClose:self]; [self clean]; [myDispatcher oldFrontWindow:self]; // Theoretically unnecessary return self; } ///////////////////////////////////////////////// // Clean things up. - clean { [ItemInfoPanel performClose:self]; return self; } ///////////////////////////////////////////////// // Target method for the browser. - cellClicked:sender { id CurrentCell; int remotePort; ThreadArgument *theArg; extern int defaultApp; char *remotePath; // Stuff borrowed from Rex Pruess's Remotes program id stuart; int code, ind; char *command; char *comArgs[10]; int pid; char portStr[16]; char loginStr[256]; char errMsg[256]; extern char **environ; /* User environment */ #ifdef DEBUG char **tempPtr; #endif // Stuff for Rex Pruess's Ph CSO Nameserver client int flag; int msgDelivered; char server[128]; char site[128]; CurrentCell = [[sender matrixInColumn:[sender selectedColumn]] selectedCell]; [ItemInfoHostName setStringValue:[CurrentCell remoteHost]]; [ItemInfoFileName setStringValue:[CurrentCell remoteName]]; [ItemInfoPortNumber setIntValue:[CurrentCell remotePort]]; switch ([CurrentCell cellType]) { case '0' : // Document : retrieve and display strncpy(currenthostname, [CurrentCell remoteHost], 255); strcpy(currentfilename, [CurrentCell remoteName]); currentport = [CurrentCell remotePort]; [ItemInfoType setStringValue:"ASCII text"]; [self displayDocument]; break; case '1' : // Directory : do nothing, taken care of in // browser:fillMatrix:inColumn: isIndexedText = NO; [ItemInfoType setStringValue:"Directory"]; [[MainText docView] setText:""]; break; case '2': // CSO Nameserver if (![self createPort:self]) break; strncpy (server, [CurrentCell remoteHost], 128); strncpy (site, [CurrentCell stringValue], 128); msgDelivered = [mySpeaker showServer:server site:site ok:&flag]; if (msgDelivered != 0) fprintf (stderr, "Sorry, showServer message not delivered.\n"); else if (!flag) { fprintf (stderr, "\n*** showServer call failed. ***\n"); fprintf (stderr, "*** Should only happen if 'server' is empty or too long.\n"); } free (mySpeaker); [ItemInfoType setStringValue:"Phone Server"]; break; case '7' : // Index search [ItemInfoType setStringValue:"Index"]; isIndexedText = YES; break; case 'R': case 'T': case '8': // Telnet session. Revised by David Lacey [ItemInfoType setStringValue:"Telnet Session"]; isIndexedText = NO; [[MainText docView] setText:""]; if ([CurrentCell remotePort] == 0) // Seems common, so fix it remotePort = 23; else remotePort = [CurrentCell remotePort]; // If there is a login ID specified and we're telnetting, alert the user if (([CurrentCell cellType] == '8') && *(remotePath = [CurrentCell remoteName])) { sprintf (errMsg, "Login to remote system as '%s'.", remotePath); NXRunAlertPanel (NULL, errMsg, NULL, NULL, NULL, NULL); } if (defaultApp == 0) { // Stewart or Terminal? ind = 0; command = "/NextApps/Terminal"; comArgs[ind++] = "Terminal"; comArgs[ind++] = "-Shell"; switch ([CurrentCell cellType]) { case 'R': // RLOGIN strcpy (loginStr, "/usr/ucb/rlogin "); if (*[CurrentCell remoteName] != '\0') { strcat (loginStr, " -l "); strcat (loginStr, [CurrentCell remoteName]); } strcat (loginStr, " "); strcat (loginStr, [CurrentCell remoteHost]); break; case '8': // TELNET: strcpy (loginStr, "/usr/ucb/telnet "); strcat (loginStr, [CurrentCell remoteHost]); if (remotePort != 23) { sprintf (portStr, " %d", remotePort); strcat (loginStr, portStr); } break; case 'T': // TN3270: strcpy (loginStr, "/usr/ucb/tn3270 "); strcat (loginStr, [CurrentCell remoteHost]); break; } comArgs[ind++] = loginStr; comArgs[ind++] = NULL; #ifdef DEBUG /* printf ("%s: %s",[NXApp appName], command); tempPtr = comArgs; while (*++tempPtr != NULL) printf (" %s", *tempPtr); printf ("\n"); } */ #endif pid = vfork (); if (pid == 0) { code = execve (command, comArgs, environ); perror ([NXApp appName]); fprintf (stderr, "%s: Execve error code=%d.\n",[NXApp appName], code); _exit (0); } } else { // Send a message to Stuart stuart = [StuartSpeaker new]; switch ([CurrentCell cellType]) { case 'R': // RLOGIN strcpy (loginStr, "rlogin "); if (*[CurrentCell remoteName] != '\0') { strcat (loginStr, " -l "); strcat (loginStr, [CurrentCell remoteName]); } strcat (loginStr, " "); strcat (loginStr, [CurrentCell remoteHost]); break; case '8': // TELNET: strcpy (loginStr, "telnet "); strcat (loginStr, [CurrentCell remoteHost]); if (remotePort != 23) { sprintf (portStr, " %d", remotePort); strcat (loginStr, portStr); } break; case 'T': // TN3270: strcpy (loginStr, "tn3270 "); strcat (loginStr, [CurrentCell remoteHost]); break; } [stuart default:"Shell" as:loginStr]; [stuart default:"WriteStuartrc" as:"NO"]; [stuart setActivate:TRUE]; if ([stuart stuartConnectAndNew]) { strcpy (errMsg, "There was a problem communicating with the Stuart "); strcat (errMsg, "application. (Speaker port unavailable.) This may "); strcat (errMsg, "have occurred because Stuart is not installed on "); strcat (errMsg, "your system or because Stuart was slow in starting."); NXRunAlertPanel (NULL, errMsg, NULL, NULL, NULL, NULL); } [stuart free]; } break; case 's' : // Sound isIndexedText = NO; [ItemInfoType setStringValue:"Sound"]; [[MainText docView] setText:""]; // Check that we're the only thread. We do it here because // we can't have NXRunAlertPanel in a thread (why not ?) if (mutex_try_lock(soundIsPlaying) == 0) { NXRunAlertPanel("Gopher error", "Only one sound at a time !", "OK", NULL, NULL); break; } mutex_unlock(soundIsPlaying); soundShouldStopFlag = 0; // The argument is malloc'ed because we may have several threads. theArg = malloc(sizeof(ThreadArgument)); strncpy(theArg->hostname, [CurrentCell remoteHost], 255); theArg->portnumber = [CurrentCell remotePort]; strncpy(theArg->filename, [CurrentCell remoteName], 1023); cthread_detach(cthread_fork((cthread_fn_t)PlaySound, (any_t)theArg)); break; default : break; } return self; } /////////////////////////////////////////////////////////////////// // Highlight next occurance of search string, if it's indexed text. // - highlightNextWord { int findIndex, spaceIndex; int minIndex = -1; char *word, *cp = NULL, *textBuffer; char wordStowage[256]; int done = 0; NXSelPt begin, end; strcpy(wordStowage, lastIndexWord); word = wordStowage; [[MainText docView] getSel:&begin :&end]; textBuffer = theText + end.cp; while (!done) { cp = strchr(word, ' '); if (cp == NULL) done = 1; else *cp='\0'; #ifdef DEBUG fprintf(stderr, "Word is: %s\n", word); #endif // Don't highlight stupid words if ((strcasecmp(word, "and") != 0) && (strcasecmp(word, "or") != 0) && (strcasecmp(word, "not") != 0) && (strcasecmp(word, "the") != 0)) { findIndex = (int)strcasestr(textBuffer, word); if (findIndex == 0) ; // Not found else { if (minIndex == -1) minIndex = findIndex; else if (minIndex > findIndex) minIndex = findIndex; } } word = cp +1; // Move on to the next word. } if (minIndex != -1) { spaceIndex = (int)index((char *)minIndex, ' '); if (spaceIndex > (int)index((char*)minIndex, '\r')) spaceIndex = (int)index((char*)minIndex, '\r'); findIndex = (int)minIndex - (int)theText; spaceIndex = (int)spaceIndex - (int)theText; // Highlight the found text, make it visible. [[MainText docView] setSel:findIndex :spaceIndex]; [[MainText docView] scrollSelToVisible]; } else NXBeep(); return self; } //////////////////////////////////////////////////////////////// // Connect to currenthostname on port currentport, retrieve // document currentfilename and display it. - displayDocument { int iLength = 1, i; int currentsocket; currentsocket = create_socket(currenthostname, currentport); if (currentsocket < 0) { switch (currentsocket) { case -1 : NXRunAlertPanel("Gopher error", "Unknown host", "OK", NULL, NULL); return self; break; case -2 : NXRunAlertPanel("Gopher error", "Socket call failed", "OK", NULL, NULL); return self; break; case -3 : NXRunAlertPanel("Gopher error", "Connect call failed", "OK", NULL, NULL); return self; break; default : break; } } writestring(currentsocket, currentfilename); writestring(currentsocket, "\r\n"); for (i=0; (i < MAXTEXTLENGTH) && iLength; i += iLength) iLength = read(currentsocket, theText + i, 10000); theText[i - 3] = '\0'; // -3 to get rid of final period [[MainText docView] setText:theText]; close(currentsocket); [[MainText docView] setSel:0 :0]; [[MainText docView] scrollSelToVisible]; if (isIndexedText) [self highlightNextWord]; /* findIndex = (int)strstr(theText, lastIndexWord); if (findIndex == 0) ; // Not found else { spaceIndex = (int)index((char *)findIndex, ' '); findIndex = (int)findIndex - (int)theText; spaceIndex = (int)spaceIndex - (int)theText; [[MainText docView] setSel:findIndex :spaceIndex]; [[MainText docView] scrollSelToVisible]; } } */ return self; } ///////////////////////////////////////////////// // Find our dispatcher - setDispatcher:sender { myDispatcher = sender; return self; } ///////////////////////////////////////////////// // Print the contents of the text window. - print { [[MainText docView] printPSCode:self]; return self; } ///////////////////////////////////////////////// // Saves the contents of the text window. - saveAs:(char *)filename { NXStream *tmpstream; tmpstream = NXOpenMemory(NULL, 0, NX_WRITEONLY); [[MainText docView] writeText:tmpstream]; NXSaveToFile(tmpstream, filename); NXCloseMemory(tmpstream, NX_FREEBUFFER); return self; } ///////////////////////////////////////////////// // - searchIndex:sender { [myIndexPanel orderOut:self]; [NXApp stopModal:1]; return self; } ///////////////////////////////////////////////// // - cancelIndex:sender { [myIndexPanel orderOut:self]; [NXApp stopModal:2]; return self; } ///////////////////////////////////////////////// // - getIndexString:(char *)theString maxLength:(int)maxlength { [myIndexPanel makeKeyAndOrderFront:self]; [IndexTextItem selectText:self]; if ([NXApp runModalFor:myIndexPanel] == 1) // User clicked OK strncpy(theString, [IndexTextItem stringValue], maxlength); else strcpy(theString, ""); // User clicked Cancel return self; } ///////////////////////////////////////////////// // - showItemInfo { [ItemInfoPanel makeKeyAndOrderFront:self]; return self; } ///////////////////////////////////////////////// // Delegate method for the browser - (int)browser:sender fillMatrix:matrix inColumn:(int)column { id tmpcell; int iLength; char inputline[512]; char *remotename, *remotehost, *remoteport; int currentsocket; id CurrentCell; CurrentCell = [[sender matrixInColumn:[sender selectedColumn]] selectedCell]; switch ([CurrentCell cellType]) { case '0' : // Document : should never happen break; case '1' : // Directory : the browser will // automatically update strncpy(currenthostname, [CurrentCell remoteHost], 250); strcpy(currentfilename, [CurrentCell remoteName]); currentport = [CurrentCell remotePort]; break; case '7' : // Index search strncpy(currenthostname, [CurrentCell remoteHost], 250); [self getIndexString:(char *)&lastIndexWord maxLength:250]; if (strlen(lastIndexWord) == 0) // User clicked Cancel return 0; strcpy(currentfilename, [CurrentCell remoteName]); strcat(currentfilename, "\t"); strcat(currentfilename, lastIndexWord); currentport = [CurrentCell remotePort]; break; case '8' : // Telnet session : should never happen break; case 's' : // Sound : should never happen break; default : break; } currentsocket = create_socket(currenthostname, currentport); if (currentsocket < 0) switch (currentsocket) { case -1 : NXRunAlertPanel("Gopher error", "Unknown host", "OK", NULL, NULL); return(0); break; case -2 : NXRunAlertPanel("Gopher error", "Socket call failed", "OK", NULL, NULL); return(0); break; case -3 : NXRunAlertPanel("Gopher error", "Connect call failed", "OK", NULL, NULL); return(0); break; default : break; } writestring(currentsocket, currentfilename); writestring(currentsocket, "\r\n"); for(;;) { iLength = readline(currentsocket, inputline, 512); if (iLength == -1) break; if (iLength == 0) break; ZapCRLF(inputline); if ((inputline[0] == '0') || (inputline[0] == '1') || (inputline[0] == '7') || (inputline[0] == '8') || (inputline[0] == 'R') || (inputline[0] == 'T') || (inputline[0] == 's') || (inputline[0] == '2')) { if ((inputline[0] == 's') && (shouldUseSound == 0)) continue; [matrix addRow]; tmpcell = [matrix cellAt:([matrix cellCount] -1) :0]; remotename = index(inputline, '\t') + 1; *(remotename-1) = '\0'; remotehost = index(remotename, '\t') + 1; *(remotehost-1) = '\0'; remoteport = index(remotehost, '\t') + 1; *(remoteport-1) = '\0'; [tmpcell setRemoteName:remotename]; [tmpcell setRemoteHost:remotehost]; [tmpcell setRemotePort:atoi(remoteport)]; [tmpcell setStringValue:(inputline+1)]; [tmpcell setLoaded:YES]; if (inputline[0] == '0') { // Document [tmpcell setLeaf:YES]; [tmpcell setCellType:'0']; } else if (inputline[0] == '1') { // Directory [tmpcell setLeaf:NO]; [tmpcell setCellType:'1']; } else if (inputline[0] == '2') { // CSO Nameserver [tmpcell setLeaf:YES]; [tmpcell setCellType:'2']; } else if (inputline[0] == '7') { // Index [tmpcell setLeaf:NO]; [tmpcell setCellType:'7']; } else if (inputline[0] == '8') { // Telnet [tmpcell setLeaf:YES]; [tmpcell setCellType:'8']; } else if (inputline[0] == 'R') { // Rlogin [tmpcell setLeaf:YES]; [tmpcell setCellType:'R']; } else if (inputline[0] == 'T') { // TN3270 [tmpcell setLeaf:YES]; [tmpcell setCellType:'T']; } else if (inputline[0] == 's') { // Sound [tmpcell setLeaf:YES]; [tmpcell setCellType:'s']; } } if ((inputline[0] == '.') && (inputline[1] == '\0')) break; } close(currentsocket); return([matrix cellCount]); } //////////////////////////////////////////////////////////////////////////// // Delegate methods for the window ///////////////////////////////////////////////// // Called when our window becomes the key window. - windowDidBecomeKey:sender { [myDispatcher newFrontWindow:self]; return self; } ///////////////////////////////////////////////// // Called when our window has stopped being the key window. - windowDidResignKey:sender { [myDispatcher oldFrontWindow:self]; return self; } ///////////////////////////////////////////////// // Called when our window is going to close. - windowWillClose:sender { [self clean]; [myDispatcher oldFrontWindow:self]; return self; } // Stuff from Rex Pruess's Ph CSO Nameserver App /*--------------------------------------------------------------------------- This internal support method sets the Speaker port and creates the PhSpeaker object. -----------------------------------------------------------------------------*/ - (BOOL) createPort:sender { port_t thePort; thePort = NXPortFromName ("Ph", NULL); if (thePort == PORT_NULL) { fprintf (stderr, "\n*** PhSpeaker port is NULL.\n"); fprintf (stderr, "*** Perhaps Ph is not installed in a standard directory.\n"); return NO; } mySpeaker = [[PhSpeaker alloc] init]; [mySpeaker setSendPort:thePort]; return YES; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.