{\rtf0\ansi{\fonttbl\f1\fmodern Courier;\f0\fswiss Helvetica;\f2\fnil Times-Roman;}
\paperw11760
\paperh7800
\margl120
\margr120
{\colortbl;\red0\green0\blue0;\red95\green36\blue130;}
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b\i0\ulnone\fs24\fc0\cf0 #import "FTPObject.h"\
#import "ArchieApp.h"\
#import "CalendarView.h"\
#import "ClockView.h"\
#import "FileWellView.h"\
#import "FtpFile.h"\
#import "InspectorManager.h"\
#import "MyBrowserCell.h"\
#import "NetrcEntry.h"\
#import "PasswordField.h"\
#import "PercentView.h"\
#import "Preferences.h"\
\
#import <appkit/appkit.h>\
\
@implementation FTPObject\
\

\b0\i\fc1\cf1 // Structure passed to the file transfer thread function
\b\i0\fc0\cf0 \
static
\fc1\cf1  char fileBuffer[16384];\

\fc0\cf0 \

\b0\fs28\fc1\cf1 /*\\ ------------- Class Methods ------------- \\*/\

\b\fs24 + (BOOL) userIsAnonymous:(const char *) username\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker535 \markername init;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  YES if username is the string "anonymous" or an alias,\
		NO otherwise;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  Compares the username string against what we currently\
		allow as an acceptable anonymous login name;
\fc0\cf0 \
*/\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 static const char *anonAliases[] = \{
\fc1\cf1 "anonymous", "ftp", 0
\fc0\cf0 \};\
BOOL 
\fc1\cf1 isAnonymous = NO
\fc0\cf0 ;\
\
	if( username != 0 )\
	\{\
	int index;\
		for(index = 0; anonAliases[index] != 0 && 
\fc1\cf1 isAnonymous
\fc0\cf0  == NO; index ++)\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 			isAnonymous = strcmp(username, 
\fc0\cf0 anonAliases[index]
\fc1\cf1 ) == 0;\

\fc0\cf0 	
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 \}\
	return 
\fc1\cf1 isAnonymous
\fc0\cf0 ;\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 \}\

\b0\i\fs28 \
/*\\ ------------ Intialization Methods ------------ \\*/
\b\i0\fs24\fc0\cf0 \
- init\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker1110 \markername init;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  Allocate the basic instance variables. Should\
		use 
\b initForHost:interactive:
\b0  to begin ftp session.;
\fc0\cf0 \
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 	[super init];\
\
	activePathNodes = [[List alloc] initCount: 0];\
	selectionList = [[List alloc] initCount: 0];\
	preferences = [NXApp preferences];\
	localTransferDir = NXCopyStringBuffer([preferences transferDir]);\
	fileInfo = [[FileInfo alloc] init];\
	sortKey = [[DirectorySortKey alloc] init];\
\
	return self;\
\} 
\b0\i // End init
\b\i0 \
\
- initForHost:(const char *) host interactive:(BOOL) interactive\
	initialPath:(const char *) initialPath\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker1704 \markername initForHost:interactive:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  [self login: host];\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  The designated intializer for the FTPObject class.\
		It messages our 
\b init
\b0  method, and connects to host using 
\b login:
\b0 .;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		host: The anonymous ftp host to connect to;\
		interactive: A flag indicating if we should display the ftp\
			browser panel;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\fs20\fc0\cf0 [self debug: MAX_DEBUG method:_cmd, "%s, interactive = %d", host, interactive];
\b\fs24 \
	if( rootDir == nil )\
		[self init];\
	return [self login: host interactive: interactive initialPath: initialPath];\
\} 
\b0\i // End initForHost: interactive:
\b\i0 \
\
- initForGeneralLogin\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker2287 \markername initForGeneralLogin;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if login completes, nil otherwise;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method simply invokes initForGeneralLogin: with a\
		hostname of 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 ;
\fc0\cf0 \
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 	return [self initForGeneralLogin: 0 initialPath: 0];\
\} 
\b0\i // End initForGeneralLogin\

\b\i0 \
- initForGeneralLogin:(const char *) hostname\
	initialPath:(const char *) initialPath\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker2628 \markername initForGeneralLogin;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if login completes, nil otherwise;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method initates an interactive session with\
		the host given by hostname. It displays a login panel allowing\
		specification of the hostname, login name, and password. If\
		hostname is not 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 , an attempt is made to fill login panel\
		fields from the user's .netrc file.;
\fc0\cf0 \
	
\i0\ul Args:\

\i\ulnone 		hostname: the hostname to initate the ftp session with,\
			possibl
\fc1\cf1 y 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 ;
\fc0\cf0 \
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f0\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\fc1\cf1 int success;\
const char *username, *passwd;\
NetrcEntry *netrcInfo = nil;\
\
	if( rootDir == nil )\
		[self init];\
	if( ftpLoginPanelID == nil )\
	\{
\fc0\cf0 	
\b0\i /* Load the login panel */
\b\i0 \
		[NXApp loadNibSection: "FtpLogin.nib" owner: self withNames: NO];\
		if( ftpLoginPanelID == nil )\
		\{\
			[self error: INTERNAL_ALERT method:_cmd\
				key: "Failed to load FTP login nib"];\
			return nil;\
		\}\
	\}\
	\
	
\b0\i\fc1\cf1 /* Get the ftp session information. We first try to retrieve the info\
		from the user's .netrc file. */
\b\i0\fc0\cf0 \

\fc1\cf1 	if( hostname != 
\fc0\cf0 0
\fc1\cf1  )\
	\{\
		[
\fc0\cf0 loginHostnameID setStringValue: hostname];\

\fc1\cf1 		netrcInfo = [NetrcEntry 
\fc0\cf0 findNetrcInfo: hostname];\
	\}\
	else\

\fc1\cf1 		[
\fc0\cf0 loginHostnameID selectText: self];\
	if( netrcInfo != 0 )\
	\{	
\b0\i /* Also set the username and password fields, disabling the\
			display of the password.  We also show the ".netrc entry"\
			string so the user knows what is going on. */
\b\i0 \
		[loginUsernameID setStringValue: [netrcInfo username]];\

\fc1\cf1 		[
\fc0\cf0 loginPas
\fc1\cf1 swdID setStringValue: [
\fc0\cf0 netrcInfo
\fc1\cf1  passwd] visible: NO];\

\fc0\cf0 	\}\
	else
\fc1\cf1 \
		[netrcStringID removeFromSuperview];\
	
\b0\i /* Display the login panel modally */
\b\i0 \

\fc0\cf0 	[visiblePasswdID setState: 0];\
	su
\fc1\cf1 ccess = [NXApp runModalFor: ftpLoginPanelID];\
	[ftpLoginPanelID endEditingFor: self];\
	[ftpLoginPanelID orderOut: self];\
	if( success == NX_RUNSTOPPED )\
		
\fc0\cf0 return nil;\
	
\b0\i\fc1\cf1 /* Retrieve the info from the interface */
\b\i0\fc0\cf0 \
	hostname = [loginHostnameID stringValue];\
	username = [loginUsernameID stringValue];\
	passwd = [loginPasswdID stringValue];\
	if( [FTPObject userIsAnonymous: username] == YES && passwd == 0 )\
		return [self login: hostname interactive: YES initialPath: initialPath];\
	[msgID setStringValue: "Establishing login..."];\
\
	return [self login: hostname user: username passwd: passwd\
		interactive: YES initialPath: initialPath];\
\} 
\b0\i\fc1\cf1 // End initForGeneralLogin
\b\i0\fc0\cf0 \

\f0 \

\f1 - free\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker4852 \markername free;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  [super free];\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  frees an instance of FTPObject class, closing\
		any host connections that exist;
\fc0\cf0 \
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 	[
\fc1\cf1 statusPanelID
\fc0\cf0  orderOut: self];\
	[logPanelID orderOut: self];\
	[ftpWindowID orderOut: self];\
	[ftpLoginPanelID orderOut: self];\
	if( 
\fc1\cf1 statusPanelID != nil )
\fc0\cf0 \
		[N
\fc1\cf1 XApp delayedFree: statusPanelID
\fc0\cf0 ];\
	if( ftpWindowID
\fc1\cf1  != nil )
\fc0\cf0 \
		[N
\fc1\cf1 XApp delayedFree: 
\fc0\cf0 ftpWindowID];\
	if( ftpLoginPanelID
\fc1\cf1  != nil )
\fc0\cf0 \
		[N
\fc1\cf1 XApp delayedFree: 
\fc0\cf0 ftpLoginPanelID];\
	[rootDir free];\
	[activePathNodes free];\
	[selectionList free];\
	if( ftpInfo != 0 )\
		[self ftpQuit: ftpInfo];\
	if( ftpHost != 0 )\
		free(ftpHost);\
	if( loginDirectory != 0 )\
		free(loginDirectory);\
	if( localTransferDir != 0 )\
		free(localTransferDir);\
	if( currentSubDir != 0 )\
		free(currentSubDir);\
\
	return [super free];\
\} 
\b0\i // End free
\b\i0 \
\

\b0\i\fs28\fc1\cf1 /*\\ -------------- FTP Session Methods -------------- \\*/
\b\i0\fs24\fc0\cf0 \
- abort: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker5749 \markername abort:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method is invoked by the Abort button\
		in the interface. It aborts the current transfer\
		and ends any modal event loop.;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		sender: The Abort or Cancel button in the interace;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 	if( transferInfo.transferThread != 0 )\
	\{	
\b0\fs20\fc1\cf1 // Abort the transfer thread
\b\fs24\fc0\cf0 \
		thread_suspend(cthread_thread(transferInfo.transferThread));\
		cthread_abort(transferInfo.transferThread);\
		transferInfo.abort = YES;\

\fc1\cf1 		
\b0\fs20 // Send transfer thread on its way again
\b\fs24 \
		thread_resume(
\fc0\cf0 cthread_thread(transferInfo.transferThread));\
		while( transferInfo.done == NO )\
			cthread_yield();\
	\}\
	
