{\rtf0\ansi{\fonttbl\f1\fswiss Helvetica;\f0\fmodern Courier;}
\paperw11760
\paperh9600
\margl40
\margr40
{\colortbl;\red0\green0\blue0;}
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\ulnone\fs24\fc0\cf0 #import <appkit/appkit.h>\
#import "Query.h"\
#import "ArchieApp.h"\
#import "ArchieSession.h"\
#import <CalendarView.h>\
#import <ClockView.h>\
#import "FileWellView.h"\
#import "File.h"\
#import "FTPObject.h"\
#import <MyBrowserCell.h>\
#import "Preferences.h"\
#import "
\fc1\cf1 PVlink
\fc0\cf0 .h"\
\
#import <objc/HashTable.h>\
#import <objc/List.h>\
#import <objc/Storage.h>\

\fc1\cf1 #import <mach/mach.h>\
#import <mach/mach_error.h>\

\fc0\cf0 #import <archie.h>\
#import <perrno.h>\
#import "thread_state.h"\
\

\b0\i\fc1\cf1 /* The query mutex defined in ArchieSession.m. Temporary fix until I make\
	the source in libArchie.subproj thread-safe. */
\b\i0\fc0\cf0 \
extern mutex_t query_thread_mutex;\
\

\b0\i /* Constants for the possible status member of QueryArgs struct */
\b\i0 \
#define QUERY_ACTIVE 1\
#define QUERY_ABORTED 2\
#define QUERY_SUCCESS 3\
#define QUERY_ERROR 4\
\

\b0\i /* Prototypes taken from pfs/perrmesg.c */
\b\i0 \
int spwarnmesg(char *buf,char *prefix,int no,char *text);\
int sperrmesg(char *buf,char *prefix,int no,char *text);\
static void QueryStatus(msg_header_t *msg, void *userData);\
static void QueryThreadProc(QueryArgsPtr args);\
static void QueryExit(QueryArgsPtr queryArgs);\
\
@implementation Query\
\

\b0\i\fc1\cf1 /* Class variables used with the Queries submenu */
\b\i0\fc0\cf0 \
static Menu *queriesMenu;\
static int queryMenuTag = 0;\
\

\b0\i\fs28 /*\\ ---------------------------------- Initialization Methods ---------------------------------- \\*/
\b\i0\fs24 \
+ queriesMenu\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker1345 \markername queriesMenu;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The ªQueriesº submenu;\
	
\i0\ul Description:
\i\ulnone  This method returns the Menu object to which Querys\
		can add items to indicate active server queries;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\f1 	if( queriesMenu == nil )\
	\{\
	MenuCell *queriesCell;\
		que
\fc1\cf1 riesCell = [[NXApp mainMenu] findCellWithTag: QUERY_MENU_CELL_TAG];\
		queriesMenu = [[Menu alloc] initTitle: "Queries"];\
		[[NXApp mainMenu] setSubmenu: queriesMenu forItem: queriesCell];\
	\}\

\fc0\cf0 \
	return queriesMenu;\
\} 
\b0\i // End queriesMenu
\b\i0 \
\
+ (int) newMenuTag\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker1842 \markername newMenuTag;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  A unique tag for use with ªQueriesº menu items;\
	
\i0\ul Description:
\i\ulnone  This method increments the queryMenuTag class variable\
		and returns it for use with a new ªQueriesº menu item;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\f1 	queryMenuTag ++;\
\
	return queryMenuTag;\
\} 
\b0\i // End newMenuTag
\b\i0 \
\
- init\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker2130 \markername init;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  The designated initializer for the Query class.\
		It allocates the classes internal objects and sets the sets the\
		references to the application objects used.;\
*/\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 	[super init];\
\
	
\b0\i /* Get the application error string table & preferences */
\b\i0 \
	errStringTable = [NXApp errTable];\
	preferences = [NXApp preferences];\
\
	
\b0\i /* Initialize the host keyed hashtable */
\b\i0 \
	hostHashTable = [[HashTable allocFromZone: [self zone]] initKeyDesc: "*"];\
	if( hostHashTable == nil )\
		return [self error: INTERNAL_ERROR method:_cmd\
			key: HOSTHASHTABLE_ALLOC_FAILED];\
	hostnames = [[Storage allocFromZone: [self zone]] 
\fc1\cf1 initCount: 0\
		elementSize: sizeof(char *) description: "%"
\fc0\cf0 ];\
	if( hostnames == nil )\
		return [self error: INTERNAL_ERROR method:_cmd key: HOSTLIST_ALLOC_FAILED];\
	selectionList = [[List allocFromZone: [self zone]] initCount: 0];\
	if( selectionList == nil )\
		return [self error: INTERNAL_ERROR method:_cmd key: HOSTLIST_ALLOC_FAILED];\
	activeQueries = [[Storage allocFromZone: [self zone]] initCount: 0\
		elementSize: sizeof(QueryArgs) description: NULL];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 	fileInfo = [[FileInfo alloc] init];\
	sortKey = [[DirectorySortKey alloc] init];\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 \
	return self;\
\} 
\b0\i // End init
\b\i0 \
\
- free\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker3365 \markername free;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  super's free value;\
	
\i0\ul Description:
\i\ulnone  Deallocates the class instance variables and\
		invokes super's free.;\
*/\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc1\cf1 	[queryPanelID free];\
	[activeQueries free];\
	if( statusPort != PORT_NULL )\
		port_deallocate(task_self(), statusPort);\

\fc0\cf0 	[hostHashTable free];\
	[hostnames free];\
	free(queryString);\
\
	return [super free];\
\} 
\b0\i // End free
\b\i0 \
\
- setDelegate: session\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker3757 \markername setDelegate:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  Use this method to set the delegate of a Query\
		instance.  The delegate must be the ArchieSession object\
		which created the Query object. The method queries the\
		delegate for the interface objects which it uses to display\
		query results and the current user selection.;\
	
\i0\ul Args:
\i\ulnone \
		session: The ArchieSession who is to be our delegate;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 \

\f1 	delegate = session;\
	mainWindowID = [delegate getMainWindowID];\
	fileBrowserID = [delegate getFileBrowserID];\
	queryFieldID = [delegate getQueryFieldID];\
	fileInfoID = [delegate getFileInfoID];\
	readMatrixID = [delegate getReadMatrixID];\
	writeMatrixID = [delegate getWriteMatrixID];\
	execMatrixID = [delegate getExecMatrixID];\
	calendarID = [delegate getCalendarID];\
	clockID = [delegate getClockID];\
	selectionViewID = [delegate getSelectionViewID];\
	msgFieldID = [delegate getSmallMsgID];\
\
	return self;\
\} 
\b0\i // End setDelegate:
\b\i0 \
\

\b0\i\fs28 /*\\ ---------------------------------- Server Request Methods ---------------------------------- \\*/
\b\i0\fs24 \
- performRequest:(const char *) host query:(const char *) string maxRep:(int) limit\
	queryType:(char) type sortProc:(int (*)(VLINK,VLINK)) cmp\
	queryFlags:(int) flags\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker4958 \markername performRequest:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self a query is initiated, nil otherwise;\
	
\i0\ul Description:
\i\ulnone  This method initiates the request to the Archie\
		server. It loads the query panel from "Query.nib", creates\
		a thread to perform the query, and allocates a Mach port\
		by which the thread can communicate its status.  The thread\
		is created by our 
\b newQueryThread:
\b0 ... method.;\
	
\i0\ul Args:
\i\ulnone \
		host: The Archie server host to query;\
		string: The query string from the interface;\
		limit: The maximum number of matches the query should generate;\
		type: The type of matching to be peformed with the query string.\
			It is one of:\
				'C' - case sensitive substring.\
				'S' - case insensitive substring.\
				'R' - regular expression.\
				'=' - exact match.\
				'c' - try exact match and then case sensitive substring.\
				's' - try exact match and then case insensitive substring.\
				'e' - try exact match and then regular expression.\
		cmp: The sort method to use;\
		flags: Whether or not sorting should be performed;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
static char *queryWinTitle;\
Info info;\
Menu *theQueriesMenu;\
MenuCell *newItem;\
int menuTag;\
\
	
\b0\i /* Load our query panel if it is not already */
\b\i0 \
	if( queryPanelID == nil )\
	\{\
		if( [NXApp loadNibSection: "Query.nib" owner: self] == nil )\
			return [self error: INTERNAL_ERROR method:_cmd key: QUERY_NIB_FAILED];\
		[queryPa
\fc1\cf1 nelID setBecomeKeyOnlyIfNeeded: YES];
\fc0\cf0 \
		[queryPa
\fc1\cf1 nelID setFloatingPanel: NO];\
	\}
\fc0\cf0 \
\
	
\b0\i /* If there are active DirListing queries, ask if they should be aborted */
\b\i0 \
	if( activeDirQueries == YES )\
	\{\
	int abort;\
		abort = [self askYesNo: "There are active directory queries. Abort them?" cancel: NO];\
		if( abort != NX_ALERTDEFAULT )\
			return nil;\
	\}\
\
	
\b0\i /* Allocate a Mach port for status messages from the query thread */
\b\i0 \
	[self newStatusPort];\
\
	
\b0\i /* Deactivate the ``Abort'' button until the first message from the\
		"Querying Archie Server...".  For some reason, the thread_suspend()\
		& cthread_abort() combination does not kill the thread proc until\
		it has gotten to a certain point.  The abort method needs to send\
		the thread to a handler using set_thread_state to avoid this\
		problem. */
\b\i0 \
	[abortID setEnabled: NO];\
	abortEnabled = NO;\
\
	
