This is Translator.m in view mode; [Download] [Up]
/*
* Written by Guy Roberts of Object Skills Ltd, drifting
* in cyberspace without a domain or email address right
* now.
* May 7 1993. You can use this as a base for a better application
* as long as it is made publically available.
*/
/* Abandon hop all who enter here */
#import "Translator.h"
#define CLASSNAMEMARKER "@interface"
#define ENDOFCLASSMARKER "@end"
#define COPYRIGHTNOTICE "Release x.x Copyright 1993\n\n\n"
#define TITLEFONTSIZE 28
#define TEXTFONTSIZE 14
#define SUBTITLEFONTSIZE 18
#define ONEINCHINTWIPS 1440
#define INITIALCOUNT 0 /* Number elements in storage object */
#define BUFSIZE 1024 /* A temporary char buffer size */
#define TRANSLATOR_DEBUG1 1
@implementation Translator
- initForHeaderFile: (char *) theName withTable: table
{
int length, maxLength;
char *streamBuffer;
name = NXCopyStringBuffer(theName);
numInstanceMethods = numClassMethods = 0;
rtfInstance = [[RTF alloc] init];
if ((fileStream = NXMapFile(name, NX_READONLY)) == NULL) {
NXRunAlertPanel(NULL, [table valueForStringKey: "fileNotFound"]
, NULL, NULL, NULL, name);
/* Should order out the window, and free everything, ie. close */
return nil;
}
NXGetMemoryBuffer(fileStream, &streamBuffer, &length, &maxLength);
if (!fileStream)
return self;
else {
const char *defaultDirPath = NXReadDefault(OWNER, "defaultDirectory");
char manPageName[BUFSIZE];
char *pathEnd;
if (!defaultDirPath) { /* Use the same path as the header file */
sprintf(manPageName, "%s", name);
pathEnd = rindex(manPageName, '.');
*pathEnd = 0;
strcat(manPageName, ".rtf");
}
else { /* A default directory path is to be used */
char *fileName = rindex(name, '/');
sprintf(manPageName, "%s%s", defaultDirPath, fileName);
pathEnd = rindex(manPageName, '.');
*pathEnd = 0;
strcat(manPageName, ".rtf");
}
/* Parse the header file */
if (![self parseBuffer: streamBuffer withKeyWordTable: table])
return nil;
/* Write the file out */
NXSaveToFile([rtfInstance stream], manPageName);
[rtfInstance free]; /* This closes the stream */
/* Only launch edit if the user chooses */
{
const char *buffer =
NXReadDefault(OWNER, "automaticallyLaunchEdit");
/* Launch the editor if the default value is not set or
* if it is set and has the value "YES" */
if ( (!buffer) || ((buffer) && (strcmp(buffer, "YES") == 0)) )
[[Application workspace]
openFile: manPageName withApplication: "Edit"];
}
NXCloseMemory(fileStream, NX_FREEBUFFER);
}
/* Free all */
[self free];
return self;
}
/* This looks a mess. It is a mess. Messages are sent to the RTF instance
* to ask for tabulation, newlines and different font treatments. This
* code was finished off by my dog while I was asleep. Critiques should
* be addressed to rover@objskills.demon.co.uk.
*/
- (BOOL) parseBuffer: (char *) buffer withKeyWordTable: table
{
id instanceMethodHT;
id classMethodHT;
char *className;
char *parentClassName;
const char *toBeWritten = [table valueForStringKey: "toBeWritten"];
/* Get the class name */
if (![self getClass: &className andParentName: &parentClassName
fromBuffer: &buffer]) {
NXRunAlertPanel(NULL, [table valueForStringKey: "invalidHeader"],
NULL, NULL, NULL, name);
/* Should order out the window, and free everything, ie. close */
return NO;
}
/* Set up the page */
[rtfInstance changeToHelvetica];
[rtfInstance append: COPYRIGHTNOTICE];
[[rtfInstance setFontSize: TITLEFONTSIZE] bold: YES];
[rtfInstance setLeftMargin: ONEINCHINTWIPS];
/* Add the class name */
[rtfInstance changeToHelvetica];
[[[rtfInstance tab] append: className] append: "\n\n"];
[rtfInstance changeToHelvetica];
[rtfInstance setFontSize: TEXTFONTSIZE];
[[rtfInstance tab]
append: [table valueForStringKey: "inheritsFrom"]];
[rtfInstance changeToTimes];
[[[[[rtfInstance tab] bold: NO]
append: parentClassName] append: "\n\n"] bold: YES];
[rtfInstance changeToHelvetica];
[[rtfInstance tab]
append: [table valueForStringKey: "declaredIn"]];
[rtfInstance append: "\n\n\n"];
[rtfInstance setFontSize: SUBTITLEFONTSIZE];
[rtfInstance append: [table valueForStringKey: "classDescription"]];
[self insertOrdinaryText: [table valueForStringKey: "toBeWritten"]];
[rtfInstance append: [table valueForStringKey: "instanceVariables"]];
/* This method writes out a list of formatted instance variables */
[self listInstanceVariables: &buffer];
[rtfInstance append: [table valueForStringKey: "methodTypes"]];
/* Gather up all of the strings for class methods into a storage
* object and do the same for instance methods. The strings are
* stored because they are used twice when composing the manual
* page.
*/
[self getInstanceMethods: &instanceMethodHT
andClassMethods: &classMethodHT
fromBuffer: &buffer];
/* Using the information in the storage objects, write out the
* message names without the types or arguments.
*/
[rtfInstance setFontSize: TEXTFONTSIZE];
[self writeMessageNames: classMethodHT];
[self writeMessageNames: instanceMethodHT];
[rtfInstance append: "\n\n\n"];
[rtfInstance setFontSize: SUBTITLEFONTSIZE];
[rtfInstance append: [table valueForStringKey: "classMethods"]];
[self writeMethods: classMethodHT stringTable: table];
[rtfInstance append: [table valueForStringKey: "instanceMethods"]];
[self writeMethods: instanceMethodHT stringTable: table];
[rtfInstance append:
[table valueForStringKey: "constantsAndDefinedTypes"]];
[rtfInstance append: "\n"];
[self insertOrdinaryText: [table valueForStringKey: "toBeWritten"]];
{
/* Free all of the strings in the hash tables */
int i, j;
for (i = 0; i < [classMethodHT count]; i++)
NX_FREE([classMethodHT valueForKey: (const void *) i]);
for (j = 0; j < [instanceMethodHT count]; j++)
NX_FREE([instanceMethodHT valueForKey: (const void *) j]);
/* Dispose of the tables */
[classMethodHT free];
[instanceMethodHT free];
}
return YES;
}
/* Put all of the class methods defined in the header file into a
* table of strings. The keys to the table are of no meaning. In
* fact I should have used the Storage class to save the strings.
*/
- (BOOL) getInstanceMethods: (id *) instanceMethodHT
andClassMethods: (id *) classMethodHT
fromBuffer: (char **) buffer
{
char *start = NULL;
char *end = NULL;
char *tmpBufferOne = NXCopyStringBuffer(*buffer);
char *tmpBufferTwo = NXCopyStringBuffer(*buffer);
*instanceMethodHT = [[HashTable alloc]
initKeyDesc: "i" valueDesc:"*"];
*classMethodHT = [[HashTable alloc]
initKeyDesc: "i" valueDesc:"*"];
/* Collect the class method definitions */
start = tmpBufferOne;
while (start = index(start, '+') ) {
char *newString = NULL;
/* Create a copy of the method definition string */
end = index(start, ';');
*end = '\0';
newString = NXCopyStringBuffer(start);
[*classMethodHT insertKey: (const void *) numClassMethods++
value: newString];
start = end + 1; /* Hop over the null character */
}
/* Collect the instance method definitions */
start = tmpBufferTwo;
while (start = index(start, '-') ) {
char *newString = NULL;
/* Create a copy of the method definition string */
end = index(start, ';');
*end = '\0';
newString = NXCopyStringBuffer(start);
[*instanceMethodHT insertKey: (const void *) numInstanceMethods++
value: newString];
start = end + 1; /* Hop over the null character */
}
/* Tidy up */
NX_FREE(tmpBufferOne);
NX_FREE(tmpBufferOne);
return NO;
}
/* This method is used to go through a table of strings and
* break down each one into the parts of a message name. They
* need to be broken apart so as to be able to make bits of
* string bold or italic.
*/
- writeMethods: table stringTable: stringTable
{
int i;
char *namePart = NULL;
char *typePart = NULL;
char *argPart = NULL;
[rtfInstance setFontSize: TEXTFONTSIZE];
for (i = 0; i < [table count]; i++) {
char buffer[BUFSIZE], *bufPtr;
BOOL isFirstNamePart = YES;
memset(buffer, '\0', BUFSIZE);
strcpy(buffer, [table valueForKey: (const void *) i]);
bufPtr = buffer;
while (bufPtr && (strcmp(bufPtr, "") != 0) ) {
[self getNamePart: &namePart typePart: &typePart
argPart: &argPart fromString: &bufPtr];
[rtfInstance bold: YES];
if (isFirstNamePart) {
char tmpName[BUFSIZE];
{
/* Get rid of the '+' or '-' character in the name.
* You are deep inside Hack land here.
*/
char *ptr = NULL;
tmpName[0] = '\0';
strcat(tmpName, namePart);
ptr = index(tmpName, '+');
if (!ptr)
ptr = index(tmpName, '-');
if (ptr)
*ptr = ' ';
}
[rtfInstance changeToHelvetica];
isFirstNamePart = NO;
[[rtfInstance append:"\n"] tab];
[rtfInstance append: tmpName];
[[[rtfInstance append:"\n"] tab] tab];
}
[rtfInstance changeToTimes];
[rtfInstance append: namePart];
[rtfInstance bold: NO];
[rtfInstance append:" "];
[rtfInstance append: typePart];
[rtfInstance italic: YES];
[rtfInstance append: argPart];
[rtfInstance italic: NO];
[[[[rtfInstance append: "\n"] tab] tab] tab];
/* Free the three strings */
NX_FREE(namePart);
NX_FREE(typePart);
NX_FREE(argPart);
}
[[[rtfInstance append:"\n"] tab]
append: [stringTable valueForStringKey: "toBeWritten"]];
} /* for */
[[rtfInstance setFontSize: SUBTITLEFONTSIZE] bold: YES];
[rtfInstance append:"\n\n"];
return self;
}
/*
* This method is used to write out a summary of every message
* name in a hash table. This goes near the top of the manual
* page. It is called twice, once with a hash table of instance
* method strings and once with the class methods.
*/
- writeMessageNames: table
{
int i;
char *namePart = NULL;
char *typePart = NULL;
char *argPart = NULL;
[rtfInstance changeToTimes];
for (i = 0; i < [table count]; i++) {
char buffer[BUFSIZE], *bufPtr;
memset(buffer, '\0', BUFSIZE);
strcpy(buffer, [table valueForKey: (const void *) i]);
bufPtr = buffer;
[[[rtfInstance append: "\n"] tab] bold: NO];
while (bufPtr && (strcmp(bufPtr, "") != 0) ) {
[self getNamePart: &namePart typePart: &typePart
argPart: &argPart fromString: &bufPtr];
[rtfInstance append: namePart];
[rtfInstance append: ""];
/* Free the three strings */
if (namePart)
NX_FREE(namePart);
if (typePart)
NX_FREE(typePart);
if (argPart)
NX_FREE(argPart);
}
} /* for */
[rtfInstance changeToHelvetica];
[rtfInstance bold: YES];
return self;
}
- listInstanceVariables: (char **) buffer
{
char *pos = index(*buffer, '{');
char type[BUFSIZE];
char nom[BUFSIZE];
[rtfInstance setFontSize: TEXTFONTSIZE];
[rtfInstance append: "\n"];
[rtfInstance changeToTimes];
while ([self getNextType: type andVariableName: nom fromBuffer: buffer]) {
[rtfInstance tab];
[rtfInstance bold: NO];
[rtfInstance append: type];
[rtfInstance tab];
[rtfInstance bold: YES];
[rtfInstance append: nom];
[rtfInstance append: "\n"];
}
[rtfInstance setFontSize: SUBTITLEFONTSIZE];
[rtfInstance append: "\n"];
[rtfInstance changeToHelvetica];
return self;
}
/*
* Used to collect the instance variable parts.
*/
- (BOOL) getNextType: (char *) type andVariableName: (char *) nom
fromBuffer: (char **) buffer
{
int i = 0, j = 0;
if (**buffer == '{')
(*buffer)++;
/* Eat white space */
while ( (**buffer != '}') && (isspace(**buffer)) )
(*buffer)++;
if (**buffer == '}') {
(*buffer)++;
return NO;
}
/* Collect the type of the instance variable */
while (!isspace(**buffer) ) {
type[i++] = **buffer;
(*buffer)++;
}
type[i] = '\0';
/* Eat white space */
while ( (**buffer != '}') && (isspace(**buffer)) )
(*buffer)++;
if (**buffer == '}') {
(*buffer)++;
return NO;
}
/* Collect the name of the variable */
while ((**buffer) != '\n') {
nom[j++] = **buffer;
(*buffer)++;
}
nom[j] = '\0';
return YES;
}
/*
* I never claimed to be an OO expert and stuff like this
* shows it.
*/
- insertOrdinaryText: (const char *) text
{
[rtfInstance changeToTimes];
[rtfInstance setFontSize: TEXTFONTSIZE];
[rtfInstance bold: NO];
[rtfInstance tab];
[rtfInstance append: text];
[rtfInstance bold: YES];
[rtfInstance setFontSize: SUBTITLEFONTSIZE];
[rtfInstance changeToHelvetica];
return self;
}
- free
{
if (name)
NX_FREE(name);
#if 0
return [super free];
#endif
return nil;
}
/*
* Recover the class name and the parent class name.
*/
- (BOOL) getClass: (char **) className
andParentName: (char **) parentClassName
fromBuffer: (char **) buf
{
char *endOfClassName = NULL;
char *endOfParentClassName = NULL;
char *buffer = *buf;
*className = strstr(buffer, CLASSNAMEMARKER);
if (!(*className))
return NO; /* Not an Objective-C header file */
*className += strlen(CLASSNAMEMARKER);
while (!isalpha(**className))
(*className)++;
*parentClassName = NULL;
endOfClassName = index(*className, ':');
*endOfClassName = '\0';
*parentClassName = endOfClassName + 1;
endOfParentClassName = index(*parentClassName, '\n');
*endOfParentClassName = '\0';
/* Move through the buffer */
*buf = endOfParentClassName + 1;
if (!parentClassName)
return NO;
else
return YES;
}
/* This method can be called repeatedly with a string containing a
* method definition. It returns the components of that string. These
* parts can then be written onto the manual page in bold or italic
* text as needed.
* It is assumed that the string passed in is of the correct format.
* If the parsing fails, NO is returned.
* Do not forget to eventually free the strings in the calling method.
* Disclaimer: I am not proud of this parsing code. I promise to learn
* about lex in the future.
*/
- (BOOL) getNamePart: (char **) namePart typePart: (char **) typePart
argPart: (char **) argPart
fromString: (char **) string
{
char buffer[BUFSIZE], *marker;
BOOL bracedType = NO;
int j = 0;
#ifdef TRANSLATOR_DEBUG
printf("%s\n", *string);
#endif
marker = *string;
/* Move past white space */
while (isspace(*marker))
marker++;
/* The name part is up to the first colon */
while ((*marker) && (*marker != ':') ) {
buffer[j++] = *marker++;
}
if (*marker == ':')
buffer[j++] = *marker++; /* Include the colon */
buffer[j] = '\0';
*namePart = NXCopyStringBuffer(buffer);
if ( (!marker) || (strcmp(marker, "") == 0) ) {
*typePart = NXCopyStringBuffer("");
*argPart = NXCopyStringBuffer("");
*string = marker; /* move on to the next part of the line */
return YES;
}
/* The type part is next, a type expression may be within braces */
j = 0;
/* Move past white space */
while (isspace(*marker))
marker++;
if (*marker == '(')
bracedType = YES;
if (!bracedType) { /* No braces used */
while ((*marker) && !isspace(*marker))
buffer[j++] = *marker++;
}
else { /* White space is part of the type expression */
while (*marker != ')')
buffer[j++] = *marker++;
buffer[j++] = *marker++; /* Include the closing brace */
}
buffer[j] = '\0';
*typePart = NXCopyStringBuffer(buffer);
/* Move past white space */
while (isspace(*marker))
marker++;
/* The argument part is next, it runs up to the next space or new line */
j = 0;
while (*marker && !isspace(*marker) && (*marker != ';') )
buffer[j++] = *marker++;
buffer[j] = '\0';
*argPart = NXCopyStringBuffer(buffer);
/* Gulp, now a confusing bit. The above code assumes that method
* definitions are made up of blocks of three parts, a name, a
* type and a variable name. Of course if the type is (id) it can
* be ommitted and the code given above fails. eg. "myMethod: anObj;"
* So if the variable name has a colon in it or is a null string,
* make the type string null and the variable name equal to the type
* string.
*/
*string = marker;
if (index(*argPart, ':') || (strcmp(*argPart, "") == 0) ) {
char *tmp;
/* Must rewind the string pointer !! Arrrg */
if (tmp = index(*argPart, ':')) {
int lengthToRewind = strlen(*argPart);
*string = *string - lengthToRewind;
}
*argPart = NXCopyStringBuffer(*typePart);
*typePart = NXCopyStringBuffer("");
}
return YES;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.