This is ExecMonitor.m in view mode; [Download] [Up]
// ------------------------------------------------------------------------------------- // ExecMonitor.m // (Indent:4, Tabs:4) // ------------------------------------------------------------------------------------- // Copyright 1996 Persistent Technologies, Inc. - all rights reserved // ------------------------------------------------------------------------------------- // This source code comes with no warranty of any kind, and the user assumes all // responsibility for its use. // ------------------------------------------------------------------------------------- #import <libc.h> #import <stdlib.h> #import <c.h> #import <errno.h> #import <ctype.h> #import <math.h> #import <sys/param.h> #import <sys/types.h> #import <sys/time.h> #import <sys/wait.h> #import <sys/resource.h> #import <dpsclient/dpsNeXT.h> #import <appkit/Listener.h> #import <appkit/appkit.h> #import "PPPMonitor.h" #import "ExecScrollText.h" #import "ExecMonitor.h" #define CHKPPPSOCK #ifdef CHKPPPSOCK #import <sys/socket.h> #import <sys/ioctl.h> #import <netdb.h> #import <net/route.h> #import <net/if.h> #endif // ------------------------------------------------------------------------------------- // local vars static id globalMonitor = (id)nil; // ExecMonitor instance static int _testMode_ = 0; // test mode flag // ------------------------------------------------------------------------------------- // ppp 'system' commands #define SYS_PPP_ISLOADED "/usr/etc/kl_util -s | /bin/grep ' ppp Loaded' " #define SYS_PPP_ISUP "/usr/etc/ifconfig ppp0 | /bin/grep RUNNING " #define SYS_PPPD_RUNNING "ps -ax | egrep '(/pppd )' | egrep -v 'egrep' " // ------------------------------------------------------------------------------------- // play sounds #define PLAY(S) { if (S) [[Sound findSoundFor:(S)] play]; } // ------------------------------------------------------------------------------------- // network applications (default when "MiniShelf" default-db is not available) #define MAX_SHELFSIZE 10 static const char *netAppList = (char*)0; static const char *netApps[MAX_SHELFSIZE + 1] = { (char*)0 }; static int netAppCount = 0; // ------------------------------------------------------------------------------------- // misc defines #define LCURL '{' #define RCURL '}' #define LPREN '(' #define RPREN ')' #define SYSTEM(C) [ExecRunCommand system:(C)] // ------------------------------------------------------------------------------------- // Mini-Shelf icon cell @interface TileButtonCell : ButtonCell { BOOL _noDots; } - (void)setDrawDots:(BOOL)flag; @end /* highlight rectangle */ static void _hiliteRect(const NXRect *r) { PSgsave(); PSsetalpha(0.5); PSsetgray(NX_WHITE); PScompositerect(r->origin.x,r->origin.y,r->size.width,r->size.height,NX_SOVER); PSgrestore(); } /* load special images used for minishelf */ static NXImage *miniShelfTile = nil, *shelfTile = nil; static NXImage *miniShelfDots = nil, *shelfDots = nil; static void _loadMiniShelfIcons(void) { { const char *tile = ICO_SHELFTILE; BOOL _tile = (tile && !strcasecmp(tile,"yes"))? YES : NO; if (!miniShelfTile && _tile) miniShelfTile = [NXImage findImageNamed:"tile"]; shelfTile = _tile? miniShelfTile : nil; } { const char *dots = ICO_SHELFDOTS; BOOL _dots = (dots && !strcasecmp(dots,"yes"))? YES : NO; if (!miniShelfDots && _dots) miniShelfDots = [NXImage findImageNamed:"dots"]; shelfDots = _dots? miniShelfDots : nil; } } // ------------------------------------------------------------------------------------- // private functions /* return interval timer value */ long _intervalTime(void) { struct timeval tp; gettimeofday(&tp, NULL); return tp.tv_sec; } /* return start of command line */ const char *_cmdLine(const char *cmd) { const char *n = cmd; while (*n && isspace(*n)) n++; if ((*n == LPREN) || (*n == LCURL)) { for (n++; *n && ((*n != RPREN) && (*n != RCURL)); n++); if (*n) n++; } while (*n && isspace(*n)) n++; return n; } /* return command name */ const char *_getCmdFile(char *buff, const char *cmd) { const char *n2, *n1 = _cmdLine(cmd); for (n2 = n1; *n2 && !isspace(*n2); n2++); // scan for space strncpy(buff, n1, n2 - n1); buff[n2 - n1] = 0; return buff; } /* return command name */ const char *_getCmdName(char *buff, const char *cmd) { const char *n = cmd; while (*n && isspace(*n)) n++; if ((*n == LPREN) || (*n == LCURL)) { char *b = buff; for (n++ ; *n && ((*n != RPREN) && (*n != RCURL)); n++) *b++ = *n; *b = 0; } else { char tmp[1024], *name = strrchr((char*)_getCmdFile(tmp,n),'/'); strcpy(buff, (name?name+1:tmp)); } return buff; } /* return true if specified file ends with ".app" */ BOOL _cmdIsApp(const char *cmd) { char tmp[1024], *n = (char*)_getCmdFile(tmp,cmd); const char *dot = strrchr(n, '.'); return (!dot || strcmp(dot,".app"))? NO : YES; } // ------------------------------------------------------------------------------------- @implementation ExecMonitor // ------------------------------------------------------------------------------------- /* show message */ - (void)message:(BOOL)isError:(const char*)fmt, ... { char msg[1024], *m = msg; va_list args; if (isError) { m += strlen(strcpy(m,"ERROR: ")); } va_start(args, fmt); vsprintf(m, fmt, args); va_end(args); if (isError) { if ([cmdMessage shouldDrawColor]) [cmdMessage setTextColor:NX_COLORRED]; else [cmdMessage setTextGray:NX_WHITE]; } else { if ([cmdMessage shouldDrawColor]) [cmdMessage setTextColor:NX_COLORDKGRAY]; else [cmdMessage setTextGray:NX_DKGRAY]; } [cmdMessage setStringValue:msg]; } // ------------------------------------------------------------------------------------- // polling/timer support /* target/action structure */ typedef struct { BOOL abort; id self; SEL meth; void *arg; DPSTimedEntry timerTag; } _timerHandlerData_t; /* periodic timer handler */ static void _timerHandler(DPSTimedEntry tag, double now, void *userData) { _timerHandlerData_t *t = (_timerHandlerData_t*)userData; if (t->abort || ![t->self perform:t->meth with:(id)(t->arg)]) { [t->self perform:@selector(_stopTimer:) with:(id)t]; } } /* start periodic timer */ - (_timerHandlerData_t*)_startTimer:(SEL)theSelector arg:(void*)arg freq:(float)freq { _timerHandlerData_t *t = (_timerHandlerData_t*)malloc(sizeof(_timerHandlerData_t)); t->abort = NO; t->self = self; t->meth = theSelector; t->arg = arg; t->timerTag = DPSAddTimedEntry((double)freq,_timerHandler,(void*)t,30); return t; } /* stop timer */ - (void)_stopTimer:(_timerHandlerData_t*)t { if (t) { DPSRemoveTimedEntry(t->timerTag); free((void*)t); } } /* abbreviated delayed perform */ - (void)_perform:(SEL)sel:(id)objId delay:(int)delay { [self perform:sel with:objId afterDelay:delay cancelPrevious:YES]; } // ------------------------------------------------------------------------------------- // fill mini-shelf /* fill mini-shelf */ - (void)_fillMiniShelf { int i; static ButtonCell *tileProto = nil; /* reset/load shelf tile icon */ _loadMiniShelfIcons(); /* clear matrix */ [appMatrix removeRowAt:0 andFree:YES]; /* parse/count shelf apps */ if (CMD_MINISHELF) { char *n = (char*)(netAppList=STRDUP(CMD_MINISHELF)); netAppCount = 0; while (*n) { char *b1, *b2; while ((*n == ';') || isspace(*n)) *(n++) = 0; if (!*n) break; netApps[netAppCount++] = b1 = n; while (*n && (*n != ';')) n++; b2 = n - 1; if (*n) *n++ = 0; while ((b2 > b1) && isspace(*b2)) *b2-- = 0; if (netAppCount > MAX_SHELFSIZE) break; } netApps[netAppCount] = (char*)nil; } /* set prototype */ if (!tileProto) { // ButtonCell *proto = [appMatrix prototype]; tileProto = [[TileButtonCell alloc] initIconCell:"NXAppTile"]; [tileProto setHighlightsBy:NX_NONE]; [appMatrix setPrototype:tileProto]; } /* create app buttons */ [appMatrix renewRows:1 cols:netAppCount]; for (i = 0; i < netAppCount; i++) { id btnCell = [appMatrix cellAt:0:i]; if (!netApps[i]) { netAppCount = i; break; } if (_cmdIsApp(netApps[i])) { char buff[1024], *cmdFile = (char*)_getCmdFile(buff,netApps[i]); id appIcon = [[Application workspace] getIconForFile:cmdFile]; [btnCell setIconPosition:NX_ICONONLY]; [btnCell setImage:appIcon]; } else { char cmdName[128]; _getCmdName(cmdName,netApps[i]); [btnCell setIconPosition:NX_ICONABOVE]; [btnCell setIcon:"cmdButton"]; [btnCell setTitle:cmdName]; } [btnCell setTag:i]; } [appMatrix display]; } /* called when new user defaults have been loaded */ + (void)updateFromDefaults { if (globalMonitor) [globalMonitor updateFromDefaults]; } /* called when new user defaults have been loaded */ - (void)updateFromDefaults { const char *showSec = FLG_SHOWSECONDS; if (showSec) { int val = atoi(showSec); _showTileTime = ((val >= 0) && (val <= 2))? val : 0; } [self _fillMiniShelf]; [self enableConnectButton:nil]; [self tailLogFile:self]; } // ------------------------------------------------------------------------------------- // object initialization /* crete single shared instance */ + (id)sharedPPPMonitor { if (!globalMonitor) globalMonitor = [[self alloc] init]; return globalMonitor; } /* init */ - init { /* init super */ [super init]; exeWindow = (id)nil; pppLogExeId = nil; connectExeId = nil; pingExeId = nil; _alreadyConnected = NO; _showTileTime = 0; _isConnected = NO; _isDisconnecting = NO; _shutDown = NO; _connectTime = 0L; /* clear timers */ pppdTimer = (void*)nil; clockTimer = (void*)nil; ppingTimer = (void*)nil; checkTimer = (void*)nil; /* test mode */ { const char *val = [NSApp readDefault:"_TestMode_"]; _testMode_ = (val && (*val == '1'))? 1 : 0; if (_testMode_) fprintf(stderr,"WARNING: PPPMonitor running in TEST mode.\n"); } /* load nib */ if (![NSApp loadNibSection:"ExecMonitor.nib" owner:self]) { [NSApp errorPanel:"Could not load nib file 'ExecMonitor.nib'\n" "(Check that the nib file exists and try again)"]; [NSApp delayedFree:self]; return nil; } /* spot-check nib loading */ if (!exeWindow || !appMatrix) { [NSApp errorPanel:"'ExecMonitor.nib' did not load properly\n" "(The nib file has apparently been corrupted)"]; [NSApp delayedFree:self]; return nil; } /* initially clear messages */ [pingMessage setStringValue:""]; [self message:0:""]; /* set connection state */ if ([self isReallyConnected] || [self isPPPDRunning]) _alreadyConnected = YES; /* set mini-shelf target */ [appMatrix setTarget:self]; [appMatrix setDoubleAction:@selector(runApplication:)]; [appMatrix setAction:(SEL)0]; /* update default settings */ [self updateFromDefaults]; /* init panel */ [exeWindow setFrameAutosaveName:[NSApp appName]]; [exeWindow setDelegate:self]; [exeWindow makeKeyAndOrderFront:(id)nil]; /* already connected */ if (_alreadyConnected) { [self message:1:"Already connected (connection not made by this PPPMonitor)"]; [NSApp errorPanel:"Already connected:\n" "PPPMonitor does not currently support\n" "previously established connections."]; } return self; } /* free */ - _free:(id)sender { return [self free]; } - free { _shutDown = YES; if (pppLogExeId) [pppLogExeId killCommand]; if (connectExeId || pingExeId) { if (connectExeId && (connectExeId != self)) { // terminate ppp connections (actually, should already be terminated); } if (pingExeId) [pingExeId killCommand]; [self perform:@selector(_free:) with:self afterDelay:500 cancelPrevious:YES]; return (id)nil; } [exeWindow free]; globalMonitor = (id)nil; return [super free]; } // ------------------------------------------------------------------------------------- // shutting down /* can shut down */ - (BOOL)canShutDown { return (connectExeId || pingExeId)? NO : YES; } /* can shut down */ + (BOOL)canShutDown { return (!globalMonitor || [globalMonitor canShutDown])? YES : NO; } /* return shut down state */ - (BOOL)isShuttingDown { return _shutDown; } /* return connection flag state */ + (BOOL)shutDown { if (globalMonitor && ![globalMonitor isShuttingDown]) [globalMonitor free]; return globalMonitor? NO : YES; } // ------------------------------------------------------------------------------------- // connection state checks /* check for true connection */ - (BOOL)isReallyConnected { #ifdef CHKPPPSOCK /* open socket to check ppp0 */ // (Thanks to Erik Doernenburg for his contributions to this section) static int sock = -1; // maintain socket connection if (sock == -1) sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock != -1) { struct ifreq ifr; strcpy(ifr.ifr_name, "ppp0"); if (ioctl(sock,SIOCGIFFLAGS,&ifr) != -1) { return (ifr.ifr_flags & IFF_UP)? YES : NO; } } // fprintf(stdout,"DEBUG - PPPMonitor: ppp0 socket check failed ...\n"); #endif return SYSTEM(SYS_PPP_ISUP)? NO : YES; // fallback if all else fails } /* check for true connection */ - (BOOL)isPPPDRunning { return SYSTEM(SYS_PPPD_RUNNING)? NO : YES; } /* check for ppp loaded */ - (BOOL)isPPPDriverLoaded { return SYSTEM(SYS_PPP_ISLOADED)? NO : YES; } /* enable/disable button options */ - (void)enableConnectButton:(id)sender { if (sender) { // delay [btnConnect setEnabled:NO]; [self _perform:@selector(enableConnectButton:):nil delay:1000]; return; } else if (!_alreadyConnected) { // set 'Connect' button enabled state if (!connectExeId && !_isConnected) { [btnConnect setTitle:"Connect"]; } else if ( connectExeId && !_isConnected) { [btnConnect setTitle:"(Stop)"]; } else { [btnConnect setTitle:"Disconnect"]; } [btnConnect setEnabled:YES]; } else { [btnConnect setEnabled:NO]; } } // ------------------------------------------------------------------------------------- // connect time keeper /* create connect time image */ - (void)setConnectTimeIconImage:(int)hh :(int)mm :(int)ss { if (_isConnected) { static NXImage *appImage = (id)nil; static NXImage *conImage = (id)nil; if (!appImage) appImage = [[NXImage alloc] initFromSection:"appConnect"]; if (!conImage) conImage = [NXImage findImageNamed:"appConnect"]; if ([appImage lockFocus]) { char tm[16]; float w, h; NXPoint orgPt = { 0.0, 0.0 }, txtPt = { 0.0, 6.0 }; [conImage composite:NX_SOVER toPoint:&orgPt]; if ((_showTileTime == 1) || (ss < 0)) sprintf(tm,"%d:%02d", hh, mm); else sprintf(tm,"%d:%02d:%02d", hh, mm, ss); PSsetgray(NX_BLACK); PSselectfont("Ohlfs",9.0); PSstringwidth(tm,&w,&h); txtPt.x = ((48.0 - w) / 2.0) + 1.0; PSmoveto(txtPt.x,txtPt.y); PSshow(tm); [appImage unlockFocus]; [NSApp setApplicationIconImage:appImage]; } } else { [NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]]; } } /* TIMER: update connection timer */ - (id)_updClockTime:(id)sender /* 'nil' sender resets timer */ { if (clockTimer && _isConnected) { long interval = _intervalTime(); int hh = 0, mm = 0, ss = 0, tt; static int lastMM = -1, lastMode = -1; if (!sender || !_connectTime) { _connectTime = interval; } tt = interval - _connectTime; hh = tt / 3600, mm = (tt % 3600) / 60, ss = tt % 60; [self message:0:"Connected (%2d:%02d:%02d)", hh, mm, ss]; if (_showTileTime == 2) { [self setConnectTimeIconImage:hh:mm:ss]; } else if ((_showTileTime == 1) && ((lastMM != mm) || (lastMode != 1))) { [self setConnectTimeIconImage:hh:mm:-1]; lastMM = mm; } else if ((_showTileTime == 0) && (lastMode != 0)) { [NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]]; } if (lastMode != _showTileTime) lastMode = _showTileTime; } return clockTimer? self : nil; } /* start/stop timer */ - (void)_enableConnectClock:(BOOL)flag { if (clockTimer) { [self _stopTimer:(_timerHandlerData_t*)clockTimer]; clockTimer = (void*)nil; [NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]]; } if (flag) { _timerHandlerData_t *timer; float freq = 0.99; // just shy of a second [self _updClockTime:nil]; // reset timer timer = [self _startTimer:@selector(_updClockTime:) arg:self freq:freq]; clockTimer = (void*)timer; } } // ------------------------------------------------------------------------------------- // periodic command execution /* TIMER: periodic ping */ - (id)_periodicPing:(id)sender { if (!ppingTimer || !_isConnected || !CMD_PING || _testMode_) { ppingTimer = (void*)nil; } else if (!pingExeId) { [pingMessage setStringValue:"Ping"]; pingExeId = [ExecRunCommand runCommand:CMD_PING output:self]; if (!pingExeId) [pingMessage setStringValue:"****"]; } return ppingTimer? self : nil; } /* start/stop ping */ - (void)_enablePeriodicPing:(BOOL)flag { if (ppingTimer) { // cancel if active [self _stopTimer:(_timerHandlerData_t*)ppingTimer]; ppingTimer = (void*)nil; if (pingExeId) [pingExeId killCommand]; } if (flag) { _timerHandlerData_t *timer; int sec = CMD_PINGINTERVAL? atoi(CMD_PINGINTERVAL) : 3 * 60; if (sec < 60) sec = 60; timer = [self _startTimer:@selector(_periodicPing:) arg:self freq:(float)sec]; ppingTimer = timer; } } // ------------------------------------------------------------------------------------- // connection monitor /* TIMER: connection established */ - (id)_checkConnect:(id)sender { if (!connectExeId || _isConnected) { // if no connection in progress checkTimer = (void*)nil; } else if (_testMode_ || [self isReallyConnected]) { // if connected PLAY(SND_CONNECT); _isConnected = YES; [self enableConnectButton:self]; // delayed [self _enablePeriodicPing:YES]; [self _enableConnectClock:YES]; checkTimer = (void*)nil; } return checkTimer? self : nil; } /* start connection check */ - (void)_enableCheckConnect:(BOOL)flag { if (flag) { _timerHandlerData_t *timer; timer = [self _startTimer:@selector(_checkConnect:) arg:self freq:5.0]; checkTimer = timer; } else if (checkTimer) { [self _stopTimer:(_timerHandlerData_t*)checkTimer]; checkTimer = (void*)nil; } } // ------------------------------------------------------------------------------------- // pppd monitor /* monitor '/pppd' command */ - (id)_monitorPPPD:(id)sender { if (![self isPPPDRunning]) { pppdTimer = (void*)nil; [self commandDidComplete:connectExeId withError:0]; } return pppdTimer? self : nil; } /* start monitor */ - (void)_enablePPPDMonitor:(BOOL)flag { if (pppdTimer) { [self _stopTimer:(_timerHandlerData_t*)pppdTimer]; pppdTimer = (void*)nil; } if (flag && (connectExeId == self)) { _timerHandlerData_t *timer; float freq = 15.0; timer = [self _startTimer:@selector(_monitorPPPD:) arg:self freq:freq]; pppdTimer = (void*)timer; } } // ------------------------------------------------------------------------------------- // PPP log file monitor /* print message to ppplog scroll text */ - (void)logMessage:(BOOL)isError:(const char*)fmt, ... { va_list args; /* text color (on a white background) */ if ([[pppLogScroll docView] shouldDrawColor]) { if (isError) { [pppLogScroll setTextAttributeColor:NX_COLORRED]; } else { [pppLogScroll setTextAttributeGray:NX_DKGRAY]; } } else { if (isError) { [pppLogScroll setTextAttributeGray:NX_DKGRAY]; } else { [pppLogScroll setTextAttributeGray:NX_DKGRAY]; } // same gray } /* print message */ va_start(args, fmt); [pppLogScroll textPrintf:fmt args:args]; va_end(args); /* reset color to BLACK */ [pppLogScroll setTextAttributeGray:NX_BLACK]; } /* tail log file */ - (void)tailLogFile:(id)sender { /* initialize pppLogScroll */ if (![pppLogScroll isMemberOf:[ExecScrollText class]]) { pppLogScroll = [ExecScrollText newExecScrollText:pppLogScroll]; [pppLogScroll setDelegate:self]; [pppLogScroll setTab:[textFont getWidthOf:" "] count:10]; [pppLogScroll textPrintf:"\n"]; [pppLogScroll clearScrollText]; [pppLogScroll setTextAttributeGray:NX_BLACK]; } /* start tail command */ if (!pppLogExeId) { [pppLogScroll clearScrollText]; [pppLogScroll setTextAttributeGray:NX_BLACK]; if (CMD_TAILLOG) { char *ld1 = "/usr/adm/"; char *ld2 = "/private/adm/"; char tailBuff[64], *tail = (char*)CMD_TAILLOG; if ((strlen(tail) < 32) && !strchr(tail,' ') && (!strncmp(tail,ld1,strlen(ld1)) || !strncmp(tail,ld2,strlen(ld2)))) { sprintf(tailBuff,"/usr/ucb/tail -1f %s;", tail); tail = tailBuff; } [self logMessage:0:"(%s)\n",tail]; pppLogExeId = [pppLogScroll runCommand:tail user:0]; if (!pppLogExeId) [self logMessage:1:"'tail' command failed to start.\n"]; } else { [self logMessage:1:"'tail' command disabled (not specified).\n"]; } } } /* clear text */ - (void)clearLog:(id)sender { [pppLogScroll clearScrollText]; [pppLogScroll setTextAttributeGray:NX_BLACK]; } // ------------------------------------------------------------------------------------- // user commands /* connect */ - (void)connect:(id)sender { /* disconnect */ if (connectExeId) { if (!CMD_DISCONNECT) { [self message:1:"'Disconnect' command has not been specified"]; return; } [self message:0:"Disconnecting ... "]; [autoConnect setState:0]; // clear auto-reconnect [btnConnect setEnabled:NO]; [self _enableCheckConnect:NO]; // cancel connection check [self _enablePeriodicPing:NO]; // cancel ping [self _enableConnectClock:NO]; // cancel connection timer _isDisconnecting = YES; if (!_testMode_) { if (SYSTEM(CMD_DISCONNECT)) { [self message:1:"'Disconnect' command failed"]; _isDisconnecting = NO; [self enableConnectButton:self]; // delayed } } else { [self commandDidComplete:self withError:0]; } return; } _isDisconnecting = NO; /* ignore connect-request during shutdown */ if (_shutDown) return; /* load PPP driver is necessary */ if (!_testMode_ && ![self isPPPDriverLoaded]) { if (CMD_LOADPPPLKS) { [self message:0:"Loading PPP driver ... "]; { [[appMatrix window] flushWindow]; NXPing(); } if (SYSTEM(CMD_LOADPPPLKS)) { [self message:1:"Error encountered while loading PPP driver"]; } else { [self message:0:"Loading PPP driver ... successful"]; } if (![self isPPPDriverLoaded]) return; } else { [self message:1:"PPP driver must be loaded before connecting"]; return; } } /* check for 'Connect' command availability */ if (!CMD_CONNECT) { [self message:1:"'Connect' command has not been specified"]; return; } /* check for already connected */ if (![self isReallyConnected]) { [self message:0:"Connecting ..."]; _connectStart = _intervalTime(); if (!_testMode_) { connectExeId = [ExecRunCommand runCommand:CMD_CONNECT output:self]; if (!connectExeId) { [self message:1:"Connection failed to start"]; } else { [self _enableCheckConnect:YES]; } } else { connectExeId = self; [self _enableCheckConnect:YES]; } } else { _alreadyConnected = YES; [self message:1:"Already connected"]; } [self enableConnectButton:self]; // delayed } /* run application */ - (void)runApplication:(id)sender { id btnCell = [appMatrix selectedCell]; int appNdx = [btnCell tag]; if ((appNdx >= 0) && (appNdx < netAppCount) && netApps[appNdx]) { const char *app = netApps[appNdx]; int r = -1, c = -1; NXRect cellFrame; [appMatrix getRow:&r andCol:&c ofCell:btnCell]; [appMatrix getCellFrame:&cellFrame at:r:c]; [appMatrix lockFocus]; _hiliteRect(&cellFrame); { [[appMatrix window] flushWindow]; NXPing(); } if (_cmdIsApp(app)) { char buff[1024], *cmdFile = (char*)_getCmdFile(buff,app); NXPortFromName(cmdFile,(char*)nil); } else { const char *cmdLine = _cmdLine(app); char *ext = ";\n", *cmd = (char*)malloc(strlen(cmdLine)+strlen(ext)+1); sprintf(cmd,"%s%s",cmdLine,ext); [ExecMonitor terminalCommand:cmd title:"Command"]; free(cmd); } [btnCell setDrawDots:NO]; [btnCell drawSelf:&cellFrame inView:appMatrix]; { [[appMatrix window] flushWindow]; NXPing(); } [appMatrix unlockFocus]; } else NXBeep(); } // ------------------------------------------------------------------------------------- // command execution/completion // ------------------------------------------------------------------------------------- /* command output (when we are the delegate) */ - (void)commandOutput:(id)execId buffer:(const char*)buffer len:(int)len { /* ignore anything we get */ } /* call-back from shell command (main thread) */ - (void)commandDidComplete:execId withError:(int)err { /* check for connection termination */ if (execId == connectExeId) { _isConnected = NO; connectExeId = nil; [NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]]; if (_isDisconnecting) { // manual disconnect [self message:0:"Connection terminated"]; } else if ((_intervalTime() - _connectStart) <= 7) { // failed in <= X seconds if ([self isPPPDRunning]) { // pppd still running _alreadyConnected = YES; [exeWindow makeKeyAndOrderFront:(id)nil]; [self message:1:"'pppd' still running " "('-detach' apparently not specified)"]; #if 1 [autoConnect setState:0]; #else // Assuming that '-detach' was not specified on the 'pppd' command: // At this point the 'pppd' command will be monitored to attempt to detect // detect when it is destroyed. We attempt to simulate the same interface // used in 'ExecRunServer' to monitor it's own child processes. connectExeId = self; [self _enablePPPDMonitor:YES]; _isConnected = [self isReallyConnected]; return; #endif } else { [self message:1:"Connection did not start properly"]; } } else if ([autoConnect state]) { // re-connect PLAY(SND_DISCONNECT); [self message:1:"Connection terminated (auto-reconnect)"]; [self _perform:@selector(connect:):nil delay:1000]; } else { PLAY(SND_DISCONNECT); [self message:1:"Connection terminated"]; } _isDisconnecting = NO; [exeWindow makeKeyAndOrderFront:(id)nil]; [self enableConnectButton:self]; return; } /* check for ping */ if (execId == pingExeId) { pingExeId = nil; [pingMessage setStringValue:""]; return; } /* ppp log scroller */ if (execId == pppLogExeId) { [self logMessage:err:"'tail' command terminated.\n"]; pppLogExeId = nil; return; } /* error */ fprintf(stderr,"Unknown execId in call to 'commandDidComplete:withError:'"); return; } // ------------------------------------------------------------------------------------- // output monitor // ------------------------------------------------------------------------------------- /* same as "strstr()", except with limit check */ const char *strnstr(const char *s1, const char *s2, int n) { int i, n2 = strlen(s2), e = n - n2; for (i = 0; i < e; i++) { while ((i < e) && (s1[i] != s2[0])) i++; if ((i < e) && !strncmp(&s1[i],s2,n2)) return &s1[i]; } return (char*)0; } /* monitor scroll text output */ - (void)monitorOutput:scrollId buffer:(const char*)buffer len:(int)len { if (scrollId != pppLogScroll) return; // not fully implemented } // ------------------------------------------------------------------------------------- // Terminal invoke // ------------------------------------------------------------------------------------- /* invoke command in Terminal window */ + terminalCommand:(const char*)cmd title:(const char*)title { Speaker *speaker; port_t terminalPort; /* can't find Terminal */ if (!(terminalPort = NXPortFromName("Terminal", NULL))) return (id)nil; /* launch Terminal */ [NSApp deactivateSelf]; creat("/tmp/.reallyignorethis.term", 0444); [[Application workspace] openFile:"/tmp/.reallyignorethis.term" fromImage:(id)nil at:(NXPoint*)nil inView:(id)nil]; /* run command */ speaker = [NSApp appSpeaker]; [speaker setSendPort: terminalPort]; [speaker selectorRPC:"runCommand:usingShell:inFolder:windowTitle:closeOnExit:" paramTypes: "cccci", cmd, "", "", title, NO]; return self; } // ------------------------------------------------------------------------------------- @end // ------------------------------------------------------------------------------------- // TileButtonCell // ------------------------------------------------------------------------------------- @implementation TileButtonCell - drawSelf:(const NXRect *)cellFrame inView:controlView { NXSize imageSize; NXPoint imageOrigin, iconOrigin = cellFrame->origin; /* draw tile */ iconOrigin.y += cellFrame->size.height; // assume frameHeight == tileHeight if (shelfTile) { [shelfTile composite:NX_COPY toPoint:&iconOrigin]; } else { NXDrawButton(cellFrame, cellFrame); } /* draw image */ [[self image] getSize:&imageSize]; imageOrigin.x = iconOrigin.x + (cellFrame->size.width - imageSize.width )/2.0; imageOrigin.y = iconOrigin.y - (cellFrame->size.height - imageSize.height)/2.0; [[self image] composite:NX_SOVER toPoint:&imageOrigin]; /* draw text */ if ([self iconPosition] == NX_ICONABOVE) { NXSize titleSize; NXRect titleRect = *cellFrame; static Cell *titleCell = nil; if (!titleCell) { titleCell = [[Cell alloc] initTextCell:""]; [titleCell setAlignment:NX_CENTERED]; [titleCell setFont:[controlView font]]; } [titleCell setStringValue:[self title]]; [titleCell calcCellSize:&titleSize inRect:cellFrame]; titleRect.origin.y += titleRect.size.height - titleSize.height - 7; titleRect.origin.x += floor((titleRect.size.width-titleSize.width)/2.0); titleRect.size.width = titleSize.width; titleRect.size.height = titleSize.height; [titleCell drawSelf:&titleRect inView:controlView]; } /* draw dots */ if (shelfDots && !_noDots) [shelfDots composite:NX_SOVER toPoint:&iconOrigin]; return self; } - highlight:(const NXRect*)cellFrame inView:aView lit:(BOOL)flag { return self; } - (void)setDrawDots:(BOOL)flag { _noDots = flag? NO : YES; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.