home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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
-