\b0\fs20\fc1\cf1 // Send an abort to the ftp server
\b\fs24\fc0\cf0 \
	[self ftpAbort: ftpInfo];\
	
\b0\i\fc1\cf1 // Abort any modal sessions
\b\i0 \
	[NXApp stopModal: NX_RUNSTOPPED];\
\

\fc0\cf0 	return self;\
\} 
\b0\i // End abort:
\b\i0 \
\
- doLogin: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker6552 \markername doLogin:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method is invoked by the ªLoginº button\
		in the login interface. It stops the modal session with a\
		value of 1 to indicate we should proceed.;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		sender: The Login button in the login interface;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 	[NXApp stopModal: 1];\
\
	return self;\

\fc0\cf0 \} 
\b0\i // End doLogin:
\b\i0 \
\
- login:(const char *) hostname interactive:(BOOL) interactive\
	initialPath:(const char *) initialPath\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker6985 \markername login:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if we connect, nil otherwise;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method establishes a connection to the\
		Anonymous FTP host given by hostname and in so doing\
		allocates our ftpInfo variable. It also logs the user\
		in as an anonymous user. If we fail to make the\
		connection, ftpInfo is 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1  and an error message is reported.\
		This work is performed using our 
\b login:user:passwd:
\b0  method.;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		hostname: The hostname of the anonymous FTP host to\
			open a connection with;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 const char *passwd;\
	
\b0\i // Login as an anonymous user
\b\i0 \
	passwd = [preferences anonPasswd];\
	if( passwd == 0 )\
		passwd = NXUserName();\
	[msgID setStringValue: "Establishing anonymous login..."];\
\
	return [self login: hostname user: "anonymous" passwd: passwd\
		interactive: interactive initialPath: initialPath];\
\} 
\b0\i // End login:
\b\i0 \
\
\
- login:(const char *) hostname user:(const char *) username\
	passwd:(const char *) passwd interactive:(BOOL) interactive\
	initialPath:(const char *) initialPath\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker7985 \markername login:user:passwd:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if we connect, nil otherwise;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method establishes a connection to the\
		FTP host given by hostname and in so doing\
		allocates our ftpInfo variable. It also logs the user\
		under the username argument with passwd. If we fail to make the\
		connection, ftpInfo is 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1  and an error message is reported.;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		hostname: The hostname of the FTP host to open a\
			connection with;\
		username: The name of the FTP user;\
		passwd: The password to use;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 ObjectList <ObjectArchival> *rootFileList;\
ListingEntry dummyRoot = \{"drwxrwxrwx", 512, 1, 1, 1970, 0, 0, "/"\};\
\

\b0\fs20 [self debug: MAX_DEBUG method:_cmd, "%s, %s", hostname, username];
\b\fs24 \
	isInteractive = interactive;\
	if( hostname == 0 || *hostname == 0 )\
	\{\
		[self alert: "FTP" msg: "A null host
\fc1\cf1 name has been passed to\\n%s"\
			btn1: 0 btn2: 0 btn3: 0, sel_getName(_cmd)];\
		return [self free];\

\fc0\cf0 	\}\
	ftpHost = NXCopyStringBuffer(hostname);\
	if( isInteractive == NO )\
		[self showFTPStatus: self];\
	[bytesTransferredID setIntValue: 0];\
	[msgID setStringValue: "Connecting to host..."];\
	[clockViewID startMinSecTimer: self];\
	[per
\fc1\cf1 centID displayPercent: 
\fc0\cf0 0
\fc1\cf1 .
\fc0\cf0 0
\fc1\cf1 ];\
	[statusPanelID display];\
\
	
\b0\i /* Open a connection to the ftp host. We send ourselves a showFTPLog:\
		message to ensure the log nib and hence the logging text object\
		are loaded. */
\b\i0 \
	[self showFTPLog: self];\
	[ftpLogID setText: 0];\
	ft
\fc0\cf0 pInfo = [self ftpOpen: hostname textConsole: ftpLogID];\
	if( ftpInfo == 0 )\
		return [self free];\
\
	
\b0\i\fc1\cf1 /* Login using the username and passwd arguments */
\b\i0\fc0\cf0 \
	if( [self ftpLogin: ftpInfo user:(char *) username\
		passwd:(char *) passwd] == nil )\
		return [self free];\
	[msgID setStringValue: "Login successful"];\
	[hostnameID setStringValue: hostname];\

\fc1\cf1 	[statusPanelID display];\

\fc0\cf0 	[self ftpType: ftpInfo type: "binary"];\
	currentDirectory = [self ftpPwd: ftpInfo];\
	loginDirectory = NXCopyStringBuffer(currentDirectory);\
\
	
\b0\i\fc1\cf1 /* Prepare for an interactive session */
\b\i0\fc0\cf0 \
	if( isInteractive == YES )\
	\{	
\b0\i\fc1\cf1 /* Build the list of root files */
\b\i0\fc0\cf0 \
\
		
\b0\i\fc1\cf1 /* First try to cd to the root directory */
\b\i0\fc0\cf0 \
		if( [FTPObject userIsAnonymous: username] == NO &&\
			[self ftpCwd: ftpInfo newDir: "/"] != 0 )\
		\{	
\b0\i\fc1\cf1 /* We failed to move to root directory, use the login\
				directory as the root of the file system */
\b\i0\fc0\cf0 \
			currentDirectory = loginDirectory;\
			[self ftpCwd: ftpInfo newDir: (char *) loginDirectory];\
		\}\
		else\
		\{\
			currentDirectory = "/";\
		\}\
\
		
\b0\i\fc1\cf1 /* Create a dummy root FtpFile to hold the root directory\
			contents of the remote ftp top level directory */
\b\i0\fc0\cf0 \
		rootDir = [[FtpFile alloc] ftpInit: &dummyRoot\
			
\fc1\cf1 host: hostname parentDir: 
\fc0\cf0 0
\fc1\cf1  info: ftpInfo];\
		[rootDir setDelegate: self];\
		rootFileList = [
\fc0\cf0 rootDir ftpLoadDir: self];\
		
\b0\i\fc1\cf1 /* See if we found any files */
\b\i0\fc0\cf0 \
		if( [rootFileList count] == 0 )\
		\{\
			[self alertOk: "Failed to find any files" quit: NO];\
			return [self logout: self];\
		\}\
\
		
\b0\i /* Init the orphan dir list. The files in this list are those\
			directories that have been loaded while trying to resolve\
			links that do not have their parent within the rootDir heirarchy */
\b\i0 \
		orphanDirs = [[ObjectList alloc] initCount: 0 sortAlgorithm: eShellSort\
			typeID: eStringType keyMethod: @selector(sourcePathname)];\
\
		
\b0\i\fc1\cf1 /* Display the ftp session window */
\b\i0\fc0\cf0 \
		if( ftpWindowID == nil )\
		\{\
			[NXApp loadNibSection: "FtpSession.nib" owner: self withNames: NO];\
			// Set the auto frame save name\
			[ftpWindowI
\fc1\cf1 D setFrameAutosaveName: "FtpSessionFrame"];\
			// Restore the last saved frame size\
			[ftpWindowID setFrameUsingName: "FtpSessionFrame"];\
		\}\

\fc0\cf0 		if( ftpWindowID == nil )\
		\{\
			[self error: INTERNAL_ALERT method:_cmd key:\
				"Failed to load FtpSession.nib"];\
			return [self logout: self];\
		\}\

\b0\fs20 [self debug: MAX_DEBUG method:_cmd, "ftpWindow = %p", ftpWindowID];
\b\fs24 \
		[ftpWindowID makeKeyAndOrderFront: self];\
		[statusMsgID setStringValue: 0];\
		[ftpWindowID setTitle: ftpHost];\
		[fileBrowserID
\fc1\cf1  setCellClass: [MyBrowserCell class]];\
		[fileBrowserID setMultipleSelectionEnabled: YES];\
		[fileBrowserID setDoubleAction: @selector(openInWorkspace:)];\
		if( loginDirectory == 
\fc0\cf0 0
\fc1\cf1  )\
		\{	
\b0\i /* Try to limp along but if we could not get the login dir,\
				but something is wrong */
\b\i0 \
			[fileBrowserID setTitle: ftpHost ofColumn: 
\fc0\cf0 0
\fc1\cf1 ];\
		\}\
		else if( strcmp(
\fc0\cf0 currentDirectory
\fc1\cf1 , loginDirectory) == 
\fc0\cf0 0
\fc1\cf1  )\
			[fileBrowserID setTitle: loginDirectory ofColumn: 
\fc0\cf0 0
\fc1\cf1 ];\
		else\
			[fileBrowserID setTitle: "/" ofColumn: 
\fc0\cf0 0
\fc1\cf1 ];\
		[fileBrowserID loadColumnZero];\

\fc0\cf0 		\
		if( initialPath != 0 && strcmp(initialPath, currentDirectory) != 0 )\
		\{	// Try to select to the initialPath if it has been set\

\fc1\cf1 			[fileBrowserID setPath: 
\fc0\cf0 initialPath
\fc1\cf1 ];\
			[fileBrowserID sendAction];\

\fc0\cf0 		\}
\fc1\cf1 \
		else if( strcmp(
\fc0\cf0 currentDirectory
\fc1\cf1 , loginDirectory) != 
\fc0\cf0 0
\fc1\cf1  )\
		\{	
\b0\i // Set the current selection to the user login dir
\b\i0 \
			[fileBrowserID setPath: loginDirectory];\
			[fileBrowserID sendAction];\
		\}\
		[fileWellID setDelegate: self];\

\fc0\cf0 	\}\
\
	return self;\
\} 
\b0\i // En
\fc1\cf1 d login: user: passwd:
\b\i0\fc0\cf0 \

\f0 \

\f1 - logout: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker12902 \markername logout:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if this isInteractive is NO, [self free] otherwise;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method closes the connection to the\
		Anonymous FTP host;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		sender: Any old object;\
