home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 23 / IOPROG_23.ISO / SOFT / DOCJET.ZIP / data.z / Arguments.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-11-21  |  23.3 KB  |  798 lines

  1. /* Copyright (C) 1997-1998, Tall Tree Software Co.
  2.  *
  3.  * This code is provided AS-IS, with no warranty expressed or implied.
  4.  *  in no way shall Tall Tree Software Co. be held liable for damages
  5.  *  brought about by the use of this software.
  6.  */
  7.  
  8.  
  9. #include <afxwin.h>
  10.  
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <assert.h>
  14. #include <Rewriter Hook.h>
  15. #include <StoredObjectSet.h>
  16.  
  17. #undef IDC_STATIC
  18. #include "resource.h"
  19. #include "Settings.h"
  20.  
  21. // This hook is designed to pull the comments for function/method
  22. //   arguments out of the comment of the function and put them with
  23. //   the parameter.  For example, if we have a function like this:
  24. //
  25. //  /*
  26. //    Description: My Function
  27. //
  28. //    Arguments:
  29. //      arg1 - This is arg1
  30. //      arg2 - This is arg2
  31. //    ...
  32. //   */
  33. //   void f( int arg1, int arg2 )
  34. //
  35. //  Without this hook in place, the scanner will report three objects,
  36. //    "f", "f::arg1", and "f::arg2".  The comment for arg1 and arg2
  37. //    will be null, but we'll still see a "Arguments" section in the
  38. //    output because the output format is designed to first look for
  39. //    an "Arguments" comment section and only use the comments associated
  40. //    directly with the arguments if the comment section does not exist.
  41. //
  42. //  The net effect of this hook is to make it appear to DocJet as if the
  43. //    code block looks like this:
  44. //
  45. //  /*
  46. //    Description: My Function
  47. //
  48. //    ...
  49. //   */
  50. //   void f( int arg1 /* This is arg1  */, int arg2 /* This is arg2 */ )
  51. //
  52. //   When DocJet sees a block of code like this, it reports three objects,
  53. //   as before, except that the parameter comments are filled in.  The
  54. //   output will be exactly the same as before, because the output format
  55. //   is designed to generate the same thing both ways.
  56. //
  57. // The advantage of having your comments directly associated with the
  58. //   parameter is twofold.  First, you can detect when parameters aren't
  59. //   documented and second you can use DocJet's knowledge of things like
  60. //   the parameter's declaration in your output.
  61. //
  62. //
  63. // We implement this functionality by snooping on the object stream as
  64. //   it comes back from the scanner, looking for function and parameter
  65. //   objects.  When we see a function, we hunt for an "Arguments" section,
  66. //   stash it for future reference, and delete it from the comment.
  67. //   When we see a parameter, we look through our list of stashed
  68. //   function "Arguments" section for the one that belongs to the function
  69. //   this parameter is a part of and then see if we can find the part
  70. //   of the function's arguments section that pertains to this parameter.
  71. //   If we can find such a thing, we use that as the parameter's comment.
  72. //
  73. // This fellow implements a settings dialog.  The dialog itself is pretty
  74. //   basic (implemented in "Settings.cpp").  We use the settings to store
  75. //   the names of the keywords that introduce sections in the comments.
  76. //
  77. // Most folks will be able to use this DLL straight out of the box,
  78. //   but if you have to mess with it, the most likely cause is that
  79. //   we're not correctly recognizing or parsing the arguments section.
  80. //   The function responsible for locating the Arguments section is
  81. //   FindArgumentSection, and it relies heavily on the variables
  82. //   argSectionNames and otherSectionNames.  The function that breaks
  83. //   down the argument section is FindArgumentComment.
  84.  
  85.  
  86.  
  87.  
  88. ///////////////////////////////////////////////////////////////////////
  89. //  Preliminary junk
  90.  
  91. static HookReportFunction nextOnObjectFound;
  92.  
  93. extern "C" DLLEXPORT void SetNextOnObjectFound( HookReportFunction onObjectFound )
  94. {
  95.     nextOnObjectFound = onObjectFound;
  96. }
  97.  
  98. ///////////////////////////////////////////////////////////////////////
  99. //  This is the code that stores the argument sections of functions.
  100. //    The data structure is just a simple flat array of FunctionStorage
  101. //    structures.  We dynamically grow it using realloc.
  102.  
  103.  
  104. struct FunctionStorage {
  105.     char *funcname;
  106.     char *argsection;
  107.     char *inputArgSection;
  108.     char *outputArgSection;
  109. };
  110.  
  111. static FunctionStorage *storage = 0;
  112. static int numInStorage = 0;
  113. static int storageSize = 0;
  114.  
  115. static char null_comment[] = "";
  116.  
  117. void AddFunction( const char *name, char *argSection,
  118.           char *inputArgSection, char *outputArgSection )
  119. {
  120.     if ( storage == 0 ) {
  121.         assert( numInStorage == 0 );
  122.         storageSize = 100;
  123.         storage = (FunctionStorage *)malloc(
  124.             storageSize * sizeof(FunctionStorage) );
  125.     }
  126.     else if ( numInStorage == storageSize ) {
  127.         storageSize *= 2;
  128.         storage = (FunctionStorage *)realloc( storage,
  129.             storageSize * sizeof(FunctionStorage) );
  130.     }
  131.     storage[numInStorage].funcname = strdup( name );
  132.     storage[numInStorage].argsection = argSection;
  133.     storage[numInStorage].inputArgSection = inputArgSection;
  134.     storage[numInStorage].outputArgSection = outputArgSection;
  135.     ++numInStorage;
  136. }
  137.  
  138. FunctionStorage *FindArgSection( const char *funname )
  139. {
  140.     for ( int i = 0; i < numInStorage; ++i )
  141.       if ( 0 == strcmp( storage[i].funcname, funname ) )
  142.         return( storage+i );
  143.     return( 0 );
  144. }
  145.  
  146.  
  147.  
  148. ///////////////////////////////////////////////////////////////////////
  149. //  This is the code that breaks out the arguments section(s) of a
  150. //    comment.  It does so by looking for lines that start with one
  151. //    of the known comment section names (listed below) and then an
  152. //    end of line character or colon or dash.
  153. //
  154. // The function "isSectionStart" is used to discover if a line is a
  155. //   section starter or not.  It returns a pointer to the point in the
  156. //   line just past the header.  "Sections" will either be argSectionNames,
  157. //   a list of argument section headers or otherSectionNames, which is
  158. //   a list of all the other known section names.
  159.  
  160.  
  161. char *defaultArgSectionNames[] = { "arguments", "parameters", "args", 0 };
  162. char *defaultInputArgSectionNames[] = { "input", "inputs",
  163.                     "input arguments",
  164.                     "input parameters", 0 };
  165. char *defaultOutputArgSectionNames[] = { "output", "outputs",
  166.                          "output arguments",
  167.                      "output parameters", 0 };
  168. char *defaultOtherSectionNames[] = { "Remarks", "Description", "Summary",
  169.                   "Function", "Function Name", "Synopsis",
  170.                   "Result", "Results", "Returns",
  171.                   "Return Value", "Purpose", "Author",
  172.                   "Example", "Examples", "Header", "Include",
  173.                   "Name", "Function Name", "Samples", "See Also",
  174.                   "Modification History", "Log", "Modification Log",
  175.                   "Includes", "Required Header", "Required Includes",
  176.                   "Settings", "Notes", "Return", "Return Code",
  177.                   0 };
  178.  
  179. char **argSectionNames = defaultArgSectionNames;
  180. char **inputArgSectionNames = defaultInputArgSectionNames;
  181. char **outputArgSectionNames = defaultOutputArgSectionNames;
  182. char **otherSectionNames = defaultOtherSectionNames;
  183.  
  184. static const char *isSectionStart( const char *commentLine, char **sections )
  185. {
  186.     // See if the line starts with one of the strings in sections
  187.     commentLine += strspn( commentLine, " \t" );
  188.     for ( ; *sections != 0; ++sections ) {
  189.         if ( 0 == _strnicmp( commentLine, *sections, strlen(*sections) ) ) {
  190.             const char *sp = commentLine + strlen(*sections);
  191.             sp += strspn( sp, " \t" );
  192.             if ( *sp == ':' || *sp == '\n' ) {
  193.                 // Yup, we've got a section here.  Let's
  194.                 //   find the start of the body.
  195.                 return( sp+1+strspn(sp+1, " \t\n") );
  196.             }
  197.         }
  198.     }
  199.     return( 0 );
  200. }
  201.  
  202. static const char *isArgSectionStart( const char *commentLine, char *& argPart,
  203.                       char *&inputArgPart, char *&outputArgPart,
  204.                       char **&messWith )
  205. {
  206.     const char *rc;
  207.     rc = isSectionStart( commentLine, inputArgSectionNames );
  208.     if ( rc != 0 ) {
  209.         messWith = &inputArgPart;
  210.         return rc;
  211.     }
  212.     rc = isSectionStart( commentLine, outputArgSectionNames );
  213.     if ( rc != 0 ) {
  214.         messWith = &outputArgPart;
  215.         return rc;
  216.     }
  217.     rc = isSectionStart( commentLine, argSectionNames );
  218.     if ( rc != 0 ) {
  219.         messWith = &argPart;
  220.         return rc;
  221.     }
  222.     return 0;
  223. }
  224.  
  225. //  This breaks the comment down into the arguments sections and the
  226. //    sections not to do with arguments.  If the comment has no
  227. //    recognizable argument section, it returns false.  If it does,
  228. //    it will return true and nonArgPart will point to a malloc'ed
  229. //    string that contains just those parts of the comment not in
  230. //    the arguments section and argPart will point to a malloc'ed
  231. //    string that contains the arguments section(s).
  232.  
  233. void FindArgumentSection( const char *comment, char *&nonArgPart,
  234.               char *&argPart, char *&inputArgPart,
  235.               char *&outputArgPart )
  236. {
  237.     nonArgPart = 0;
  238.     argPart = 0;
  239.     inputArgPart = 0;
  240.     outputArgPart = 0;
  241.  
  242.     const char *lastSectionStart = comment;
  243.     const char *s = comment;
  244.     while ( *s != '\0' ) {
  245.         char **messWith;
  246.         const char *nss = isArgSectionStart( s, argPart, inputArgPart,
  247.                              outputArgPart, messWith);
  248.         if ( nss != 0 ) {
  249.             if ( s != lastSectionStart ) {
  250.                 if ( nonArgPart == 0 ) {
  251.                     nonArgPart = (char *)malloc(
  252.                         strlen( comment ) + 1 );
  253.                     *nonArgPart = '\0';
  254.                 }
  255.                 char old = *s;
  256.                 *(char *)s = '\0';
  257.                 strcat( nonArgPart, lastSectionStart );
  258.                 *(char *)s = old;
  259.             }
  260.             // Advance s until we find the end or
  261.             //   the start of a new section
  262.             s = nss;
  263.             while ( *s != '\0' && 0 == isSectionStart( s, otherSectionNames ) &&
  264.                 0 == isSectionStart( s, argSectionNames ) &&
  265.                 0 == isSectionStart( s, inputArgSectionNames ) &&
  266.                 0 == isSectionStart( s, outputArgSectionNames ) ) {
  267.  
  268.                 if ( strchr( s, '\n' ) == 0 )
  269.                   s += strlen(s);
  270.                 else
  271.                   s = 1+strchr( s, '\n' );
  272.             }
  273.             if ( *messWith == 0 ) {
  274.                 *messWith = (char *)malloc( strlen(comment)+1 );
  275.                 **messWith = '\0';
  276.             }
  277.             char old = *s;
  278.             *(char *)s = '\0';
  279.             strcat( *messWith, nss );
  280.             *(char *)s = old;
  281.             lastSectionStart = s;
  282.         }
  283.         else {
  284.             if ( strchr( s, '\n' ) == 0 )
  285.               s += strlen(s);
  286.             else
  287.               s = 1+strchr( s, '\n' );
  288.         }
  289.     }
  290.     if ( s > lastSectionStart && ( argPart != 0 || inputArgPart != 0 || outputArgPart != 0 ) ) {
  291.         if ( nonArgPart == 0 ) {
  292.             nonArgPart = (char *)malloc(
  293.                 strlen( comment ) + 1 );
  294.             *nonArgPart = '\0';
  295.         }
  296.         char old = *s;
  297.         *(char *)s = '\0';
  298.         strcat( nonArgPart, lastSectionStart );
  299.         *(char *)s = old;
  300.     }
  301.     if ( argPart != 0 && nonArgPart == 0 )
  302.       nonArgPart = strdup( "" );
  303. }
  304.  
  305.  
  306. ///////////////////////////////////////////////////////////////////////
  307. // This function hunts down a particular variable's comment within an
  308. //   argument section (which was previously parsed out by
  309. //   FindArgumentSection.
  310.  
  311. static char *FindArgumentComment( const char *argSection, const char *varname )
  312. {
  313.     const char *s = argSection;
  314.     int vl = strlen(varname);
  315.     while ( *s != '\0' ) {
  316.         s += strspn( s, " \t" );
  317.         if ( 0 == strncmp( s, varname, vl ) &&
  318.              0 != strchr( "-=:", s[vl+strspn(s+vl," \t")] ) ) {
  319.             const char *start = s + vl + strspn(s+vl, " \t") + 1;
  320.             start += strspn( start, " \t" );
  321.             s = strchr( start, '\n' );
  322.             if ( s == 0 )
  323.               s = start+strlen(start);
  324.             else
  325.               ++s;
  326.             while ( *s != '\0' ) {
  327.                 s += strspn( s, " \t" );
  328.                 int fwl = 0;
  329.                 while ( isalnum(s[fwl]) || s[fwl] == '_' )
  330.                   ++fwl;
  331.                 fwl += strspn( s+fwl, " \t" );
  332.                 if ( fwl > 0 && 0 != strchr( "-=:", s[fwl] ) )
  333.                   break;
  334.                 else {
  335.                     if ( strchr( s, '\n' ) == 0 )
  336.                       s = s+strlen(s);
  337.                     else
  338.                       s = strchr( s, '\n' )+1;
  339.                 }
  340.             }
  341.             char old = *s;
  342.             *(char *)s = '\0';
  343.             start = strdup( start );
  344.             *(char *)s = old;
  345.             return( (char *)start );
  346.         }
  347.         else {
  348.             if ( strchr( s, '\n' ) == 0 )
  349.               s = s+strlen(s);
  350.             else
  351.               s = strchr( s, '\n' )+1;
  352.         }
  353.     }
  354.     return( 0 );
  355. }
  356.  
  357. enum ArgDir { Input, Output, Neutral };
  358.  
  359. static const char *FindTagsInComment( const char *originalComment,
  360.                       ArgDir &argDir )
  361. {
  362.     const char *s = originalComment + strspn( originalComment, " \t\r\n" );
  363.     if ( *s != '[' )
  364.       return originalComment;
  365.  
  366.     ++s;
  367.     s += strspn( s, " \t\r\n" );
  368.     argDir = Neutral;
  369.  
  370.     while ( *s != '\0' ) {
  371.         int l = strcspn( s, " \t\r\n,]" );
  372.         if ( ( l == 2 && 0 == strncmp( s, "in", l ) ) ||
  373.              ( l == 5 && 0 == strncmp( s, "input", l ) ) )
  374.           argDir = Input;
  375.         else if ( ( l == 3 && 0 == strncmp( s, "out", l ) ) ||
  376.               ( l == 6 && 0 == strncmp( s, "output", l ) ) )
  377.           argDir = Output;
  378.         else if ( ( l  == 6 && 0 == strncmp( s, "retval", l ) ) )
  379.           argDir = Output; // Maybe argDir = RetVal?
  380.         
  381.         s  += l;
  382.         s += strspn( s, " \t\r\n" );
  383.         if ( *s == ']' )
  384.           return s+1+strspn(s+1," \t\r\n");
  385.         else if ( *s == ',' )
  386.           s += 1+strspn( s+1, " \t\r\n" );
  387.         else
  388.           break;
  389.     }
  390.  
  391.     // TODO: Perhaps throw a syntax error here
  392.     argDir = Neutral;
  393.     return originalComment;
  394. }
  395.  
  396. static char *FindArgumentComment( FunctionStorage *argSection, const char *varname,
  397.                   ArgDir &dir )
  398. {
  399.     if ( argSection->argsection != 0 ) {
  400.         char *c = FindArgumentComment( argSection->argsection, varname );
  401.         if ( c != 0 ) {
  402.             const char *nc = FindTagsInComment( c, dir );
  403.             if ( nc != c ) {
  404.                 char *newC = strdup( nc );
  405.                 free( c );
  406.                 c = newC;
  407.             }
  408.             return c;
  409.         }
  410.     }
  411.     if ( argSection->inputArgSection != 0 ) {
  412.         char *c = FindArgumentComment( argSection->inputArgSection, varname );
  413.         if ( c != 0 ) {
  414.             dir = Input;
  415.             return c;
  416.         }
  417.     }
  418.     if ( argSection->outputArgSection != 0 ) {
  419.         char *c = FindArgumentComment( argSection->outputArgSection, varname );
  420.         if ( c != 0 ) {
  421.             dir = Output;
  422.             return c;
  423.         }
  424.     }
  425.     return 0;
  426. }
  427.  
  428.  
  429.  
  430. static StoredObjectSet parms;
  431.  
  432. void CheckStoredParms( const char *name )
  433. {
  434.     unsigned int namelen = strlen(name);
  435.     StoredObjectSet::Iterator i( parms );
  436.     while ( i ) {
  437.         StoredObjectSet::Object &p = *i;
  438.  
  439.         if ( 0 == strncmp( p.objectName, name, namelen ) &&
  440.              ( p.objectName[namelen] == ':' ||
  441.                p.objectName[namelen] == '.' ) ) {
  442.             FunctionStorage *argSection =
  443.                 FindArgSection( name );
  444.             assert( argSection != 0 );
  445.  
  446.             ArgDir dir;
  447.             char *comment =
  448.               FindArgumentComment( argSection,
  449.                p.objectName+namelen +
  450.                 (p.objectName[namelen] == ':' ? 2 : 1 ), dir );
  451.  
  452.             ExtrasSet xtras( p );
  453.             if ( dir == Input )
  454.               xtras.Set( ATTR_ARGDIR, AV_ARGDIR_BYVAL );
  455.             else if ( dir == Output )
  456.               xtras.Set( ATTR_ARGDIR, AV_ARGDIR_BYREF );
  457.  
  458.             // TODO: Inspect the comment to see if it's got [in]
  459.             //   directives.
  460.             (*nextOnObjectFound)( p.sourceFile, p.commentStartLine,
  461.                   p.declStartLine, p.objectName, *p.subtype,
  462.                   "", p.declaration,
  463.                   (comment == 0 ? p.comment : comment),
  464.                   p.body, (char const **)p.extras );
  465.             if ( comment != 0 )
  466.               free( comment );
  467.  
  468.             parms.Destroy( i );
  469.         }
  470.         else
  471.           ++i;
  472.     }
  473. }
  474.  
  475. /////////////////////////////////////////////////////////////////////
  476. // This is the business end of the thing, it's called by a source
  477. //   file scanner when an object is recognized.  Our job is to see
  478. //   if it's a function or parameter, do our magic with the comments,
  479. //   and pass it on.
  480.  
  481. extern "C" DLLEXPORT void OnObjectFound( const char *p_sourceFile,
  482.                      unsigned int p_commentStartLine,
  483.                      unsigned int p_declStartLine,
  484.                      const char *p_objectName,
  485.                      const Subtype &p_subtype,
  486.                      const char *p_superclass,
  487.                      const char *p_declaration,
  488.                      const char *p_comment,
  489.                      const char *p_body,
  490.                      char const **p_extras )
  491. {
  492.     assert( nextOnObjectFound != 0 );
  493.  
  494.     ExtrasSet xtras( p_extras );
  495.  
  496.     char *comment = 0;
  497.     if ( p_subtype.roughType == ARGUMENT_ID && *p_comment == '\0' ) {
  498.         // Extract the function name here...
  499.         char *oldp = (char *)p_objectName + strlen(p_objectName);
  500.         while ( oldp > p_objectName &&
  501.             *oldp != '.' &&
  502.             (*oldp != ':' || oldp[1] != ':') )
  503.           --oldp;
  504.         assert( oldp > p_objectName );
  505.         char old = *oldp;
  506.         *oldp = '\0';
  507.         // Look up the argument section of the comment of the
  508.         //   function.
  509.         FunctionStorage *argSection = FindArgSection( p_objectName );
  510.         *oldp = old;
  511.         // TODO: If we weren't relying on the fact that functions
  512.         //   are always reported before their arguments, we'd need
  513.         //   to see if argSection was 0 and, if so, store the
  514.         //   parameter for later (when the function actually
  515.         //   showed up.)
  516.         if ( argSection == 0 ) {
  517.             assert( p_superclass == 0 || *p_superclass == '\0' );
  518.             parms.Add( new StoredObjectSet::Object
  519.               ( p_sourceFile, p_commentStartLine,
  520.                 p_declStartLine, p_objectName, p_subtype,
  521.                 p_superclass, p_declaration,
  522.                 p_comment, p_body, p_extras ) );
  523.             return;
  524.         }
  525.  
  526.         ArgDir dir;
  527.         comment = FindArgumentComment( argSection,
  528.             oldp + ( *oldp == ':' ? 2 : 1 ), dir );
  529.         // TODO: Look for [in] direction directives.
  530.         if ( dir == Input )
  531.           xtras.Set( ATTR_ARGDIR, AV_ARGDIR_BYVAL );
  532.         else if ( dir == Output )
  533.           xtras.Set( ATTR_ARGDIR, AV_ARGDIR_BYREF );
  534.     }
  535.     else if ( p_subtype.roughType != ARGUMENT_ID ) {
  536.         // If this is a function, store the object name and the
  537.         //   argument section of the comment
  538.         char *argPart, *inputArgPart, *outputArgPart;
  539.         FindArgumentSection( p_comment, comment, argPart,
  540.                      inputArgPart, outputArgPart );
  541.         AddFunction( p_objectName, argPart, inputArgPart,  outputArgPart );
  542.         CheckStoredParms( p_objectName );
  543.     }
  544.     (*nextOnObjectFound)( p_sourceFile, p_commentStartLine,
  545.                   p_declStartLine, p_objectName, p_subtype,
  546.                   p_superclass, p_declaration,
  547.                   (comment == 0 ? p_comment : comment),
  548.                   p_body, xtras );
  549.     if ( comment != 0 )
  550.       free( comment );
  551. }
  552.  
  553. //  All OnScanComplete has to do is clean up our storage.
  554.  
  555. extern "C" DLLEXPORT void OnScanComplete()
  556. {
  557.     // TODO: If we weren't relying on the fact that functions are always
  558.     //   reported before their arguments, we'd try to figure out the
  559.     //   comments for any argument objects we had to punt on here.
  560.  
  561.     // Free all the storage we allocated.
  562.     for ( int i = 0; i < numInStorage; ++i ) {
  563.         free( storage[i].funcname );
  564.         if ( storage[i].argsection != 0 )
  565.           free( storage[i].argsection );
  566.         if ( storage[i].inputArgSection != 0 )
  567.           free( storage[i].inputArgSection );
  568.         if ( storage[i].outputArgSection != 0 )
  569.           free( storage[i].outputArgSection );
  570.     }
  571.     free( storage );
  572.  
  573.     parms.PassAll( nextOnObjectFound );
  574.  
  575.     // Let's be anal...
  576.     storage = 0;
  577.     numInStorage = 0;
  578.     storageSize = 0;
  579. }
  580.  
  581.  
  582.  
  583.  
  584. extern "C" DLLEXPORT void UseSettings( const void *data, unsigned int dataLen )
  585. {
  586.     unsigned int n;
  587.     char **sp;
  588.  
  589.     if ( argSectionNames != defaultArgSectionNames ) {
  590.         n = 1;
  591.         for ( sp = argSectionNames; *sp != 0; ++sp ) {
  592.             free( *sp );
  593.             ++n;
  594.         }
  595.         delete[n] argSectionNames;
  596.     }
  597.     if ( inputArgSectionNames != defaultInputArgSectionNames ) {
  598.         n = 1;
  599.         for ( sp = inputArgSectionNames; *sp != 0; ++sp ) {
  600.             free( *sp );
  601.             ++n;
  602.         }
  603.         delete[n] inputArgSectionNames;
  604.     }
  605.     if ( outputArgSectionNames != defaultOutputArgSectionNames ) {
  606.         n = 1;
  607.         for ( sp = outputArgSectionNames; *sp != 0; ++sp ) {
  608.             free( *sp );
  609.             ++n;
  610.         }
  611.         delete[n] outputArgSectionNames;
  612.     }
  613.     if ( otherSectionNames != defaultOtherSectionNames ) {
  614.         n = 1;
  615.         for ( sp = otherSectionNames; *sp != 0; ++sp ) {
  616.             free( *sp );
  617.             ++n;
  618.         }
  619.         delete[n] otherSectionNames;
  620.     }
  621.  
  622.     if ( data == 0 ) {
  623.         argSectionNames = defaultArgSectionNames;
  624.         otherSectionNames = defaultOtherSectionNames;
  625.         return;
  626.     }
  627.  
  628.     if ( *(int *)data < 0 || *(int *)data > 1 ) {
  629.         // Something's wrong with the data.
  630.         MessageBox( 0, "Bogus data passed to Argument Hook.UseSettings!", "Error", MB_OK );
  631.         return;
  632.     }
  633.  
  634.     int version = *(int *)data;
  635.     const char *oss = ((const char *)data) + sizeof(int);
  636.  
  637.     n = 1;
  638.     for ( const char *s = oss; *s != '\0'; s += strlen(s)+1 )
  639.       ++n;
  640.  
  641.     argSectionNames = new char*[n];
  642.     sp = argSectionNames;
  643.     for ( s = oss; *s != '\0'; s += strlen(s)+1 )
  644.       *sp++ = strdup(s);
  645.     *sp = 0;
  646.     oss = s+1;
  647.  
  648.     n = 1;
  649.     for ( s = oss; *s != '\0'; s += strlen(s)+1 )
  650.       ++n;
  651.  
  652.     otherSectionNames = new char*[n];
  653.     sp = otherSectionNames;
  654.     for ( s = oss; *s != '\0'; s += strlen(s)+1 )
  655.       *sp++ = strdup(s);
  656.     *sp = 0;
  657.  
  658.     oss = s+1;
  659.  
  660.     if ( version == 0 ) {
  661.         int inputs = 0;
  662.         int outputs = 0;
  663.         int others = 0;
  664.         for ( sp = argSectionNames; *sp != 0; ++sp ) {
  665.             if ( 0 == _strnicmp( *sp, "input", 5 ) )
  666.               ++inputs;
  667.             else  if ( 0 == _strnicmp( *sp, "output", 6 ) )
  668.               ++outputs;
  669.             else
  670.               ++others;
  671.         }
  672.         inputArgSectionNames = new char*[inputs+1];
  673.         outputArgSectionNames = new char*[outputs+1];
  674.         char **newArgSectionNames = new char*[others+1];
  675.         inputs = outputs = others = 0;
  676.         for ( sp = argSectionNames; *sp != 0; ++sp ) {
  677.             if ( 0 == _strnicmp( *sp, "input", 5 ) )
  678.               inputArgSectionNames[inputs++] = *sp;
  679.             else  if ( 0 == _strnicmp( *sp, "output", 6 ) )
  680.               outputArgSectionNames[outputs++] = *sp;
  681.             else
  682.               newArgSectionNames[others++] = *sp;
  683.             inputArgSectionNames[inputs] = 0;
  684.             outputArgSectionNames[outputs] = 0;
  685.             newArgSectionNames[others] = 0;
  686.             delete[inputs+outputs+others+1] argSectionNames;
  687.             argSectionNames = newArgSectionNames;
  688.         }
  689.     }
  690.     else {
  691.         n = 1;
  692.         for ( s = oss; *s != '\0'; s += strlen(s)+1 )
  693.           ++n;
  694.  
  695.         inputArgSectionNames = new char*[n];
  696.         sp = inputArgSectionNames;
  697.         for ( s = oss; *s != '\0'; s += strlen(s)+1 )
  698.           *sp++ = strdup(s);
  699.         *sp = 0;
  700.         oss = s+1;
  701.  
  702.         n = 1;
  703.         for ( s = oss; *s != '\0'; s += strlen(s)+1 )
  704.           ++n;
  705.  
  706.         outputArgSectionNames = new char*[n];
  707.         sp = outputArgSectionNames;
  708.         for ( s = oss; *s != '\0'; s += strlen(s)+1 )
  709.           *sp++ = strdup(s);
  710.         *sp = 0;
  711.         oss = s+1;
  712.     }
  713. }
  714.  
  715. static char *splat( const char *from, char *to )
  716. {
  717.     from += strspn( from, "\r\n\t ," );
  718.     while ( *from != '\0' ) {
  719.         if ( *from == ' ' || *from == '\t' ||
  720.              *from == '\r' || *from == '\n' ) {
  721.             from += strspn( from, " \t\r\n" );
  722.             if ( *from == ',' ) {
  723.                 *to++ = '\0';
  724.                 ++from;
  725.                 from += strspn( from, ", \r\n" );
  726.             }
  727.             else if ( *from == '\0' ) {
  728.                 // good enuf
  729.             }
  730.             else {
  731.                 *to++ = ' ';
  732.             }
  733.         }
  734.         else if ( *from == ',' ) {
  735.             *to++ = '\0';
  736.             ++from;
  737.             from += strspn( from, ", \t\r\n" );
  738.         }
  739.         else {
  740.             *to++ = *from++;
  741.         }
  742.     }
  743.     if ( to[-1] != '\0' )
  744.       *to++ = '\0';
  745.     *to++ = '\0';
  746.     return( to );
  747. }
  748.  
  749. extern "C" DLLEXPORT void *ShowSettingsDialog( unsigned int &dataLen )
  750. {
  751.     AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
  752.  
  753.     CSettings dlg;
  754.     char **sp;
  755.     for ( sp = argSectionNames; *sp != '\0'; ++sp ) {
  756.         if ( dlg.m_arg_sections.GetLength() != 0 )
  757.           dlg.m_arg_sections += ", ";
  758.         dlg.m_arg_sections += *sp;
  759.     }
  760.     for ( sp = inputArgSectionNames; *sp != '\0'; ++sp ) {
  761.         if ( dlg.m_input_arg_sections.GetLength() != 0 )
  762.           dlg.m_input_arg_sections += ", ";
  763.         dlg.m_input_arg_sections += *sp;
  764.     }
  765.     for ( sp = outputArgSectionNames; *sp != '\0'; ++sp ) {
  766.         if ( dlg.m_output_arg_sections.GetLength() != 0 )
  767.           dlg.m_output_arg_sections += ", ";
  768.         dlg.m_output_arg_sections += *sp;
  769.     }
  770.     for ( sp = otherSectionNames; *sp != '\0'; ++sp ) {
  771.         if ( dlg.m_other_sections.GetLength() != 0 )
  772.           dlg.m_other_sections += ", ";
  773.         dlg.m_other_sections += *sp;
  774.     }
  775.  
  776.     if ( IDOK == dlg.DoModal() ) {
  777.         char *rc = (char *)malloc( 4 + dlg.m_arg_sections.GetLength() +
  778.                        dlg.m_other_sections.GetLength() +
  779.                        dlg.m_input_arg_sections.GetLength() +
  780.                        dlg.m_output_arg_sections.GetLength()  +
  781.                        sizeof(int) );
  782.         *(int *)rc = 1;
  783.         char *n = rc + sizeof(int);
  784.         n = splat( dlg.m_arg_sections, n );
  785.         n = splat( dlg.m_other_sections, n );
  786.         n = splat( dlg.m_input_arg_sections, n );
  787.         n = splat( dlg.m_output_arg_sections, n );
  788.         dataLen = n - rc;
  789.  
  790.         UseSettings( rc, dataLen );
  791.  
  792.         return( rc );
  793.     }
  794.     else
  795.       return( 0 );
  796. }
  797.  
  798.