\b0\i /* Initialize and display the query panel */
\b\i0 \
	[hostnameID setStringValue: host];\
	[statusID setStringValue: "Querying Archie Server..."];\
	[queryTimerID startMinSecTimer: self];\
	if( queryWinTitle != 0 )\
		[self free: queryWinTitle];\
	queryWinTitle = (char *) [self malloc: strlen(string)+strlen(host)+16];\
	menuTag = [Query newMenuTag];\
	sprintf(queryWinTitle, "#%d %s -- Query: %s", menuTag, host, string);\
	[queryPanelID setTitle:(const char *) queryWinTitle];\
	[queryPanelID orderFront: self];\
	theQueriesMenu = [Query queriesMenu];\
	newItem = [theQueriesMenu addItem: queryWinTitle action: @sel
\fc1\cf1 ector(orderFront:)\
		keyEquivalent: 0];\
	[newItem setTarget: 
\fc0\cf0 queryPanelID];\

\fc1\cf1 	[newItem setTag: 
\fc0\cf0 menuTag];\
	[theQueriesMenu display];\
\
	
\b0\i /* Create an ArchieQuery type of thread to query the Archie server */
\b\i0 \
	queryString = NXCopyStringBuffer(string);\
	info.archie.maxReplies = limit;\
	info.archie.type = type;\
	[self  newQueryThread: ArchieQuery host: host string: queryString\
		cmp: cmp flags: flags info: &info tag: menuTag];\
	
\fc1\cf1 activeArchieQuery
\fc0\cf0  = YES;\
\
	return self;\
\} 
\b0\i // End performRequest: query: maxRep: queryType: sortProc: queryFlags:
\b\i0 \
\
- newQueryThread:(Task) type host:(const char *) host\
	string:(const char *) string cmp:(int (*)(VLINK, VLINK)) cmp\
	flags:(int) flags info:(InfoPtr) info tag:(int) menuTag\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker8442 \markername newQueryThread:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This method creates the thread which peforms the\
		query.  It allocates a QueryArg structure for the task,\
		assigns it the values passed to the method, and saves it\
		in our activeQueries Storage variable.;\
	
\i0\ul Args:
\i\ulnone \
		type: The type of the query - ArchieQuery or DirListingQuery;\
		host: The Archie server host to query;\
		string: The query string from the interface;\
		cmp: The sort method to use;\
		flags: Whether or not sorting should be performed;\
		info: Additional info specific to the type of task;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
QueryArgsPtr args;\
\
	
\b0\i /* Allocate and init a new QueryArgs struct */
\b\i0 \
	args = [self malloc: sizeof(QueryArgs)];\
	args->status = QUERY_ACTIVE;\
	args->type = type;\
	args->hostname = host;\
	args->string = string;\
	args->cmp = cmp;\
	args->flags = flags;\
	args->port = statusPort;\
	args->links = NULL;\
	args->menuTag = menuTag;\
	args->self = self;\
	
\b0\i /* Open a pipe that will be used to abort the query thread if it is in the\
		select() system call */
\b\i0 \
	if( pipe(args->abortPipe) < 0 )\
		[args->self systemErr: "Failed to open abort pipe" method:NULL];\
	
\b0\i /* Copy the task specific info */
\b\i0 \
	bcopy(info, &args->info, sizeof(Info));\
\
	
\b0\i /* Add the args struct to the active query list */
\b\i0 \
	args->msg.
\fc1\cf1 queryIndex
\fc0\cf0  = [activeQueries count];\
	[activeQueries addElement: args];\
	
\b0\i /* Since the Storage addElement: method makes a copy of its argument data,\
		we need to free struct we added and obtain a reference to the data in\
		the Storage object. We must do this so that we can update the value\
		of the struct inside the Storage object. */
\b\i0 \
	[self free: args];\
	args = (QueryArgsPtr)[activeQueries elementAt: [activeQueries count]-1];\
\
	
\b0\i /* Fork the query thread, passing the reference to the QueryArgs struct\
		in activeQueries as the sole argument to the thre
\fc1\cf1 ad proc.\
		
{{\NeXTHelpLink10256 \markername ;\linkFilename CodeHelp.rtf;\linkMarkername cthread_fork;}
,}\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b0\i\ulnone\fs24\fc1\cf1  Link to cthread_fork()  description. */
\b\i0\fc0\cf0 \
	args->cthread = cthread_fork((cthread_fn_t) QueryThreadProc, (any_t) args);\

\f0\b0\fs20 [self debug: MAX_DEBUG method: _cmd, "Query %p task %p forked t = %p\\n",\
	self, cthread_self(), args->cthread];
\f1\b\fs24 \
\
	return self;\
\} 
\b0\i\fc1\cf1 // End newQueryThread: host: string: cmp: flags: info:
\b\i0\fc0\cf0 \
\
- newStatusPort\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker10580 \markername newStatusPort;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This method allocates a Mach port through which our\
		query threads can communicate their status. We must use this\
		form communication because only the main thread can message\
		the AppKit objects in our interface.;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
kern_return_t rtnValue;\
\
	
\b0\i /* Allocate a Mach port.  
{{\NeXTHelpLink10910 \markername ;\linkFilename CodeHelp.rtf;\linkMarkername port_allocate;}
,}\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b0\i\ulnone\fs24\fc0\cf0  link to port_allocate() */
\b\i0 \
	rtnValue = port_allocate(task_self(), &statusPort);\
	if( rtnValue != KERN_SUCCESS )\
		return [self error: INTERNAL_ERROR method:_cmd key: PORT_ALLOC_FAILED];\
\
	
\b0\i /* Add the handler function that fields the Mach messages sent to\
		our statusPort.  
{{\NeXTHelpLink11185 \markername ;\linkFilename CodeHelp.rtf;\linkMarkername DPSAddPort;}
,}\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b0\i\ulnone\fs24\fc0\cf0  link to DPSAddPort() */
\b\i0 \
	DPSAddPort(statusPort, QueryStatus, sizeof(StatusMsg), self,\
		NX_MODALRESPTHRESHOLD +1);\
\
	return self;\
\} 
\b0\i // End newStatusPort
\b\i0 \
\
- parseResponse:(VLINK) links\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker11375 \markername parseResponse:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if successful, nil if an internal error occurs;\
	
\i0\ul Description:
\i\ulnone  This method converts the linked list of VLINK\
		structs to PVlink objects and assigns them to List\
		objects, one List for every host. We maintain two objects\
		to facilitate access to the PVlink s. One is a Storage\
		object (hostnames) that contains the names of the hosts\
		in the reply. This info is displayed as column 0 in the\
		files browser. The other is a HashTable (hostHashTable)\
		which holds the PVlink List objects, keyed by the\
		hostname.;\
	
\i0\ul Args:
\i\ulnone \
		links: A linked list of VLINK structures as returned by\
			the archieQuery() (libArchie.subproj/query.c) function;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
PVlink *v;\
List  <ObjectArchival> *hostFiles;\
const char *hostname;\
\
	
\b0\i /* Order out and free the query panel */
\b\i0 \
	[queryTimerID endTimer: self];\
	[queryPanelID orderOut: self];\
	[queryPanelID free];\
	queryPanelID = nil;\
\
	matchCount = 0;\
	while( links )\
	\{	
\b0\i /* Step 1 - Create a ProsperoVLINK object this VLINK */
\b\i0 \
		v = [[
\fc1\cf1 PVlink
\fc0\cf0  allocFromZone: [self zone]] initVLINK: links];\
		if( v == nil )\
		\{\
			[self error: INTERNAL_ALERT method:_cmd key: VLINK_ALLOC_FAILED];\
			return nil;\
		\}\
		
\b0\i /* Step 2 - If the hostname is a key into the host HashTable, retrieve\
			the List of ProsperoVLINKs for the host */
\b\i0 \
		hostname = [v sourceHost];\
		if( [hostHashTable isKey: hostname] == YES )\
		\{	
\b0\i /* Get the List of files for this host */
\b\i0 \
			hostFiles = (List <ObjectArchival> *) [hostHashTable valueForKey: hostname];\
			if( hostFiles == nil )\
			\{\
				[self error: INTERNAL_ERROR method:_cmd key: VALID_KEY_NIL_ENTRY];\
				return nil;\
			\}\
			
\b0\i /* Add this file to the host list */
\b\i0 \
			[hostFiles addObject: v];\
		\}\
		else\
		\{	
\b0\i /* Create a new List for this host and add it to the hostHashTable \
				for fast access and save the hostname in the hostnames list to maintain\
				the sort order of the reply */
\b\i0 \
			hostFiles = (List <ObjectArchival> *) [[List allocFromZone: [self zone]] initCount: 0];\
			if(hostFiles == nil)\
			\{\
				[self error: INTERNAL_ERROR method:_cmd key: HOSTFILES_ALLOC_FAILED];\
				return nil;\
			\}\
			[hostFiles addObject: v];\
			[hostHashTable insertKey: hostname value: hostFiles];\
			hostname = [v sourceHost];\
			[hostnames addElement: &hostname];\
		\}\
		links = links->next;\
		matchCount ++;\
	\}\
\
	
\b0\i /* Ask our ArchieSession delegate to display the query result & mark\
		the document changed */\

\b\i0 	[delegate displayQuery: self];\
	[mainWindowID setDocEdited: YES];\
\
	return self;\
\} 
\b0\i // End parseResponse:
\b\i0 \
\
- interruptRequest: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker13902 \markername interruptRequest:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This is the action method of the "Abort" button\
		in the query status panel;\
	
\i0\ul Args:
\i\ulnone \
		sender: The "Abort" button in the query panel;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
QueryArgsPtr args;\
kern_return_t rtnVal;\
thread_t theThread;\

\fc1\cf1 \
REGS_STRUCT *threadState;\
int count;\
\

\fc0\cf0 	
\b0\i /* Suspend and abort the query thread. There should only\
		be a single query thread at this point. */
\b\i0 \
	count = [activeQueries count];\

\f0\b0\fs20 [self debug: MAX_DEBUG method:_cmd, "Query %p; aq = %d", self, count];
\f1\b\fs24 \
	if( count != 1 )\
		[self error: INTERNAL_ALERT method:_cmd\
			key: "Assertion query count == 1 failed, count = %d", count];\
	args = (QueryArgsPtr) [activeQueries elementAt: 0];\
	if( args == NULL )\
	\{\
		[self error: INTERNAL_ERROR method: _cmd key: "Failed to get query struct"];\
		return nil;\
	\}\