*/
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc0\cf0 \
	[clockViewID endTimer: self];\
	if( [ftpWindowID isVisible] == YES )\
		[ftpWindowID orderOut: self];\
	if( [
\fc1\cf1 statusPanelID
\fc0\cf0  isVisible] == YES )\
		[
\fc1\cf1 statusPanelID
\fc0\cf0  orderOut: self];\
	if( ftpInfo != 0 )\
		[self ftpQuit: ftpInfo];\
\
	ftpInfo = 0;\
	ftpHost = 0;\
	if( isInteractive == YES )\
		return [self free];\
\
	return self;\
\} 
\b0\i // End logout
\b\i0 \
\
- (NXStream *) retrieveFiles: fileList\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker13486 \markername retrieveFiles:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  A new NXStream of tab delimited pathnames of the\
		files that were transfered;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method performs a non-interactive retrieval\
		of the Files contained in fileList;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		fileList: A List of File subclass objects;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 int fileIndex;\
const char *localPathname;\
NXStream *nameStream;\
File *file;\
Button *hideBtn;\
\
	if( 
\fc1\cf1 statusPanelID
\fc0\cf0  == nil )\
		[NXApp loadNibSection: "Ftp.nib" owner: self withNames: YES];\
	if( 
\fc1\cf1 statusPanelID
\fc0\cf0  == nil )\
	\{\
		[self error: INTERNAL_ERROR method:_cmd key: "Failed to load Ftp.nib"];\
		return 0;\
	\}\
	ftpSess
\fc1\cf1 ion = [NXApp beginModalSession: 
\fc0\cf0 0
\fc1\cf1  for: statusPanelID];\
	
\fc0\cf0 hideBtn
\fc1\cf1  = NXGetNamedObject("HideApp", statusPanelID);\
	[
\fc0\cf0 hostnameID
\fc1\cf1  setStringValue: 
\fc0\cf0 ftpHost
\fc1\cf1 ];\
	[
\fc0\cf0 bytesTransferredID
\fc1\cf1  setIntValue: 0];\
	[
\fc0\cf0 percentID
\fc1\cf1  displayPercent: 0];\
	[[[statusPanelID contentView] addSubview: hideBtn] display];\
	[statusPanelID makeKeyAndOrderFront: self];\

\fc0\cf0 	[clockViewID startMinSecTimer: self];\

\fc1\cf1 	[msgID setStrin
\fc0\cf0 gValue: "Connecting to ftp host..."];\
\
NX_DURING\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 	if( ftpInfo == 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 0
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0  )\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 		NX_RAISE(eNoFtpHost, 0, 0);\
\
	
\b0\i\fc1\cf1 /* Open a memory stream for the tab delimited list of names\
		that are transfered */
\b\i0\fc0\cf0 \

\fc1\cf1 	nameStream = NXOpenMemory(
\fc0\cf0 0
\fc1\cf1 , 
\fc0\cf0 0
\fc1\cf1 , NX_READWRITE);\
	if( nameStream == 
\fc0\cf0 0
\fc1\cf1  )\
		NX_RAISE(eRtnMemoryErr, 
\fc0\cf0 0
\fc1\cf1 , 
\fc0\cf0 0
\fc1\cf1 );\

\fc0\cf0 \
	
\b0\i\fc1\cf1 /* Retreive the requested files */
\b\i0\fc0\cf0 \
	[msgID setStringValue: "Retrieving files..."];\
	fileIndex = 0;\
	while( (file = [fileList objectAt: fileIndex]) != nil )\
	\{\
	
\fc1\cf1 	if ( [NXApp runModalSession: ftpSession] != NX_RUNCONTINUES )\
			NX_RAISE(eFTPAbort, 
\fc0\cf0 0
\fc1\cf1 , 
\fc0\cf0 0
\fc1\cf1 );\

\fc0\cf0 \
		
\b0\i /* Get the local name of the file */
\b\i0 \
		localPathname = [file localPathname];\
		if( localPathname == 0 )\
		\{	
\b0\i /* File has no local copy, get one */
\b\i0 \
			if( (localPathname = [self createLocalFile: file]) == 0 )
\fc1\cf1 \
				NX_RAISE(eRetreiveErr, 
\fc0\cf0 [file sourceName]
\fc1\cf1 , 
\fc0\cf0 0
\fc1\cf1 );\
		\}
\fc0\cf0 \
\

\fc1\cf1 		NXPrintf(nameStream
\fc0\cf0 , "%s\\t", localPathname);\
		fileIndex ++;\
	\}\
NX_HANDLER\
	s
\fc1\cf1 witch( NXLocalHandler.code )\
	\{	
\b0\i /* Handle the exceptions which can be raised */
\b\i0 \
		case eFTPAbort:\
			if( currentSubDir != 
\fc0\cf0 0
\fc1\cf1  )\
				free(currentSubDir);\
			currentSubDir = 
\fc0\cf0 0
\fc1\cf1 ;\
			break;\
		case eNoFtpHost:\
			[self alert: "FTP" msg: "FTPOject not connected to any host"\
		
\fc0\cf0 		btn1: "Ok" btn2: 0 btn3: 0];\
			break;\
		case eOpenFailure:\
			[self alert: "FTP" msg: "Failed to open data connection for: %s"\
				btn1: "Ok" btn2: 0 btn3: 0, NXLocalHandler.data1];\
			break;\
		case 
\fc1\cf1 eRtnMemoryErr
\fc0\cf0 :\
			[self error: INTERNAL_ERROR method:_cmd\
				key: "Failed to create memory for file transfer list"];\
			break;\
		case eNoFileEntry:	// data1 = filename\
			[self alert: "FTP" msg: "Failed to find [%s]"\
				btn1: "Ok" btn2: 0 btn3: 0, NXLocalHandler.data1];\
			break;\
		case 
\fc1\cf1 eRetreiveErr
\fc0\cf0 :	// data1 = filename\
			[self alert: "FTP" msg: "Failed to initiate retreival of [%s]"\
				btn1: "Ok" btn2: 0 btn3: 0, NXLocalHandler.data1];\
			break;\
		case 
\fc1\cf1 eFileMemoryErr:\

\fc0\cf0 			[self error: INTERNAL_ERROR method:_cmd\
				key: "Failed to create memory for file transfer"];\
			break;\
		default:\
			[self alert: "FTP" msg: "Unexpected exception, code = %d"\
				btn1: "Ok" btn2: 0 btn3: 0, NXLocalHandler.code];\
			break;\
	\}\
	if( nameStream != 0 )\
		NXCloseMemory(nameStream, NX_FREEBUFFER);\
	nameStream = 0;\
NX_ENDHANDLER\

\fc1\cf1 \
	if( [preferences audioNotification] == YES )\
	\{\
	Sound *notifySnd = [preferences notificationSnd: FTPTransfer];\
		[notifySnd play];\
	\}\

\fc0\cf0 	[msgID setStringValue: "Transfer Complete"];\
	[filenameID setStringValue: 0];\

\fc1\cf1 	[NXApp endModalSession: ftpSession];\
	ftpSession = 
\fc0\cf0 0
\fc1\cf1 ;\
	[hideBtn removeFromSuperview];\
	[statusPanelID orderOut: self];\
	[clockViewID endTimer: self];\
	[NXApp unhide: self];\

\fc0\cf0 \
	return nameStream;\
\}
\b0\i  // End retrieveFiles:
\b\i0 \

\f0 \

\f1 - setLocalTransferDir:(const char *) transferDir\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker17125 \markername setLocalTransferDir:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self a new directory is set, nil otherwise;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method sets the local host directory\
		for file transfers.  If transferDir is 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 , an OpenPanel\
		is run prompting the user for a directory.;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		
\fc1\cf1 transferDir
\fc0\cf0 : The directory to use, 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1  
\fc0\cf0 if we should\
			prompt the user;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 OpenPanel *openPanel;\
\
	if( transferDir == 0 )\
	\{	
\b0\i\fc1\cf1 /* Prompt the user for the directory */
\b\i0\fc0\cf0 \
		open
\fc1\cf1 Panel = [OpenPanel new];\
		[openPanel chooseDirectories: YES];\
		if( [openPanel runModalForTypes: 
\fc0\cf0 0
\fc1\cf1 ] == 
\fc0\cf0 0
\fc1\cf1  )\
			return nil;\
		transferDir = [openPanel filename];\

\fc0\cf0 	\}\
\
	if( localTransferDir != 0 )\
		free(localTransferDir);\
	localTransferDir = NXCopyStringBuffer(
\fc1\cf1 transferDir);
\fc0\cf0 \
\
	return self;\
\} 
\b0\i // End setLocalTransferDir:
\b\i0 \
\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 - setTransferDir: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker17892 \markername setTransferDir:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method invokes 
\b\i0\fc0\cf0 setLocalTransferDir
\b0\i\fc1\cf1  with a 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc0\cf0 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 \
		argument to bring up an OpenPanel prompting the user for a directory.\
		This is the action method for the "Set Local Dir..." FTP menu item.;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		
\fc1\cf1 sender
\fc0\cf0 : The Matrix of the FTP submenu;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \
	[self setLocalTransferDir: 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 0
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 ];\
\
	return self;\
\} 
\b0\i\fc1\cf1 // End setTransferDir:
\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 \
- (NXStream *) streamForCmd:(const char *) command :(const char *) arg\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker18341 \markername streamForCmd: :;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  A NXStream containing the data from the server\
		if successful, 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1  otherwise;\
	
\i0\ul Description:
\i\ulnone  Invokes the ftpOpenDataConn:cmd:arg: command to\
		create a data socket for the server response to command.\
		The method copies the data from the socket to a NXSteam\
		which it returns.;\
	
\i0\ul Args:
\i\ulnone \
		command: The command to send to the ftp host;\
		arg: A single, possibly 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1  argument to command;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc0\cf0 NXStream *stream;\

\fc1\cf1 BOOL runTransferThread;\

\fc0\cf0 int modalStatus, n, priority;\

\fc1\cf1 void 
\fc0\cf0 TransferFile
\fc1\cf1 (struct 
\fc0\cf0 FileTransferInfo *);\

