/* File: Unknown.m - The 'Unknown' Application
 *
 * By: Christopher Lane
 * Symbolic Systems Resources Group
 * Knowledge Systems Laboratory
 * Stanford University
 *	
 * Contributor(s):
 *	
 *     Jiro Nakamura (jiro@heights.cit.cornell.edu)
 *
 * Date: 2 April 1991
 *
 * Copyright: 1989, 1990 & 1991 by The Leland Stanford Junior University.
 * This program may be distributed without restriction for non-commercial use.
 */
 
#import "Unknown.h"

#import <c.h>
#import <libc.h>
#import <stdlib.h>
#import <ldsyms.h>
#import <strings.h>

#import <sys/file.h>
#import <sys/loader.h>

#import <objc/typedstream.h>
#import <objc/HashTable.h>

#import <appkit/Panel.h>
#import <appkit/Speaker.h>
#import <appkit/Listener.h>
#import <appkit/defaults.h>

// #import <text/pathutil.h>

#define APPLICATION "Unknown"

#define VERIFYDEFAULTSTRING "Verify"
#define DEFAULTVERIFYSTRING "Yes"
#define REPORTERRORDEFAULTSTRING "ReportError"
#define DEFAULTREPORTERRORSTRING "Yes"

#define getDefaultValue(s) NXGetDefaultValue(APPLICATION, s)
#define registerDefaults(s) NXRegisterDefaults(APPLICATION, s)

#define getDefaultBoolValue(s) (BOOL) (strncmp(getDefaultValue(s), "Yes", 1) == EQ)

static NXDefaultsVector UnknownDefaults = {
	{ VERIFYDEFAULTSTRING, DEFAULTVERIFYSTRING },
	{ REPORTERRORDEFAULTSTRING, DEFAULTREPORTERRORSTRING },
	{ NULL }
	};

#define HASHSEGMENT "__HASHTABLE"
#define HASHSECTION "__DATA"

#define ICONSEGMENT "__ICON"
#define ICONSECTION "__header"

#define BUFFERSIZE 1024
#define BITSPERBYTE 8

#define EXECUTESTRING "Execute?"
#define ERRORSTRING "Error running '%s', exit code = %d."

#define DEFAULTBUTTON "OK"
#define ALTERNATEBUTTON "Cancel"

typedef enum { LT = -1, EQ, GT } COMPARISON;

char *basename(char *path)
{ 
	char *s;
	
	if((s = rindex(path, '/')) != NULL) return ++s;
	
	return path;
}

char *parentname(char *path)
{ 
	char *p, *s = NXCopyStringBuffer(path);
	
	if((p = rindex(s, '/')) == NULL) { p = s; *p++ = '.'; }
		
	*p = '\0';
	
	return s;
}

@implementation Unknown : Application

+ new
{
	self = [super new];
    
	(void) registerDefaults(UnknownDefaults);
	
	return self;
}

- (BOOL) appAcceptsAnotherFile:sender { return(YES); };

- (int) appOpenFile:(const char *) filename type:(const char *) aType
{
	int status;
	char buffer[BUFFERSIZE];
	const char *program, *base = basename((char *) filename);
	HashTable *table = [self loadSegment:HASHSEGMENT section:HASHSECTION];

	if(access(filename, R_OK) == CERROR) return (int) [self unix_error:"access"];
	if(chdir(parentname((char *) filename)) == CERROR) return (int) [self unix_error:"chdir"];
		
	if((program = [table valueForKey:aType]) != NULL) {
		(void) sprintf(buffer, program, base, base, base, base, base);
		if(! getDefaultBoolValue(VERIFYDEFAULTSTRING) ||
		  (NXRunAlertPanel(EXECUTESTRING, buffer, DEFAULTBUTTON, ALTERNATEBUTTON, NULL) == NX_OKTAG)) {
		  	if((status = system(buffer)) == CERROR) return (int) [self unix_error:"system"];
			status >>= BITSPERBYTE;
			if(status != EXIT_SUCCESS && getDefaultBoolValue(REPORTERRORDEFAULTSTRING))
				NXRunAlertPanel(NULL, ERRORSTRING, NULL, NULL, NULL, buffer, status);
			}
		}
	else return [self ReOpenFile:filename type:aType];

	return(YES);
}

- appDidInit:sender { return [self terminate:sender]; }

- loadSegment:(const char *) segment section:(const char *) section
{
	int size;
	id object;
	void *pointer;
	NXStream *stream;
	NXTypedStream *typedstream;
	
	pointer = getsectdata(segment, section, &size);
	stream = NXOpenMemory((const char *) pointer, size, NX_READONLY);
	typedstream = NXOpenTypedStream(stream, NX_READONLY);
	object = NXReadObject(typedstream);
	NXCloseTypedStream(typedstream);

	return object;
}

- (int) ReOpenFile:(const char *) filename type:(const char *) aType
{
	int size, result;
	void *pointer;
	port_t port;
	id speaker = [NXApp appSpeaker];
	NXStream *stream;
	char application[BUFFERSIZE], extension[BUFFERSIZE];
	
	pointer = getsectdata(ICONSEGMENT, ICONSECTION, &size);
	stream = NXOpenMemory((const char *) pointer, size, NX_READONLY);
	
	while(NXScanf(stream, "%*s %*s %s %s", application, extension) != EOF && strcmp(extension, aType) != 0);
	
	NXCloseMemory(stream, NX_FREEBUFFER);

    if(strcmp(application, [self appName]) == 0) return(YES);
	
	if((port = NXPortFromName(application, NULL)) == PORT_NULL)
		return (int) [self unix_error:"NXPortFromName"];

    [speaker setSendPort:port];
	
    if([speaker openFile:filename ok:&result] != SEND_SUCCESS)
		return (int) [self unix_error:"openFile:ok:"];

	return(YES);
}

- (BOOL) unix_error:(const char *) string
{
    perror(string);
    return(NO);
}

@end