\
	
\b0\i /* Get the thread state.  Each thread allocates a state structure and\
		assigns it to its cthread_data() item */
\b\i0 \
	theThread = cthread_thread(args->cthread);\

\fc1\cf1 	threadState = (REGS_STRUCT *) cthread_data(args->cthread);\
	if( threadState == NULL )\
	\{\

\fc0\cf0 		[self error: INTERNAL_ERROR method:_cmd key: "Failed to get thread state"];\
		return nil;\

\fc1\cf1 	\}
\fc0\cf0 \

\f0\b0\fs20 [self debug: MAX_DEBUG method:_cmd, "Query %p aborting thread %p", self, args->cthread];
\f1\b\fs24 \
\
	
\b0\i /* Suspend the thread and send it to the QueryExit() procedure.\
		
{{\NeXTHelpLink15200 \markername ;\linkFilename CodeHelp.rtf;\linkMarkername cthread_abort;}
,}\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b0\i\ulnone\fs24\fc0\cf0  link to thread_suspend() */
\b\i0 \
	args->status = QUERY_ABORTED;\
	/* Get the thread out of the select() system call */\
	write(args->abortPipe[1], "Abort\\n", 6);\
	thread_suspend(theThread);\
	rtnVal = cthread_abort(args->cthread);\
	if( rtnVal != KERN_SUCCESS )\
	\{	
\b0\i\fc1\cf1 /* Failed to set the thread's state */
\b\i0\fc0\cf0 \
	char *errString;\
		errStri
\fc1\cf1 ng = mach_error_string(rtnVal);\
		[self error: IN
\fc0\cf0 TERNAL_ERROR method:_cmd key: "Failed to abort thread:: %s",\
			errString];\
		return nil;\
	\}\
#ifdef NOT_DEFINED\
/* This is currently not working.  If we interrupt the thread while it is int the\
	select() system call, any subsequent system calls cause a TRACE (5)\
	signal to be sent.  I don't know how to disable this yet. */\
	
\b0\i\fc1\cf1 /* Step 1: Push the args pointer on the stack */
\b\i0\fc0\cf0 \
	SP_FIELD(*threadState) -= sizeof(int);  
\b0\i\fc1\cf1 // Decrement stack pointer to make room
\b\i0\fc0\cf0 \
	*(int **)SP_FIELD(*threadState) = (int *) args;  
\b0\i\fc1\cf1 // Place the address on the stack
\b\i0\fc0\cf0 \
	
\b0\i\fc1\cf1 /* Step 2: Push the thread PC register on the stack */
\b\i0\fc0\cf0 \
	SP_FIELD(*threadState) -= sizeof(int);\
	*(int *)SP_FIELD(*threadState) = threadState->pc;\
	
\b0\i\fc1\cf1 /* Step 3: Set the thread state PC to that of the QueryExit() proc */
\b\i0\fc0\cf0 \
	PC_FIELD(*threadState) = (int) QueryExit;\
	
\b0\i\fc1\cf1 /* Step 4: Update the query thread's state. 
{{\NeXTHelpLink16419 \markername ;\linkFilename CodeHelp.rtf;\linkMarkername thread_set_state;}
¬}\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b0\i\ulnone\fs24\fc1\cf1  link to thread_set_state() */
\b\i0\fc0\cf0 \
	rtnVal = thread_set_state(theThread, REGS_STATE, (thread_state_t) threadState,\
		REGS_STATE_SIZE);\
	if( rtnVal != KERN_SUCCESS )\
	\{	
\b0\i\fc1\cf1 /* Failed to set the thread's state */
\b\i0\fc0\cf0 \
	char *errString;\
		errStri
\fc1\cf1 ng = mach_error_string(rtnVal);\
		[self error: IN
\fc0\cf0 TERNAL_ERROR method:_cmd key: "Failed to set thread state:: %s",\
			errString];\
		return nil;\
	\}\
#endif\

\f0\b0\fs20 [self debug: MAX_DEBUG method:_cmd, "Query %p thread %p aborted", self, args->cthread];\

\f1\b\fs24 \
	
\b0\i /* Restart the thead so that it calls cthread_exit() */
\b\i0 \
	thread_resume(theThread);\
\
	return self;\
\} 
\b0\i // End interruptRequest:
\b\i0 \
\

\b0\i\fs28\fc1\cf1 /*\\ ---------------------- Browser Delegate Methods ---------------------- \\*/
\b\i0\fs24 \

\fc0\cf0 - (int)browser: sender fillMatrix: matrix inColumn:(int) column\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker17166 \markername browser:fillMatrix:inColumn:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The number of cells loaded in the column;\
	
\i0\ul Description:
\i\ulnone  This is normal delegate method responsible for\
		loading the entries in the given column
\fc1\cf1 . This version\
		displays the hosts in column0, the full pathanme in column1\
		and the lowest file of the response path in column2.\
		Subsequent columns list the files below the parent directory\
		of the preceeding column.;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		sender: The NXBrowser we are the delegate for;\
		matrix: The Matrix for the column;\
		column: The column number we should load;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
const char *hostname;\
int row, rows;\
id hostFileList, link, dirListing;\
MyBrowserCell *cell;\
\
	if(column == 0)\
	\{	
\b0\i /* Column zero
\i0\fc1\cf1 , load the host names */
\b\fc0\cf0 \
		rows = [hostnames count];\
		for(row = 0; row < rows; row ++)\
		\{\
			hostname = * ((const char **)  [hostnames elementAt: row]);\
			[matrix addRow];\
			cell = [matrix cellAt: row : 0];\
			[cell setStringValue: hostname];\
			[cell setLoaded:YES];\
			[cell setLeaf: NO];\
		\}\
	\}\
	else if( column == 1)\
	\{	
\b0\i /* Loop through the host's files and display the pathnames */
\b\i0 \
		hostname = [[[sender matrixInColumn: 0] selectedCell] stringValue];\
		hostFileList = (List *) [hostHashTable valueForKey: hostname];\
		rows = [hostFileList count];\
		for(row = 0; row < rows; row ++)\
		\{	
\b0\i /* Display the full pathname for each file */
\b\i0 \
			link = (PVlink *) [hostFileList objectAt: row];\
			[matrix addRow];\
			cell = [matrix cellAt: row : 0];\
				[cell setStringValue: [link sourcePathname]];\
			[cell setLoaded:YES];\
			
\b0\i /* Indicate if the file is a directory or regular file.  We\
				also set the tag of the cell to the id of the object\
				it represents for easy access when the cell is selected */
\b\i0 \
			if( strcmp("/", [link sourceDirectory]) == 0 )\
			\{	
\b0\i /* File does resid in "/" */
\b\i0 \
				if([link isDirectory] == NO)\
					[cell setLeaf: YES];\
				else\
					[cell setLeaf: NO];\
				[cell setTag: link];\
			\}\
			else\
			\{	
\b0\i /* File does not resid in "/" */
\b\i0 \
				[cell setLeaf: NO];\
				[cell setTag: nil];\
			\}\
		\}\
	\}\
	else if(column == 2)\
	\{	
\b0\i /* Load one cell and display the end item of the selected path */
\b\i0 \
		hostname = [[[sender matrixInColumn: 0] selectedCell] stringValue];\
		hostFileList = (List *) [hostHashTable valueForKey: hostname];\
		rows = 1;\
		row = [[sender matrixInColumn: 1] selectedRow];\
		link = (PVlink *) [hostFileList objectAt: row];\
		[matrix addRow];\
		cell = [matrix cellAt: 0 : 0];\
		[cell setStringValue: [link sourceName]];\
		[cell setLoaded:YES];\
		[cell setTag: link];\
		if( [link isDirectory] == NO)\
			[cell setLeaf: YES];\
		else\
			[cell setLeaf: NO];\
	\}\
	else\
	\{	
\b0\i /* The previous column is a directory vlink. Get a List of\
			ProsperoVLINKS directory below it and display them */
\b\i0 \
		cell = [[sender matrixInColumn: column - 1] selectedCell] ;\
		link = [cell tag];\
		dirListing = [link dirListing];\
		if( dirListing == nil )\
		\{	
\b0\i /* The directory contents have not been retrieved yet, or it has no files.\
				if it is the former, initiate a query to get the listing using our\
				
\b loadEntries:for:inColumn:
\b0   method.  If the latter, simply display\
				a cell saying ªNo filesº. */
\b\i0 \
			[matrix addRow];\
			cell = [matrix cellAt: 0 : 0];\
			if( [link emptyDir] == NO )\
				[cell setStringValue: "Directory not loaded"];\
			else\
				[cell setStringValue: "No files"];\
			[cell setLoaded:YES];\
			[cell setTag: nil];\
			[cell setLeaf: YES];\
			[cell setEnabled: NO];\

\b0\i 			/* Check to see that a listing query is not already active */
\b\i0 \
			if( [link emptyDir] == NO && [link dirLoading] == NO )\
			\{\
#ifndef NO_NETWORK\
				[link setDirLoading: YES];\
				if( [self loadEntries: link for: sender inColumn: column] == nil )\
					[link setDirLoading: NO];\
\
#endif\
			\}\
			return 1;\
		\}\
		
\b0\i /* Display the directory contents */
\b\i0 \
		rows = [dirListing count];\
		for(row = 0; row < rows; row ++)\
		\{\
			link = [dirListing objectAt: row];\
			[matrix addRow];\
			cell = [matrix cellAt: row : 0];\
			[cell setStringValue: [link sourceName]];\
			[cell setLoaded:YES];\
			[cell setTag: link];\
			[cell setEnabled: YES];\
			if( [link isDirectory] == NO)\
				[cell setLeaf: YES];\
			else\
				[cell setLeaf: NO];\
		\}\
		if(rows <= 0)\
		\{	
\b0\i /* The directory has no files */
\b\i0 \
			rows = 1;\
			[matrix addRow];\
			cell = [matrix cellAt: row : 0];\
			[cell setStringValue: "No files"];\
			[cell setLoaded:YES];\
			[cell setTag: nil];\
			[cell setLeaf: YES];\
			[cell setEnabled: NO];\
		\}\
	\}\