\fc1\cf1 struct thread_basic_info threadInfo;\
unsigned infoCount = THREAD_BASIC_INFO_COUNT;\
thread_t selfThead = cthread_thread(cthread_self());\
\

\fc0\cf0 	if( [self ftpOpenDataConn: ftpInfo cmd:(char *) command arg:(char *) arg] == -1 )\
		return 0;\
\

\fc1\cf1 	
\b0\i /* Open a memory stream and use it as a dynamic buffer for the\
		command output */
\b\i0 \
	stream = NXOpenMemory(
\fc0\cf0 0
\fc1\cf1 , 
\fc0\cf0 0
\fc1\cf1 , NX_READWRITE);\
	if( stream == 
\fc0\cf0 0
\fc1\cf1  )\
	\{\
		[self error: INTERNAL_ALERT method: _cmd\
			key: "Failed to open memory for command: %s", command];\
		return 
\fc0\cf0 0
\fc1\cf1 ;\
	\}\
\

\fc0\cf0 	
\b0\i\fc1\cf1 /* Read the command data connection into the memory stream.  If this\
		is a file retrieval, we update the byte transferred status. */
\b\i0\fc0\cf0 \
	
\fc1\cf1 runTransferThread
\fc0\cf0  = (strcmp(command, "RETR") == 0);\
	if( runTransferThread )\

\fc1\cf1 	\{	
\b0\i // Fork a thread for the data transfer
\b\i0 \
	long startTime, endTime;\
		transferInfo.ftpd = self;\
		transferInfo.fileStream = stream;\
		transferInfo.
\fc0\cf0 bytesTransferred = 0;\
		transferInfo.abort = NO;\
		transferInfo.done = NO;
\fc1\cf1 \
		
\fc0\cf0 transferInfo.
\fc1\cf1 transferThread = cthread_fork(
\fc0\cf0 TransferFile
\fc1\cf1 , &transferInfo);\
		cthread_detach(
\fc0\cf0 transferInfo.
\fc1\cf1 transferThread);\
		
\f2\fs28\fc0\cf0 thread_
\f1\fs24\fc1\cf1 info(selfThead, THREAD_BASIC_INFO, &threadInfo, &infoCount);\
		
\fc0\cf0 priority = [[NXApp preferences] transferPriority];\
		if( priority > 0 )\
			cthread_priority(cthread_self(), priority-1, FALSE);
\fc1\cf1 \
		time(&startTime);\
		
\b0\i // Responed to user events
\b\i0 \
		
\fc0\cf0 modalStatus 
\fc1\cf1 = NX_RUNCONTINUES;\
		do\
		\{\
			if( ftpSession != 0 )\
				
\fc0\cf0 modalStatus
\fc1\cf1  = [NXApp runModalSession: ftpSession];\
			[bytesTransferredID setIntValue: transferInfo.
\fc0\cf0 bytesTransferred
\fc1\cf1 ];\
			[percentID updateSession: transferInfo.
\fc0\cf0 bytesTransferred];\
			
\b0\i // Indicate we are not in a hurry in this thread
\b\i0 \
			cth
\fc1\cf1 read_yield();\
			
\b0\i // Check for Command-. aborts
\b\i0 \
			if( NXUserAborted() == YES )\
				[self abort: self];\
			NXResetUserAbort();\
		\} while( transferInfo.done == NO &&
\fc0\cf0  modalStatus =
\fc1\cf1 = NX_RUNCONTINUES );\

\fc0\cf0 		if( priority > 0 )\
			cthread_priority(cthread_self(
\fc1\cf1 ), threadInfo.base_priority, FA
\fc0\cf0 LSE);
\fc1\cf1 \
		transferInfo.
\fc0\cf0 transferThread
\fc1\cf1  = 0;\
		time(&endTime);\
		
\b0\fs20 [self debug: LOW_DEBUG method:_cmd, "Transfer time = %d secs\\n",\
			(endTime - startTime)];\

\b\fs24 		if( transferInfo.abort == YES )\
		\{	
\b0\i // Free the memory stream
\b\i0 \

\fc0\cf0 			NXCloseMemory(stream, NX_FREEBUFFER);\
			stream = 0;\

\fc1\cf1 		\}\
	\}\
	else\
	\{	
\b0\i // Transfer data in this thread
\b\i0 \

\fc0\cf0 		while( (n = [
\fc1\cf1 self
\fc0\cf0  ftpReadDataConn: ftpInfo buffer: 
\fc1\cf1 fileBuffer
\fc0\cf0 \
				length: 16384]) > 0 )\
			NXWrite(stream, 
\fc1\cf1 fileBuffer
\fc0\cf0 , n);\
	\}\
\
	
\b0\i // Close the data transfer connection to the ftp server
\b\i0 \
	if( [self ftpCloseDataConn:ftpInfo] == -1 && stream != 0 )\
	\{	
\b0\i // Error on close, f
\fc1\cf1 ree the memory stream
\b\i0\fc0\cf0 \
		NXCloseMemory(stream, NX_FREEBUFFER);\
		stream = 0;\
	\}\

\b0\fs20 if( [self appDebugLevel] > MAX_DEBUG )\
\{
\fc1\cf1 \
	if( runTransferThread == NO )\
	\{\
		NXSeek(stream, 0, NX_FROMSTART);\
		fprintf(stderr, "--- streamForCmd result:\\n");\
		while( (n = NXRead(stream, fileBuffer, BUFSIZ)) > 0 )\
			write(2, fileBuffer, n);\
		
\fc0\cf0 fprintf(stderr, "---\\n");\
	\}\
\}\

\b\fs24 \
	return stream;\
\} 
\b0\i // End streamForCmd:
\b\i0 \
\

\fc1\cf1 void 
\fc0\cf0 TransferFile
\fc1\cf1 (struct 
\fc0\cf0 FileTransferInfo *transferInfo)\

\fc1\cf1 \{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker21827 \markername streamForCmd: :;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  None;\
	
\i0\ul Description:
\i\ulnone  This is the FTP transfer thread's entry point. It\
		reads chunks of data off the FTP data connection using the\
		ftpReadDataConn method into the static file fileBuffer and then\
		writes each chunk to the transferInfo->fileStream memory stream. If\
		the user aborts the transfer, the transferInfo->abort flag is set\
		to YES and the transfer is aborted.;\
	
\i0\ul Args:
\i\ulnone \
		transferInfo: The struct used to communicate witht the main thread;\
*/
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 \
FTPObject *ftpd = 
\fc0\cf0 transferInfo
\fc1\cf1 ->ftpd;\
int n, priority;\
Preferences *prefs;\
\
	prefs = [NXApp preferences];\
	priority = [prefs transferPriority];\
	cthread_priority(cthread_self(), priority, FALSE);\
	cthread_set_errno_self(0);\
	do\
	\{	
\b0\i // Transfer the data to the file stream
\b\i0 \

\fc0\cf0 		n = [
\fc1\cf1 ftpd
\fc0\cf0  ftpReadDataConn: 
\fc1\cf1 ftpd->
\fc0\cf0 ftpInfo buffer: 
\fc1\cf1 fileBuffer
\fc0\cf0 \
				length: 16384];\
		if( n > 0 && transferInfo->abort == NO )\
		\{
\fc1\cf1 \
			
\fc0\cf0 transferInfo
\fc1\cf1 ->
\fc0\cf0 bytesTransferred += n;\
			NXWrite(transferInfo
\fc1\cf1 ->file
\fc0\cf0 Stream, 
\fc1\cf1 fileBuffer
\fc0\cf0 , n);\
		\}\
	\} w
\fc1\cf1 hile( n > 
\fc0\cf0 0
\fc1\cf1  && 
\fc0\cf0 transferInfo->abort == NO
\fc1\cf1  );\

\b0\fs20 	if( [ftpd appDebugLevel] > LOW_DEBUG )\
	\{\
	int terrno = cthread_errno();\
		fprintf(stderr, "TransferFile exit, error = %s\\n",\
			(terrno == 0 ? "Success" : strerror(terrno)));\
	\}\

\b\fs24 	
\fc0\cf0 transferInfo
\fc1\cf1 ->done = YES;\
	cthread_exit(
\fc0\cf0 0
\fc1\cf1 );\
\} 
\b0\i // End 
\fc0\cf0 TransferFile
\fc1\cf1 ()
\b\i0 \
\

\b0\i\fs28 /*\\ --------------- FTP Action Methods --------------- \\*/
\b\i0\fs24\fc0\cf0 \
- openInWorkspace: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker23203 \markername openInWorkspace:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This method opens the non-directory files in the\
		Workspace by sending the File object an openInWorkspace\
		message. This is the doubleAction method of the file browser.;\
	
\i0\ul Args:
\i\ulnone \
		sender: Our fileBrowserID object or the FileWellView object;\
*/\

\b\i0\fc0\cf0 FtpFile *theFile;\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 MyBrowserCell *cell;\
\
	cell = [fileBrowserID selectedCell];\
	theFile = [cell tag];\
	[theFile openInWorkspace];\
\
	return self;\
\} 
\b0\i\fc1\cf1 // End openInWorkspace:
\b\i0\fc0\cf0 \
\
- retrieve: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker23700 \markername retrieve:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This method 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 retrieve
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 s the files selected in the browser\
		using our 
\b createLocatFile:
\b0  method for each file;\
	
\i0\ul Args:
\i\ulnone \
		sender: The Matrix of the Ftp submenu;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc0\cf0 MyBrowserCell *cell;\
FtpFile *theFile;\
int fileIndex;\
Button *hideBtn;\
\
	if( 
\fc1\cf1 statusPanelID
\fc0\cf0  == nil )\
		[NXApp loadNibSection: "Ftp.nib" owner: self withNames: YES];\
	if( 
\fc1\cf1 statusPanelID
\fc0\cf0  == nil )\
	\{\
		[self error: INTERNAL_ERROR method:_cmd key: "Failed to load Ftp.nib"];\
		return 0;\
	\}\
	ftpSess
\fc1\cf1 ion = [NXApp beginModalSession: 
\fc0\cf0 0
\fc1\cf1  for: statusPanelID];\
	
