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.