GFDirectoryList/APISPLIT.CPP

// Copyright (C) 1992‑1998 Microsoft Corporation
// All rights reserved.

// This code is the basis for which the add‑in to MSDevStudio
//      for generating help files will stem. As of now it splits apart
//      the prototype for a function

// This will work on almost every function declaration
//      1)      DO NOT use on function pointer declarations. For example:
//                              "DWORD (CALLBACK* Get)(LPOLESTREAM, void FAR*, DWORD);"
//                      will fail. The parser gets confused on the (CALLBACK* Get).
//      2)      The question arises as to which which do you run this macro on,
//                              in the header declaration, or in the actual source code.
//                              If you are generating help for a function not in a class, it
//                              is probably best to run it on the header file declaration.
//                              If the function is in a class, try to run it on the source
//                              declaration     because the parser can detect the class scoping
//                              (although, as of right now parses this class scoping, but does
//                              nothing with it, except storing it into an array).
//      3)      Many OLE 2 declarations are declared with macros,
//                              WINOLEAPI_(BOOL) OleIsRunning(LPOLEOBJECT pObject);
//                              the parser is fine with this as long as there are no spaces in
//                              between the WINOLEAPI_ and (BOOL).
//      4)      The parser gets confused if the prototype is something like:
//                      void foo(CArray<CObject, CObject> bar); This happens when there is
//                      a template in the parameter list and a comma appears within this
//                      template declaration. The parser gets confused and tries to set
//                      each as a parameter, ie CArray<CObject is one parameter, and
//                      CObject> bar is another.

#include <stdafx.h>
#include <afxtempl.h>
#include <fstream.h>
#include "resource.h"
#include "apisplit.h"

// The following value is described in the comment for GetReturnType
#define DEFAULT_RETURN 1

inline void WriteString(CString strStr, CFile& file)
{ file.Write(strStr, strStr.GetLength()); }

inline void WriteString(int nIDS, CFile& file)
{
    CString strStringToWrite;
    strStringToWrite.LoadString(nIDS);
    WriteString(strStringToWrite, file);
}

void GenerateHTML(CString strHTMLPath, CString strClassFunctionName,
        CString strFunctionName,
        CArray<CString,CString>& Parameters,
        CArray<CString,CString>& ParameterNames, CString strReturn)
{
    CFile Stream(strHTMLPath,
            CFile::modeCreate|CFile::modeWrite|CFile::shareExclusive);
    WriteString(IDS_HTML1, Stream);
    WriteString(strFunctionName, Stream);
    WriteString(IDS_HTML2, Stream);
    WriteString(strClassFunctionName, Stream);
    WriteString(IDS_HTML3, Stream);

    WriteString(strReturn+" "+strFunctionName,Stream);

    WriteString(IDS_HTML4, Stream);

    if (Parameters.GetSize() != 0)
    {
        for (int i = 0 ; i < Parameters.GetSize()‑1 ; i++)
        {
            WriteString(IDS_HTML5, Stream);
            WriteString(Parameters.GetAt(i), Stream);
            WriteString(IDS_HTML6, Stream);
        }
        WriteString(IDS_HTML5, Stream);
        WriteString(Parameters.GetAt(Parameters.GetSize()‑1), Stream);
        WriteString(IDS_HTML7, Stream);
    }
    else
        WriteString(IDS_HTML8, Stream);

    WriteString(IDS_HTML9, Stream);

    for (int i = 0 ; i < ParameterNames.GetSize() ;i++)
    {
        WriteString(IDS_HTML10, Stream);
        WriteString(ParameterNames.GetAt(i), Stream);
        WriteString(IDS_HTML11, Stream);
    }
    // For some reason, the following <dt></dt> sequence is needed to keep
    //  things straight.in the resulting HTML
    WriteString(IDS_HTML12, Stream);
}

