This is cnvwrap.m in view mode; [Download] [Up]
// // cnvwrap.m // Program for automatically generating degenerate (convenience) // methods for methods with optional parameters // // Written by Carl Lindberg // Copyright (c) 1994,1995,1996 by Carl Lindberg. // Version 1.3 All rights reserved. // This notice may not be removed from this source code. // // This object is included in the MiscKit by permission from the author // and its use is governed by the MiscKit license, found in the file // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file // for a list of all applicable permissions and restrictions. // // // This program takes method declarations - with default values attached // to optional parameters - and outputs all possible degenerate methods // along with their header lines. // // // Command-line options: // // -i filename Specify input file. Reads from stdin if this // option not used. // -m filename Output method implementations to named file. Adds // '.m' to filename if not there already. Outputs to // stdout if this option is not used. // -h filename Output method headers to named file. Adds '.h' to // filename if not there already. Outputs to stdout if // this option is not used. // -f filename Just like -h filename.h -m filename.m. Automatically // adds .h and .m extensions to filename, and outputs // headers and methods, respectively, to the two files. // -e {h|m|hm} Excludes the {header|implementation|both} of the full // method from being output. For example, -em will print // out all headers, but will not print the implementation // of the full method (normally, it is printed with a // blank body). // -a Appends to output files instead of overwriting. // -H Help // // // // SYNTAX FOR INPUT LINE(S): // // Input lines are basically the same as header lines, except that // default values to parameters are put inside square (or squiggly) // brackets and tacked directly onto the end of that parameters. // Also, no semicolon at the end. // // // 1) Leave at least one space/tab after the "-" or "+". // 2) ONE line per method - don't break up a long method into 2 or more // lines. // 3) Default values signify an optional parameter. These should be // between square ("[<default value>]") or squiggly ("{<default value>"}) // brackets tacked on the end of a parameter - NO SPACES before the open // bracket. // The 'default value' may contain any character (including whitespaces) // EXCEPT a corresponding right bracket character -- that is, if you use // square brackets to define the default value, the default value itself // cannot contain a right square bracket. Even then, you are allowed to // have corresponding right bracket character(s) so long as no whitespaces // come _after_ it. Ex: // // Legal: with:(char *)[[sender stringValue]] // with:(char *){[sender stringValue]} // with:(char *)[[[sender selectedCell]stringValue]] // with:(char *){[[sender selectedCell] stringValue]} //use squiggly braces // Illegal: with:(char *)[[sender stringValue] ] // with:(char *)[[[sender selectedCell] stringValue]] // // This was as flexible as I could figure out - it's a limitation // of the regular expression used to search. If this still isn't // enough, than you could simply put in a word like FOOBAR as the // default value, and then search-and-replace FOOBAR with the real // default value on the output. // // 4) Every variable must be typecast - even if it's an id, please put // (id) as the typecast. A method return type is optional, though. // 5) If the first parameter - really the method name - is optional, // then *every* parameter must be optional too, or else you will // get duplicate methods output. // // // The program works by first parsing each line, and splitting it // up into four lists, each list containing some needed portion of // each parameter. Then the program goes into a second main loop // which works up the needed text using the values in the lists. // // For the input: // // - (int)spotOfStr:(const char *)str occurrenceNum:(int)n[0] caseSensitive:(BOOL)sense[YES] // // the four lists would end up so: // // // paramtypeList paramList paramvalList defaultList // (whole param minus any (param name (variable (default // default value) only) name) name) // -------------------------------- -------------- ------------ ----------- // Param 1: (int)spotOfStr:(const char *)str spotOfStr: str [blank] // Param 2: occurrenceNum:(int)n occurrenceNum: n 0 // Param 3: caseSensitive:(BOOL)sense caseSensitive: sense YES // // // There should be (number of optional parameters)**2 possible method // combinations. The exception is that if the first "parameter" // (spotOfStr in the example above) is optional, only one more possible // method is added -- the one where every parameter is left out. You // can't leave the first parameter out and include others, obviously. // Therefore, if the first parameter is optional, *every* parameter should // be optional -- else duplicate methods will be output. // // // // // You may thank the piles of convenience methods in MiscString for // prompting me to write this program. :-) Enjoy. // // Version 1.1 June 7, 1995 // - fixed bug where having '(' or '[' in the default value section would // confuse program // - added -e {h|m|hm} switch to exclude outputting of the last possible // header or method. This lets you code the full method first, then // just output the headers/code for the convenience methods only. // Version 1.2 July 8, 1995 // - changed the regular expression search to allow more possibilities of // default section values. // Version 1.3 Sept 16, 1996 // - changed the regular expression search to allow use of { ... } as well // as [ ... ] for default value sections. // - changed output format of implementations slightly (the closing '}' is // now on its own line). #include <appkit/appkit.h> #include <misckit/MiscString.h> void dohelp(); void usage(); void freestuff(); // These are needed to be declared outside main() for freestuff() id mainstr, headstring, specstring, hName, mName, iName, returnstr, mfilestr, hfilestr, defaultList, paramList, paramtypeList, paramvalList; void main(int argc, char **argv) { char ch; BOOL isOptArray[50]; //ith slot YES if param #i is optional BOOL append = NO; //append to given filenames, or overwrite? BOOL outputFinalHeader = YES; BOOL outputFinalMethod = YES; int i, j, numposs, numparam, thenum, len, spot2, spot3, startposs; FILE *infile, *moutfile, *houtfile; id tmp; // Initialize some stuff mainstr = [MiscString new]; specstring= [MiscString new]; //special; used if first param is optional headstring= [MiscString new]; //for building up the header line hfilestr = [MiscString new]; //for storing method headers mfilestr = [MiscString new]; //for storing method definitions returnstr = [MiscString new]; //intermediate string, used for making //the return value iName = [MiscString new]; //strings to hold filenames mName = [MiscString new]; hName = [MiscString new]; //for "caseSensitive:(BOOL)sense[YES]": defaultList = [List new]; //for storing the default values (eg "YES") paramList = [List new]; //for storing the parameter name (eg "caseSensitive:") paramvalList = [List new]; //for storing the variable name (eg "sense") paramtypeList = [List new]; //for storing whole parameter w/ typecasts // (eg "caseSensitive:(BOOL)sense") // These are defaults infile = stdin; houtfile = stdout; moutfile = stdout; append = NO; re_set_syntax(RE_NO_BK_PARENS|RE_NO_BK_VBAR); // Process arguments while ((ch = getopt(argc, argv, "Ham:h:f:i:e:")) != EOF) switch((char)ch) { case '?': usage(argv[0]); case 'H': dohelp(argv[0]); freestuff(); exit(0); case 'a': append = YES; break; case 'i': [iName setStringValue:optarg]; break; case 'm': [mName setStringValue:optarg]; [mName addExtensionIfNeeded:"m"]; break; case 'h': [hName setStringValue:optarg]; [hName addExtensionIfNeeded:"h"]; break; case 'f': [hName setStringValue:optarg]; [hName cat:".h"]; [mName setStringValue:optarg]; [mName cat:".m"]; break; case 'e': if (index(optarg,'h')) outputFinalHeader = NO; if (index(optarg,'m')) outputFinalMethod = NO; break; default: usage(argv[0]); exit(2); } if ([iName length]) { infile = fopen([iName stringValue], "r+"); if (!infile) { fprintf(stderr,"ERROR: could not open '%s'\n",[iName stringValue]); freestuff(); exit(1); } } if ([hName length]) { houtfile = fopen([hName stringValue], (append)? "a+":"w+"); if (!houtfile) { fprintf(stderr,"ERROR: could not open '%s'\n",[hName stringValue]); freestuff(); exit(1); } } if ([mName length]) { moutfile = fopen([mName stringValue], (append)? "a+":"w+"); if (!moutfile) { fprintf(stderr,"ERROR: could not open '%s'\n",[mName stringValue]); freestuff(); exit(1); } } // Repeat for every method read in... the || [mainstr length] is there // if the last line of input does not have a newline at the end - // this loop only terminates if at EOF *and* no string was read in. while ([mainstr fgets:infile keepNewline:NO] != EOF || [mainstr length]) { if ([mainstr length] < 5) continue; //break out if a blank line numparam = 0; numposs = 1; startposs = 0; // Isolate each parameter and process it - the regular expression is // what expects to see a cast for each variable. for (i=0;(spot2 = [mainstr spotOfRegex:"[^\t\n :]+[:][(][^)]*[)][^ \t\n[{]+(([[][^]]*[]]|{[^}]*})[^ \t\n]*)?" occurrenceNum:i length:&len]) >= 0;i++) { // The above parsing may not have worked if the method return type had // spaces in it, such as (const char *). This checks to see if that // happened and sets 'spot2' and 'len' to the corrected values. if ((i == 0) && ((spot3 = [mainstr spotOfRegex:"[(][^)]*[)]"]) < spot2)) { len += (spot2-spot3); spot2 = spot3; } // Now get the whole parameter in a string. tmp = [mainstr midFrom:spot2 length:len]; // Add the parameter name (sans type and variable name) to its list. // If the method has a return type, this must be stripped out too - // thus the check on the first character. if ([tmp charAt:0] == '(') { [paramList addObject:[tmp midFrom:[tmp spotOf:')']+1 to:[tmp spotOf:':']]]; } else { [paramList addObject:[tmp midFrom:0 to:[tmp spotOf:':']]]; } numparam++; if (![tmp endcmp:"]"] || ![tmp endcmp:"}"]) { // It is an optional param id tmp2 = [tmp midFrom:[tmp spotOf:':'] to:[tmp length]]; int spot = [tmp spotOf:':'] + [tmp2 spotOfChars:"[{"]; // Find beginning of default value isOptArray[i] = YES; // Get the whole parameter minus default value and add it to list [paramtypeList addObject:[tmp midFrom:0 to:spot-1]]; // Get the default value and add it to its list [defaultList addObject:[tmp midFrom:spot+1 to:[tmp length]-2]]; // Get the variable name and add it to its list //[paramvalList addObject:[tmp midFrom:[tmp rspotOf:')']+1 to:spot-1]]; [paramvalList addObject:[tmp2 midFrom:[tmp2 spotOf:')']+1 to:[tmp2 spotOfChars:"{["]-1]]; [tmp2 free]; // If the first parameter is optional, it is handled much differently. // Instead of doubling the number of potential methods, only one more // is added. This is handled by starting the second main loop at -1 // instead of 0 (startposs). Also, since in the rest of the methods // the first parameter must be there, its slot in the isOptArray should // be NO, as it really is not optional. 'specstring' holds the header // for the extra method incurred -- which is just the name (and return // type if there) of the parameter minus the colon. 'specstring' will // be used only in the -1th iteration of the second main loop. if (i==0) { [specstring setStringValue:[[tmp left:[tmp spotOf:':']] stringValueAndFree]]; [specstring insert:" "]; startposs = -1; isOptArray[0] = NO; } else { // Not the first param, so double # of possible methods. numposs *= 2; } // Free tmp here... under the else below, it is added to one of the lists // and is therefore freed then. [tmp free]; } else { // Not an optional parameter, doesn't have [XXX] at end isOptArray[i] = NO; [defaultList addObject:[MiscString new]]; // Add a dummy string to keep place [paramtypeList addObject:tmp]; [paramvalList addObject:[tmp midFrom:[tmp rspotOf:')']+1 to:[tmp length]-1]]; } } //done parsing the input line /* //debugging with parsing... for (i=0;k<numparam;k++) { printf("Full param: %s\n",[[paramtypeList objectAt:i] stringValue]); printf("Optional? %s\n",(isOptArray[i])? "YES":"NO"); printf("Default: '%s'\n",[[defaultList objectAt:i] stringValue]); printf("Value: %s\n",[[paramvalList objectAt:i] stringValue]); printf("Name: %s\n",[[paramList objectAt:i] stringValue]); } */ // Okay, now output stuff. There should be numposs methods to do. // 'startposs' is -1 if the first param is optional and the one // extra method is there to do, 0 normally. for (i=startposs;i<numposs;i++) { [headstring setStringValue:[[mainstr wordNum:0] stringValueAndFree]]; //"-" or "+" [returnstr setStringValue:"{ return [self"]; thenum = 1; // Build up the header and return for each method. hfilestr holds // the method headers, mfilestr holds the method name, and returnstr // holds the return value which gets added to mfilestr at the end of // the inner loop. for (j=0;j<numparam;j++) { // Add the method name to the return value. The argument will // either be the variable name or the default [returnstr addChar:' ']; [returnstr concatenate:[paramList objectAt:j]]; // Check to see if the current parameter deserves to be added // in this pass. If it does, then add it to the header, // and add the variable name to returnstr. Otherwise, just add // the default value to returnstr. // The special case of i==-1 should not do this part - that is handled // elsewhere. If the current parameter is not optional then it should // always be added in. // Otherwise, the third condition determines whether a parameter is // included in this pass. 'thenum' starts as 1 every iteration of i, // meaning the value of that condition for the first parameter will // be 0, then 1, then 0, then 1... as i goes up. 'thenum' gets doubled // each optional parameter, so for the second optional parameter the // condition will be 0, 0, 1, 1, 0, 0, 1, 1... as i goes up. The third // will be 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, ... and so on. Since i represents // the total number of combinations, this will account for every possible // combination. if ((i>=0) && (!(isOptArray[j]) || ((i % (thenum*2)) >= thenum))) { [headstring addChar:' ']; [headstring concatenate:[paramtypeList objectAt:j]]; [returnstr concatenate:[paramvalList objectAt:j]]; //the variable name } else { [returnstr concatenate:[defaultList objectAt:j]]; //the default value } if (isOptArray[j]) thenum *= 2; } // Special case handling for i==-1. Only the else portion of the above // will be executed, so only the returnstr is done. specstring, set in // the parsing loop, holds the header needed by hfilestr and mfilestr. // So, just add it to them. if (i==-1) { [headstring concatenate:specstring]; } [returnstr cat:"];\n}"]; if ((i <(numposs-1)) || outputFinalHeader) { [hfilestr concatenate:headstring]; [hfilestr cat:";\n"]; } if ((i < (numposs-1)) || outputFinalMethod) { [mfilestr concatenate:headstring]; } // If we are at the last possible method (i == numposs-1) we don't want // to put a method body in - it would just call itself and be an infinite // loop. I just decided to put in a blank method body - you will at least // get a warning from the compiler if you forget to take this out or implement // it. At least if you use -Wall. // Otherwise, returnstr holds the entire method body, so add that to mfilestr. if (i < (numposs-1)) //don't put in return for richest method [mfilestr catFromFormat:"\n%s\n",[returnstr stringValue]]; else if (outputFinalMethod) [mfilestr cat:"\n{\n}\n\n"]; } [hfilestr addChar:'\n']; // Leave a blank line between sets of headers [mfilestr cat:"\n\n\n"]; // Leave three blank lines between sets of // convenience methods. // Empty out all the lists to start over with the next input line [defaultList freeObjects]; [paramList freeObjects]; [paramvalList freeObjects]; [paramtypeList freeObjects]; } // Output the info... yeah, it only gets output at the end instead of // as it's processed, but that's the price to pay if you want headers // grouped together instead of interspersed with the actual methods // when both outputs go to stdout. fprintf(houtfile,"%s\n\n",[hfilestr stringValue]); fprintf(moutfile,"%s\n",[mfilestr stringValue]); // And clean up... if ([hName length]) fclose(houtfile); if ([mName length]) fclose(moutfile); if ([iName length]) fclose(infile); freestuff(); } void freestuff() { [hName free]; [mName free]; [iName free]; [specstring free]; [headstring free]; [returnstr free]; [mfilestr free]; [hfilestr free]; [[defaultList freeObjects] free]; [[paramList freeObjects] free]; [[paramvalList freeObjects] free]; [[paramtypeList freeObjects] free]; [mainstr free]; } void usage(char *progname) { fprintf(stderr,"\nusage: %s [-Ha] [-i inputfilename]\n",progname); fprintf(stderr," [-h hfilename -m mfilename | -f basefilename]\n"); fprintf(stderr," [-e {h|m|hm}]\n\n"); fprintf(stderr," %s -H for more verbose help.\n\n",progname); freestuff(); exit(2); } void dohelp(char *progname) { printf("usage: %s [-Ha] [-i filename] [-e {h|m|hm}]\n",progname); printf(" [-h hfilename -m mfilename | -f basefilename]\n\n"); printf(" This program takes method headers with specified default values for\n"); printf(" optional parameters, and outputs both headers and methods for\n"); printf(" all possible degenerate (convenience) methods.\n"); printf(" Reads from stdin if infile not specified, and outputs headers\n"); printf(" and/or methods to stdout if destination is not specified.\n\n"); printf(" -H this help\n"); printf(" -a append to named output files; don't overwrite\n"); printf(" -i take input from named file\n"); printf(" -h/-m output headers/methods to named file. Adds '.h'/'.m' to\n"); printf(" filename if not there already.\n"); printf(" -f same as -h basefilename.h -m basefilename.m\n"); printf(" -e {h|m|hm} Excludes final {header|method|both} from being output.\n"); printf("\n"); printf(" INPUT SYNTAX: (one line per method)\n"); printf(" - (int)spotOf:(char)aChar caseSensitive:(BOOL)sense[YES]\n"); printf(" ^ ^ ^__________________________^ ^\n"); printf(" | | | |__NO space\n"); printf(" SPACE | CAST each parameter--even if id\n"); printf(" Method cast is optional\n"); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.