\
	return rows;\
\} 
\b0\i\fc1\cf1 // End browser: sender  fillMatrix: matrix inColumn:
\b\i0\fc0\cf0 \
\
- loadEntries:(PVlink *) link for:(NXBrowser *) browser\
	inColumn:(int) column\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker21676 \markername loadEntries:for:inColumn:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This method either initiates a query to obtain\
		the listing of its link argument, or asks its browser\
		arg to display a newly received listing
\fc1\cf1 ;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		link: The PVlink whose contents are to be retrieved;\
		browser: Th
\fc1\cf1 e NXBrowser who
\fc0\cf0 se delegate we are;\
		column: The column number we should load;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
Info info;\
\
	if( [link dirLoading] == NO )\
	\{	
\b0\i /* This method previously invoked a thread to query\
			for this directory link's entries.  Now update the link\
			and ask the browser to display the entries */
\b\i0 \
	const char *title;\
		if( [preferences audioNotification] == YES )\
		\{\
		Sound *notifySnd = [preferences notificationSnd: DirLoaded];\
			[notifySnd play];\
		\}\
\
		
\b0\i /* Update the directory in the browser */
\b\i0 \
		[browser reloadColumn: column];\
		
\b0\i /* Reset the column title to the name of the cell selected in\
			the previous column */
\b\i0 \
		title = [[browser 
\fc1\cf1 selectedCell
\fc0\cf0 ] stringValue];\
		[browser setTitle: title ofColumn: column];\
		[mainWindowID setDocEdited: YES];\
\
		return self;\
	\}\
\
	if( mutex_try_lock(query_thread_mutex) == 0 )\
	\{	
\b0\i /* The prospero code is not set up to handle multiple threads yet. */
\b\i0 \
		[self alertOk: "Only one query thread is currently allowed" quit: NO];\
		return nil;\
	\}\
	\
	if( statusPort == PORT_NULL )\
	\{	
\b0\i /* Allocate Mach port so the query thread has some place\
			to send its QUERY_DONE status message */
\b\i0 \
		port_allocate(task_self(), &statusPort);\
		DPSAddPort(statusPort, QueryStatus, sizeof(StatusMsg), self,\
			NX_MODALRESPTHRESHOLD +1);\
	\}\
\
	/* Create a thread to query the Prospero server */\
	info.dlist.dirVLINK = link;\
	info.dlist.column = column;\
	info.dlist.browser = browser;\
	info.dlist.selection = (char *) [self malloc: 128];\
	[browser 
\fc1\cf1 getPath: 
\fc0\cf0 info.dlist.selection
\fc1\cf1  toColumn: column
\fc0\cf0 ];\
	[self newQueryThread: DirListingQuery host: [link vlinkHostname]\
		string: [link vlinkFilename] cmp: [preferences cmpProc]\
		flags:  [preferences sortFlag] info: &info tag: 0];\
\
	return self;\
\} 
\b0\i // End loadEntries: for: inColumn:
\b\i0 \
\
- (NXStream *) provideSrcData:(NXAtom) type\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker23740 \markername provideSrcData:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\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 ask our FTPObject to 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;\
*/\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 Matrix *column0;\
int hostIndex, 
\fc1\cf1 index = 0;
\fc0\cf0 \
NXAtom hostname;\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 NXStream *dataStream;\
List *fileList;\
MyBrowserCell *cell = nil;\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f0\b0\fs20\fc0\cf0 [self debug: MAX_DEBUG method:_cmd, "selection count = %d, type = %s",\
	[selectionList count], type];
\f1\b\fs24 \
	if( [selectionList count] == 0 || type != 
\fc1\cf1 NXFilenamePboardType
\f0\b0\i\fc0\cf0  
\f1\b\i0 )\
		return NULL;\
\
	column0 = [
\fc1\cf1 fileBrowserID
\fc0\cf0  matrixInColumn: 0];\
	if( column0 == nil )\
		return NULL;\
	hostIndex = [column0 selectedRow];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 	hostname = *((NXAtom *) [hostnames elementAt: hostIndex]);\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc1\cf1 \
	
\b0\i /* Create a list of PVlink objects to give the the ftp server */
\b\i0 \
	fileList = [[Lis
\fc0\cf0 t alloc] initCount: 0];\
	while( (cell = [selectionList objectAt: index ++]) != nil )\
		[fileList addObject: [cell tag]];\
\
	ftpServer = [[FTPObject new] initForHost: hostname interactive: NO initialPath: 0];\
	if( ftpServer == nil )\
	\{	
\b0\i\fc1\cf1 /* Report failure to user */
\b\i0\fc0\cf0 \
		[self alertOk: "Archie failed to retrieve the file(s) from the ftp host" quit: NO];\
		return NULL;\
	\}\
	if( localTransferDir != NULL )\
		[ftpServer setLocalTransferDir: localTransferDir];\
	
\b0\i\fc1\cf1 /* Transfer the files and obtain a list of the local pathnames */
\b\i0\fc0\cf0 \
	
\fc1\cf1 dataStream = 
\fc0\cf0 [ftpServer  retrieveFiles: fileList];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 	ftpServer = [ftpServer free];\
	[fileList free];\
\
	return dataStream;\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 \} 
\b0\i // End provideSrcData:
\b\i0 \
\
#define Read 4\
#define Write 2\
#define Exec 1\
#define Owner 448\
#define Group 56\
#define Other 7\
\
- fileAttributes: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker25910 \markername fileAttributes:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone  This is action method of the interface browser.\
		It is called to display info for the cell selected by the user.
\fc1\cf1 ;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		sender: Th
\fc1\cf1 e NXBrowser who
\fc0\cf0 se delegate we are;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
PVlink *link;\
short hr, min, sec;\
short day, month, year;\
int owner, group, other, mode;\
int column, selSize, n, count;\
char selName[128], filePath[MAXPATHLEN], *tmpPtr;\
const char *first, *last;\
MyBrowserCell *cell;\
id matrix;\
\
	
\b0\i /* Determine the selected cell(s) */
\b\i0 \
	column = [sender selectedColumn];\
	matrix = [sender matrixInColumn: column];\
	[[selectionList empty] setAvailableCapacity: 0];\
	
\b0\i /* Get a List of the selected cells */
\b\i0 \
	[
\fc1\cf1 matrix getSelectedCells: selectionList];
\fc0\cf0 \
	count = [selectionList count];\
\
	if( column == 0 )\
	\{	
\b0\i /* We don't display any host info */
\b\i0 \
		[delegate clearFields];\
		selectedFile = nil;\
		return self;\
	\}\
\
	cell = [selectionList objectAt: 0];\
	if( [cell isLeaf] == NO && column == 1 )\
	\{	
\b0\i /* Don't display any info for the leading pathnames */
\b\i0 \
		[delegate clearFields];\
		selectedFile = nil;\
		return self;\
	\}\
\
	link = (PVlink *) [cell tag];\
	strcpy(filePath, [link sourcePathname]);\
	if( count > 1 )\
	\{	
\b0\i /* Multiple file selection. Calculate the size of the selection */
\b\i0 \

\fc1\cf1 		[self inspectObject: nil msg: "Multiple Selection"];\

\fc0\cf0 		first = [link sourceName];\
		cell = [selectionList objectAt: count-1] ;\
		last =  [[cell tag] sourceName];\
		sprintf(selName, "%s..%s", first, last);\
		selSize = 0;\
		for(n = 0; n < count; n ++)\
		\{\
			cell = [selectionList objectAt: n];\
			link = (PVlink *) [cell tag];\
			selSize += [link fileSize];\
		\}\
		tmpPtr = strrchr(filePath,'/');\
		*tmpPtr = '\\0';\
	\}\
	else\
	\{	
\b0\i\fc1\cf1 /* Single cell selected */
\b\i0\fc0\cf0 \
		strcpy(selName, [link sourceName]);\
		selSize = [link fileSize];\
		selectedFile = link;\

\fc1\cf1 		[self inspectObject: 
\fc0\cf0 selectedFile
\fc1\cf1  msg: "File Object"];\

\fc0\cf0 	\}\
\
	/* Set the selection icon */\
	if( count > 1 )\
		[selectionViewID showImage: eMultipleSelection];\
	else if( [link isDirectory] == YES )\
		[selectionViewID showImage: eFolderSelection];\
	else\
		[selectionViewID showImage: eDocumentSelection];\
\
	
\b0\i /* Set the modification time and date */
\b\i0 \
	[link time: &hr : &min : &sec];\
	[clockID setTime: hr : min : sec];\
	day = [link day];\
	month = [link month];	
\b0\i\fc1\cf1 // Runs from 0-11
\b\i0\fc0\cf0 \
	year = [link year];\
	
\b0\i\fc1\cf1 /* Expects month to run from 1-12 for consistency with string dates */
\b\i0\fc0\cf0 \
	[calendarID setDate: day : month+1 : year];\
\
	
\b0\i\fc1\cf1 /* Set the file info Form */
\b\i0\fc0\cf0 \
	[fileInfoID setStringValue: [link sourceHost] at: 0];\
	[fileInfoID setStringValue: filePath at: 1];\
	[fileInfoID setStringValue: selName at: 2];\
	[fileInfoID setIntValue: selSize at: 3];\
	if( count != 1 )\
		return self;\
	[fileInfoID setStringValue: ([link isDirectory] == YES ? "Directory" : "File") at: 4];\
	\
	