\fc0\cf0 hideBtn
\fc1\cf1  = NXGetNamedObject("HideApp", statusPanelID);\
	[
\fc0\cf0 hostnameID
\fc1\cf1  setStringValue: ftpHost];\
	[
\fc0\cf0 bytesTransferredID
\fc1\cf1  setIntValue: 0];\
	[
\fc0\cf0 percentID displayPercent: 0];
\fc1\cf1 \
	[[[statusPanelID contentView] addSubview: hideBtn] display];\
	[statusPanelID makeKeyAndOrderFront: self];\

\fc0\cf0 	[clockViewID startMinSecTimer: self];\

\fc1\cf1 	[msgID setStrin
\fc0\cf0 gValue: "Connecting to ftp host..."];\
\
NX_DURING\
	
\b0\i\fc1\cf1 /* Retreive the requested files */
\b\i0\fc0\cf0 \
	[msgID setStringValue: "Retrieving files..."];\
	fileIndex = 0;\
	while( (cell = [selectionList objectAt: fileIndex]) != nil )\
	\{\
	
\fc1\cf1 	if ( [NXApp runModalSession: ftpSession] != NX_RUNCONTINUES )\
			NX_RAISE(eFTPAbort, 
\fc0\cf0 0
\fc1\cf1 , 
\fc0\cf0 0
\fc1\cf1 );\

\fc0\cf0 \
		theFile = [cell tag];\
		[self createLocalFile: theFile];\
		fileIndex ++;\
	\}\
\
NX_HANDLER\
	s
\fc1\cf1 witch( NXLocalHandler.code )\
	\{	
\b0\i /* Handle the exceptions which can be raised */
\b\i0 \
		case eFTPAbort:\
			if( currentSubDir != 
\fc0\cf0 0
\fc1\cf1  )\
				free(currentSubDir);\
			currentSubDir = 
\fc0\cf0 0
\fc1\cf1 ;\
			break;\
		case eNoFtpHost:\
			[self alert: "FTP" msg: "FTPOject not connected to any host"\
		
\fc0\cf0 		btn1: "Ok" btn2: 0 btn3: 0];\
			break;\
		case eOpenFailure:\
			[self alert: "FTP" msg: "Failed to open data connection for: %s"\
				btn1: "Ok" btn2: 0 btn3: 0, NXLocalHandler.data1];\
			break;\
		case 
\fc1\cf1 eRtnMemoryErr
\fc0\cf0 :\
			[self error: INTERNAL_ERROR method:_cmd\
				key: "Failed to create memory for file transfer list"];\
			break;\
		case eNoFileEntry:	// data1 = filename\
			[self alert: "FTP" msg: "Failed to find [%s]"\
				btn1: "Ok" btn2: 0 btn3: 0, NXLocalHandler.data1];\
			break;\
		case 
\fc1\cf1 eRetreiveErr
\fc0\cf0 :	// data1 = filename\
			[self alert: "FTP" msg: "Failed to initiate retreival of [%s]"\
				btn1: "Ok" btn2: 0 btn3: 0, NXLocalHandler.data1];\
			break;\
		case 
\fc1\cf1 eFileMemoryErr:\

\fc0\cf0 			[self error: INTERNAL_ERROR method:_cmd\
				key: "Failed to create memory for file transfer"];\
			break;\
		default:\
			[self alert: "FTP" msg: "Unexpected exception, code = %d"\
				btn1: "Ok" btn2: 0 btn3: 0, NXLocalHandler.code];\
			break;\
	\}\
NX_ENDHANDLER\

\fc1\cf1 \
	if( [preferences audioNotification] == YES )\
	\{\
	Sound *notifySnd = [preferences notificationSnd: FTPTransfer];\
		[notifySnd play];\
	\}\
	[msgID setStringValue: "Transfer Complete"];\

\fc0\cf0 	[filenameID setStringValue: 0];\

\fc1\cf1 	[NXApp endModalSession: ftpSession];\
	ftpSession = 
\fc0\cf0 0
\fc1\cf1 ;\
	[hideBtn removeFromSuperview];\
	[statusPanelID orderOut: self];\
	[clockViewID endTimer: self];\
	[NXApp unhide: self];\

\fc0\cf0 \
	return self;\
\} 
\b0\i\fc1\cf1 // End retrieve:
\b\i0\fc0\cf0 \
\
- showFTPLog: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker26649 \markername showFTPLog:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  Displays a panel with a scrollable Text object to which\
		the output from the remote ftpd is dumped;\
	
\i0\ul Args:
\i\ulnone \
		sender: The Matrix from the FTP submenu;\
*/
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 \
	if( logPanelID == nil )\
	\{
\b0\i \

\b\i0 	char title[128];\
		logPanelID = NXGetNamedObject("FTPLog", NXApp);\
		if( logPanelID == nil )\
			[
\fc0\cf0 NX
\fc1\cf1 App loadNibSection: "FtpLog.nib" owner: self withNames: YES];\
		NXNameObject("FTPLog", logPanelID, NXApp);\
		sp
\fc0\cf0 rintf(title, "%s -- FTP Log", ftpHost);\
		[logPanelID setTitle: title];\
	\}\

\b0\fs20 [self debug: MAX_DEBUG method:_cmd, "textConsole = %p", ftpLogID];
\b\fs24 \
	if( sender != self )\
		[logPanelID orderFront: self];\
\
	return self;\
\} 
\b0\i // End showFTPLog:
\b\i0 \
\
- showFTPStatus: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\gray250\fc2\cf2 /*
{{\NeXTHelpMarker27362 \markername showFTPStatus:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\gray250\fc2\cf2  --- MethodDescription\
	ReturnValue: self;\
	Description: Displays a panel with various FTP session status\
		information;\
	Args:\
		sender: The Matrix from the FTP submenu;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 Button *hideBtn;\
	if( statusPanelID == nil )\
	\{
\b0\i \

\b\i0 	char title[128];\

\fc0\cf0 		[NXApp loadNibSection: "Ftp.nib" owner: self withNames: YES];\
		sprintf(title, "%s -- FTP Status", ftpHost);\
		[
\fc1\cf1 statusPanelID
\fc0\cf0  setTitle: title];\
		[hostnameID setStringValue: ftpHost];\
	\}\

\b0\fs20 [self debug: MAX_DEBUG method:_cmd, "status panel = %p", statusPanelID];
\b\fs24 \
	hideBtn = NXGetNamedObject("HideApp", 
\fc1\cf1 statusPanelID);
\fc0\cf0 \
	[hideBtn removeFromSuperview];\
	[[statusPanelID orderFront: self] display];\
\
	return self;\
\} 
\b0\i // End showFTPStatus:
\b\i0 \

\fc1\cf1 - clearStatusMsg: ignored\

\fc0\cf0 \{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\gray250\fc2\cf2 /*
{{\NeXTHelpMarker28066 \markername showFTPStatus:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\gray250\fc2\cf2  --- MethodDescription\
	ReturnValue: self;\
	Description: Clears the status message TextField located above the\
		interactive FTP window browser;\
	Args:\
		ignored: self;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 	[statusMsgID setStringValue: 0];\
	return self;
\fc0\cf0 \
\}\
- fileFromPath:(const char *) path\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker28328 \markername fileFromPath:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The FtpFile object for path is it exits in our cache,\
		nil otherwise;\
	
\i0\ul Description:
\i\ulnone  Searches for the FtpFile object corresponding to the\
		path argument by traversing from the top level files in rootFileList;\
	
\i0\ul Args:
\i\ulnone \
		path: The fully qualified path to the file;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 FtpFile *ftpFile;\
\
	ftpFile = [rootDir fileFromPath: path];\
	if( ftpFile == 
\fc0\cf0 0
\fc1\cf1  )\
	\{	
\b0\i // Check to see if this is an orphan dir
\b\i0 \

\fc0\cf0 	int fileIndex = [orphanDirs binarySearchUsing:(void *) path canFail: NO];\
		if( fileIndex > 0 )\
		\{	
\b0\i\fc1\cf1 // Yes it is
\b\i0\fc0\cf0 \
			fileIndex --;\

\fc1\cf1 			ftpFile
\fc0\cf0  = [orphanDirs objectAt: fileIndex];\
		\}
\fc1\cf1 \
	\}\
\
	return ftpFile;\

\fc0\cf0 \} 
\b0\i // End fileFromPath:
\b\i0 \
\
- addOrphanDir:(FtpFile *) ftpDir\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker29021 \markername fileFromPath:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  Inserts the ftpDir object info the list of directories\
		for which we don't have a parent directory listing. This ocurrs\
		when we resolve a link that points to a directory in some portion\
		of the file system not yet visited. An orphan dir is reattached to\
		the rootFile when its real parent(not parent of the link) has\
		been loaded and a user selects the directory(see ftpLoadDir: FtpFile).;\
	
\i0\ul Args:
\i\ulnone \
		ftpDir: The orphan dir object;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc0\cf0 	[orphanDirs insertObject: ftpDir];\
	return self;\
\} 
\b0\i // End addOrphanDir:
\b\i0 \
\
- removeOrphanDir:(const char *) path\
\{\

\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker29634 \markername ftpLoadDir:;}
,}\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The FtpFile dir if one exists for path, nil otherwise;\
	
\i0\ul Description:
\i\ulnone  This method searches the orphanDir list for a dir\
		corresponding to path. If a match is found the dir is removed from\
		the list and returned.;\
	
\i0\ul Args:
\i\ulnone \
		path: The fully qualified path to the directory;\
*/
\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 int fileIndex;\
FtpFile *ftpDir = nil;\
\
	
\b0\i /* Search the orphanDirs list for an exact match. The index returned\
		is either one past the actual index if a match was found, or -1. */
\b\i0 \
	fileIndex = [orphanDirs binarySearchUsing:(void *) path canFail: NO];\
	fileIndex --;\
	if( fileIndex >= 0 )\
		ftpDir = [orphanDirs removeObjectAt: fileIndex];\
\
	return ftpDir;\
