/* HFunction.m	by Paul Kunz	May 1992
 * Base class for Hippo Functions
 *
 * Copyright (C)  1991-1992  The Board of Trustees of
 * The Leland Stanford Junior University.  All Rights Reserved.
 */ 

#import "HFunction.h"

const char HFunction_h_rcsid[] = HFUNCTION_H_ID;
const char HFunction_m_rcsid[] = "$Id: HFunction.m,v 2.16.2.1 1994/01/05 02:22:11 rensing Exp $";

#import <objc/objc-load.h>
#import <mach-o/rld.h>
#import <appkit/appkit.h>

#define ERRORFILE "/tmp/compErrors"

@interface HFunction(PrivateMethods)

- appendSuffix:(char *)buf;
 /*
  * Appends a suffix to the filename according to the language.
  */

- (void *) findSymbol:(const char *)string;
 /*
  * Finds the function string in the symbol table and returns
  * its address if found or 0 if not.
  */

@end

@implementation HFunction

- init 
{
    flags = NXCopyStringBuffer("");
    outputDir = NULL;
    language = ANSI_C;
    function = NULL;
    return self;
}

- setFileName:(const char *)fname
{
    if (filename) NX_FREE(filename);
    filename = NXCopyStringBuffer(fname);
    return self;
}

- setOutputDir:(const char *)fname
{
    if (outputDir) NX_FREE(outputDir);
    outputDir = NXCopyStringBuffer(fname);
    return self;
}

- (const char *)outputDir
{
    return outputDir;
}

- initFromDirectory:(const char *)path
{
    NXStream	*oldCodeStream;	
    char	buf[MAXPATHLEN+1];
    
    [self init];
    
    strcpy(buf, path );
    strcat(buf, "/");
    strcat(buf,name);
    [self appendSuffix:buf];
    oldCodeStream = codeStream;
    codeStream = NXMapFile( buf, NX_READONLY );
    if ( !codeStream ) {
        codeStream = oldCodeStream;
    }
    return self;
}

- (const char *)functionName
{
    return name;
}

- setFunctionName:(const char *)fname
{
    if (name) NX_FREE(name);
    name = NXCopyStringBuffer(fname);
    return self;
}

- (LANGUAGE) language
{
    return language;
}
- setLanguage: (LANGUAGE)lang
{
     language = lang;
     return self;
}
     
- (int) saveToTmpFile
{
    int		pos, fname_len;
    int		irc;
    
    if ( !codeStream ) return (-1);
    
    pos = strlen(name) + 5;
    fname_len = pos + 9;
    NX_MALLOC(filename, char, fname_len);
    sprintf(filename, "/tmp/%sXXXXXX", name);
    filename = NXGetTempFilename(filename, pos);
    [self appendSuffix:filename];
    irc = NXSaveToFile( codeStream, filename);
    NXCloseMemory( codeStream, NX_FREEBUFFER);

    return irc;
}

-(const char*) filename
{
    return filename;
}

- (void *) funcPointer
{
     return function;
}

- setCompileFlags: (const char *)flgs
{
     if (flags) NX_FREE(flags);
     flags = NXCopyStringBuffer(flgs);
     return self;
}

- (const char *) compileFlags;
{
     return flags;
}

- getFunctionFrom:sender
{
    function = [sender findSymbol:name];
    return self;
}
- saveToDirectory:(const char *) directory
{
    char	buf[MAXPATHLEN+1];
    
    strcpy(buf, directory);
    strcat(buf, "/");
    strcat(buf, name);
    [self appendSuffix:buf ];
    codeStream = NXMapFile( filename, NX_READONLY );
    if ( codeStream ) {
	NXSaveToFile( codeStream, buf);
    } else {
        NXRunAlertPanel( "Error", "Can not open function file: %s",
		         "Ok", NULL, NULL, filename );
    }
    NXCloseMemory( codeStream, NX_FREEBUFFER);
    return self;
}