void GenerateHelpFile(CString strRTFPath, CString strClassFunctionName,
                      CString strFunctionName,
                      CArray<CString,CString>& Parameters,
                      CArray<CString,CString>& ParameterNames,
                      CString strReturn, CString ContextID, CString TopicName)
{
    CString sStringToWrite;
    CFile Stream(strRTFPath,
            CFile::modeCreate|CFile::modeWrite|CFile::shareExclusive);

    WriteString(IDS_RTF1, Stream);
    WriteString(IDS_RTF2, Stream);
    WriteString(IDS_RTF3, Stream);

    // Send the context ID for the page being generated
    WriteString(ContextID, Stream);
    // Dump secondary help header
    WriteString(IDS_RTF4, Stream);
    WriteString("SIMPLE Help Topic 1", Stream);

    // More header info
    WriteString(IDS_RTF5, Stream);

    // Dump the name this is known as in help topics.index dialog
    WriteString(TopicName, Stream);

    // Output prototype dependant information
    WriteString(IDS_RTF6, Stream);
    WriteString(strClassFunctionName, Stream);
    WriteString(IDS_RTF7, Stream);

    WriteString(strReturn+" "+strFunctionName+"(\n", Stream);
    WriteString("\\par ", Stream);

    if (Parameters.GetSize() != 0)
    {
        for (int i = 0 ; i < Parameters.GetSize()‑1 ; i++)
        {
            WriteString("  "+Parameters.GetAt(i)+",\n", Stream);
            WriteString("\\par\n", Stream);
        }
        WriteString("  "+Parameters.GetAt(Parameters.GetSize()‑1), Stream);
    }

    WriteString(IDS_RTF8, Stream);

    for (int i = 0 ; i < ParameterNames.GetSize() ;i++)
    {
        WriteString(ParameterNames.GetAt(i)+"\n", Stream);
        WriteString(IDS_RTF9, Stream);
    }
    WriteString(IDS_RTF10, Stream);
    Stream.Close();
}

// Because double spaces only get in the way, this routine removes them.
// It also removes spaces around '::' so that we can find scope resolution
void RemoveUnnecessary(CString& str)
{
    // First, strip out all those pesky, unwanted double spaces
    str.TrimLeft();
    str.TrimRight();
    int nLoc = str.Find("  ");
    while (nLoc > 0)
    {
        str = str.Left(nLoc) + str.Right(str.GetLength()‑nLoc‑1);
        nLoc = str.Find("  ");
    }

    // Now get rid of and ' ::' or ':: '
    nLoc = str.Find(" ::");
    while(nLoc > 0)
    {
        str = str.Left(nLoc) + str.Right(str.GetLength()‑nLoc‑1);
        nLoc = str.Find(" ::");
    }
    nLoc = str.Find(":: ");
    while(nLoc > 0)
    {
        str = str.Left(nLoc+2) + str.Right(str.GetLength()‑nLoc‑3);
        nLoc = str.Find(":: ");
    }
}

void GetFunctAndScope(CString& strFuncName, CArray<CString,CString>& Scoping,
                      CString& strNewFunc)
{
    strNewFunc = strFuncName;
    int nLoc = strNewFunc.Find("::");
    while (nLoc > 0)
    {
        Scoping.Add(strNewFunc.Left(nLoc));
        strNewFunc = strNewFunc.Right(strNewFunc.GetLength()‑nLoc‑2);
        nLoc = strNewFunc.Find("::");
    }
}

void ConvertWSToSpace (CString& str)
{
    // To fix the problem of when there is a comment describing parameters,
    //      the first step is to scan the string, and see if there are any '// '
    int nLoc = str.Find("// ");
    while (nLoc != ‑1)
    {
        // There is a '// ', so we then take the string from there to the
        //  end of the string, and find a '\n'
        CString strComment = str.Right(str.GetLength()‑nLoc);
        str = str.Left(nLoc);
        // within comment, search for the '\n'
        int nCRLoc = strComment.Find('\n');
        if (nCRLoc != ‑1)
        {
            CString strDecommented = strComment.Right(
                    strComment.GetLength()‑nCRLoc);
            str += strDecommented;
            // An enhancement would be to save this comment, and use it
            //  to describe the parameter in the help file
            nLoc = str.Find("// ");
        }
        else
        {
            nLoc = ‑1;
        }
    }

    // Next, a search for the old style comments (/**/) is performed
    nLoc = str.Find("/*");
    while(nLoc != ‑1)
    {
        CString strComment = str.Right(str.GetLength()‑nLoc);
        str = str.Left(nLoc);
        // within the comment, search for the closing '*/'
        int nEndComm = strComment.Find("*/");
        if (nEndComm != ‑1)
        {
            CString strDecommented = strComment.Right(
                    strComment.GetLength()‑nEndComm‑2);
            str += strDecommented;
            // An enhancement would be to save this comment,
            //  and use it to describe the parameter in the help file
            nLoc = str.Find("/*");
        }
        else
        {
            nLoc = ‑1;
        }
    }
    nLoc = str.Find("\t");
    while(nLoc != ‑1)
    {
        char foo = str.GetAt(nLoc);
        str.SetAt(nLoc, ' ');
        nLoc = str.Find("\t");

    }
    nLoc = str.Find("\n");
    while(nLoc != ‑1)
    {
        char foo = str.GetAt(nLoc);
        str.SetAt(nLoc, ' ');
        nLoc = str.Find("\n");
    }
    /*for (int i = 0 ; i < str.GetLength()‑1 ; i++)
        if ((str[i] == '\t') || (str[i] == '\n'))
            str.SetAt(i, ' ');*/
}