\} 
\b0\i // End removeOrphanDir:
\b\i0 \
\

\b0\i\fs28\fc1\cf1 /*\\ --------------- File Delegate Methods --------------- \\*/
\b\i0\fs24\fc0\cf0 \
- incSubDirPath:(const char *) newDir\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker30433 \markername incSubDirPath:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  Add the newDir directory to currentSubDir;\
	
\i0\ul Args:
\i\ulnone \
		newDir: The directory to add;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc0\cf0 char path[MAXPATHLEN];\
\
	if( currentSubDir == 0 )\
		strcpy(path, newDir);\
	else\
		sprintf(path, "%s/%s", currentSubDir, newDir);\
	if( currentSubDir != 0 )\
		free(currentSubDir);\
	currentSubDir = NXCopyStringBuffer(path);\
\
	return self;\
\} 
\b0\i\fc1\cf1 // End incSubDirPath:
\b\i0\fc0\cf0 \
\
- decSubDirPath\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker30857 \markername decSubDirPath;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  Remove the last component of currentSubDir;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc0\cf0 char *bottomDir;\
\
	if( currentSubDir == 0 )\
		return self;\
	bottomDir = rindex(currentSubDir, '/');\
	if( bottomDir == 0 )\
	\{	
\b0\i\fc1\cf1 /* This is the top of the sub directory path */
\b\i0\fc0\cf0 \
		free(currentSubDir);\
		currentSubDir = 0;\
	\}\
	else\
	\{	
\b0\i\fc1\cf1 /* 
\b\i0\fc0\cf0 0
\b0\i\fc1\cf1  terminate the path to remove the last directory */
\b\i0\fc0\cf0 \
		*bottomDir = '\\0';\
	\}\
\
	return self;\
\} 
\b0\i\fc1\cf1 // End decSubDirPath
\b\i0\fc0\cf0 \

\f0 \

\f1 - (const char *) createLocalFile: theFile\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker31356 \markername createLocalFile:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The pathname for the file on the local host if\
		sucessful, 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1  otherwise;\
	
\i0\ul Description:
\i\ulnone  This version of the method expects that it is\
		retreiving a file from the current ftpHost it is connected to.\
		As such, it transfers the file specified by [theFile sourcePath]\
		method to localTransferDir/[theFile sourceName].\
		Future versions will initiate ftp connections as necessary to\
		reteive the local copy of the file.;\
	
\i0\ul Args:
\i\ulnone \
		theFile: The File object requesting the local copy;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc0\cf0 char path[MAXPATHLEN], *localPathname;\
const char *localRootPathname, *sourceHost, *sourcePath, *tmpPath;\
NXStream *fileStream;\
BOOL getSubDirs, collisionWarnings;\
ObjectList *dirListing = nil;\
FtpFile *file, *rootFile = nil;\
int fileIndex = 0;\
\
	sourceHost = [theFile sourceHost];\
	if( strcmp(ftpHost, sourceHost) != 0 )\
	\{	
\b0\i\fc1\cf1 /* Do something useful in the future, for now return 
\b\i0\fc0\cf0 0
\b0\i\fc1\cf1  */
\b\i0\fc0\cf0 \
		return 0;\
	\}\
\
	localPathname = localRootPathname = 0;\
	getSubDirs = [preferences retrieveSubDirs];\
	collisionWarnings = [preferences collisionWarnings];\
	if( [theFile isDirectory] == YES )\
	\{	
\b0\i\fc1\cf1 /* Get the directory listing */
\b\i0\fc0\cf0 \
		if( [theFile isKindOf: [FtpFile class]] == NO )\
		\{	
\b0\i\fc1\cf1 /* Must be another subclass of File. Create a FtpFile object */
\b\i0\fc0\cf0 \
			file = [[FtpFile alloc] init: [theFile sourcePathname]\
				mode: [theFile modeString] size: [theFile fileSize]];\
			[file setFTPInfo: ftpInfo];\
			[file setDelegate: self];\
			[file setSourceHost: ftpHost];\
			theFile = file;\
		\}\
		
\b0\i\fc1\cf1 /* Form the local path for the new directory */
\b\i0\fc0\cf0 \
		if( currentSubDir != 0 )\
			sprintf(path, "%s/%s/%s", localTransferDir, currentSubDir,\
				[theFile sourceName]);\
		else\
			sprintf(path, "%s/%s", localTransferDir, [theFile sourceName]);\
		file = [theFile createLocalFile: 0 path: path\
			collisionWarnings: collisionWarnings];\
		if( file == nil )\
		\{\
			[self decSubDirPath];\
			return 0;\
		\}\
		
\b0\i\fc1\cf1 /* Update the current sub directory path */
\b\i0\fc0\cf0 \
		[self incSubDirPath: [theFile sourceName]];\
		
\b0\i\fc1\cf1 /* Obtain a listing of the directory contents */
\b\i0\fc0\cf0 \
		sourcePath = [theFile sourcePathname];\
		dirListing = [theFile dirListing: sourcePath ftpd: self];\
		theFile = [dirListing objectAt: fileIndex ++];\
		if( theFile == nil )\
			return 0;\
	\}\
\
	do\
	\{	
\b0\i\fc1\cf1 /* First see if this is a directory */
\b\i0\fc0\cf0 \
		if( [theFile isDirectory] == YES )\
		\{\
			if( getSubDirs == YES )\
			\{	
\b0\i\fc1\cf1 /* Recursively invoke this method to retreive its contents */
\b\i0\fc0\cf0 \
				if( [self createLocalFile: theFile] == 0 )\
					return 0;\
			\}\
			theFile = [dirListing objectAt: fileIndex ++];\
			continue;\
		\}\
\
		
\b0\i\fc1\cf1 /* Get the remote file location & build the local pathname */
\b\i0\fc0\cf0 \
		sourcePath = [theFile sourcePathname];\
		if( currentSubDir != 0 )\
			sprintf(path, "%s/%s/%s", localTransferDir, currentSubDir,\
				[theFile sourceName]);\
		else\
			sprintf(path, "%s/%s", localTransferDir, [theFile sourceName]);\
	\
		
\b0\i\fc1\cf1 /* Ask for the file */
\b\i0\fc0\cf0 \
		[msgID setStringValue: "Retrieving..."];\
		[filenameID setStringValue: [theFile sourceName]];\

\fc1\cf1 		[bytesTransferredID setIntValue: 
\fc0\cf0 0
\fc1\cf1 ];\
		[percentID newSession: [
\fc0\cf0 theFile fileSize]];\
		[statusPanelID display];\
		tmpPath = (char *) sourcePath;\
		if( *sourcePath == '/' )\
			tmpPath = sourcePath + 1;
\fc1\cf1 \

\fc0\cf0 		fileStream = [self streamForCmd: "RETR" : tmpPath];\
		if( fileStream != 0 )\
		\{	
\b0\i\fc1\cf1 // We got the data
\b\i0\fc0\cf0 \

\b0\fs20 [self debug: MAX_DEBUG method:_cmd, "writing local file as %s", path];
\b\fs24 \
			file = [theFile createLocalFile: 
\fc1\cf1 fileStream
\fc0\cf0  path: path\
				collisionWarnings: collisionWarnings];\
			if( file != nil )\
				localPathname = NXCopyStringBuffer(path);\
			NXCloseMemory(fileStream, NX_FREEBUFFER);\
		\}\
		if( localRootPathname == 0 )\
		\{	
\b0\i\fc1\cf1 /* Save the first filename since it is either the only file or the\
				root directory. Set localPathname to 0 so that it is not\
				 freed prior to returning. */
\b\i0\fc0\cf0 \
			rootFile = theFile;\
			localRootPathname = localPathname;\
			localPathname = 0;\
		\}\
		
\b0\i\fc1\cf1 /* If we are running a modal session (ftpSession != 0), we must\
			message NXApp with runModalSession: */
\b\i0\fc0\cf0 \
	
\fc1\cf1 	if ( ftpSession != 
\fc0\cf0 0
\fc1\cf1  &&\
				[NXApp runModalSession: ftpSession] != NX_RUNCONTINUES )\
		\{	// Session has been aborted, raise the abort\
			NX_RAISE(eFTPAbort, 
\fc0\cf0 0
\fc1\cf1 , 
\fc0\cf0 0
\fc1\cf1 );\
		\}\

\fc0\cf0 		theFile = [dirListing objectAt: fileIndex ++];\
	\} while( theFile != nil );\
\
	if( localPathname != 0 )\
		free(localPathname);\
	[self decSubDirPath];\
\
	return localRootPathname;\
\} 
\b0\i // End createLocalFile:
\b\i0 \
\
\

\b0\i\fs28\fc1\cf1 /*\\ ------------- Browser Delegates Methods ------------- \\*/
\b\i0\fs24\fc0\cf0 \

\fc1\cf1 - (int) browser: sender getNumRowsInColumn:(int) column\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker35780 \markername browser:getNumRowsInColumn:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The number of rows that could be displayed in the column;\
	
\i0\ul Description:
\i\ulnone  This method calculates the number of files (rows) for\
		the directory FtpFile corresponding to the branch selection in the\
		previous column.  If the information has not been loaded,\
		we ask the FtpFile to load itself.;\
	
\i0\ul Args:
\i\ulnone \
		sender: The browser we are delegate to;\
		column: The column number we are loading; \
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 static List *fileList;\
FtpFile *activeDir;\
int rows;\
ObjectList *
\fc0\cf0 rootFileList = [rootDir dirListing];\