\b0\i\fc1\cf1 /* Set the file permissions */
\b\i0\fc0\cf0 \
	mode = [link mode];\
	owner = (mode  & Owner) >> 6;\
	group = (mode & Group) >> 3;\
	other = mode & Other;\
	if(owner & Read)\
		[readMatrixID setState: YES at: 0 : 0];\
	else\
		[readMatrixID setState: NO at: 0 : 0];\
	if(group & Read)\
		[readMatrixID setState: YES at: 0 : 1];\
	else\
		[readMatrixID setState: NO at: 0 : 1];\
	if(other & Read)\
		[readMatrixID setState: YES at: 0 : 2];\
	else\
		[readMatrixID setState: NO at: 0 : 2];\
\
	if(owner & Write)\
		[writeMatrixID setState: YES at: 0 : 0];\
	else\
		[writeMatrixID setState: NO at: 0 : 0];\
	if(group & Write)\
		[writeMatrixID setState: YES at: 0 : 1];\
	else\
		[writeMatrixID setState: NO at: 0 : 1];\
	if(other & Write)\
		[writeMatrixID setState: YES at: 0 : 2];\
	else\
		[writeMatrixID setState: NO at: 0 : 2];\
\
	if(owner & Exec)\
		[execMatrixID setState: YES at: 0 : 0];\
	else\
		[execMatrixID setState: NO at: 0 : 0];\
	if(group & Exec)\
		[execMatrixID setState: YES at: 0 : 1];\
	else\
		[execMatrixID setState: NO at: 0 : 1];\
	if(other & Exec)\
		[execMatrixID setState: YES at: 0 : 2];\
	else\
		[execMatrixID setState: NO at: 0 : 2];\
\
	return self;\
\} 
\b0\i // End fileAttributes:
\b\i0 \
\
- (BOOL) queriesAreActive\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker29822 \markername queriesAreActive;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  YES if query threads are active, NO otherwise;\
	
\i0\ul Description:
\i\ulnone  This simply checks the count of the activeQueries\
		Storage list and returns YES if count > 0, NO otherwise
\fc1\cf1 ;
\fc0\cf0 \
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
	if( [activeQueries count] > 0 )\
		return YES;\
	return NO;\
\} 
\b0\i // End queriesAreActive
\b\i0 \
\
- selectionList\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker30139 \markername selectionList;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The List of cells selected in the files browser
\fc1\cf1 ;
\fc0\cf0 \
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
	return selectionList;\
\} 
\b0\i // End selectionList
\b\i0 \
\
- selectedFile\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker30295 \markername selectedFile;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The PVlink for the current selection
\fc1\cf1 ;
\fc0\cf0 \
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
	return selectedFile;\
\} 
\b0\i // End selectedFile
\b\i0 \
\
- (int) matchCount\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker30442 \markername matchCount;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The number of matches (responses) sent by the\
		Archie server to the last query we made
\fc1\cf1 ;
\fc0\cf0 \
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
	return matchCount;\
\} 
\b0\i // End matchCount
\b\i0 \
\
- (const char *) queryString\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker30646 \markername matchCount;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The string for this query
\fc1\cf1 ;
\fc0\cf0 \
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
	return (const char *) queryString;\
\} 
\b0\i // End queryString
\b\i0 \
\

\b0\i\fs28\fc1\cf1 /*\\ ---------------------------------- Thread Task ---------------------------------- \\*/
\b\i0\fs24\fc0\cf0 \
static void QueryThreadProc(QueryArgsPtr queryArgs)\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker30918 \markername QueryThreadProc;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone\fc1\cf1  0 on sucess, nonzero if an error occurs.;
\fc0\cf0 \
	
\i0\ul Description:
\i\ulnone  This is the function passed to the cthread_fork()\
		call that created the query thread.  It initializes the\
		Mach message struct of its queryArgs argument and calls\
		either archieQuery() or stringQuery() (libArchie.suproj/query.c)\
		depend
\fc1\cf1 ing on queryArgs->type (ArchieQuery, DirListingQuery).;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		
\fc1\cf1 queryArgs
\fc0\cf0 : A pointer to the QueryArgs struct created and\
			placed into the activeQueries Storage list for the thread\
			in which we live;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
VLINK archieQuery(const char *host, const char *string,int max_hits,int offset,\
	char query_type, int (*cmp_proc)(), int flags, StatusMsgPtr msg);\
VLINK stringQuery(const char *host, const char *string, StatusMsgPtr msg);\
VLINK SortList(VLINK links, int (*cmp_proc)());\
void SetThreadAbortFD(int fd);\
REGS_STRUCT *threadState;\
int fp, pc, sp;\
\
	/* Allocate a thread state structure and assign it as our\
		cthread_data() */\
	threadState = (REGS_STRUCT *) [queryArgs->self malloc: REGS_STATE_SIZE * sizeof(int)];\
	cthread_set_data(cthread_self(), (any_t) threadState);\
\
	
\b0\i /* Initialize the status message */\
	
\fc1\cf1 // 
{\b\i0\fc0\cf0{\NeXTHelpLink32069 \markername ;\linkFilename CodeHelp.rtf;\linkMarkername msg_header_t;}
¬}\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\ulnone\fs24\fc0\cf0  
\b0\i\fc1\cf1 Standard Mach message header info
\b\i0\fc0\cf0 \
	queryArgs->msg.h.msg_simple = TRUE;\
	queryArgs->msg.h.msg_size = sizeof(StatusMsg);\
	queryArgs->msg.h.msg_type = MSG_TYPE_NORMAL;\
	queryArgs->msg.h.msg_local_port = PORT_NULL;\
	queryArgs->msg.h.msg_remote_port = queryArgs->port;\
		
\b0\i\fc1\cf1 // Actually set by the status macros in query.c, dirsend.c
\b\i0\fc0\cf0 \
	queryArgs->msg.h.msg_id = 0;\
	
\b0\i\fc1\cf1 // 
{{\NeXTHelpLink32430 \markername ;\linkFilename CodeHelp.rtf;\linkMarkername msg_type_t;}
¬}\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\ulnone\fs24\fc0\cf0  
\b0\i\fc1\cf1 Standard Mach message type info
\b\i0\fc0\cf0 \
	queryArgs->msg.t.msg_type_name = MSG_TYPE_BYTE;\
	queryArgs->msg.t.msg_type_size = 8;\
	queryArgs->msg.t.msg_type_number = 4+64+4+64;\
	queryArgs->msg.t.msg_type_inline = TRUE;\
	queryArgs->msg.t.msg_type_longform = FALSE;\
	queryArgs->msg.t.msg_type_deallocate = FALSE;\
	
\b0\i\fc1\cf1 // The queryIndex part of the message was set in 
\b newQueryThread
\b0  ...
\b\i0\fc0\cf0 \
\
	
\b0\i /* Initialize the Prospero error variables */
\b\i0  \
	perrno = 0; *p_err_string = '\\0';\
	pwarn = 0;  *p_warn_string = '\\0';\
\
	/* Set the threadAbortFD to the read end of the abortPipe */\
	SetThreadAbortFD(queryArgs->abortPipe[0]);\
\

\f0\b0\fs20 [queryArgs->self debug: MAX_DEBUG method:NULL, "QueryThreadProc(): task %s beginning: t = %p",\
	(queryArgs->type == ArchieQuery ? "ArchieQuery" : "DirListingQuery"),\
	cthread_self()];\

\f1\b\fs24 	/* Set our thread state to reflect our current state */\
#ifdef mc68000\
	asm("movel a6, a6@(-8)");		// Frame pointer\
	asm("movel a7, a6@(-16)");	// Stack pointer\
	asm("lea pc@, a0");\
	asm("movel a0, a6@(-12)");	// Current program counter\
#endif\

\f0\b0\fs20\fc1\cf1 [queryArgs->self debug: MAX_DEBUG method:NULL, 
\f1\b\fs24\fc0\cf0 "
\f0\b0\fs20 QueryThreadProc(): fp = %x; sp = %x; pc = %x", fp, sp, pc];
\f1\b\fs24 \
	FP_FIELD(*threadState) = fp;\
	SP_FIELD(*threadState) = sp;\
	PC_FIELD(*threadState) = pc;\
\
	if( queryArgs->type == ArchieQuery )\
		queryArgs->links = archieQuery(queryArgs->hostname, queryArgs->string,\
			queryArgs->info.archie.maxReplies, 0, queryArgs->info.archie.type,\
			queryArgs->cmp, queryArgs->flags, &(queryArgs->msg));\
	else\
	\{\
		queryArgs->links = stringQuery(queryArgs->hostname, queryArgs->string,\
			&(queryArgs->msg));\
		if ( (queryArgs->flags & AQ_NOSORT) == 0 )\
			queryArgs->links = SortList(queryArgs->links, queryArgs->cmp);\
	\}\

\f0\b0\fs20 [queryArgs->self debug: MAX_DEBUG method:NULL, "QueryThreadProc(): task %s finished, status = %d: t = %p, statusID = %d\\n",\
	(queryArgs->type == ArchieQuery ? "ArchieQuery" : "DirListingQuery"),\
	queryArgs->status, cthread_self(), queryArgs->msg.h.msg_id];\

\f1\b\fs24 \
	
\b0\i /* Cl
\fc1\cf1 ear the query mutex */
\b\i0 \
	mutex_unlock(query_thread_mutex);\

\fc0\cf0 \
	QueryExit(queryArgs);\
\} 
\b0\i // End QueryThreadProc()
\b\i0 \
\
static void QueryExit(QueryArgsPtr queryArgs)\
\{\
REGS_STRUCT *threadState;\
\
	/* Free our state structure */\
	threadState = (REGS_STRUCT *) cthread_data(cthread_self());\
	free(threadState);\
\
	/* Close both ends of the abort pipe */\
	close(queryArgs->abortPipe[0]);\
	close(queryArgs->abortPipe[1]);\
\
#ifdef NOT_DEFINED\
	
\b0\i /* Don't need to do this if we are not being sent here by a\
		set_thread_state() call.  In fact this is screwed up since I am\
		not setting the required members of the msg (e.g. queryIndex). */
\b\i0 \
	if( queryArgs->status == QUERY_ABORTED )\
	\{	/* Send ourselves a QUERY_DONE update message */\
	StatusMsg msg;\
		msg.h.msg_id = QUERY_DONE;\
		msg.h.msg_local_port = PORT_NULL;\
		msg.h.msg_remote_port = queryArgs->port;\
		strcpy(msg.format, "Query Aborted");\
		msg_send((msg_header_t *) &msg, MSG_OPTION_NONE, 0); \
\
		cthread_exit( (any_t) 1 );\
	\}\
#endif\
\
	if( queryArgs->status == QUERY_ABORTED )\
		cthread_exit( (any_t) 1 );\
	else if( queryArgs->links == NULL )\
	\{	
\b0\i /* No matches found, or Prospero error */
\b\i0 \
		queryArgs->status = QUERY_ERROR;\
		cthread_exit( (any_t) 2 );\
	\}\
\
	
\b0\i /* Successful query */
\b\i0 \
	queryArgs->status = QUERY_SUCCESS;\
	cthread_exit( (any_t) 0 );\
\} // End QueryExit()\
\