void GetReturnType(CString& str, CArray<CString,CString>& ReturnsArray,
                   CString& strSuggested, int nAppendStar)
{
    // There are many conflicts that could arise from
    //      finding the return type, for example look at the prototype
    //      from the windows header files for FreeLibrary:
    //      WINBASEAPI BOOL WINAPI FreeLibrary(HMODULE hLibModule);
    //  If we start at FreeLibrary and scan backwards for the first
    //      word, we have WINAPI as the return type. If we start at
    //      the beginning of the line and scan foreward for the first
    //      word, we have WINBASEAPI as the return type. Things could
    //      also get more complicated, we could start throwing FAR's,
    //      _declspec's, and much more (static _declspec BOOL FAR foo(char*))
    //  The way this is remedied is as follows:
    //      1) Count the number of items that appears before the function name
    //      2) if Count == 0, no return type is assumed
    //               if Count == 1, use the found return type
    //               if Count >= 2, use the DEFAULT_RETURN word as the return type
    //      If the wrong word is grabbed, it is trivial to change it in the
    //      help file, but under most circumstances this should work.*/

    // we know the string is of the form:  words words ...  funcname
    str = str.Left(str.ReverseFind(' '));
    str.TrimRight();

    if (str.GetLength() != 0)
    {
        int nEndOfItem = str.Find(' ');
        while ((str.GetLength() != 0) && (nEndOfItem != ‑1))
        {
            ReturnsArray.Add(str.Left(nEndOfItem));

            str = str.Right(str.GetLength()‑nEndOfItem);
            str.TrimLeft();
            str.TrimRight();
            nEndOfItem = str.Find(' ');
        }
        ReturnsArray.Add(str);

        // Right now there could possibly be a problem with pointers, if the
        //      prototype is of the form HWND *foo(...) the pointer is attached to
        //  the function name, when in actuality the '*' belongs with HWND.
        //  Also there is the case when the prototype is HWND * foo(...), it
        //  ends up that * is viewed as a return type, that is fixed up here.
        for (int i = 0 ; i < ReturnsArray.GetSize() ; i++)
        {
            // Check to see if the element is a '*'
            if (ReturnsArray.GetAt(i) == "*")
            {
                // Delete it...
                ReturnsArray.RemoveAt(i);
                // Change old value before it
                ReturnsArray.SetAt(i‑1, ReturnsArray.GetAt(i‑1) + "*");
            }
        }

        // Check for the HWND *foo(...) case
        for (i = nAppendStar ; i > 0 ; i‑‑)
            ReturnsArray.SetAt(ReturnsArray.GetSize()‑1,
                    ReturnsArray.GetAt(ReturnsArray.GetSize()‑1) + " *");

        // Here we decide which value we use as the return type,
        //      there probably is a better way of calculating this, but that
        //  will be
        //      left until later.
        if (ReturnsArray.GetSize() == 1)
            strSuggested = ReturnsArray.GetAt(0);
        else
        {
            // If DEFAULT_RETURN is greater than the actual number of possible
            //  return types, then give back the last possible one
            if (DEFAULT_RETURN > ReturnsArray.GetSize())
                strSuggested = ReturnsArray.GetAt(ReturnsArray.GetSize()‑1);
            else
                strSuggested = ReturnsArray.GetAt(DEFAULT_RETURN);
        }
    }
}

void GetListOfParamNames(CArray<CString,CString>& ParamList,
                CArray<CString,CString>& ParamNames)
{
    for (int i = 0 ; i < ParamList.GetSize() ; i++)
    {
        CString strTemp = ParamList.GetAt(i);
        strTemp = strTemp.Right(strTemp.GetLength()‑strTemp.ReverseFind(' ')‑1);
        while (strTemp[0] == '*')
            strTemp = strTemp.Right(strTemp.GetLength()‑1);
        ParamNames.Add(strTemp);
    }
}

int GetParameters(CString& strParameters, CString& strPrototype,
                CArray<CString,CString>& ParamList)
{
    // Start off by getting to the end of the argument list
    strParameters = strPrototype.Left(strPrototype.ReverseFind(')'));
    strParameters.TrimRight();

    int nBeginningOfParameters = strParameters.ReverseFind('(');
    strParameters = strParameters.Right(strParameters.GetLength()‑
            nBeginningOfParameters‑1);
    strParameters.TrimLeft();

    // now pull off each parameter
    if (strParameters.GetLength() != 0)
    {
        int nComma = strParameters.Find(',');
        while (nComma != ‑1)
        {
            ParamList.Add(strParameters.Left(nComma));
            strParameters = strParameters.Right(strParameters.GetLength()‑
                    nComma‑1);
            strParameters.TrimLeft();
            strParameters.TrimRight();
            nComma = strParameters.Find(',');
        }
        ParamList.Add(strParameters);
    }
    return nBeginningOfParameters;
}


[END OF FILE]