This is ExecScrollText.m in view mode; [Download] [Up]
// -------------------------------------------------------------------------------------
// ExecScrollText.m
// -------------------------------------------------------------------------------------
// Permission is granted to freely redistribute this source code, and to use fragments
// of this code in your own applications if you find them to be useful. This class,
// along with the source code, come with no warranty of any kind, and the user assumes
// all responsibility for its use.
// -------------------------------------------------------------------------------------
#import <appkit/appkit.h>
#import <libc.h>
#import <mach/cthreads.h>
#import <stdlib.h>
#import <stdarg.h>
#import <string.h>
#import <pwd.h>
#import <sys/types.h>
#import <sys/wait.h>
#import "ExecScrollText.h"
// -------------------------------------------------------------------------------------
// null text attribute structure
static NXColor nullColor = { 0 };
static textAttr_t nullAttr = { (id)nil, 0 };
#define isNullAttr(X) (!X.fontId && !X.colorMode)
// -------------------------------------------------------------------------------------
// Object notified when command completes
@interface Object(ExecScrollText_Delegate)
- commandDidComplete:shellId withError:(int)errorCode;
@end
// -------------------------------------------------------------------------------------
// private methods
@interface ExecScrollText(Private)
- _setTextAttrFont:fontId color:(int)mode:(NXColor)color;
- (BOOL)_appendTextToView:(const char*)buffer len:(int)len attr:(textAttr_t)tAttr;
- _appendTextAndMakeVisible:(const char*)buffer attr:(textAttr_t)tAttr;
- _stopCommand;
- (int)_popen:(const char*)cmd;
- (int)_pclose;
- (void)_gotData;
@end
// -------------------------------------------------------------------------------------
/* convert color to gray */
static float cvtColor2Gray(NXColor color)
{
float gray;
NXConvertColorToGray(color, &gray);
return gray;
}
// -------------------------------------------------------------------------------------
@implementation ExecScrollText
// -------------------------------------------------------------------------------------
/* initializes the outlet (to be executed by main thread only!) */
+ newExecScrollText:anObject
{
/* init */
self = [super new];
scrollView = anObject;
textView = [scrollView docView];
wasEditable = [textView isEditable];
autoLf = NO;
runAttr = nullAttr;
cmdChild = 0;
/* set textView attributes */
//[textView setEditable:NO];
//[textView setMonoFont:NO];
return self;
}
/* return textView id (docView) */
- docView
{
return textView;
}
/* return scroll view */
- scrollView
{
return scrollView;
}
/* set delegate */
- setDelegate:theDelegate
{
delegate = theDelegate;
return self;
}
/* set auto linefeed mode */
- setAutoLineFeed:(BOOL)mode
{
autoLf = mode;
return self;
}
/* free object */
- free
{
[self killCommand];
return [super free];
}
// --------------------------------------------------------------------------------
/* set font */
- _setTextAttrFont:fontId color:(int)mode:(NXColor)color
{
textAttr_t tAttr = nullAttr;
tAttr.fontId = fontId;
tAttr.colorMode = mode;
tAttr.color = color;
[self _appendTextAndMakeVisible:(char*)nil attr:tAttr];
return self;
}
/* set font */
- setTextAttributeFont:fontId
{
return [self _setTextAttrFont:fontId color:0:nullColor];
}
/* set gray */
- setTextAttributeGray:(float)aGray
{
return [self _setTextAttrFont:(id)nil color:1:NXConvertGrayToColor(aGray)];
}
/* set gray */
- setTextAttributeColor:(NXColor)aColor
{
return [self _setTextAttrFont:(id)nil color:2:aColor];
}
/* set default tabs */
- setTabStops:(float*)tabArray count:(int)c
{
NXTextStyle style = *((NXTextStyle*)[textView defaultParaStyle]);
style.numTabs = (short)c;
style.tabs = (NXTabStop*)malloc(sizeof(NXTabStop) * style.numTabs);
while (--c >= 0) { style.tabs[c].kind = NX_LEFTTAB; style.tabs[c].x = tabArray[c]; }
[textView setParaStyle:(void*)&style];
return self;
}
/* repeat given tab multiple times */
- setTab:(float)tabSize count:(int)c
{
int i;
NXTextStyle style = *((NXTextStyle*)[textView defaultParaStyle]);
style.numTabs = (short)c;
style.tabs = (NXTabStop*)malloc(sizeof(NXTabStop) * style.numTabs);
for (i = 0; i < c; i++) {
style.tabs[i].kind = NX_LEFTTAB;
style.tabs[i].x = (float)(i + 1) * tabSize;
}
[textView setParaStyle:(void*)&style];
return self;
}
// --------------------------------------------------------------------------------
/* clear text scroll view area */
- clearScrollText
{
[textView setEditable:YES];
[textView setText:""];
if (!wasEditable) [textView setEditable:NO];
[scrollView display];
return self;
}
- clear:sender
{
return [self clearScrollText];
}
// --------------------------------------------------------------------------------
// append text to scroll view
/* append buffer to view: return YES if text was visible */
- (BOOL)_appendTextToView:(const char*)buffer len:(int)len attr:(textAttr_t)tAttr
{
int txtLen;
NXSelPt startPt, endPt;
NXRect rect;
/* check for font/gray change (save state) */
if (tAttr.fontId) {
runAttr.fontId = tAttr.fontId;
}
if (tAttr.colorMode) {
runAttr.colorMode = tAttr.colorMode;
runAttr.color = tAttr.color;
}
if (!buffer || !*buffer || (len == 0)) return NO;
/* get ready to print text */
[textView getVisibleRect:&rect]; // visible rectangle
[textView setEditable:YES];
txtLen = [textView textLength];
[textView setSel:txtLen :txtLen];
[textView getSel:&startPt :&endPt]; // selected coordinates
/* set text run attributes if specified */
if (!isNullAttr(runAttr)) {
if ([textView isMonoFont]) [textView setMonoFont:NO];
if (!txtLen) { [textView replaceSel:" "]; [textView setSel:0 :1]; }
if (runAttr.fontId) [textView setSelFont:runAttr.fontId];
if (runAttr.colorMode == 1) [textView setSelGray:cvtColor2Gray(runAttr.color)]; else
if (runAttr.colorMode == 2) [textView setSelColor:runAttr.color];
runAttr = nullAttr;
}
/* print text */
if (len > 0) [textView replaceSel:buffer length:len];
else [textView replaceSel:buffer];
if (!wasEditable) [textView setEditable:NO];
return (rect.origin.y + rect.size.height > endPt.y); // was visible?
}
/* append text to view and scroll to visible */
- _appendTextAndMakeVisible:(const char*)buffer attr:(textAttr_t)tAttr
{
BOOL wasVisible;
/* print buffer */
wasVisible = [self _appendTextToView:buffer len:-1 attr:tAttr];
if (autoLf && buffer) [self _appendTextToView:"\n" len:-1 attr:nullAttr];
if (wasVisible) [textView scrollSelToVisible];
return self;
}
// --------------------------------------------------------------------------------
// append a formatted text string message into a text view
/* append text unformatted */
- (int)textPrint:(const char*)buffer
{
[self _appendTextAndMakeVisible:buffer attr:nullAttr];
return strlen(buffer);
}
/* append text with variable args into textView */
- (int)textPrintf:(const char*)fmt args:(va_list)args
{
char tempString[textStringSIZE] = { 0 };
int retVal = vsprintf(tempString, fmt, args);
[self _appendTextAndMakeVisible:tempString attr:nullAttr];
return retVal;
}
/* append text with variable args into textView */
- (int)textPrintf:(const char*)fmt, ...
{
va_list args;
int retVal;
va_start(args, fmt);
retVal = [self textPrintf:fmt args:args];
va_end(args);
return retVal;
}
// --------------------------------------------------------------------------------
// executing commands within a shell, using the scroll text as output
/* stop command */
- _stopCommand
{
int error;
DPSRemoveFD(inputDescriptor);
error = [self _pclose];
close(inputDescriptor);
[self commandDidCompleteWithError:error];
return self;
}
/* open pipe to shell and execute command */
- (int)_popen:(const char*)cmd
{
int inputP[2], hisOutput, myInput;
const char **locEnv = environ;
/* open pipe */
pipe(inputP);
myInput = inputP[0];
hisOutput = inputP[1];
/* fork and execute shell */
if ((cmdChild = vfork()) == 0) {
int i;
char **env;
/* make local copy of environment table */
for (i = 0; locEnv[i]; i++);
env = (char**)alloca(sizeof(char*) * (i + 1)); // allocate on stack
memcpy(env, locEnv, sizeof(char*) * (i + 1));
/* modify environment as desired here */
/* set up pipe handles */
close(myInput);
if (hisOutput != 1) dup2(hisOutput, 1);
if (hisOutput != 2) dup2(hisOutput, 2);
if ((hisOutput != 1) && (hisOutput != 2)) close(hisOutput);
/* execute command */
setpgrp(0, getpid());
execle("/bin/csh", "csh", "-f", "-c", cmd, (char *)nil, env);
_exit(RUNCMD_EXEC);
}
/* set io */
if (cmdChild == -1) { close(myInput); myInput = -1; }
close(hisOutput);
return myInput;
}
/* close shell command pipe */
- (int)_pclose
{
int pid, omask, err;
union wait status;
omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
while ((pid = wait(&status)) != cmdChild && pid != -1);
(void)sigsetmask(omask);
return (err = (status.w_status & 0xFF))? -1 : (status.w_status >> 8) & 0xFF;
}
/* filter for data piped from command shell */
- (void)_gotData
{
char data[1024];
int n, cnt = 0;
BOOL doScroll = NO;
/* read available text */
do {
if ((n = read(inputDescriptor, data, sizeof(data))) >= 0) {
cnt += n;
if (n && [self _appendTextToView:data len:n attr:nullAttr]) doScroll = YES;
}
} while (n == sizeof(data));
/* show text */
if (doScroll) [textView scrollSelToVisible];
if (!cnt) [self _stopCommand];
}
/* fd routine for receiving data */
static void gotData(int fd, void *self)
{
[(ExecScrollText*)self _gotData];
}
/* execute a command */
- runCommand:(const char*)command
{
inputDescriptor = [self _popen:command];
if (inputDescriptor >= 0) {
DPSAddFD(inputDescriptor, gotData, self, NX_BASETHRESHOLD);
return self;
}
return (id)nil;
}
/* terminate command */
- terminateCommand
{
if (cmdChild > 0) {
killpg(cmdChild, SIGTERM);
kill(cmdChild, SIGTERM);
}
return self;
}
/* kill command */
- killCommand
{
if (cmdChild > 0) {
killpg(cmdChild, SIGKILL);
kill(cmdChild, SIGKILL);
}
return self;
}
// --------------------------------------------------------------------------------
// support for remote shell server output
/* copy text to scrollView (MAIN THREAD ONLY!) */
- (oneway void)commandOutput:(const char*)buffer len:(int)len
{
if (len && buffer) {
if ([self _appendTextToView:buffer len:len attr:nullAttr])
[textView scrollSelToVisible];
free((char*)buffer);
}
}
/* indicate that the shell has completed */
- (oneway void)commandDidCompleteWithError:(int)errorCode;
{
if (delegate && [delegate respondsTo:@selector(commandDidComplete:withError:)])
[delegate commandDidComplete:self withError:errorCode];
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.