\b0\i\fs28\fc1\cf1 /*\\ --------------------------------- Mach Message Handler --------------------------------- \\*/
\b\i0\fs24\fc0\cf0 \
static void QueryStatus(msg_header_t *msg, void *userData)\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker35843 \markername QueryStatus;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone\fc1\cf1  None;
\fc0\cf0 \
	
\i0\ul Description:
\i\ulnone  This is the function called at the start of every\
		event loop cycle if there is data waiting at our statusPort.\
		It reads the status message from the Mach message msg and\
		displays it as approriate. We can do whatever we want with\
		the interface objects from this function since it runs\
		in the main thread.
\fc1\cf1 ;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		
\fc1\cf1 msg
\fc0\cf0 : The Mach message structure sent by the query thread;\
		userData: A reference to Query *self;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
StatusMsgPtr statusMsg = (StatusMsgPtr) msg;\
Query *self = (Query *) userData;\
QueryArgsPtr args;\
char statusMsgBuf[128], smallMsgBuf[36], *statusMsgPtr = statusMsgBuf;\
BOOL done = NO;\
Menu *theQueriesMenu;\
MenuCell *menuCell;\
Matrix *menuItems;\
int row, col;\
\
	
\b0\i /* First check for a user abort by typing Command-. */
\b\i0 \
	if( 
\fc1\cf1 NXUserAborted
\fc0\cf0 () == YES )\
	\{\

\f0\b0\fs20 [self debug: MAX_DEBUG method:NULL, "
\fc1\cf1 NXUserAborted() is yes"];
\f1\b\fs24\fc0\cf0 \
		[self interruptRequest: self];\
	\}\
\
	
\b0\i /* Get the QueryArgs struct for the thread sending the message */
\b\i0 \
	args = [self->activeQueries elementAt: statusMsg->queryIndex];\
	if( args == NULL )\
	\{\
		[self error: INTERNAL_ERROR method: NULL key: QUERY_NOT_IN_LIST];\
	\}\
\
	
\b0\i /* Enable the abort button */
\b\i0 \
	if( args->type == ArchieQuery && self->abortEnabled == NO )\
	\{\
		[self->abortID setEnabled: YES];\
		self->abortEnabled = YES;\
	\}\
\
	
\b0\i /* Display the current query status */
\b\i0 \
	switch( statusMsg->h.msg_id )\
	\{\
		case STATUS0:\
			statusMsgPtr = statusMsg->format;\
			break;\
		case STATUS1:\
			sprintf(statusMsgBuf, statusMsg->format,\
				statusMsg->status1Arg);\
			break;\
		case STATUS2:\
			sprintf(statusMsgBuf, statusMsg->format,\
				statusMsg->status2Args[0],\
				statusMsg->status2Args[1]);\
			break;\
		case QUERY_DONE:\
		default:\
			statusMsgPtr = statusMsg->format;\
			done = YES;\
			break;\
	\}\
	strncpy(smallMsgBuf, statusMsgPtr, 35);\
	smallMsgBuf[35] = '\\0';\
	[self->msgFieldID setStringValue: smallMsgBuf];\

\f0\b0\fs20 [self debug: MAX_DEBUG method:NULL, "Query %p, t = %p; Status%d: %s; args->status: %d\\n",\
	self, args->cthread, statusMsg->h.msg_id, statusMsgPtr, args->status];
\f1\b\fs24 \
\
	if( args->type == ArchieQuery )\
		[self->statusID setStringValue: statusMsgPtr];\
	else if( statusMsg->h.msg_id == STATUS0 )\
	\{	
\b0\i /* This is the first status message from a DirListingQuery.\
			Update the title of the browser column we are loading */
\b\i0 \
		[args->info.dlist.browser setTitle: "Loading Entries..."\
			ofColumn: args->info.dlist.column];\
	\}\
\
	if( done == YES )\
	\{	
\b0\i /* Remove the QueryArgs record from the query list */
\b\i0 \
		[self->activeQueries removeElementAt: statusMsg->queryIndex];\
\

\f0\b0\fs20 [self debug: MAX_DEBUG method:NULL, "QUERY_DONE recieved"];\

\f1\b\fs24 		
\b0\i /* Join the thread.  We join the thread to so that we know that links\
			member of the args has been updated. */
\b\i0 \
		cthread_join(args->cthread);\
\
		
\b0\i /* Remove the "Queries" menu item */
\b\i0 \
		if( 
\fc1\cf1 args->menuTag
\fc0\cf0  > 0 )\
		\{\
			theQueriesMenu = [Query queriesMenu];\
			menuCell = [theQueries
\fc1\cf1 Menu findCellWithTag: args->menuTag];\
			menuItems = [theQueries
\fc0\cf0 Menu itemList];\
			[menuItems getRow: &row andCol: &col ofCell: menuCell];\
			[menuItems removeRowAt: row andFree: YES];\
			[theQueriesMenu sizeToFit];\
		\}\
\
		
\b0\i /* Clear the interface msg field */
\b\i0 \
		[self->msgFieldID setStringValue: NULL];\
\
		
\b0\i /* Check result status */
\b\i0 \
		if( args->status == QUERY_ERROR || args->status == QUERY_ABORTED )\
		\{	\
			if( self->queryPanelID != nil )\
			\{	
\b0\i /* Order out and free the query panel */
\b\i0 \
				[self->queryTimerID endTimer: self];\
				[self->queryPanelID orderOut: self];\
				[self->queryPanelID free];\
				self->queryPanelID = nil;\
			\}\
			if( args->status == QUERY_ERROR )\
			\{	
\b0\i /* Display failure/error message */
\b\i0 \
				if( perrno == 0 )\
					strcpy(statusMsgBuf, "No matches found");\
				else\
					sperrmesg(statusMsgBuf, "Propsero Error Message:\\n",0,NULL);\
				[self alertOk: statusMsgBuf quit: NO];\
			\}\
			
\b0\i /* Ask our ArchieSession delegate to delete us if this was the initial ArchieQuery */
\b\i0 \
			if( args->type == ArchieQuery )\
				[self->delegate delete: self];\
			else\
			\{	
\b0\i\fc1\cf1 /* Mark the directory as empty and reload the column to update its title\
					and cell message */
\b\i0\fc0\cf0 \
				[args->info.dlist.dirVLINK setEmptyDir: YES];\
				[args->info.dlist.b
\fc1\cf1 rowser setTitle: [
\fc0\cf0 args->info.dlist.dirVLINK
\fc1\cf1  sourceName]\
					ofColumn: ar
\fc0\cf0 gs->info.dlist.column];\
				[args->info.dlist.b
\fc1\cf1 rowser reloadColumn: ar
\fc0\cf0 gs->info.dlist.column];\
			\}\
			return;\
		\}\
\
		
\b0\i /* Invoke the method that should handle the query response */
\b\i0 \
		if( args->type == ArchieQuery )\
			[self parseResponse: args->links];\
		else\
		\{\
			[args->info.dlist.dirVLINK setListing: args->links];\
			
\b0\i /* Check the current browser path */
\b\i0 \
			[args->info.dlist.browser 
\fc1\cf1 getPath: 
\fc0\cf0 statusMsgBuf
\fc1\cf1 \
				toColumn: 
\fc0\cf0 args->info.dlist.column];\
			if( strcmp(statusMsgBuf, args->info.dlist.selection) != 0 ||\
				[args->info.dlist.browser delegate] != self )\
			\{	
\b0\i /* User must have made a new selection in the browser */
\b\i0 \

\f0\b0\fs20 [self debug: MAX_DEBUG method:NULL,  "Listing for %s received\\n\\tColumn not loaded\\n",	args->info.dlist.selection];
\f1\b\fs24 \
				return;\
			\}\
			[self  loadEntries: args->info.dlist.dirVLINK\
				for: args->info.dlist.browser\
				inColumn: args->info.dlist.column];\
		\}\
	\}\
\} 
\b0\i // End QueryStatus()
\b\i0 \
\

\b0\i\fs28\fc1\cf1 /*\\ ---------------------------- Archival Methods ---------------------------- \\*/\

\b\i0\fs24\fc0\cf0 static inline char *ClassName() \{ return "Query"; \}\

\fc1\cf1 static inline long ArchiveVersion()\
\{\

\fc0\cf0 long release, subrelease, type, version;\
\
	rele
\fc1\cf1 ase = QUERY_VERS;\
	subrelease = QUERY_SUBVERS;\
	type = QUERY_TYPE;\
	version = 1000 * release + 10 * subrelease + type;\
\
	return version;\
\}\
\
- initFromTStream:(NXTypedStream *) stream\
\{\

\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker41376 \markername initFromTStream:;}
¬}\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  The value fro
\fc1\cf1 m 
\b readFromTStream:
\b0 ;
\fc0\cf0 \
	
\i0\ul Description:
\i\ulnone  This method initializes a newly allocated Query object\
		by unarchiving its instance variable from the typed stream argument.\
		The unarchiving is performed by our 