\fc1\cf1 \
	if( column == 
\fc0\cf0 0
\fc1\cf1  )\
	\{	
\b0\i /* Get the file count from our rootFileList */
\b\i0 \
		rows = [
\fc0\cf0 rootFileList
\fc1\cf1  count];\
	\}\
	else\
	\{	
\b0\i /* Get the selected branch in column-1.  This File is the new node\
			at index column-1 of the activePathNodes. Note that nodes after\
			column-1 are not removed. */
\b\i0 \
		activeDir = [(MyBrowserCell *)[sender selectedCell] tag];\
		if( [activePathNodes count] >= column )\
		\{	
\b0\i /* A previously selected directory is the current node at\
				the column-1 level.  We replace that node with the new\
				selection. */
\b\i0 \
			[activePathNodes replaceObjectAt: column-1 with: activeDir];\
		\}\
		else\
			[activePathNodes addObject: activeDir];\
		fileList = [activeDir dirListing];\
		if( fileList == nil )\
		\{	
\b0\i /* This directory File has not loaded a listing of its\
				files.  We ask it to do so. */
\b\i0 \
			[activeDir ftpLoadDir: self];\
		\}\
		fileList = [activeDir dirListing];\
		rows = [fileList count];\
		
\b0\i /* The directory has no files if rows is 
\b\i0\fc0\cf0 0
\b0\i\fc1\cf1 .  We will display a\
			cell with "No files" */
\b\i0 \
		if( rows == 
\fc0\cf0 0
\fc1\cf1  )\
			rows = 1;\
	\}\
\
	return rows;\
\} 
\b0\i // End browser: getNumRowsInColumn:
\b\i0 \
\
- browser: sender loadCell: cell atRow:(int) row inColumn:(int) column\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker37459 \markername browser:loadCell:atRow:inColumn:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This method loads the argument cell with the info\
		for the FtpFile object it represents;\
	
\i0\ul Args:
\i\ulnone \
		sender: The browser we are delegate to;\
		cell: A MyBrowserCell object in need of loading;\
		row: The row number of the cell;\
		column: The column number we are loading; \
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 FtpFile *theFile, *lnTarget;\
ObjectList *fileList;\

\fc0\cf0 MyBrowserCell *theCell = cell;\

\fc1\cf1 Matrix *colMatrix;\
const char *fileName = "No files";\
BOOL enable = NO, isLeaf = YES, isLink = NO;\
\
	if( column == 
\fc0\cf0 0
\fc1\cf1  )	
\b0\i // File in top level directory
\b\i0 \
		theFile = [
\fc0\cf0 [rootDir dirListing]
\fc1\cf1  objectAt: row];\
	else\
	\{	
\b0\i /* Get the listing for the parent directory corresponding to the\
			cell being loaded, and  obtain the FtpFile for the cell.*/
\b\i0 \
		fileList = [[activePathNodes objectAt: column-1] dirListing];\
		theFile = [fileList objectAt: row];\
	\}\
	if( theFile != nil )\
	\{	
\b0\i // theFile will be nil if the parent directory contains no files
\b\i0 \
		if( [theFile isLink] == YES )\
		\{	
\b0\i // If the file does not have a link target just display theFile
\b\i0 \
			lnTarget = [theFile linkTarget];\
			isLeaf = ![lnTarget isDirectory];\
			fileName = [theFile sourceNameWithLink];\
			if( lnTarget != nil )\
			\{	/* Remove the -> portion of the file name and set\
					the file object to the link target */\
				fileName = [theFile sourceName];\
				theFile = lnTarget;\
				isLink = YES;\
			\}\
		\}\
		else\
		\{\
			isLeaf = ![theFile isDirectory];\
			fileName = [theFile sourceName];\
		\}\
		enable = YES;\
	\}\
\
	[
\fc0\cf0 theCell
\fc1\cf1  setStringValue: fileName];\
	[
\fc0\cf0 theCell
\fc1\cf1  setLeaf: isLeaf];\
	[
\fc0\cf0 theCell
\fc1\cf1  setLoaded: YES];\
	[
\fc0\cf0 theCell
\fc1\cf1  setTag: theFile];\
	[
\fc0\cf0 theCell
\fc1\cf1  setEnabled: enable];\
	if( isLink == YES )\
	\{	
\b0\i /* We need to redraw the link cell and have the matrix send\
			its action method to have links to directories load\
			the next column */
\b\i0 \
	int selectedCol;\
		colMatrix = [sender matrixInColumn: column];\
		[colMatrix drawCellAt: row : 0];\
		selectedCol = [sender selectedColumn];\
		if( selectedCol == column )\
		\{\
			[colMatrix selectCellAt: row : 0];\
			[colMatrix sendAction];\
		\}\
	\}\
\
	return self;\
\} 
\b0\i // End browser:loadCell:atRow:inColumn:
\b\i0 \
\
- (BOOL) browser: sender selectCell:(const char *) entry inColumn:(int) column\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker39638 \markername browser:loadCell:atRow:inColumn:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  YES if entry is located, NO otherwise;\
	
\i0\ul Description:
\i\ulnone  This method searches the ObjectList of files corresponding\
		to column for the file whose sourceName method returns entry. If such\
		an entry if found we setup, select and scroll to the corresponding cell.;\
	
\i0\ul Args:
\i\ulnone \
		sender: The browser we are delegate to;\
		entry: The partial filename displayed in the browser cell;\
		column: The column number in which the selection is being made;\
*/
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 \
Matrix *colMatrix;\
FtpFile *theFile, *parent, *lnTarget;\
ObjectList *fileList;\

\fc0\cf0 MyBrowserCell *theCell;\
BOOL isLeaf;\

\fc1\cf1 const char *fileName;\
int fileIndex;\
\
	colMatrix = [sender matrixInColumn: column];\
	if( column == 
\fc0\cf0 0
\fc1\cf1  )	
\b0\i // File in top level directory
\b\i0 \
		parent = rootDir;\
	else\
	\{	
\b0\i /* Get the listing for the parent directory corresponding to the\
			cell being selected */
\b\i0 \
		parent = [activePathNodes objectAt: column-1];\
	\}\
	fileList = [parent dirListing];\
	fileName = entry;\
	fileIndex = [fileList binarySearchUsing:(void *) fileName canFail: NO];\
	
\b0\i // binarySearchUsing:canFail: returns the index just after the item
\b\i0 \
	fileIndex --;\
	theFile = [fileList objectAt: fileIndex];\
	if( theFile == nil )\
	\{\

\b0\fs20\fc0\cf0 [self debug: LOW_DEBUG method:_cmd, "failed to select: %s", entry];\

\b\fs24\fc1\cf1 		[colMatrix selectCellAt: -1 : -1];\
		return NO;\
	\}\
\
	
\b0\i // Load, select and scroll to this cell
\b\i0 \
	theCell = [colMatrix cellAt: fileIndex : 
\fc0\cf0 0
\fc1\cf1  ];\
	[theCell setStringValue: [theFile sourceNameWithLink]];\
	lnTarget = [theFile linkTarget];\
	if( lnTarget != nil )\
	\{\
		[theCell setStringValue: [theFile sourceName]];\
		[theCell setTag: lnTarget];\
		isLeaf = ![lnTarget isDirectory];\
	\}\
	else\
	\{\
		[
\fc0\cf0 theCell
\fc1\cf1  setTag: theFile];\
		isLeaf = ![theFile isDirectory];\
	\}\
	[
\fc0\cf0 theCell
\fc1\cf1  setLoaded: YES];\
	[
\fc0\cf0 theCell
\fc1\cf1  setLeaf: isLeaf];\
	[
\fc0\cf0 theCell
\fc1\cf1  setEnabled: YES];\
	[colMatrix scrollCellToVisible: fileIndex : 
\fc0\cf0 0
\fc1\cf1 ];\
	[colMatrix selectCellAt: fileIndex : 
\fc0\cf0 0
\fc1\cf1 ];\

\b0\fs20\fc0\cf0 [self debug: LOW_DEBUG method:_cmd, "Select: %s", entry];\

\b\fs24\fc1\cf1 \
	return YES;\
\} 
\b0\i // End browser:selectCell:inColumn:
\b\i0 \
\
- displayLink:(FtpFile *) theFile\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker41659 \markername displaySelection:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This method is called to reload a link that was selected\
		and resolved in our displaySelection: method. It accomplishes this\
		by getting the selected cell and setting its loaded state to NO\
		and then asking the browser to load the cell via a\
		getLoadedCellAtRow:inColumn: message.;\
	
\i0\ul Args:
\i\ulnone \
		theFile: The link that was resolved;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 MyBrowserCell *
\fc0\cf0 theCell
\fc1\cf1 ;\
int row, col;\
Matrix *colMatrix;\
\

\b0\fs20\fc0\cf0 [self debug: LOW_DEBUG method:_cmd, "setting path to: %s", [theFile sourceNameWithLink]];\

\b\fs24\fc1\cf1 	
\fc0\cf0 theCell
\fc1\cf1  = [fileBrowserID selectedCell];\
	colMatrix = [fileBrowserID matrixInColumn: [fileBrowserID selectedColumn]];\
	[colMatrix getRow: &row andCol: &col ofCell: theCell];\
	col = [fileBrowserID selectedColumn];\
	[theCell setLoaded: NO];\
	theCell = [fileBrowserID getLoadedCellAtRow: row inColumn: col];\
	
\b0\i /* We disabled the double action method in displaySelection: to\
		prevent the user from accidently invoking a transfer during the\
		resolution phase after the initial selection. Here we restore it. */
\b\i0 \
	[fileBrowserID setDoubleAction: @selector(openInWorkspace:)];\
\
	return self;\
\} 
\b0\i // End displayLink:
\b\i0 \
\
- displaySelection: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker42839 \markername displaySelection:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This method updates the interface to reflect\
		the current selection in the file browser. This is the action\
		method of the file browser.;\
	
\i0\ul Args:
\i\ulnone \
		sender: The browser we are delegate to;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 FtpFile *theFile;\
