For those familiar with Terminal in OpenStep for Mach, notes specific to this release are included. Also, a section is devoted to some of Terminal's less obvious features. Finally, this document includes information about Terminal's execution service, which is of interest mainly to developers.
The following new features have been added to the Terminal application in this release.
These new bugs have appeared since OpenStep for Mach 4.2.
Issue | The Page Up and Page Down keys don't work. |
Issue | Characters lost when pasting text. |
Description | Pasting a large body of text into a Terminal window will sometimes result in many of the characters being lost. |
The TSTerminalDOServices protocol, declared in the header /System/Developer/Headers/Apps/TerminalDOProtocol.h, allows applications to request that the Terminal application perform commands on their behalf. This is useful if a program wants to open up a shell window for the user to interact with, and it may be easier than setting up pipes and doing a fork/exec of a program yourself.
Terminal cannot provide services if it is not running. It may be necessary to use the Application Kit's NSWorkspace API to launch Terminal if the connection to Terminal fails.
You may run the commands in a window, in which case an integer window handle is returned to allow a window to be reused for subsequent commands, or the commands can performed in the background. In addition, you can specify the window's title, as well as the working directory and environment to be used when executing the command.
In the case of commands performed in the background only, you can pass in an NSData object whose data will be fed to the command's standard input, and if you pass in a pointer to an NSData, Terminal can hand you back the output of the command's standard output and standard error. If you pass a pointer to get these back, the data at the pointer should be nil before you invoke the command. Upon completion, the pointer points to an newly created NSData containing the output.
For details on the specific methods in the protocol, see the header file referenced above.
By default, Terminal registers a port for the execution service with the Mach bootstrap server. This means that only processes descended from the Workspace application have access to this service. You can also have Terminal register this port with the Mach netname server, which allows access from other machines on the network. This is enabled by setting the default
/usr/bin/defaults write Terminal PublicDOServices YES
Running with this default is a security hazard, as Terminal has no way of verifying who is making the remote connection.
Any user may disable this service by executing the following command:
/usr/bin/defaults write Terminal DisableDOServices YES
The superuser can disable it for all users on a given machine by giving this command:
touch /System/Administration/Terminal.app/.DisableDOServices
The program /usr/bin/terminal, mentioned above in "A Tool for Creating New Terminal Windows," is a simple client which utilizes this protocol. The following is the source code for this program:
//
// terminal.m
//
// Sample client for Terminal.app's Distributed Objects services.
// Copyright 1994-1997, Apple Computer, Inc. All rights reserved.
//
//
// This program opens a terminal window in the current directory. It's installed
// in /usr/bin/terminal.
//
// Author: sam streeper
// Updated by: Barry Locklear, Grant Baillie
//
// To compile:
//
// cc terminal.m -I/System/Developer/Headers -framework Foundation -o terminal
//
// Usage:
// terminal [-shell] [-dir <dir>] [-noclose] [-env] [command]
//
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#import <stdio.h>
#import <mach/mach.h>
#import <servers/bootstrap.h>
#import <Apps/TerminalDOProtocol.h>
static void usage(void) {
fprintf(stderr,"terminal: open a new Terminal.app window\\n\\n");
fprintf(stderr,"Usage: terminal [-shell] [-dir <dir>] [-noclose] [-env] [command]\\n");
fprintf(stderr," -shell - Run command from default shell\\n");
fprintf(stderr," -dir - Specify working directory (default is current)\\n");
fprintf(stderr," -noclose - Retain window upon exit\\n");
fprintf(stderr," -env - Export current environment\\n");
exit(-1);
}
static id<TSTerminalDOServices> lookupTerminalDOServer(void) {
port_t sendMachPort;
NSDistantObject *rootProxy = nil;
id<TSTerminalDOServices> result;
// First, try look up Terminal's DO object in the bootstrap server.
// This is where the app registers it by default.
if ((BOOTSTRAP_SUCCESS == bootstrap_look_up(bootstrap_port, "TerminalDO", &sendMachPort)) && (PORT_NULL != sendMachPort)) {
NSConnection *conn = [NSConnection connectionWithReceivePort:[NSPort port] sendPort:[NSPort portWithMachPort:sendMachPort]];
rootProxy = [conn rootProxy];
}
// If the previous call failed, the following might succeed if the user
// logged in is running Terminal with the PublicDOServices user default
// set.
if (!rootProxy) {
rootProxy = [NSConnection rootProxyForConnectionWithRegisteredName:@"TerminalDO" host:@""];
}
// We could also try to launch Terminal at this point, using
// the NSWorkspace protocol.
if (!rootProxy) {
fprintf(stderr,"Can't connect to Terminal\\n");
exit(-1);
}
[rootProxy setProtocolForProxy:@protocol(TSTerminalDOServices)];
result = (id<TSTerminalDOServices>)rootProxy;
if ([result protocolVersion] < 0x10002) {
fprintf(stderr,"Incompatible terminal protocol version\\n");
exit(-4);
}
return result;
}
int main(void) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id <TSTerminalDOServices> terminal = lookupTerminalDOServer();
NSEnumerator *argsEnum = [[[NSProcessInfo processInfo] arguments] objectEnumerator];
NSString *thisArg;
int returnCode;
NSMutableString *command = nil;
NSDictionary *environment = nil;
NSString *directory = nil;
TSShellType shellType = TSShellNone;
TSExitAction exitAction = TSCloseUnlessError;
int winHandle;
NSData *inputData = nil;
NSData *outputData = nil;
NSData *errorData = nil;
thisArg = [argsEnum nextObject]; /* Skip the program name */
if (nil == thisArg) {
shellType = TSShellDefault;
}
// Run through the command-line arguments. We only set command to
// something non-nil once we encounter an argument that isn't an
// option. Once we've done this, though, we treat the rest of
// the arguments as part of the command.
//
while(thisArg = [argsEnum nextObject]) {
if (command) {
[command appendFormat:@" %@", thisArg];
}
else if (![thisArg hasPrefix:@"-"]) {
command = [[thisArg mutableCopy] autorelease];
}
else if ([@"-shell" isEqualToString:thisArg]) {
shellType = TSShellDefault;
}
else if ([@"-dir" isEqualToString:thisArg]) {
if (!(thisArg = [argsEnum nextObject])) usage();
directory = thisArg;
}
else if ([@"-noclose" isEqualToString:thisArg]) {
exitAction = TSDontCloseOnExit;
}
else if ([@"-env" isEqualToString:thisArg]) {
environment = [[NSProcessInfo processInfo] environment];
}
else {
usage();
}
}
if (!directory) directory = [[NSFileManager defaultManager] currentDirectoryPath];
if (!command) command = [NSMutableString stringWithCString:""];
[terminal runCommand:command
inputData:inputData
outputData:&outputData
errorData:&errorData
waitForReturn:NO
windowType:TSWindowNew
windowHandle:&winHandle
exitAction:exitAction
shellType:shellType
windowTitle:command
directory:directory
environment:environment
returnCode:&returnCode];
[pool release];
exit(returnCode);
}