\b readFromTStream:
\b0  method.;\
	
\i0\ul Args:
\i\ulnone  \
		stream: the typed stream from which to read the object;\
*/
\f1 \

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc1\cf1 	[self init];\
\
	return [self readFromTStream: stream];\
\} 
\b0\i // End initFromTStream:
\b\i0 \

\b0 \

\b\fc0\cf0 - 
\fc1\cf1 readFromTStream
\fc0\cf0 :(NXTypedStream *) stream\
\{\

\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker41849 \markername readFromTStream:;}
¬}\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if successful, nil on failure;\
	
\i0\ul Description:
\i\ulnone  This method unarchives a Query object description from the\
		typed stream that was previously save by the 
\b writeToTStream:
\b0  method.\
		The receiving Query object must have been allocated and inited using\
		
\b [[Query alloc] init]
\b0  or 
\b [[Query alloc] 
\fc1\cf1 initFromTStream
\fc0\cf0 :]
\b0 .\
		If the object is sucessfully read from the stream, self is returned.\
		If an exception occurs the object is freed and nil is returned.;\
	
\i0\ul Args:
\i\ulnone  \
		stream: the typed stream from which to read the object;\
*/
\f1 \

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 long version;\
int hostCount;\
List <ObjectArchival> *hostFiles;\
const char *hostname, *className;\
void *data1 = NULL, *data2 = NULL;\
\
NX_DURING\

\f0\b0\fs20\fc1\cf1 [self debug: SUPER_DEBUG method: _cmd, "class = %s; superclass = %s\\n",\
	[[self class] name], [[self superclass] name]];\
[self debug: SUPER_DEBUG method: _cmd, "\\tinstance = %s\\n", ClassName()];
\f1\b\fs24\fc0\cf0 \
\
	
\b0\i /* First read the class name & archive version info */
\b\i0 \
	NXReadTypes(stream, "*i", &className, &version);\
	if( strcmp(className, ClassName()) != 0 )\
	\{\
		NXAllocErrorData(strlen(className)+1, &data1);\
		NXAllocErrorData(strlen(ClassName())+1, &data2);\
		strcpy(data1, className);\
		strcpy(data2, ClassName());\
		NX_RAISE(eWrongClassName, data1, data2);\
	\}\
\
	
\b0\i /* Unarchive our instance variables */
\b\i0 \
	switch( version )\
	\{\
		ca
\fc1\cf1 se QUERY_VERSION_0:\

\fc0\cf0 			
\b0\i /* Read in the query string */
\b\i0 \
			NXReadType(stream, "*", &queryString);\
			
\b0\i\fc1\cf1 /* Read in the host count */
\b\i0\fc0\cf0 \
			NXReadType(stream, "i", &hostCount);\

\b0\i 			/* For each host allocate a List to hold its 
\fc1\cf1 ProsperoVLINK 
\fc0\cf0 objects\
				and initialize them by having them read their data from the\
				typed stream */\

\b\i0 			matchCount = 0;\
			while( [hostnames count] < hostCount )\
			\{	
\b0\i\fc1\cf1 /* Read the next host's vlink object list */
\b\i0\fc0\cf0 \
				hostFiles = [[List alloc] 
\fc1\cf1 initFromTStream
\fc0\cf0 : stream];\
				if( hostFiles == nil )\
					NX_RAISE(eListReadErr, NULL, NULL);\
				matchCount += [hostFiles count];\

\b0\i\fc1\cf1 				/* Get the hostname from the last vlink and save the files list\
					in the hash table and the host's name in the hostnames list */
\b\i0\fc0\cf0 \
				hostname = [[hostFiles objectAt: 0] sourceHost];\
				[hostHashTable insertKey: hostname value: hostFiles];\
				[hostnames addElement: &hostname];\
			\}\
			break;\
\
		default:\
			NXAllocErrorData(strlen([[self class] name])+1, &data1);\
			NXAllocErrorData(sizeof(long), &data2);\
			strcpy(data1, [[self class] name]);\
			*((long *) data2) = version;\
			NX_RAISE(eBadObjVersion, data1, data2);\
			break;\
	\}\

\fc1\cf1 NX_HANDLER\
	NX_RERAISE();\
NX_ENDHANDLER\

\fc0\cf0 \
	return self;\
\} 
\b0\i // End readFromTStream:
\b\i0 \
\

\fc1\cf1 - writeToTStream:(NXTypedStream *) stream\

\fc0\cf0 \{\

\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker44432 \markername writeToTStream:;}
¬}\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if successful, nil on failure;\
	
\i0\ul Description:
\i\ulnone  This method archives a Query object description to the\
		typed stream.  If the object is sucessfully written to the stream,\
		 self is returned.  If an exception occurs it is rerasied.;\
	
\i0\ul Args:
\i\ulnone  \
		stream: the typed stream to write the object to;\
*/
\f1 \

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\b\i0\fc0\cf0 long version;\
int hostCount, host;\
List <ObjectArchival> *hostFiles;\
const char *hostname, *className;\
\
NX_DURING\

\f0\b0\fs20 [self debug: SUPER_DEBUG method: _cmd, "class = %s; superclass = %s\\n",\
	[[self class] name], [[self superclass] name]];\
[self debug: SUPER_DEBUG method: _cmd, "\\tinstance = %s\\n", ClassName()];
\f1\b\fs24 \
\
	
\b0\i /* First write the class name & version */
\b\i0 \
	className = ClassName();\
	version = ArchiveVersion();\
	NXWriteTypes(stream, "*i", &className, &version);\
\
	
\b0\i\fc1\cf1 /* Write the query string */
\b\i0\fc0\cf0 \
	NXWriteType(stream, "*", &queryString);\
	
\b0\i\fc1\cf1 /* Write out the host count */
\b\i0\fc0\cf0 \
	hostCount = [hostnames count];\
	NXWriteType(stream, "i", &hostCount);\

\b0\i 	/* Write each host's list of  
\fc1\cf1 ProsperoVLINK 
\fc0\cf0 objects to the typed stream */\

\b\i0 	host = 0;\
	while( host < hostCount )\
	\{\
		hostname = *((const char **) [hostnames elementAt: host ++]);\
		hostFiles = (List <ObjectArchival> 
\fc1\cf1 *)[hostHashTable valueForKey: hostnam
\fc0\cf0 e];\

\b0\i\fc1\cf1 		/* Write this host's file list */
\b\i0\fc0\cf0 \
		[hostFiles 
\b0\i writeToTStream
\b\i0 : stream];\
	\}\
\

\fc1\cf1 NX_HANDLER
\fc0\cf0 \

\fc1\cf1 	NX_RERAISE();\
NX_ENDHANDLER\

\fc0\cf0 \
	return self;\
\} 
\b0\i // End writeToTStream:
\b\i0 \
\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fs28\fc1\cf1 /*\\ ---------------------- Menu Update Methods ---------------------- \\*/
\b\i0\fs24\fc0\cf0 \
- (BOOL) validateCommand:(MenuCell *) menuCell\
\{\

\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker45946 \markername validateCommand:;}
¬}\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\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\f1\b\i0\fc0\cf0 SEL action = [menuCell action];\
BOOL enable = YES, isSelection;\
int browserColumn;\
\
	
\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 \
	isSelection = [
\fc1\cf1 selectionList
\fc0\cf0  count] > 0;\
	browserColumn = [fileBrowserID selectedColumn];\
	if( action == @selector(connect:) )\
		enable = isSelection;\
	else if( action == @sele
\fc1\cf1 ctor(
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc1\cf1 retrieve
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 :) || action == @selector(openInWorkspace:) )\
		enable = (isSelection && (brow
\fc0\cf0 serColumn > 0));\
	else if( action == @selector(showFTPLog:) )\
		enable = (ftpServer != nil);\
\

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

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 \

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

\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker47167 \markername connect: ;}
¬}\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if we connect, nil otherwise;\
	
\i0\ul Description:
\i\ulnone  This method initiates an interactive FTP session with\
		the host associated with the current browser selection;\
	
\i0\ul Args:
\i\ulnone  \
		sender: The Matrix from the FTP submenu;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
Matrix *column0;\
int hostIndex;\
NXAtom hostname;\
\
	column0 = [
\fc1\cf1 fileBrowserID
\fc0\cf0  matrixInColumn: 0];\
	if( column0 == nil )\
		return nil;\
	hostIndex = [column0 selectedRow];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 	hostname = *((NXAtom *) [hostnames elementAt: hostIndex]);\
\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 	ftpServer = [[FTPObject alloc] initForHost: hostname interactive: YES  initialPath: 0];\
	if( localTransferDir != NULL )\
		[ftpServer setLocalTransferDir: localTransferDir];\
\
	return self;\
\} 
\b0\i // End connect:
\b\i0 \
\

\fc1\cf1 - retrieve: sender\

\fc0\cf0 \{\

\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker47880 \markername retrieve:;}
¬}\pard\tx180\tx360\tx540\tx720\tx900\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc1\cf1  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if the selection is not in column0, nil otherwise;\
	
\i0\ul Description:
\i\ulnone  This method retrieves the selected file(s) in the\
		browser interface from the associated host via anonymous ftp;\
	
\i0\ul Args:
\i\ulnone  \
		sender: The Matrix from the FTP submenu;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \
Matrix *column0;\
int hostIndex, 
\fc1\cf1 index = 0;
\fc0\cf0 \
NXAtom hostname;\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 NXStream *dataStream;\
List *fileList;\
MyBrowserCell *cell = nil;\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f0\b0\fs20\fc0\cf0 [self debug: MAX_DEBUG method:_cmd, "selection count = %d", [selectionList count]];
\f1\b\fs24 \
\
	column0 = [
\fc1\cf1 fileBrowserID
\fc0\cf0  matrixInColumn: 0];\
	if( column0 == nil )\
		return nil;\
	hostIndex = [column0 selectedRow];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 	hostname = *((NXAtom *) [hostnames elementAt: hostIndex]);\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 \
	
