This is support.m in view mode; [Download] [Up]
/** ** support.m ** ** supporting functions for Eval **/ #import <objc/objc-load.h> #import <sys/fcntl.h> #import <sys/file.h> #import <dpsclient/psops.h> #import <dpsclient/dpsclient.h> #import <stdlib.h> #import <strings.h> #import <bsd/libc.h> #import <sys/dir.h> #import <sys/file.h> #import <sys/types.h> #import <sys/stat.h> #import <objc/List.h> #import "Eval.h" #import "EvalString.h" #import "EvalWrap.h" int classNames(const char *filePath, const char **buf) { // Assumes filePath is the path to a .o file containing a class definition. // Sets buf to point to a string containing the name of all classes // defined in the module, in the form // className1:superclassName1 className2(category) // The string buf "belongs to" this procedure, and shouldn't be // altered in any way. It is only valid until the next call // to superClassName. struct mach_header mh ; struct segment_command sc ; struct section sect ; struct objc_class oc_cl ; struct objc_category oc_ca ; static NXStream *aStream = NULL ; int fn,i,j,k,unused ; off_t saveLoadLoc, saveSegLoc ; char c ; if(!aStream) aStream = NXOpenMemory(NULL, 0, NX_WRITEONLY) ; else NXSeek(aStream,0,NX_FROMSTART) ; if((fn = open(filePath, O_RDONLY)) < 0) return 0 ; lseek(fn, 0l, 0) ; if(read(fn,&mh,sizeof(struct mach_header)) < sizeof(struct mach_header)) return 0 ; // short file for(i = 0 ; i < mh.ncmds ; i++) // for each load command { if(read(fn,&sc,sizeof(struct load_command)) < sizeof(struct load_command)) return 0 ; // short file saveLoadLoc = lseek(fn,0,L_INCR) ; if(sc.cmd != LC_SEGMENT) // not a segment command, skip it if(lseek(fn, sc.cmdsize - sizeof(struct load_command) ,L_INCR) == -1) return 0 ; // can't seek else // segment command...get rest of header { if(read(fn,&sc.segname,sizeof(struct segment_command) - sizeof(struct load_command)) < sizeof(struct segment_command) - sizeof(struct load_command)) return 0 ; // short file else { for(j = 0 ; j < (sc.nsects) ; j++) // for each segment header { if(read(fn,§,sizeof(struct section)) < sizeof(struct section)) return 0 ; // short file ; saveSegLoc = lseek(fn,0,L_INCR) ; if(!strcmp(sect.segname, SEG_OBJC)) { // found an objc segement...look for class or category section if(!strcmp(sect.sectname,"__class")) { // we've found an objc class section for(k = 0 ; k < (sect.nreloc/3) ;k++) { // Examine each relocation entry (making the assumption that // their length is sizeof(struct objc_class)). I know empirically that // that class generates 3 relocation entries, and that the entries we // are interested in appear first, hence sect.nreloc/3. if(lseek(fn,sect.offset + (k * sizeof(struct objc_class)),L_SET) == -1) return 0 ; // can't seek if(read(fn,&oc_cl,sizeof(struct objc_class)) // read in the objc_class structure < sizeof(struct objc_class)) return 0 ; // short file if(CLS_GETINFO(&oc_cl,CLS_CLASS)) { if(lseek(fn,mh.sizeofcmds + (long int) oc_cl.name + sizeof(struct mach_header), L_SET) == -1) return 0 ; // can't seek while(read(fn,&c,1)) { if(c) NXPutc(aStream,c) ; else { NXPutc(aStream,':') ; break ; } } if(lseek(fn,mh.sizeofcmds + (long int) oc_cl.super_class + sizeof(struct mach_header), L_SET) == -1) return 0 ; // can't seek while(read(fn,&c,1)) { if(c) NXPutc(aStream,c) ; else { NXWrite(aStream," ",3) ; break ; } } } } } else if(!strcmp(sect.sectname,"__category")) { // we've found an objc category section for(k = 0 ; k < (sect.nreloc >> 1) ; k++ ) { // examine each relocation entry (making the assumption that // their length is sizeof(struct objc_category)). I know empirically that // each category generates 2 relocation entries, and that the entries we // are interested in appear first, hence sect.nreloc >> 1. if(lseek(fn,sect.offset + (k * sizeof(struct objc_category)),L_SET) == -1) return 0 ; // can't seek if(read(fn,&oc_ca,sizeof(struct objc_category)) // read in the objc_category structure < sizeof(struct objc_category)) return 0 ; // short file { if(lseek(fn,mh.sizeofcmds + (long int) oc_ca.class_name + sizeof(struct mach_header), L_SET) == -1) return 0 ; // can't seek while(read(fn,&c,1)) // read in class name { if(c) NXPutc(aStream,c) ; else { NXPutc(aStream,'(') ; break ; } } if(lseek(fn,mh.sizeofcmds + (long int) oc_ca.category_name + sizeof(struct mach_header), L_SET) == -1) return 0 ; // can't seek while(read(fn,&c,1)) // read in category name { if(c) NXPutc(aStream,c) ; else { NXWrite(aStream,") ",4) ; break ; } } } } } } lseek(fn,saveSegLoc,L_SET) ; } } } lseek(fn,saveLoadLoc,L_SET) ; } close(fn) ; NXPutc(aStream,'\0') ; NXGetMemoryBuffer(aStream,buf,&unused,&unused) ; return 1 ; } void setSelectionRange(NXSelPt *start, NXSelPt *end, int length) { // check that start and end "make sense", and if start == end, // set them to embrace entire text, from 0 to length. if((start->cp < 0) || (start->cp > length)) start->cp = 0 ; if((end->cp <0) || (end->cp > length)) end->cp = 0 ; if(start->cp >= end->cp) { start->cp = 0 ; end->cp = length ; } } BOOL EvalGets(NXStream *aStream, char * aBuf) { // Procedure to get the next line (or remainder of // stream) from a stream. The newline is discarded. // If newline is present, returns YES, else if instead // the end of stream is found, returns NO (but aBuf may // still contain data). int c,i = 0; c = NXGetc(aStream) ; do { if(c == -1) // end of stream { aBuf[i] = '\0' ; return NO ; } else if(c == '\n') { aBuf[i] = '\0' ; return YES ; } else { aBuf[i++] = (char) c ; c = NXGetc(aStream) ; } } while(1) ; } BOOL mkTempFile(char *path, char *fileName) { // fileName should contain eight trailing X's. // Make a "temporary" file. Similar to mktemp, but // not only ensures that the filename is unique, but // also that there is no filename with the same prefix. // i.e. if fileName is set to FooXXXXXXXX, you are assured // that no files match FooXXXXXXXX*. If path is // nonNull, than fileName is unique within the directory // path. Else is path is null, than fileName is assumed // to contain the complete path. The temporary file name // is placed "in" fileName. Returns YES on success, NO if // can't create the file. char *thePath, *lastSlash = NULL ; int i,fileNameIndex = 0, fileIndex = 0, oldFileNameLen, newFileNameLen, fileCount ; int pid = getpid() ; static int maxFileIndex = 1000 ; static int XCount = 8 ; // how many X's at end of filename struct direct **filesList ; if(path) thePath = path ; else { lastSlash = strrchr(fileName,'/') ; if(lastSlash) { fileNameIndex = lastSlash - fileName + 1 ; // index of beginning of fileName thePath = (char *) malloc(fileNameIndex + 1) ; strncpy(thePath,fileName,fileNameIndex - 1) ; thePath[fileNameIndex - 1] = '\0' ; } else // search current directory thePath = "." ; } oldFileNameLen = strlen(&fileName[fileNameIndex]) ; do { // create a candidate file name sprintf(&fileName[fileNameIndex + oldFileNameLen - XCount],"%d%d",pid,fileIndex++) ; newFileNameLen = strlen(&fileName[fileNameIndex]) ; // check if its unique. fileCount = scandir(thePath,&filesList,NULL,NULL) ; if(fileCount == -1) break ; for(i = 0 ; i < fileCount ; i++) { if(!strncmp(&fileName[fileNameIndex],(*filesList[i]).d_name,newFileNameLen)) break ; } if(i == fileCount) break ; // no conflicts found: go with name } while(fileIndex < maxFileIndex) ; if(lastSlash) free(thePath) ; if((fileCount == -1) || (fileIndex == maxFileIndex)) return NO ; return YES ; } void SwitchContextsWithFocus(DPSContext newContext) { // borowsed from YAP: switch to newContext in order to // ``protect'' the main context float c1x, c1y, c2x, c2y; float winCTM[6]; int realWinNum; GetFocus(&c1x, &c1y, &c2x, &c2y, winCTM, &realWinNum); DPSSetContext(newContext); ReFocus(realWinNum, winCTM, c1x, c1y, c2x, c2y); } List *tmpFileList(char *path) { // Creates and returns a list of "temporary file names" in // directory "path". These are files which match path/EvalXXXXXXXX, // (where the X's are replaced by the process id of current process). // In other words, files which were created by mkTempFile() in this // process. This will be a list of EvalStrings, each of which contains // the full path of each such file. It is the callers responsibility // to free these EvalStrings and the List (although, in normal usage, // this is never done, because this procedure is called by // from Eval::appWillTerminate in order to clean up before exiting). int i,len,fileCount ; struct direct **filesList ; List *aList = [List new] ; char *fullPath ; char fileName[20] ; sprintf(fileName,"Eval%d",getpid()) ; [Eval printf: "%s\n",fileName] ; len = strlen(fileName) ; fullPath = (char *) alloca(strlen(path) + len + 256) ; fileCount = scandir(path,&filesList,NULL,NULL) ; for(i = 0 ; i < fileCount ; i++) { if(!strncmp(fileName,(*filesList[i]).d_name,len)) { sprintf(fullPath,"%s/%s",path,(*filesList[i]).d_name) ; [aList addObject: [EvalString new: fullPath]] ; } } return aList ; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.