const char *pathname, *modeString;\
int size = 
\fc0\cf0 0
\fc1\cf1 , length = 
\fc0\cf0 0
\fc1\cf1 ;\
short hr, min, sec;\
BOOL isDirectory;\
\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc1\cf1 	[selectionList empty];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 	[sender getSelectedCells: selectionList];\
	if( [selectionList count] > 1 )\
	\{	
\b0\i /* Multiple selction display */
\b\i0 \
	const char *firstFile, *lastFile, *location;\
	int index;\
		[self inspectObject: nil msg: "Multiple Selection"];\
\
		
\b0\i /* Build up the name to display */
\b\i0 \
		theFile = [(MyBrowserCell *)[selectionList lastObject] tag];\
		lastFile = [theFile sourceName];\
		theFile = [(MyBrowserCell *)[selectionList objectAt: 
\fc0\cf0 0
\fc1\cf1 ] tag];\
		location = [theFile sourceDirectory];\
		firstFile = [theFile sourceName];\
		length = strlen(location) + strlen(firstFile) + strlen(lastFile) + 8;\
		pathname = (char *) [self malloc: length];\
		sprintf((char *)pathname, "%s%s...%s", location, firstFile, lastFile);\
		
\b0\i /* Calculate the selection size */
\b\i0 \
		index = 1;\
		do \{\
			size += [theFile fileSize];\
			theFile = [(MyBrowserCell *)[selectionList objectAt: index ++] tag];\
		\} while( theFile != nil );\
		
\b0\i /* Mode is not applicable for multiple selection */
\b\i0 \
		modeString = "Not Applicable";\
		
\b0\i /* Set the FileWellView icon to multiple selection */
\b\i0 \
		[fileWellID showImage: eMultipleSelection];\
		theFile = [(MyBrowserCell *)[selectionList lastObject] tag];\
	\}\
	else\
	\{	
\b0\i /* Single file display */
\b\i0 \
		theFile = [(MyBrowserCell *)[selectionList objectAt: 
\fc0\cf0 0
\fc1\cf1 ] tag];\
		isDirectory = [theFile isDirectory];\
		if( [theFile isLink] == YES )\
		\{\
		FtpFile *lnTarget = [theFile linkTarget];\
			if( lnTarget == nil )\
			\{	
\b0\i\gray250\fc2\cf2 // Try to resolve the link
\b\i0\gray0\fc1\cf1 \
			const char *linkPath = [theFile linkTargetName: YES];\
				
\b0\i\gray250\fc2\cf2 /* Indicate we are trying to resolve the link and setup\
					a delayed message to clear the msg field after this\
					event loop is completed */
\b\i0\gray0\fc1\cf1 \
				
\fc0\cf0 [statusMsgID setStringValue: "Resolving link target..."];\
				NXPing();
\fc1\cf1 \
				[self perform:@selector(clearStatusMsg:) with: self\
					afterDelay: 
\fc0\cf0 0
\fc1\cf1  cancelPrevious: YES];\
				lnTarget = [self fileFromPath: linkPath];\
				if( lnTarget == nil )\
					lnTarget = [theFile 
\fc0\cf0 resolveLink: 0 ftpd: self
\fc1\cf1 ];\
				if( lnTarget == (FtpFile *) -1 )\
					return self;\
				else if( lnTarget != nil )\
				\{	
\b0\i /* We resolved the link, update theFile's link target and\
						reload the browser cell. I could not get the browser\
						to reload a cell from within the select action and\
						so it is done via a delayed msg. The double action\
						method is disabled to prevent inadvertent file\
						transfers. */
\b\i0 \
					[theFile 
\fc0\cf0 setLinkTarget
\fc1\cf1 : lnTarget];\
					[self perform:@selector(displayLink:) with: theFile\
						afterDelay: 
\fc0\cf0 0
\fc1\cf1  cancelPrevious: YES];\
					[fileBrowserID setDoubleAction: 0];\
				\}\
				else\
				\{	
\b0\i // The target cannot be resolved
\b\i0 \
					[theFile 
\fc0\cf0 setLinkTarget
\fc1\cf1 : theFile];\
				\}\
				
\b0\i // Determine type of link file
\b\i0 \
				if( lnTarget != nil )\
					isDirectory = [lnTarget isDirectory];\
			\}\
		\}\
		[self inspectObject: theFile msg: "File Object"];\
		pathname = [theFile sourcePathname];\
		size = [theFile fileSize];\
		modeString = [theFile modeString];\
		if( isDirectory == YES )\
			[fileWellID showImage: eFolderSelection];\
		else\
			[fileWellID showImage: eDocumentSelection];\
	\}\
\
	
\b0\i /* Display file selection info */
\b\i0 \
	[fileInfoID setStringValue: pathname at: 
\fc0\cf0 0
\fc1\cf1 ];\
	[fileInfoID setStringValue: modeString at: 1];\
	[fileInfoID setIntValue: size at: 2];\
	if( length > 
\fc0\cf0 0
\fc1\cf1  )\
		free(pathname);\
\
	
\b0\i /* Display the date & time of theFile */
\b\i0 \
	[calViewID setDate: [theFile julianDate]];\
	[theFile time : &hr : &min : &sec];\
	[clockViewID setTime: hr : min : sec];\
\
	return self;\
\} 
\b0\i // End displaySelection:
\b\i0 \
\

\b0\i\fs28 /*\\ ------------- Window Delegate Methods ------------- \\*/
\b\i0\fs24 \
- windowWillClose: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker46720 \markername windowWillClose:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \

\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 	
\i0\ul ReturnValue:
\i\ulnone  [self logout: self];\
	
\i0\ul Description:
\i\ulnone  This method is invoked when the close button or\
		close Windows menu item is selected. We simply return our\
		
\b logout:
\b0  method value passing self as the argument;\
	
\i0\ul Args:
\i\ulnone  \
		sender: The Window being closed;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 	[sender orderOut: self];\
	[NXApp inspector: self];\
	return [self logout: sender];\
\} 
\b0\i // End windowWillClose:
\b\i0 \
\

\b0\i\fs28 /*\\ -------------- Menu Update Methods -------------- \\*/
\b\i0\fs24\fc0\cf0 \
- (BOOL) validateCommand:(MenuCell *) menuCell\
\{\

\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker47219 \markername validateCommand:;}
¬}\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  YES if the menuCell should be redrawn, NO otherwise;\
	
\i0\ul Description:
\i\ulnone  This method determines the enabled state of its argument\
		MenuCell based on the current value of the application items\
		which the item can affect;\
	
\i0\ul Args:
\i\ulnone  \
		menuCell: the MenuCell whose status is being checked;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc0\cf0 SEL action = [menuCell action];\
BOOL enable = YES;\
\
	
\b0\i /* Determine which menu item the cell represents by its\
		action method and set its state.  Menu item actions that\
		are sent to us but are not caught in the if statements\
		are always enabled because of the default YES return. */
\b\i0 \
	if ( action == @selector(retrieve:) || action == @selector(openInWorkspace:) )\
		enable = ([
\fc1\cf1 selectionList
\fc0\cf0  count] > 0 ? YES : NO);\
\

\b0\fs20 [self debug: SUPER_DEBUG method:_cmd, "item = %s, enable = %d", [menuCell title],\
	enable];
\b\fs24 \
	return enable;\
\} 
\b0\i // End validateCommand:
\b\i0 \
\

\b0\fs28\fc1\cf1 /*\\ ------------- Text Delegate Methods ------------- \\*/
\b\fs24\fc0\cf0 \

\fc1\cf1 - (BOOL) textWillEnd: textObject\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker48186 \markername provideSrcData:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  YES if the field value is ok, NO otherwise;\
	
\i0\ul Description:
\i\ulnone  This method is invoked by one of the TextField objects\
		in the general FTP login panel. We check for non-empty hostname and\
		username fields.;\
		
\i0\ul Args:
\i\ulnone \
			textObject: The Text object being used by the TextField;\
*/\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\i0\fc1\cf1 TextField *theField;\
int textLength;\
BOOL endText = YES;\
\

\b0\i\fc0\cf0 	// The field using this textObject should be its superview\

\b\i0\fc1\cf1 	theField = [textObject superview];\
	if( [theField isKindOf: [TextField class]] == YES )\
	\{\
		textLength = [textObject textLength];\
		if( theField == 
\fc0\cf0 loginHostnameID
\fc1\cf1  || theField == 
\fc0\cf0 loginUsernameID
\fc1\cf1  )\
			endText = (textLength ? NO : YES);\
	\}\
	return endText;\
\}\

\b0\i\fs28 \
/*\\ ----------- FileWellView Delegate Methods ----------- \\*/
\b\i0\fs24\fc0\cf0 \
- (NXStream *) provideSrcData:(NXAtom) type\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker48986 \markername provideSrcData:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f1\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  An NXStream * containing a tab delimited list\
		of pathnames corresponding to the files browser selection;\
	
\i0\ul Description:
\i\ulnone  This method is invoked from within the FileWellView's\
		pasteboard:provideData: method to obtain a NXFilenamePboardType\
		representation for the user selection
\fc1\cf1  in the files browser. The\
		need for the file list occurs when a user drags the selection\
		image from the FileWellView to a NXDraggingDestination which is\
		registered to recieve 
\fc0\cf0 NXFilenamePboardType data.\
		In order to provide the filenames, we must have the files on the\
		local host. Therefore, we transfer the files to the current\
		transfer directory and base the filename list from there.;\
		
\i0\ul Args:
\i\ulnone \
			type: The type of data to place on the pasteboard;\
*/\

\b\i0\fc1\cf1 NXStream *dataStream;\
List *fileList;\
MyBrowserCell *cell = nil;\
int index = 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 0
\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 ;\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b0\fs20\fc0\cf0 [self debug: MAX_DEBUG method:_cmd, "type = %s", type];
\b\fs24 \
\

\fc1\cf1 	
\b0\i /* Create a list of FtpFile objects to give the the ftp server */
\b\i0 \
	fileList = [[Lis
\fc0\cf0 t alloc] initCount: 
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 0
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 ];\
	while( (cell = [selectionList objectAt: index ++]) != nil )\
		[fileList addObject: [cell tag]];\
\
	
\b0\i\fc1\cf1 /* Transfer the files and obtain a list of the local pathnames */
\b\i0\fc0\cf0 \
	
\fc1\cf1 dataStream = 
\fc0\cf0 [self  retrieveFiles: fileList];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 \
	[fileList free];\
\
	return dataStream;
\fc0\cf0 \
\} 
\b0\i // End provideSrcData:
\b\i0 \
\
@end\

}
