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.