\b0\i /* Create a list of PVlink objects to give the the ftp server */
\b\i0 \
	fileList = [[List alloc] initCount: 0];\
	while( (cell = [selectionList objectAt: index ++]) != nil )\
		[fileList addObject: [cell tag]];\
\
	
\b0\i /* Transfer the files and obtain a list of the local pathnames */
\b\i0 \
	ftpServer = [[FTPObject alloc] initForHost: 
\fc1\cf1 hostname
\fc0\cf0  interactive: NO initialPath: 0];\
	if( ftpServer == nil )\
	\{	
\b0\i\fc1\cf1 /* Report failure to user */
\b\i0\fc0\cf0 \
		[self alertOk: "Archie failed to retrieve the file(s) from the ftp host" quit: NO];\
		return NULL;\
	\}\
	if( localTransferDir != NULL )\
		[ftpServer setLocalTransferDir: localTransferDir];\
	
\fc1\cf1 dataStream = 
\fc0\cf0 [ftpServer  retrieveFiles: fileList];\
	if( dataStream != 0 )\
		NXCloseMemory(dataStream, NX_FREEBUFFER);\
	ftpServer = [ftpServer free];\
\
	return self;\
\} 
\b0\i\fc1\cf1 // End retrieve:
\b\i0\fc0\cf0 \
\
- openInWorkspace: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc1\cf1 /*
{{\NeXTHelpMarker49364 \markername openInWorkspace:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\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;\
*/\

\f1\b\i0\fc0\cf0 File *theFile;\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 MyBrowserCell *cell;\
static FTPObject *workspaceFtpServer;\
FTPObject *session;\
int browserColumn;\
BOOL isSelection;\
\
	
\b0\i /* Since this method is invoked as the file browser double action, we\
		must perform the same check we do in validateCommand: to see\
		if this is a logical thing to do */
\b\i0 \
	isSelection = [
\fc1\cf1 selectionList
\fc0\cf0  count] > 0;\
	browserColumn = [fileBrowserID selectedColumn];\

\fc1\cf1 	if( isSelection == NO || brow
\fc0\cf0 serColumn == 0 )\
		return self;	
\b0\i\fc1\cf1 // Not a valid action
\b\i0\fc0\cf0 \
\
	if( [sender isKindOf: [NXBrowser class]] == NO )\
		sender = fileBrowserID;\
\
	cell = [sender selectedCell];\
	theFile = [cell tag];\
	if( workspaceFtpServer == nil )\
		workspaceFtpServer = [[FTPObject alloc] init];\
	session = [workspaceFtpServer initForHost: [theFile sourceHost] interactive: NO\
		initialPath: 0];\
	if( session == nil )\
	\{	
\b0\i /* Failed to connect to remote host */
\b\i0 \
		workspaceFtpServer = nil;\
		return self;\
	\}\
	[theFile setDelegate: workspaceFtpServer];\
	[theFile openInWorkspace];\
	[workspaceFtpServer logout: self];\

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 \
	return self;\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 \} 
\b0\i\fc1\cf1 // End openInWorkspace:
\b\i0\fc0\cf0 \

\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\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker50722 \markername setTransferDir:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method brings up an OpenPanel prompting the\
		user for a directory to use as the target for ftp file transfers.\
		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\f1\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 OpenPanel *openPanel;\
const char *transferDir;\
\
	open
\fc1\cf1 Panel = [OpenPanel new];\
	[openPanel chooseDirectories: YES];\
	if( [openPanel runModalForTypes: NULL] == 0 )\
		return nil;\
	transferDir = [openPanel filename];
\fc0\cf0 \
	localTransferDir = NXCopyStringBuffer(transferDir);\
\

\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 \
\
#ifdef NOT_DEFINED\
- showFTPLog: sender\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker51370 \markername showFTPLog:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method asks the current ftpServer object to\
		display its ftp log;
\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\f1\b\i0\fc0\cf0 \
	[ftpServer showFTPLog: sender];\
\
	return self;\
\} 
\b0\i // End showFTPLog:
\b\i0 \
#endif\
\

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

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b0\i\fc0\cf0 /* For now use hard coded RTF formatting */
\b\i0 \
static char *rtf_header = "\\\\rtf0\\\\ansi\{\\\\fonttbl\\\\f0\\\\fswiss Helvetica;\}\\\\smartcopy0";\
static char *rtf_colors = "\{\\\\colortbl;\\\\red0\\\\green0\\\\blue0;\}";\
static char *rtf_host_prefix = "\\\\pard\\\\tx733\\\\f0\\\\b\\\\i0\\\\ul\\\\fs24\\\\fc0\\\\cf0 Host";\
static char *rtf_location_prefix = "\\\\pard\\\\tx1800\\\\b0\\\\ul\\\\li360\\\\fc0\\\\cf0 Location:";\
static char *rtf_info_prefix = "\\\\pard\\\\tx1980\\\\tx3240\\\\tx4680\\\\tx6120\\\\fs20\\\\fi-180\\\\li720\\\\fc0\\\\cf0";\
static char *rtf_info_suffix = "\\\\b\\\\fs24 \\\\";\
#define HOSTNAME(s, hostname) NXPrintf(s, "\\\\ulnone \\t%s\\\\\\n", hostname);\
#define LOC(s, location) NXPrintf(s, "\\\\ulnone \\t%s\\\\\\n", location);\

\fc1\cf1 \
- copyTo: (NXStream *) pbData type:(const NXAtom) type\
\{\

\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\fc0\cf0 /*
{{\NeXTHelpMarker52424 \markername copyTo:type:;}
¬}\pard\tx180\tx360\tx540\tx720\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs24\fc0\cf0  --- 
\ul MethodDescription
\ulnone \
	
\i0\ul ReturnValue:
\i\ulnone  self if we can do the type, nil otherwise;\
	
\i0\ul Description:
\i\ulnone\fc1\cf1  This method writes a formatted version of the current\
		query response to the pasteboard stream. We currently can do\
		either RTF or ASCII.;
\fc0\cf0 \
	
\i0\ul Args:
\i\ulnone \
		
\fc1\cf1 pbData
\fc0\cf0 : The pasteboard stream;\
		type: The type of data to place in the stream;\
*/
\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\f1\b\i0\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 BOOL isRTF = ( type == NXRTFPboardType ? YES : NO );\
NXAtom hostname;\
List *hostFiles;\
PVlink *vlink;\
const char *location;\
int host, file;\
\
	if( type != NXRTFPboardType && type != NXAsciiPboardType )\
		return nil;\
\
	if( isRTF == YES )\
	\{\
		NXPrintf(pbData, "\{%s\\n%s\\n", rtf_header, rtf_colors);\
		NXPrintf(pbData, "\{\\\\info \{\\\\author ArchieApp\}\}\\n");\
	\}\
\
	for( host = 0; host < [hostnames count]; host ++)\
	\{\
		hostname = *((NXAtom *) [hostnames elementAt: host]);\
		hostFiles = (List *) [hostHashTable valueForKey: hostname];\
		if( isRTF == YES )\
		\{\
			NXPrintf(pbData, "%s\\n", 
\fc0\cf0 rtf_host_prefix
\fc1\cf1 );\
			HOSTNAME(pbData, 
\fc0\cf0 hostname);\
		\}\
		else\
			NXPrintf(pbData, "Host\\t%s\\n", hostname);\
		for( file = 0; file < [hostFiles count]; file ++)\
		\{\
			vlink = [hostFiles objectAt: file];\
			location = [vlink sourceDirectory];\
			\

\fc1\cf1 			if( isRTF == YES )\
			\{\
				NXPrintf(pbData, "%s\\n", 
\fc0\cf0 rtf_location_prefix);\

\fc1\cf1 				LOC(pbData, 
\fc0\cf0 location);\
				NXPrintf(pbData, "%s ", rtf_info_prefix);\
			\}\
			else\
			\{\
				NXPrintf(pbData, "Location:\\t%s\\n", location);\
				NXPrintf(pbData, "\\t\\t");\
			\}\
			free(location);\
			[vlink writeInfo: pbData];\
			if( isRTF == YES )\
				NXPrintf(pbData, "%s\\n", rtf_info_suffix);\
		\}
\fc1\cf1 \
	\}\

\fc0\cf0 	if( isRTF == YES )\
		NXPrintf(pbData, "\}\\n", rtf_info_suffix);\

\fc1\cf1 \
	return self;\
\} 
\b0\i // End copyTo: type:
\b\i0 \

\pard\tx480\tx960\tx1440\tx1920\tx2400\tx2880\tx3360\tx3840\tx4320\tx4800\fc0\cf0 \
@end\

\b0\i /* RCS Information:\
	$Author: me $;\
	$Date: 94/01/08 14:41:10 $;\
	$Source: /usr1/me/NeXTSrc/Archie/RCS/Query.m,v $;\
	$Revision: 1.1 $;\
	$Log:	Query.m,v $Revision 1.1  94/01/08  14:41:10  meCheck point of 2.09a version.Revision 1.7  93/03/29  02:04:28  meUpdated to act as the file inspector delegate.\
Revision 1.6  93/02/26  03:09:20  meAdded check to openInWorkspace: to prevent a crash when one double clicked on hostname in column zero.\
Revision 1.5  93/02/24  18:30:44  meUpdated to use the File class emptyDir flag for failed DirLoadings.\
Revision 1.4  93/02/24  17:57:59  meOnly delete Query in QueryStatus() if it was an ArchieQuery task.  Also added code to reload the browser column of a failed DirLoadinging tasks to reset the column title and cell text.\
Revision 1.3  93/02/24  17:41:02  meNow check if queryPanelID is nil in QueryStatsu() before messaging.  This would crash the program if a DirLoading task failed to find any files.\
Revision 1.2  93/02/24  11:44:16  meAdded the display of error message panels to the retrieve: and openInWorkspace: methods on failure to login to the ftp server.\
Revision 1.1  93/02/23  02:11:51  meVersion 2.01a of the project.;\
*/\

}
