home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / mslang / mh / mh.c next >
Encoding:
C/C++ Source or Header  |  1993-09-04  |  38.8 KB  |  1,437 lines

  1. // Printout is in \foo.c
  2. // Still need to create .PRJ file.
  3.  
  4. // Doesn't handle text containing { or }.
  5. // All calls to Parse() need error checking.
  6. // Check header documentation, such as "To compile" note.
  7. // Search for this:
  8. //    puts("XXX Error--Improve this error handling!");
  9. // Search for all XXX occurrences, which represent code to improve.
  10. // Maybe a feature that creates an index or table of contents?
  11.  
  12. // Ask on WINSDK
  13. // - How to specify bold or italic?
  14. // - What's the max length of a line in HC.EXE?
  15. // - How do you create a table of contents?
  16.  
  17. /* ==========================================================
  18.    File:          MH.C -- MiniHelp
  19.    What it is:    Simplifies the creation of help topic files
  20.                   by converting dot commands to RTF.
  21.                   Also eliminates the need for an RTF editor
  22.                   when creating simple help files.
  23.    Compiler:      ANSI C
  24.    By:            Tom Campbell
  25.    Notes:
  26.    To compile:    Link with
  27.  
  28.                   Example using Borland C:
  29.  
  30.                     bcc mh.c
  31.  
  32.                   Example using Microsoft C:
  33.  
  34.                     cl mh.c
  35.  
  36.  
  37.    To test:
  38.  
  39.    To run:
  40.  
  41.    Copyright      1993 by Tom Campbell.  All rights reserved.
  42.    ========================================================== */
  43.  
  44. #include <stdio.h>
  45. #include <stdlib.h>
  46. #include <string.h>
  47. #include <ctype.h>
  48.  
  49. /* ==========================================================
  50.    TYPE DECLARATIONS
  51.    ========================================================== */
  52.  
  53. typedef int BOOL;
  54. typedef unsigned WORD;
  55.  
  56. /* ==========================================================
  57.    FUNCTION DECLARATIONS
  58.    ========================================================== */
  59.  
  60.   void AddExtension(char *str, char *ext);
  61.    int CreateHelpFromMetascript(char *InFilename,
  62.      char *OutFilename, BOOL MakeTopicKeyword);
  63.   void CreateKeyword(FILE *OutFile, char *Keyword, int Line);
  64.   void CreateTitle(FILE *OutFile, char *Title, int Line);
  65.   void Error(int Line, char *Msg);
  66.   int HasExtension(char *Filename);
  67.   int Parse(char *Input, char *Separators, WORD StartAt,
  68.              BOOL ForceUpper, WORD MaxLen,
  69.              char *Buffer);
  70.   int PossibleLinkInLine(FILE *OutFile, char *NextLine,
  71.     WORD StartAt, int LineNo);
  72.   void Quit(char *Msg, int ErrorLevel);
  73.   void ReplaceExtension(char *Filename, char *Ext);
  74.   int StripExtension(char *input);
  75.   int UpStr(char *Str);
  76.   void WriteChars(FILE *OutFile, char *Line, int Count,
  77.    int LineNo);
  78.   void WriteLine(FILE *OutFile, char *Line, int LineNo);
  79.  
  80. /* ==========================================================
  81.    CONSTANTS
  82.    ========================================================== */
  83.  
  84. #define FALSE 0
  85. #define TRUE (!FALSE)
  86. #define MAX_LINE 500
  87. #define MAX_FILENAME 140
  88.  
  89.  
  90. /* ==========================================================
  91.    AddExtension()
  92.    ========================================================== */
  93.  
  94.   void AddExtension(char *str, char *ext)
  95.  
  96.   /* ==========================================================
  97.      What it does:
  98.        Adds the ext to str if str doesn't already have an extension.
  99.        A period by itself counts as an extension.
  100.        Samples:
  101.          AddExtension("foobar", "bak")  == "foobar.bak"
  102.          AddExtension("combat.", "lib") == "combat."
  103.          AddExtension("vid.obj", ".bak"). == "vid.obj"
  104.  
  105.      Parameters:
  106.        str : Filename w/out extension.
  107.  
  108.        ext : Desired extension.
  109.  
  110.      Returns:
  111.        TRUE on success.
  112.        FALSE on failure.
  113.  
  114.      Before calling:
  115.        The file must be opened with dbfUse().
  116.  
  117.      Standard #include files required:
  118.        string.h
  119.  
  120.      See also:
  121.        HasExtension(), ReplaceExtension()
  122.  
  123.      Example:
  124.        char request[80], response[80];
  125.        printf("\n\nTesting AddExtension().  Please enter a string or \"q\" to quit. ");
  126.        gets(request);
  127.        printf("\nAdd what extension? ");
  128.        gets(response);
  129.        AddExtension(request, response);
  130.        printf("\nResult: \"%s\"", request);
  131.  
  132.  
  133.      Copyright 1992 by Tom Campbell.  All rights reserved.
  134.      ======================================================== */
  135.   {
  136.  
  137.     char *index;
  138.     index = strchr(str, '.');   /* Is there a period in the filename? */
  139.     if (index == NULL) {        /* No. It's safe to add an extension. */
  140.       str = strcat(str, ".");   /* Append a period, */
  141.       str = strcat(str, ext);   /* then then extension. */
  142.     }
  143.     else {                      /* Yes, there was an extension. Write over */
  144.       /* str = str + (index-str); */
  145.       strcpy(++index, ext);
  146.     }
  147.  
  148.   } /* AddExtension() */
  149.  
  150. /* ========================================================== */
  151.    void Error(int Line, char *Msg)
  152. /* ========================================================== */
  153.  
  154. /* ==========================================================
  155.    What it does:
  156.      Displays the line number and the provided error message.
  157.  
  158.      If the line number is 0, doesn't print it (used in a
  159.      case where it's irrelevant, such as not being able to
  160.      create the output file).
  161.  
  162.    Parameters:
  163.      Line : Line number error occurred on.
  164.  
  165.      Msg : Text of error message.
  166.  
  167.    Copyright 1993 by Tom Campbell.  All rights reserved.
  168.    ======================================================== */
  169. {
  170.    if (Error)
  171.      printf("Error on line %d: %s\n", Line, Msg);
  172.    else
  173.      printf("Error: %s\n", Msg);
  174.  
  175. } /* Error() */
  176.  
  177.  
  178. /* ========================================================== */
  179.    int HasExtension(char *Filename)
  180. /* ========================================================== */
  181.  
  182. /* ==========================================================
  183.    What it does:
  184.      Determines whether the specified DOS filename has an
  185.      extension.
  186.  
  187.    Parameters:
  188.      *Filename : File specification, possibly with an
  189.      extension.
  190.  
  191.    Returns:
  192.      Character position in array of extension if present.
  193.      0 if not.
  194.  
  195.    Copyright 1993 by Tom Campbell.  All rights reserved.
  196.    ======================================================== */
  197. {
  198.   int LastPossible, Length;
  199.   Length = strlen(Filename);
  200.   if (Length < 2)
  201.     return 0;
  202.   LastPossible = Length-1;
  203.   while(LastPossible) {
  204.     if (Filename[LastPossible] == '.')
  205.       return LastPossible;
  206.     else
  207.       LastPossible--;
  208.   }
  209.   return 0;
  210. } /* HasExtension() */
  211.  
  212. /* ==========================================================
  213.    Parse()
  214.    ========================================================== */
  215.  
  216.   int Parse(char *Input, char *Separators, WORD StartAt,
  217.              BOOL ForceUpper, WORD MaxLen,
  218.              char *Buffer)
  219.  
  220.   /* ==========================================================
  221.      What it does:
  222.        Copies the next word or token or whatever from Input and
  223.        copies it into Buffer.
  224.  
  225.      Parameters:
  226.        Input : String to be separators into tokens.
  227.  
  228.        Separators : Characters to be used as token delimiters.
  229.  
  230.        StartAt : Index of character at which to start parsing.
  231.  
  232.        ForceUpper : If TRUE, forces the output (which is copied
  233.        to Buffer) to uppercase.
  234.  
  235.        MaxLen : Maximum # of chars to be copied into Buffer.
  236.  
  237.        Buffer : Buffer at least MaxLen chars wide to hold the
  238.        output.
  239.  
  240.      Returns:
  241.        Character position in string after the routine
  242.        was called.
  243.  
  244.        0 if had to return due to error condition, such as a
  245.        separator or input string of length 0.
  246.  
  247.      Standard #include files required:
  248.        string.h
  249.        ctype.h
  250.  
  251.      Example:
  252.        void main(void)
  253.        {
  254.  
  255.          #define MAX_LEN 100
  256.          int EndedAt;
  257.          char Input[MAX_LEN];
  258.          int Len;
  259.          char Separators[MAX_LEN];
  260.          WORD StartAt = 0;
  261.          BOOL ForceUpper = FALSE;
  262.          WORD MaxLen = MAX_LEN;
  263.          #define MAX_TOKEN 5
  264.          char Buffer[MAX_TOKEN + 1];
  265.  
  266.          do {
  267.            Input[0] = '\0';
  268.            printf("String to parse: ");
  269.            gets(Input);
  270.            if (Input[0]) {
  271.              printf("Separators: ");
  272.              gets(Separators);
  273.              StartAt = 0;
  274.              do {
  275.                Len = strlen(Input);
  276.                EndedAt = Parse(Input, Separators, StartAt,
  277.                  ForceUpper, MAX_TOKEN, Buffer);
  278.                printf(
  279.                  "Parsed string: \"%s\"\nEnded parse at: %d\n",
  280.                  Buffer, EndedAt);
  281.                StartAt = EndedAt;
  282.              } while(EndedAt < Len);
  283.              puts("");
  284.            }
  285.  
  286.  
  287.          } while (Input[0] != '\0');
  288.        }
  289.  
  290.      Copyright 1992 by Tom Campbell.  All rights reserved.
  291.      ======================================================== */
  292.   {
  293.     /* Character currently being processed. */
  294.     int Ch;
  295.  
  296.     /* # of chars in return value. */
  297.     WORD Count = 0;
  298.  
  299.     /* Loop counter. */
  300.     int Index;
  301.  
  302.     /* Length of input string.  */
  303.     int TextLen = strlen(Input);
  304.  
  305.     /* Position in Input of any separator character(s). */
  306.     char *SepPos;
  307.  
  308.     /* Exit immediately if there's no input, or there are no
  309.        separators specified, or if the position to start at
  310.        is outside the string. */
  311.     if ( (!&Input[StartAt]) || (!Separators) || (StartAt > TextLen) )
  312.       return 0;
  313.  
  314.     /* Loop through each char of the input string. */
  315.     for (Index = StartAt; Index < TextLen; Index++) {
  316.  
  317.       /* Quit if reached the maximum length for the output
  318.          token. */
  319.       if (Count >= MaxLen) {
  320.  
  321.         /* Make this a C string. */
  322.         Buffer[Count] = '\0';
  323.  
  324.         /* Return the current position in the string. */
  325.         return Index;
  326.  
  327.       }
  328.  
  329.       /* Get the next character from the input. */
  330.       Ch = Input[Index];
  331.  
  332.       /* Force to uppercase if necessary. */
  333.       if (ForceUpper)
  334.         Ch = toupper(Ch);
  335.  
  336.       /* See if this is a separator. */
  337.       SepPos = strchr(Separators, Ch);
  338.  
  339.       /* Remove separators. */
  340.       if (SepPos) {
  341.  
  342.         /* If here, you're on trailing separators. That means
  343.            it's time to quit. */
  344.         if (Count > 0) {
  345.  
  346.           /* Make this a C string. */
  347.           Buffer[Count] = '\0';
  348.  
  349.           /* Return the current position int the string. */
  350.           return Index;
  351.  
  352.         }  /* if Count */
  353.  
  354.       } /* if SepPos */
  355.  
  356.       else {
  357.         /* Add to string if not a separator. */
  358.  
  359.         /* Make it a C string. */
  360.         Buffer[Count] = Ch;
  361.  
  362.         /* Track # of chars in string. */
  363.         Count++;
  364.  
  365.       } /* if SepPos */
  366.  
  367.     } /* for Index */
  368.  
  369.    /* Make this a C string. */
  370.    Buffer[Count] = '\0';
  371.  
  372.    /* Return position in input string. */
  373.    return Index;
  374.  
  375.   } /* Parse() */
  376.  
  377.  
  378. /* ========================================================== */
  379.    void Quit(char *Msg, int ErrorLevel)
  380. /* ========================================================== */
  381.  
  382. /* ==========================================================
  383.    What it does:
  384.      Displays the provided error message and exits to DOS,
  385.      setting the supplied error code.
  386.  
  387.    Parameters:
  388.      Msg : Text of error message.
  389.  
  390.      ErrorLevel : ERRORLEVEL is set to this value.
  391.  
  392.    Copyright 1993 by Tom Campbell.  All rights reserved.
  393.    ======================================================== */
  394. {
  395.   puts(Msg);
  396.   exit(ErrorLevel);
  397.  
  398. } /* Quit() */
  399.  
  400.  
  401. /* ==========================================================
  402.    ReplaceExtension()
  403.    ========================================================== */
  404.  
  405.   void ReplaceExtension(char *Filename, char *Ext)
  406.  
  407.   /* ==========================================================
  408.      What it does:
  409.        Replaces the extension of Filename.
  410.  
  411.      Parameters:
  412.        DBF : Pointer to information for the .DBF file.
  413.  
  414.      Returns:
  415.        TRUE on success.
  416.        FALSE on failure.
  417.  
  418.      Before calling:
  419.        The file must be opened with dbfUse().
  420.  
  421.      Example:
  422.       if (!HasExtension(DBF->FullPathNameStr))
  423.         ReplaceExtension(DBF->FullPathNameStr, "DBF");
  424.  
  425.      See also:
  426.        AddExtension(), HasExtension()
  427.  
  428.      Copyright 1992 by Tom Campbell.  All rights reserved.
  429.      ======================================================== */
  430.   {
  431.  
  432.     StripExtension(Filename);
  433.     AddExtension(Filename, Ext);
  434.  
  435.   } /* ReplaceExtension() */
  436.  
  437.  
  438. /* ==========================================================
  439.    StripExtension()
  440.    ========================================================== */
  441.  
  442.   int StripExtension(char *input)
  443.  
  444.   /* ==========================================================
  445.      What it does:
  446.       If input ends in a dot and up to 3 letters, returns it
  447.       without the dot and letters.
  448.  
  449.      Parameters:
  450.        input : Filename, such as "ACCOUNTS.DBF".
  451.  
  452.      Returns:
  453.        -1 and new string in intput if a change was made.
  454.        0  and input if no change was made.
  455.  
  456.      Standard #include files required:
  457.        string.h
  458.  
  459.      See also:
  460.        HasExtension(), ReplaceExtension()
  461.  
  462.      Example 1.
  463.        if(StripExtension(response))
  464.          printf("\nResult: \"%s\"", response);
  465.        else
  466.          puts("No change made.");
  467.  
  468.      Example 2.
  469.        void main(void)
  470.        {
  471.          int EachChar;
  472.  
  473.          char Input[100], Output[100];
  474.  
  475.          int Changed;
  476.  
  477.          do {
  478.            Input[0] = '\0';
  479.  
  480.            printf("Please enter a filename, or ");
  481.            printf("[Enter by itself to quit\n> ");
  482.            fflush(stdin);
  483.            gets(Input);
  484.            if (Input[0] == '\0')
  485.              exit(0);
  486.            Changed = StripExtension(Input);
  487.            if (Changed)
  488.              printf("Extension was removed. ");
  489.              printf("String is now:\n\n\t%s\n", Input);
  490.            else
  491.              puts("String had no extension and was unchanged.");
  492.  
  493.          } while( (Input[0] != '\0') ||
  494.                   (!stricmp(Input, "quit")) );
  495.        }
  496.  
  497.      Notes:
  498.        Even handles (correctly) such pathologically weird
  499.        input as "a.b\c", in which the period is not an
  500.        extension and should therefore not be removed.
  501.  
  502.      Copyright 1992 by Tom Campbell.  All rights reserved.
  503.      ======================================================== */
  504.   {
  505.     /* If input has an extension, this points to it. */
  506.     char *dot;
  507.  
  508.     /* If input has one or more backslashes, this is the one
  509.        closes to the end. */
  510.     char *backslash;
  511.  
  512.     /* Length of input string. */
  513.     int inlen;
  514.  
  515.     /* Where to start looking in input for the dot. */
  516.     char *start;
  517.  
  518.     /* We have to start at the end of the string, due to the remote
  519.        possibility of a pathname whose directory entries contain
  520.        extensions. */
  521.  
  522.     /* Length of input. */
  523.     inlen = strlen(input);
  524.     /* Dot must be in final 4 chars. */
  525.     inlen -= 4;
  526.     /* Point at last chars of string. */
  527.     start = input + inlen;
  528.  
  529.     /* Search for last dot in the input. */
  530.     dot = strrchr(start, '.');
  531.  
  532.     /* Search for the last backslash.  */
  533.     backslash = strrchr(start, '\\');
  534.  
  535.     /* If it's after the dot, there's no extension. */
  536.     if (dot)
  537.       if (backslash > dot)
  538.         /* Despite the presence of both backslash and dot, exit
  539.            doing nothing because the backslash follows the
  540.            dot. */
  541.         return 0;
  542.  
  543.     /* Extension found at end of string. */
  544.     if (dot != NULL) {
  545.       dot[0] = '\0';
  546.       /* strcpy(output, input); */
  547.       /* Return with success code. */
  548.       return -1;
  549.     }
  550.  
  551.     /* No extension found. */
  552.     else
  553.       /* Quit with failure code. */
  554.       return 0;
  555.  
  556.   } /* StripExtension() */
  557.  
  558. /* ==========================================================
  559.    UpStr()
  560.    ========================================================== */
  561.  
  562.   int UpStr(char *Str)
  563.  
  564.   /* ==========================================================
  565.      What it does:
  566.        Forces string *Str to uppercase.
  567.  
  568.      Parameters:
  569.        *Str : String to force to uppercase.
  570.  
  571.      Returns:
  572.        # of characters in string.
  573.  
  574.      Standard #include files required:
  575.        ctype.h, string.h
  576.  
  577.      Example:
  578.        if (Input[0]) {
  579.          Len = UpStr(Input);
  580.          printf("Converted %d chars to string: \"%s\"\n",
  581.            Len, Input);
  582.        }
  583.  
  584.      Copyright 1992 by Tom Campbell.  All rights reserved.
  585.      ======================================================== */
  586.   {
  587.     /* Loop counter. */
  588.     int EachChar;
  589.  
  590.     /* String length value. */
  591.     int Len;
  592.  
  593.     /* Get length of string to convert. */
  594.     Len = strlen(Str);
  595.  
  596.     /* Loop through each character of the string, converting
  597.        to uppercase. */
  598.     for (EachChar = 0; EachChar < Len; EachChar++)
  599.       Str[EachChar] = toupper(Str[EachChar]);
  600.  
  601.     /* Add the terminating 0. */
  602.     Str[Len] = '\0';
  603.  
  604.     /* # of characters in string. */
  605.     return Len;
  606.  
  607.   } /* UpStr() */
  608.  
  609.  
  610.  
  611. /* ========================================================== */
  612.    void WriteChars(FILE *OutFile, char *Line, int Count,
  613.    int LineNo)
  614. /* ========================================================== */
  615.  
  616. /* ==========================================================
  617.    What it does:
  618.      Writes the the specified number of characters to
  619.      the output file.  Does not look for a terminating
  620.      0, as does WriteLine().
  621.  
  622.      If unable to, exits to OS with an appropriate
  623.      error message.
  624.  
  625.    Parameters:
  626.      OutFile : Descriptor of file open for output.
  627.  
  628.      Count : # of chars to write.
  629.  
  630.      Line : Text to write.
  631.  
  632.      LineNo : Current line number in OutFile.
  633.  
  634.    Before calling:
  635.      Open OutFile with read/write access in text mode.
  636.  
  637.    Standard #include files required:
  638.      stdio.h
  639.  
  640.    Copyright 1993 by Tom Campbell.  All rights reserved.
  641.    ======================================================== */
  642. {
  643.  
  644.   if (fwrite(Line, Count, 1, OutFile) != 1) {
  645.     Error(LineNo, "Unable to write to output file.");
  646.     Quit("", 1);
  647.   }
  648.  
  649. } /* WriteChars() */
  650.  
  651.  
  652. /* ========================================================== */
  653.    void WriteLine(FILE *OutFile, char *Line, int LineNo)
  654. /* ========================================================== */
  655.  
  656. /* ==========================================================
  657.    What it does:
  658.      Writes the supplied line to the supplied file.
  659.      If unable to, exits to OS with an appropriate
  660.      error message.
  661.  
  662.    Parameters:
  663.      OutFile : Descriptor of file open for output.
  664.  
  665.      Line : Text to write.
  666.  
  667.      LineNo : Current line number in OutFile.
  668.  
  669.    Before calling:
  670.      Open OutFile with read/write access in text mode.
  671.  
  672.    Standard #include files required:
  673.      stdio.h
  674.  
  675.    Copyright 1993 by Tom Campbell.  All rights reserved.
  676.    ======================================================== */
  677. {
  678.  
  679.   if (fwrite(Line, strlen(Line), 1, OutFile) != 1) {
  680.     Error(LineNo, "Unable to write to output file.");
  681.     Quit("", 1);
  682.   }
  683.  
  684. } /* WriteLine() */
  685.  
  686.  
  687. /* ========================================================== */
  688.    void CreateKeyword(FILE *OutFile, char *Keyword, int Line)
  689. /* ========================================================== */
  690.  
  691. /* ==========================================================
  692.    What it does:
  693.      Creates a keyword topic in the RTF file.
  694.  
  695.      Ends with a newline for clarity.
  696.  
  697.    Parameters:
  698.      OutFile : RTF output file.
  699.  
  700.      *Keyword : String with keyword entry.
  701.  
  702.      Line : Line number of input source file.
  703.  
  704.    Before calling:
  705.      Open *OutFile.
  706.  
  707.    Standard #include files required:
  708.      stdio.h
  709.  
  710.    See also:
  711.      CreateTitle()
  712.  
  713.    Copyright 1993 by Tom Campbell.  All rights reserved.
  714.    ======================================================== */
  715. {
  716.   WriteLine(OutFile, "K{\\footnote ", Line);
  717.   WriteLine(OutFile, Keyword, Line);
  718.   WriteLine(OutFile, "}\n", Line);
  719. } /* CreateKeyword() */
  720.  
  721.  
  722. /* ========================================================== */
  723.    void CreateLink(FILE *OutFile, char *LinkName, char *LinkText, int Line)
  724. /* ========================================================== */
  725.  
  726. /* ==========================================================
  727.    What it does:
  728.      Creates a link in the RTF file.
  729.  
  730.    Parameters:
  731.      OutFile : RTF output file.
  732.  
  733.      *LinkName: String with link name.
  734.  
  735.      *LinkText : String with link text.
  736.  
  737.      Line : Line number of input source file.
  738.  
  739.    Before calling:
  740.      Open *OutFile.
  741.  
  742.    Standard #include files required:
  743.      stdio.h
  744.  
  745.    See also:
  746.      CreateTitle()
  747.  
  748.    Copyright 1993 by Tom Campbell.  All rights reserved.
  749.    ======================================================== */
  750. {
  751.   /* This will be used to see if the last char on the line
  752.      is a space. */
  753.   int Len = strlen(LinkText);
  754.  
  755.   /* Flags whether a trailing space need be added. */
  756.   BOOL PadEnd;
  757.   if (Len)
  758.     Len--;
  759.   else
  760.     /* XXX Length of 0 is an error! */
  761.     return;
  762.  
  763.   /* Special case: A space after the first quote is replaced
  764.      by a space *preceding* the link. */
  765.   if (LinkText[0] == ' ') {
  766.     WriteLine(OutFile, " ", Line);
  767.     LinkText++;
  768.     Len--;
  769.   }
  770.  
  771.   /* Likewise for a trailing space. */
  772.   if (LinkText[Len] == ' ') {
  773.     PadEnd = TRUE;
  774.     LinkText[Len] = '\0';
  775.   }
  776.  
  777.   WriteLine(OutFile, "{\\uldb ", Line);
  778.   WriteLine(OutFile, LinkText, Line);
  779.   WriteLine(OutFile, "}", Line);
  780.  
  781.   WriteLine(OutFile, "{\\v ", Line);
  782.   WriteLine(OutFile, LinkName, Line);
  783.   WriteLine(OutFile, "}", Line);
  784.  
  785.   if (PadEnd)
  786.     WriteLine(OutFile, " ", Line);
  787.  
  788. } /* CreateLink() */
  789.  
  790.  
  791. /* ========================================================== */
  792.   void CreateTitle(FILE *OutFile, char *Title, int Line)
  793. /* ========================================================== */
  794.  
  795. /* ==========================================================
  796.    What it does:
  797.      Creates a title entry in the RTF file.
  798.  
  799.      Ends with a newline for clarity.
  800.  
  801.    Parameters:
  802.      OutFile : RTF output file.
  803.  
  804.      *Title : String with title name.
  805.  
  806.      Line : Line number of input source file.
  807.  
  808.    Before calling:
  809.      Open *OutFile.
  810.  
  811.    See also:
  812.      CreateKeyword()
  813.  
  814.    Copyright 1993 by Tom Campbell.  All rights reserved.
  815.    ======================================================== */
  816. {
  817.   WriteLine(OutFile, "${\\footnote ", Line);
  818.   WriteLine(OutFile, Title, Line);
  819.   WriteLine(OutFile, "}\n", Line);
  820. } /* CreateTitle() */
  821.  
  822.  
  823. /* ========================================================== */
  824.    int CreateHelpFromMetascript(char *InFilename,
  825.      char *OutFilename, BOOL MakeTopicKeyword)
  826. /* ========================================================== */
  827.  
  828. /* ==========================================================
  829.    What it does:
  830.      Script-driven routine that takes a CHELP script and
  831.      and creates a help source file for the Microsoft Windows
  832.      HC.EXE compiler from the specified filename.
  833.  
  834.    Parameters:
  835.      InFilename : Name of script file.
  836.  
  837.      OutFilename : Name of file to create.
  838.  
  839.      MakeTopicKeyword : If TRUE (nonzero), automatically create
  840.      a keyword entry for teach topic.
  841.  
  842.    Returns:
  843.      Nonzero on success.
  844.      Zero on failure.
  845.  
  846.    Example:
  847.      XXX
  848.  
  849.    Notes:
  850.      XXX
  851.  
  852.    Copyright 1993 by Tom Campbell.  All rights reserved.
  853.    ======================================================== */
  854. {
  855.  
  856.   /* Position on line in the specified char was found. */
  857.   int CharIndex;
  858.  
  859.   /* Used for the .keywords dot command to separate the
  860.      remainder of the line (after the .keyword dot command)
  861.      into tokens. */
  862.   int EndedAt;
  863.  
  864.   /* Copy of the first token, forced to uppercase. */
  865.   char FirstToken[MAX_LINE+1];
  866.  
  867.   /* Used in search for a particular char on the line
  868.      (such as { for links).  */
  869.   char *FoundChar;
  870.  
  871.   /* Descriptor for script file. */
  872.   FILE *InFile = NULL;
  873.  
  874.   /* Each topic must end with a \page statement. If the .TOPIC
  875.      dot command is found and InTopic is TRUE, it means a topic
  876.      had been started and it's time to close it off with a
  877.      \page. Ditto for a file with only 1 topic and EOF is
  878.      reached. */
  879.   BOOL InTopic = FALSE;
  880.  
  881.   /* # of line being processed. */
  882.   int Line = 0;
  883.  
  884.   /* # of chars in line. */
  885.   int LineLen;
  886.  
  887.   /* The symbolic name of the link itself, used in the help
  888.      compiler's symbol table. */
  889.   char LinkName[MAX_LINE+1];
  890.  
  891.   /* The text shown in place of the link; in quotes after the
  892.      .LINK's link name. */
  893.   char LinkText[MAX_LINE+1];
  894.  
  895.   /* Buffer for each line read from the script file. */
  896.   char NextLine[MAX_LINE+1];
  897.  
  898.   /* Successive tokens after the first one. */
  899.   char NextToken[MAX_LINE+1];
  900.  
  901.   /* Descriptor for file created by this program. */
  902.   FILE *OutFile = NULL;
  903.  
  904.   /* Token delimiters. */
  905.   /* char Separators[] = " ,\t\n-/:"; */
  906.   char Separators[] = " \n";
  907.  
  908.   /* Starting position of next token in the line. */
  909.   int StartNext;
  910.  
  911.   /* Each topic should have a title (if it doesn't, HC
  912.      creates a warning and puts an "<<Unititled topic>>"
  913.      in the .HLP output file). */
  914.   BOOL TopicHasTitle = FALSE;
  915.  
  916.   /* Name of topic is used instead of FirstToken because
  917.      it might also be used to generate a title. */
  918.   char TopicName[MAX_LINE+1];
  919.  
  920.   /* Open the script file in text mode, read-only access. */
  921.   InFile = fopen(InFilename, "r");
  922.   if (!InFile) {
  923.     puts("XXX Unable to open script file.");
  924.     return 0;
  925.   }
  926.  
  927.   /* Create the output file in text mode, read/write access. */
  928.   OutFile = fopen(OutFilename, "wt");
  929.   if (!OutFile) {
  930.     Error(0, "Unable to create output file");
  931.     Quit("", 1);
  932.   }
  933.  
  934.   /* Create the prolog. */
  935.   Line++;
  936.   WriteLine(OutFile, "{\\rtf1\\ansi \\deff2\n", Line);
  937.   Line++;
  938.   WriteLine(OutFile, "{\\fonttbl\n", Line);
  939.   Line++;
  940.   WriteLine(OutFile, "{\\f0\\froman Times New Roman;}\n", Line);
  941.   Line++;
  942.   WriteLine(OutFile, "{\\f1\\fdecor Courier New;}\n", Line);
  943.   Line++;
  944.   WriteLine(OutFile, "{\\f2\\fswiss Arial;}\n", Line);
  945.   Line++;
  946.   WriteLine(OutFile, "}\n", Line);
  947.  
  948. /* ==========================================================
  949.    MAIN LOOP
  950.    ========================================================== */
  951.  
  952.   while (1) {
  953.  
  954.     /* Copy the next line of the file into NextLine. */
  955.     if (fgets(NextLine, MAX_LINE, InFile)== NULL)
  956.       break;
  957.  
  958.     /* Track line number of source file. */
  959.     Line++;
  960.  
  961. /* ==========================================================
  962.     Force a copy of the first token to uppercase
  963.    ========================================================== */
  964.  
  965.   /* Get the first token from NextLine.
  966.      Use the characters defined in Separators as token separators.
  967.      Start at position 0.
  968.      The 1 means force to uppercase.
  969.      Copy only MAX_LINE or less characters to the output buffer,
  970.      which is FirstToken.
  971.  
  972.      Write the position of the character after the first token
  973.      in StartNext. */
  974.   StartNext = Parse(NextLine, Separators, 0, 1, MAX_LINE, FirstToken);
  975.  
  976.   /* Don't process if it's a comment line. */
  977.   if (FirstToken[0] == ';') {
  978.     continue;
  979.   }
  980.  
  981. /* ==========================================================
  982.    Link
  983.  
  984.    Example:
  985.  
  986.      Try the
  987.      .link EditMenu "Edit Menu"
  988.      for more information.
  989.  
  990.    Generates this text in the output file:
  991.  
  992.      Try the {\uldb Edit Menu}{\v EditMenu} for more information.
  993.  
  994.    ========================================================== */
  995.  
  996.     if (!strcmp(FirstToken, ".LINK")) {
  997.  
  998.       /* Get the link name. */
  999.       StartNext = Parse(NextLine, Separators, StartNext, 0,
  1000.         MAX_LINE, NextToken);
  1001.  
  1002.       /* Get the quoted link text. */
  1003.       StartNext = Parse(NextLine, "\"", StartNext+1, 0,
  1004.         MAX_LINE, LinkText);
  1005.  
  1006.       CreateLink(OutFile, NextToken, LinkText, Line);
  1007.       /* Go to top of loop. */
  1008.       continue;
  1009.  
  1010.     }
  1011.  
  1012. /* ==========================================================
  1013.    Topic
  1014.  
  1015.    Example:
  1016.  
  1017.      .topic MenuEdit
  1018.  
  1019.    Generates this text in the output file, followed by
  1020.    a newline:
  1021.  
  1022.       #{\footnote MenuEdit}
  1023.  
  1024.    ========================================================== */
  1025.  
  1026.     if (!strcmp(FirstToken, ".TOPIC")) {
  1027.  
  1028.       /* Write out the \page if we were in a topic already;
  1029.          this means we're starting a new one. */
  1030.       if (InTopic) {
  1031.         WriteLine(OutFile, "\\page\n", Line);
  1032.         Line++;
  1033.       }
  1034.  
  1035.       /* Note that we're in a new topic. */
  1036.       InTopic = TRUE;
  1037.  
  1038.       /* Each topic should have a title. The topic
  1039.          text will be used to generate one if the
  1040.          .topic dot command doesn't show up. */
  1041.       TopicHasTitle = FALSE;
  1042.  
  1043.       /* Get the topic name. */
  1044.       StartNext = Parse(NextLine, Separators, StartNext, 0,
  1045.         MAX_LINE, TopicName);
  1046.  
  1047.       /* Write out the topic flag, a # footnote.
  1048.          Also write out a newline for clarity. */
  1049.       WriteLine(OutFile, "#{\\footnote ", Line);
  1050.       WriteLine(OutFile, TopicName, Line);
  1051.       WriteLine(OutFile, "}\n", Line);
  1052.       Line++;
  1053.  
  1054.       /* If desired, automatically create a keyword for this
  1055.          topic. */
  1056.       if (MakeTopicKeyword) {
  1057.         CreateKeyword(OutFile, TopicName, Line);
  1058.         Line++;
  1059.       }
  1060.  
  1061.       /* Go to top of loop. */
  1062.       continue;
  1063.  
  1064.     }
  1065.  
  1066. /* ==========================================================
  1067.    Title
  1068.  
  1069.    Example:
  1070.  
  1071.      .title Edit Menu
  1072.  
  1073.    Generates this text in the output file, followed by
  1074.    a newline:
  1075.  
  1076.       ${\footnote Edit Menu}
  1077.  
  1078.    ========================================================== */
  1079.  
  1080.     if (!strcmp(FirstToken, ".TITLE")) {
  1081.  
  1082.       /* Note that this topic has a title. */
  1083.       TopicHasTitle = TRUE;
  1084.  
  1085.       WriteLine(OutFile, "${\\footnote ", Line);
  1086.       WriteLine(OutFile, &NextLine[++StartNext], Line);
  1087.       WriteLine(OutFile, "}\n", Line);
  1088.       Line++;
  1089.  
  1090.       /* Go to top of loop. */
  1091.       continue;
  1092.  
  1093.     }
  1094.  
  1095. /* ==========================================================
  1096.    Keywords
  1097.      They can have spaces. Separated by semicolons.
  1098.  
  1099.    Example:
  1100.  
  1101.      .Keyword Search; Find and Replace; Look for
  1102.  
  1103.    ========================================================== */
  1104.     if (!strcmp(FirstToken, ".KEYWORD") || !strcmp(FirstToken, ".KEYWORDS")) {
  1105.  
  1106.       LineLen = strlen(NextLine) - StartNext;
  1107.  
  1108.       /* Loop for all keywords on the line. */
  1109.       do {
  1110.         EndedAt = Parse(NextLine, ";\n", StartNext,
  1111.           0, MAX_LINE, NextToken);
  1112.         CreateKeyword(OutFile, NextToken, Line);
  1113.         Line++;
  1114.         StartNext = EndedAt;
  1115.       } while(EndedAt < LineLen);
  1116.  
  1117.       /* Go to top of loop. */
  1118.       continue;
  1119.  
  1120.     }
  1121.  
  1122. /* ==========================================================
  1123.    Blank line
  1124.  
  1125.    Blank lines are significant in the source files; they're
  1126.    turned into RTF newline (\par) commands.
  1127.  
  1128.    ========================================================== */
  1129.     if (!strcmp(NextLine, "\n")) {
  1130.  
  1131.       WriteLine(OutFile, "\\par\n", Line);
  1132.       Line++;
  1133.       /*Go to top of loop. */
  1134.       continue;
  1135.  
  1136.     }
  1137.  
  1138. /* ==========================================================
  1139.    FontSize
  1140.  
  1141.    Example:
  1142.  
  1143.      .FontSize 10
  1144.  
  1145.    ========================================================== */
  1146.  
  1147.     if (!strcmp(FirstToken, ".FONTSIZE")) {
  1148.  
  1149.       /* Converted from ASCII. Note that the /fs RTF commmand
  1150.          is in half points, so we double the value given. */
  1151.       int FontSize;
  1152.  
  1153.       /* Get the font size specified. */
  1154.       StartNext = Parse(NextLine, Separators, StartNext, 0,
  1155.         MAX_LINE, NextToken);
  1156.  
  1157.       sscanf(NextToken, "%d", &FontSize);
  1158.       FontSize *= 2;
  1159.       sprintf(NextToken, "\n\\fs%d\n", FontSize);
  1160.       WriteLine(OutFile, NextToken, Line);
  1161.  
  1162.       Line++;
  1163.  
  1164.       /* Go to top of loop. */
  1165.       continue;
  1166.  
  1167.     }
  1168.  
  1169.  
  1170. /* ==========================================================
  1171.    Links
  1172.    ========================================================== */
  1173.   FoundChar = strchr(NextLine, '{');
  1174.  
  1175.   /* Found the opening { for a link. */
  1176.   if (FoundChar) {
  1177.  
  1178.     /* Position on which { was found. */
  1179.     CharIndex = FoundChar - NextLine;
  1180.  
  1181.     /* There may be a link on this line. If at least one, returns
  1182.        TRUE. If not, returns FALSE, so drop through to the
  1183.        default action. */
  1184.     if (PossibleLinkInLine(OutFile, NextLine, CharIndex, Line))
  1185.      /* Go to top of loop. */
  1186.      continue;
  1187.  
  1188.   }
  1189.  
  1190. /* ==========================================================
  1191.    Default: Write out the line as is.
  1192.    If there wasn't a .title dot command for this topic,
  1193.    generate it from the topic name.
  1194.    ========================================================== */
  1195.     if (TopicHasTitle == FALSE) {
  1196.  
  1197.       CreateTitle(OutFile, TopicName, Line);
  1198.       Line++;
  1199.  
  1200.       /* But do this only once. */
  1201.       TopicHasTitle = TRUE;
  1202.     }
  1203.  
  1204.     WriteLine(OutFile, NextLine, Line);
  1205.     Line++;
  1206.  
  1207.   } /* while */
  1208.  
  1209. /* ==========================================================
  1210.    END OF TOPIC FILE
  1211.    Write out the end-of-topic \page marker.
  1212.    ========================================================== */
  1213.  
  1214.   /* Write out the \page if we were in a topic. */
  1215.   if (InTopic) {
  1216.     WriteLine(OutFile, "\\page", Line);
  1217.     Line++;
  1218.   }
  1219.  
  1220.   /* The file must open with a brace and--here--close with
  1221.      a brace. */
  1222.   Line++;
  1223.   WriteLine(OutFile, "\n}", Line);
  1224.  
  1225.   fclose(InFile);
  1226.   fclose(OutFile);
  1227.  
  1228.   /* Nonzero means no error occurred. */
  1229.   return -1;
  1230.  
  1231. } /* CreateHelpFromMetascript() */
  1232.  
  1233.  
  1234. /* ========================================================== */
  1235.    int PossibleLinkInLine(FILE *OutFile, char *NextLine,
  1236.      WORD StartAt, int LineNo)
  1237. /* ========================================================== */
  1238.  
  1239. /* ==========================================================
  1240.    What it does:
  1241.      Some lines have embedded links like this:
  1242.  
  1243.        See the {main dictionary:dict1} for a defintion.
  1244.  
  1245.      This means that the text "main dictionary" will be
  1246.      highlighted, but pressing Enter on the highlighted
  1247.      text will go to a link named dict1, not
  1248.      "main dictionary". And the "dict1" doesn't appear.
  1249.  
  1250.      At this point the first { has been sighted. We don't know
  1251.      if the line contains a link or not.
  1252.  
  1253.      This is all complicated by the allowance of more than
  1254.      one link per line. Not only do we allow escaping the
  1255.      { so that it can be used, multiple links make the
  1256.      processing quite complicated. A line like this is
  1257.      suspected to have a link when this routine is called
  1258.      but actually doesn't:
  1259.  
  1260.        The curly brace (\{) starts a compound statement.
  1261.  
  1262.    Parameters:
  1263.      OutFile : RTF Help output file.
  1264.  
  1265.      NextLine : Line of text from the source file.
  1266.  
  1267.      StartAt : Position where the { was found.
  1268.  
  1269.      LineNo : Current source line number.
  1270.  
  1271.    See also:
  1272.      XXX
  1273.  
  1274.    Example:
  1275.      XXX
  1276.  
  1277.    Notes:
  1278.      XXX
  1279.  
  1280.    Copyright 1993 by Tom Campbell.  All rights reserved.
  1281.    ======================================================== */
  1282. {
  1283.  
  1284.   /* Position on line in the specified char was found. */
  1285.   int CharIndex;
  1286.  
  1287.   /* Used in search for a particular char on the line
  1288.      (such as { for links).  */
  1289.   char *FoundChar;
  1290.  
  1291.   /* Length of the entire input line, starting with position
  1292.      0, not StartAt. */
  1293.   int LineLen = strlen(NextLine);
  1294.  
  1295.   /* The symbolic name of the link itself, used in the help
  1296.      compiler's symbol table. */
  1297.   char *LinkName;
  1298.  
  1299.   /* The text shown in place of the link; in quotes after the
  1300.      .LINK's link name. */
  1301.   char *LinkText;
  1302.  
  1303.   /* Starting position of next token in the line. */
  1304.   int StartNext;
  1305.  
  1306.   /* Allocate LinkName and LinkText dynamically because this
  1307.      routine might be called recursively. */
  1308.   LinkName = malloc((MAX_LINE+1) * sizeof(char));
  1309.   if (LinkName == NULL) {
  1310.     Error(LineNo, "Out of memory.");
  1311.     Quit("", 1);
  1312.   }
  1313.  
  1314.   LinkText = malloc((MAX_LINE+1) * sizeof(char));
  1315.   if (LinkText == NULL) {
  1316.     free(LinkName);
  1317.     Error(LineNo, "Out of memory.");
  1318.     Quit("", 1);
  1319.   }
  1320.  
  1321.  
  1322.     /* Get the chars between the '{' and the ':' as in
  1323.        "LinkName" if the text of the link is
  1324.        "{LinkName:link0}". */
  1325.     StartNext = Parse(NextLine, ":", StartAt+1, 0, MAX_LINE, LinkText);
  1326.  
  1327.     /* There was an opening '{', but no ':'. */
  1328.     if (StartNext >= LineLen) {
  1329.       free(LinkText);
  1330.       free(LinkName);
  1331.       /* This line doesn't contain a valid link. */
  1332.       return FALSE;
  1333.     }
  1334.  
  1335.     /* LinkText now has the link text, which the user sees. Now
  1336.        get the symbolic link name required by the compiler.
  1337.        Force to uppercase.  */
  1338.     StartNext = Parse(NextLine, "}", StartNext+1, 1, MAX_LINE, LinkName);
  1339.  
  1340.     /* There was an opening '{', and a ':', but no closing
  1341.        '}'. */
  1342.     if (StartNext >= LineLen) {
  1343.       free(LinkText);
  1344.       free(LinkName);
  1345.       /* This line doesn't contain a valid link. */
  1346.       return FALSE;
  1347.     }
  1348.  
  1349.     /* Write any characters preceding the link to the output
  1350.        file. */
  1351.     WriteChars(OutFile, NextLine, StartAt, LineNo);
  1352.  
  1353.     /* The link name and the link text, separated by a ':' and
  1354.        enclosed in curly braces, were found. Create a link. */
  1355.     CreateLink(OutFile, LinkName, LinkText, LineNo);
  1356.     free(LinkText);
  1357.     free(LinkName);
  1358.  
  1359.       /* Skip past the closing brace of the previous
  1360.          link. */
  1361.       StartNext++;
  1362.  
  1363.       FoundChar = strchr(&NextLine[StartNext], '{');
  1364.  
  1365.       /* Found the opening { for a link. */
  1366.       if (FoundChar) {
  1367.  
  1368.         /* Position on which { was found. */
  1369.         StartAt = FoundChar - &NextLine[StartNext];
  1370.  
  1371.         PossibleLinkInLine(OutFile, &NextLine[StartNext], StartAt, LineNo);
  1372.       }
  1373.       else {
  1374.       ///
  1375.         WriteChars(OutFile, &NextLine[StartNext], LineLen-StartNext, LineNo);
  1376.         LineLen += LineLen-StartNext;
  1377.       }
  1378.  
  1379.  
  1380.     /* Yes, there was a valid link. */
  1381.     return (TRUE);
  1382.  
  1383.  
  1384. } /* PossibleLinkInLine() */
  1385.  
  1386.  
  1387. void main(int argc, char *argv[])
  1388. {
  1389.   /* Used when user enters a filename. */
  1390.   char InFilename[FILENAME_MAX] = "\0";
  1391.  
  1392.   /* Used to generate an error/success message. */
  1393.   char Msg[FILENAME_MAX*2];
  1394.  
  1395.   /* With this option, topics are automatically made into
  1396.      keywords. That lets them find and view them using
  1397.      the Search feature. Enable this feature
  1398.      with the -K option. */
  1399.   BOOL MakeTopicKeyword = FALSE;
  1400.  
  1401.   /* Name of file created by this program. */
  1402.   char OutFilename[FILENAME_MAX] = "\0";
  1403.  
  1404.   /* Continue while there are command-line switches or
  1405.      filenames. */
  1406.   while (argc-- && **argv++ && argc) {
  1407.     /* Cast what is normally a string pointer to an int, because
  1408.        C can think of a two-char value as an int. */
  1409.     switch (*( (int *) *argv )) {
  1410.       case '/k':
  1411.       case '-K':
  1412.         MakeTopicKeyword = TRUE;
  1413.         break;
  1414.       default:
  1415.         strcpy(InFilename, *argv);
  1416.         strcpy(OutFilename, *argv);
  1417.         if (!HasExtension(InFilename))
  1418.           ReplaceExtension(InFilename, "SRC");
  1419.         UpStr(InFilename);
  1420.         UpStr(OutFilename);
  1421.         ReplaceExtension(OutFilename, "RTF");
  1422.         if (!strcmp(InFilename, OutFilename)) {
  1423.           Quit("Input and output files have the same name.", 1);
  1424.         }
  1425.         break;
  1426.     } /* switch */
  1427.   } /* switch */
  1428.   if (InFilename[0] == 0)
  1429.     Quit("Need to specify an input file.", 1);
  1430.   if(CreateHelpFromMetascript(InFilename, OutFilename, MakeTopicKeyword)) {
  1431.     sprintf(Msg, "Created topic file \"%s\" successfully.", OutFilename);
  1432.     Quit(Msg, 0);
  1433.   }
  1434. } /* main() */
  1435.  
  1436.  
  1437.