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.