This is Module.m in view mode; [Download] [Up]
/**
** Module.m
**/
#import "Eval.h"
#import "Module.h"
#import <objc/objc-load.h>
#include <mach/mach_interface.h>
#import <mach-o/arch.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <strings.h>
static int unused ;
@implementation Module: Object
{ EvalString *path ; // path to .a, .o, or .m file
EvalString *name ; // a unique, humanly-readable name for this module
EvalString *location ; // for .a or .o: path of the file or directory we created. nil for .m
char **components ; // array of char *'s with dir paths to component .o and .a files
moduleType modType ; // the type, i.e. DOTM,DOTO,DOTA
BOOL loaded ; // == YES iff currently loaded, else == NO
}
- (EvalString *) compile: (char *) filePath libs:(char **) aLibString ;
{ // Compile (but don't link) the file at filePath.
// If successful, returns the full path of the resulting .o file which
// is created and left in /tmp. It is the callers responsibility
// to eventually free this file, as well as the returned EvalString.
// If unsuccessful, returns nil. filePath should have a .m extension.
// This file may contain, as its first line, a #pragma compile <flags>,
// specifying compiler switches. If it does not,
// the NXdefault "compilerSwitches" (set in the prefs panel) is used instead.
// filePath may contain, as its second line (or first, if no #pragma compile line
// is present), a #pragma link <modulePath1, modulePath2, ...modulePathn> line,
// specifying a list of libraries to be searched when this file is linked. If
// this line is not present, aLibString will be set to NULL, else aLibString will
// be set to the string following #pragma link, and caller is responsible for
// freeing
char *buf, *errorFileName, *compileBuf ;
char tmpName[20] = "/tmp/EvalXXXXXXXX" ;
const char *compilerFlags ;
int unused, rval = NO, sysRval ;
NXStream *fileStream, *errorStream, *aStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
fileStream = NXMapFile(filePath, NX_READONLY) ;
if(!fileStream)
{ [Eval printf: "Can't open file: %s", buf] ;
NXRunAlertPanel("Eval","Can't open file: %s\n",NULL,NULL,NULL,buf) ;
}
else // check first two lines of file for #pragma compile and #pragma link
{ [self findCompilePragma: &compileBuf linkPragma: aLibString inStream: fileStream] ;
NXCloseMemory(fileStream,NX_FREEBUFFER) ;
compilerFlags = compileBuf ?
compileBuf:NXGetDefaultValue([NXApp appName],"CompilerSwitches");
mkTempFile(NULL,tmpName) ;
errorStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
NXPrintf(errorStream,"%s.cc_errors%c",tmpName,0) ;
NXGetMemoryBuffer(errorStream,&errorFileName,&unused,&unused) ;
NXSeek(aStream,0L,NX_FROMSTART) ;
NXPrintf(aStream,"cc -c %s -o %s.o %s 2> %s%c",
compilerFlags,tmpName,filePath,errorFileName,0) ;
NXGetMemoryBuffer(aStream, &buf, &unused, &unused);
[NXApp message: "Compiling..."] ;
sysRval = system(buf) ;
if(sysRval)
{ // Non-zero exit means errors....show compiler output.
[NXApp printFileToTranscript: errorFileName] ;
NXRunAlertPanel("Eval","Compilation errors: see Transcript Window\n",
NULL,NULL,NULL) ;
}
else
{ // Compile succeeded, only show compiler output if requested.
if(!strcmp(NXGetDefaultValue([NXApp appName],"CompilerOutput"),"1"))
[NXApp printFileToTranscript: errorFileName] ; // show all compiler output
rval = YES ;
}
unlink(errorFileName) ;
NXCloseMemory(errorStream,NX_FREEBUFFER) ; // freeup streams
NXCloseMemory(aStream,NX_FREEBUFFER) ;
if(compileBuf) // free compile switches buffer, if necessary
vm_deallocate(task_self(),(vm_address_t) compileBuf,strlen(compileBuf)+1) ;
}
[NXApp message: ""] ;
if(rval)
{ strcat(tmpName,".o") ;
return [EvalString new: tmpName] ;
}
else
return nil ;
}
- (char **) components ;
{ return components ;
}
- componentsFromList: (List *) aList ;
{ // Allocate and set our componentsArray from
// the list of EvalString's in aList. The EvalString's
// are then "partialFree"'d...i.e. we don't free
// the char * which they wrap, because these are
// used by our components.
int i,knt = [aList count] ;
components = (char **) malloc((knt + 1) * sizeof(char *)) ;
for(i = 0 ; i < knt ; i++)
components[i] = [[aList objectAt: i] partialFree] ;
components[i] = NULL ; // null terminate
return self ;
}
- (BOOL) equals: (Module *) aMod ;
{ if(!strcmp([aMod name],[self name]))
return YES ;
return NO ;
}
- findCompilePragma: (char **) compileBuf
linkPragma: (char **) linkBuf
inStream: (NXStream *) fileStream ;
{ // fileStream must point to a valid stream opened for reading.
// It is examined for a #pragma compile ...test... and
// #pragma link ...text... If either is found, the corresponding
// bufs will be allocated and set to <text>, null-terminated, otherwise they
// will be NULL. A compile pragma (if present)
// must precede a link pragma (if present). The pragma(s), if present, must
// begin at the beginning of fileStream. Continuation lines (i.e. last char
// of a line is a backslash) may be used to allow the pragmas to
// span multiple lines,. It is the caller's responsibility
// to free any allocated buffers using vm_deallocate.
int i,j, c ;
NXStream *aStream ;
NXSeek(fileStream,0L,NX_FROMSTART) ;
i = 0 ;
while(NXGetc(fileStream) == ("#pragma compile ")[i++]) ;
if(i == 17) // compilation line found
{ NXUngetc(fileStream) ;
aStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
while(!NXAtEOS(fileStream) && (c = NXGetc(fileStream)) != '\n')
{ if(c == '\\') // continuation line?
{ if(NXGetc(fileStream) == '\n')
continue ;
else
NXUngetc(fileStream) ;
}
NXPutc(aStream,c) ;
}
NXPutc(aStream,'\0') ;
NXGetMemoryBuffer(aStream,compileBuf, &i, &j);
NXCloseMemory(aStream,NX_TRUNCATEBUFFER) ;
}
else
{ *compileBuf = NULL ;
NXSeek(fileStream,0L,NX_FROMSTART) ;
}
i = 0 ;
while(NXGetc(fileStream) == ("#pragma link ")[i++]) ;
if(i == 14) // link line found
{ NXUngetc(fileStream) ;
aStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
while(!NXAtEOS(fileStream) && (c = NXGetc(fileStream)) != '\n')
{ if(c == '\\') // continuation line?
{ if(NXGetc(fileStream) == '\n')
continue ;
else
NXUngetc(fileStream) ;
}
NXPutc(aStream,c) ;
}
NXPutc(aStream,'\0') ;
NXGetMemoryBuffer(aStream, linkBuf, &i, &j);
NXCloseMemory(aStream,NX_TRUNCATEBUFFER) ;
}
else
*linkBuf = NULL ;
return self ;
}
- free ;
{ if((modType == DOTA) || (modType == DOTM))
{ if(location)
{ char *buf ;
NXStream *aStream = NXOpenMemory(NULL,0,NX_READWRITE) ;
NXPrintf(aStream,"rm -r %s 2> /dev/null%c",[location cString],0) ;
NXGetMemoryBuffer(aStream,&buf,&unused,&unused) ;
system(buf) ;
[location free] ;
NXCloseMemory(aStream,NX_FREEBUFFER) ;
}
}
if(path)
[path free] ;
if(name)
[name free] ;
return [super free] ;
}
- initPath:(char *)aPath ;
{ // Create a Module instance. If there
// are errors, frees self and returns nil.
struct stat statBuf ;
char extension, *libString = NULL ;
List *componentList ;
const char *nameBuf ;
EvalString *dotOFilePath ;
if(!aPath || stat(aPath,&statBuf))
{ // Make sure file exists.
[self free] ;
return nil ;
}
// save path to file
path = [[EvalString alloc] init: aPath] ;
extension = aPath[strlen(aPath) - 1] ;
switch(extension) // What type of file is it: .m,.o,or .a?
{ // we initialize differently for .m,.o, and .a files
case 'a':
{ // this is by far the most complex case...
char tmpDirectory[20] = "/tmp/EvalXXXXXXXX", *buf ;
int i, j, knt ;
struct direct **filesList ;
long pathEnd ;
NXStream *aStream ;
// DOTA Modules are named by corresponding library
char *lastSlash = rindex(aPath,'/') ;
if(lastSlash)
name = [[EvalString alloc] init: ++lastSlash] ;
else
name = [[EvalString alloc] init: aPath] ;
// create a temporary directory
mkTempFile(NULL,tmpDirectory) ;
if(mkdir(tmpDirectory, 0xffff))
{ // non-zero return = report mkdir error here and return
[Eval printf: "Can't create temporary directory: %s\n",tmpDirectory] ;
return nil ;
}
location = [[EvalString alloc] init: tmpDirectory] ;
// extract archives into temporary directory: if ar fails, we then
// try lipo in case this is a fat file
aStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
NXPrintf(aStream,"cd %s ; if ar x %s 2> /dev/null\n"
" then exit\n"
" elif lipo -thin `arch` -output __tmp.a %s 2> /dev/null\n"
" then ar x __tmp.a ; rm __tmp.a\n"
"fi%c",
tmpDirectory, aPath, aPath, 0) ;
NXGetMemoryBuffer(aStream,&buf,&unused,&unused) ;
if(system(buf))
{ // non-zero return == report error and return
[Eval printf: "Cant extract archives via: %s\n",buf] ;
NXCloseMemory(aStream,NX_FREEBUFFER) ;
return nil ;
}
NXSeek(aStream,0,NX_FROMSTART) ;
componentList = [self tokensFromString: NXGetDefaultValue([NXApp appName],"Libraries")] ;
// stat the tmp directory to get all filenames, then put them all
// into the componentList, provided they don't start with a "__".
knt = scandir(tmpDirectory,&filesList,NULL,NULL) ;
NXPrintf(aStream,"%s/",tmpDirectory) ;
pathEnd = NXTell(aStream);
for(i = j = 0 ; i < knt ; i++)
{ // load modules with file names, skipping ., .., and __.SYMDEF*.
buf = (*filesList[i]).d_name ;
if(strcmp(buf,".") && strcmp(buf,"..") &&
strncmp(buf,"__.SYMDEF",9))
{ NXPrintf(aStream,"%s%c",buf,0) ;
NXGetMemoryBuffer(aStream,&buf,&unused,&unused) ;
[componentList insertObject: [[EvalString alloc] init: buf] at: j++] ;
NXSeek(aStream,pathEnd,NX_FROMSTART) ;
}
free(filesList[i]) ;
}
free(filesList) ;
NXCloseMemory(aStream,NX_FREEBUFFER) ;
// create the components array
[self componentsFromList: componentList] ;
[componentList free] ;
modType = DOTA ;
break ;
}
case 'm':
{ // a DOTM file must be compiled, creating a .o file
if((dotOFilePath = [self compile: aPath libs: &libString]) == nil)
{ // compile failed
[self free] ;
return nil ;
}
location = [[EvalString alloc] init: [dotOFilePath cString]] ;
modType = DOTM ;
}
case 'o':
{ // From this point on, .m and .o files are treated virtually
// identically, so we combine the two cases:
if(extension == 'o')
{ dotOFilePath = [[EvalString alloc] init: aPath] ;
modType = DOTO ;
}
if(!libString) // no libString supplied in .m...use preferences value
componentList =
[self tokensFromString: NXGetDefaultValue([NXApp appName],"Libraries")] ;
else
{ componentList = [self tokensFromString: libString] ;
vm_deallocate(task_self(),(vm_address_t) libString,strlen(libString)+1) ;
}
[componentList insertObject: dotOFilePath at: 0] ;
classNames([dotOFilePath cString], &nameBuf) ;
name = [EvalString new: nameBuf] ;
// create the components array
[self componentsFromList: componentList] ;
[componentList free] ;
// NOTE: dotOFilePath is freed in componentsFromList:
break ;
}
default:
{ // unknown type
[self free] ;
return nil ;
}
}
return self ;
}
- load ;
{ // If not loaded, load this module and, if
// successful, return self, else return nil.
id rval = nil ;
if(!loaded)
{ NXStream *aStream = NXOpenMemory(NULL, 0, NX_WRITEONLY) ;
char *buf ;
if(objc_loadModules(components, aStream, NULL, NULL, NULL))
{ // load failed...
NXPutc(aStream,'\0') ;
NXGetMemoryBuffer(aStream, &buf, &unused, &unused);
[Eval printf: buf];
NXRunAlertPanel("Eval","Load of module %s failed,\nsee transcript for details",
NULL,NULL,NULL,[self name]) ;
}
else
{ rval = self ;
loaded = YES ;
}
NXCloseMemory(aStream,NX_FREEBUFFER) ;
}
return rval ;
}
- (const char *) name ;
{ return [name cString] ;
}
- (const char *) path ;
{ return [path cString] ;
}
- (List *) tokensFromString: (const char *) str
{ // allocate and return a list containing
// tokens from the given string. Caller is
// responsible for freeing this list.
const char *delims = " \n\t" ;
const char *aString ;
List * aList = [[List alloc] init] ;
if(aString = strtok((char *) str,delims))
{ do
{ [aList addObject: [EvalString new: aString]] ;
} while(aString = strtok(NULL,delims)) ;
}
return aList ;
}
- unload ;
{ // If loaded, pop a module from the module stack
// and return self.
// It is caller's responsibility to ensure that
// this module is on top of the stack...there
// is no way to verify this here. Returns nil if not
// currently loaded (and doesn't do the pop).
if(loaded)
{ objc_unloadModules(NULL, NULL) ;
loaded = NO ;
return self ;
}
return nil ;
}
@end
/* // remove the tmp directory's contents
for(i = 0 ; i < fileCount ; i++)
{ // delete modules.
char *moduleName = (*filesList[i]).d_name ;
if(strcmp(moduleName,".") && strcmp(moduleName,".."))
{ if(unlink(moduleName))
{ // error, couldn't unlink
return nil ;
}
}
}
if(rmdir(tmpDirectory))
{ // errors, couldn't delete tmp directory
return self ;
}
*/
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.