- (int) compile
{

    const char *cmd[] = { "cc -Wall", "f77", "cc -Wall -ObjC++", 
                             "cc -Wall" };
    char *command;
    char *dot;
    int rc;
    
    if (!outputDir)
    {
	 ofilename = NXCopyStringBuffer(filename);
    }
    else
    {
	 char *sl = strrchr(filename,'/');
	 if (!sl) sl = filename;
	 else sl++;
	 ofilename = NXZoneMalloc([self zone],
				  (strlen(sl) + strlen(outputDir) +
				   2)*sizeof(char) );
	 strcpy(ofilename,outputDir);
	 strcat(ofilename,"/");
	 strcat(ofilename,sl);
    }
    dot = strrchr(ofilename,'.');
    dot[1] = 'o';
    dot[2] = '\0';
	 
    command = (char *)malloc( (strlen(cmd[language]) + strlen(flags) +
			       strlen(ofilename) + strlen(filename) +
			       40)*sizeof(char) );
    
    sprintf(command, "%s -g -c -o %s %s %s > %s 2>&1",
	    cmd[language], ofilename, flags, filename, ERRORFILE);

    rc = system(command);
 /*    if ( rc != 0 )
    {
    	free(command);
	return rc;
    }
    
    sprintf(command, "ld -r -o %s %s.o >> /tmp/compError 2>&1", ofilename,
             ofilename);
    rc = system(command);
*/
    free(command);

    return rc;
}

- (int) link
{
    NXStream           *errorStream;
    char                template[24] = {"/tmp/compXXXXXX"};
    char               *tempchar;
    char		*moduleList[2] = { NULL, NULL };
    int                 irc = 0;

    /* errors will get placed here */
    errorStream = NXOpenFile(2, NX_WRITEONLY); // stderr is fileno 2
    moduleList[0] = NXCopyStringBuffer(ofilename);
    
    tempchar = NXGetTempFilename(template, 9);
    irc = objc_loadModules( moduleList, errorStream, NULL, NULL, tempchar );
    NXClose(errorStream);
    function = [self findSymbol:name];
    return irc;
}
- (int) unload
{
    NXStream    *errorStream;
    int		irc = 0;
    
    errorStream = NXOpenFile(2, NX_WRITEONLY);
    irc = objc_unloadModules(errorStream, NULL);
    NXClose(errorStream);
    return !irc;
}


- (NXStream *) openMessages
{
    NXStream 	*msgStream;
    
    msgStream = NXMapFile(ERRORFILE, NX_READONLY);
    return msgStream;
}

- write:(NXTypedStream *)ts
{
    [super write:ts];
    NXWriteTypes( ts, "*i", &name, &language );
    return self;
}

- read:(NXTypedStream *)ts
{
    [super read:ts];
    NXReadTypes( ts, "*i", &name, &language );
    return self;
}

- free
{
    if (codeStream) NXClose(codeStream);
    if (filename) NX_FREE(filename);
    if (ofilename) NX_FREE(ofilename);
    if (flags) NX_FREE(flags);
    if (name) NX_FREE(name);
    return [super free];
}
@end

@implementation HFunction(PrivateMethods)

- appendSuffix:(char *)buf
{
    switch (language) {
    case OBJECTIVE_C:
	strcat(buf, ".m");
	break;
    case FORTRAN:
	strcat(buf, ".F");
	  break;
    case CPP:
	strcat(buf, ".C");
	break;
    case ANSI_C:
	strcat(buf, ".c");
    }
    return self;
}

- (void *) findSymbol:(const char *)string
{
    NXStream            *errorStream;
    char		*symbol_string;
    unsigned long	symbol = 0;
    long 		irc;
    int			length;

    length = strlen(string)+4;
    NX_MALLOC( symbol_string, char, length );
    sprintf( symbol_string, "_%s", string);
    errorStream = NXOpenFile(2, NX_WRITEONLY);
    irc = rld_lookup( errorStream, symbol_string, &symbol );
    if ( !irc ) {
	symbol = 0;
    }
    NXClose(errorStream);
    NX_FREE(symbol_string);
    return (void *)symbol;
}
@end
