home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwplascr.zip / XWPL0208.ZIP / tools / h2i / h2i.cpp next >
C/C++ Source or Header  |  2002-07-21  |  95KB  |  3,060 lines

  1.  
  2. /*
  3.  *@@sourcefile h2i.cpp:
  4.  *      the one and only source file for h2i.exe.
  5.  *
  6.  *      h2i is a replacement for HTML2IPF.CMD and now used
  7.  *      by the XWorkplace and WarpIN makefiles to convert
  8.  *      the various HTML sources in 001\ to IPF code, which
  9.  *      can then be fed into IPFC.
  10.  *
  11.  *      Compared to HTML2IPF.CMD, h2i has the following
  12.  *      advantages:
  13.  *
  14.  *      --  Even though it's fairly sloppy code, it's
  15.  *          MAGNITUDES faster.
  16.  *
  17.  *      --  It can process C include files to allow for
  18.  *          character entity references. In those include
  19.  *          files, only #define statements are evaluated.
  20.  *          This can be used for string replacements.
  21.  *
  22.  *      --  It supports IFDEF and IFNDEF blocks like this:
  23.  *
  24.  +              <IFDEF some_c_definition>
  25.  +                  Lots of text here, which can optionally
  26.  +                  be ignored.
  27.  +              </IFDEF>
  28.  *
  29.  *      --  It supports a RESID attribute to the HTML
  30.  *          tag to allow for setting a resid explicitly.
  31.  *          Together with the include facility, this
  32.  *          is very useful for the XWorkplace help file.
  33.  *
  34.  *      --  This also fixes some minor formatting bugs
  35.  *          that HTML2IPF.CMD exhibited, mostly with
  36.  *          lists and PRE sections.
  37.  *
  38.  *      Current limitations:
  39.  *
  40.  *      --  This does not automatically convert GIFs or
  41.  *          JPEGs to OS/2 1.3 BMP files, like HTML2IPF.CMD
  42.  *          does.
  43.  *
  44.  *      --  <HTML SUBLINKS> and <HTML NOSUBLINKS> are not
  45.  *          yet supported.
  46.  *
  47.  *@@header "h2i.h"
  48.  *@@added V0.9.13 (2001-06-23) [umoeller]
  49.  */
  50.  
  51. /*
  52.  *      Copyright (C) 2001-2002 Ulrich Möller.
  53.  *      This program is free software; you can redistribute it and/or modify
  54.  *      it under the terms of the GNU General Public License as published by
  55.  *      the Free Software Foundation, in version 2 as it comes in the COPYING
  56.  *      file of the XFolder main distribution.
  57.  *      This program is distributed in the hope that it will be useful,
  58.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  59.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  60.  *      GNU General Public License for more details.
  61.  */
  62.  
  63. #define OS2EMX_PLAIN_CHAR
  64.  
  65. #include <stdlib.h>
  66. #include <string.h>
  67. #include <stdio.h>
  68. #include <setjmp.h>
  69. #include <io.h>
  70.  
  71. #define INCL_DOS
  72. #define INCL_DOSERRORS
  73. #include <os2.h>
  74.  
  75. #include "setup.h"
  76. #include "bldlevel.h"
  77.  
  78. #include "helpers\dosh.h"
  79. #include "helpers\except.h"
  80. #include "helpers\linklist.h"
  81. #include "helpers\standards.h"
  82. #include "helpers\stringh.h"
  83. #include "helpers\tree.h"
  84. #include "helpers\xstring.h"
  85.  
  86. #include "h2i.h"
  87.  
  88. #pragma info (nocnv)
  89.  
  90. /* ******************************************************************
  91.  *
  92.  *   NLS strings
  93.  *
  94.  ********************************************************************/
  95.  
  96. PCSZ    G_pcszResourcesOnInternetTitle = "Resources on the Internet",
  97.         G_pcszResourcesOnInternetBody =
  98.             "This chapter contains all external links referenced in this book. "
  99.             "Each link contained herein is an Unified Resource Locator (URL) "
  100.             "to a certain location on the Internet. Simply double-click on one "
  101.             "of them to launch Netscape with the respective URL.",
  102.         G_pcszClickBelow =
  103.             "Click below to launch Netscape with this URL&colon.";
  104.  
  105. /* ******************************************************************
  106.  *
  107.  *   Private declarations
  108.  *
  109.  ********************************************************************/
  110.  
  111. /*
  112.  *@@ DEFINENODE:
  113.  *      represents a #define from a C header.
  114.  */
  115.  
  116. typedef struct _DEFINENODE
  117. {
  118.     TREE        Tree;
  119.                         // Tree.ulKey has the (PSZ) identifier
  120.     PSZ         pszValue;
  121.                         // value without quotes
  122.     ULONG       ulValueLength;
  123.                         // strlen(pszValue)
  124. } DEFINENODE, *PDEFINENODE;
  125.  
  126. /*
  127.  *@@ ARTICLETREENODE:
  128.  *      represents one article to be worked on.
  129.  *
  130.  *      This comes from one HTML file each and
  131.  *      will turn into an IPF article.
  132.  */
  133.  
  134. typedef struct _ARTICLETREENODE
  135. {
  136.     TREE        Tree;               // Tree.ulKey has the (PSZ) filename
  137.  
  138.     ULONG       ulHeaderLevel;      // IPF header level (1 if root file;
  139.                                     // -1 if link for "Internet" section)
  140.  
  141.     ULONG       ulResID;            // the resid of this link
  142.  
  143.     BOOL        fProcessed;         // TRUE after this has been parsed;
  144.                                     // if FALSE, everything below is
  145.                                     // still undefined
  146.  
  147.     // after parsing, the following are valid:
  148.  
  149.     XSTRING     strIPF;             // mostly translated IPF source
  150.  
  151.     XSTRING     strTitle;
  152.  
  153.     struct _ARTICLETREENODE *pFirstReferencedFrom;
  154.  
  155.     // misc data from HTML source
  156.  
  157.     LONG        lGroup;
  158.     PSZ         pszWidth,
  159.                 pszXPos;
  160.     BOOL        fHidden;
  161.  
  162.     // sublinks, nosublinks
  163.  
  164.     BOOL        fWritten;          // TRUE after this article has been
  165.                                    // written to the main buffer; this
  166.                                    // is used when we sort out parent
  167.                                    // articles so we won't write the
  168.                                    // article twice
  169.  
  170. } ARTICLETREENODE, *PARTICLETREENODE;
  171.  
  172. #define LIST_UL             1
  173. #define LIST_OL             2
  174. #define LIST_DL             3
  175.  
  176. /*
  177.  *@@ STATUS:
  178.  *      parser status.
  179.  */
  180.  
  181. typedef struct _STATUS
  182. {
  183.     // temp data for handlers
  184.     PSZ         pSource;          // current source pointer;
  185.                                   // with HandleTag:
  186.                                   //    in: points to '<' char,
  187.                                   //    out: should point to '>' char
  188.  
  189.     PSZ         pNextClose;       // ptr to next closing tag while in HandleTag
  190.  
  191.     // accumulated line length (for inserting line breaks automatically)
  192.     ULONG       ulLineLength;
  193.  
  194.     // current state of formatting
  195.     BOOL        fInHead,
  196.                 fItalics,
  197.                 fBold,
  198.                 fCode,
  199.                 fUnderlined,
  200.                 fInPre,
  201.                 fInTable,
  202.  
  203.                 fJustHadSpace,
  204.                 fNeedsP;
  205.  
  206.     ULONG       ulInLink;           // 0: not in <A block
  207.                                     // 1: in regular <A HREF= block, must be closed
  208.                                     // 2: in special <A AUTO= block, must not be closed
  209.  
  210.     XSTRING     strLinkTag;         // if ulInLink > 0, current link tag (for
  211.                                     // image link support); this has something
  212.                                     // like
  213.                                     // :link reftype=hd res=58.
  214.                                     // V0.9.20 (2002-07-12) [umoeller]
  215.  
  216.     LINKLIST    llListStack;        // linklist abused as a stack for list tags;
  217.                                     // the list item is simply a ULONG with a
  218.                                     // LISTFL_* value
  219.  
  220.     ULONG       ulDefinition;       // if inside a <DL>, this is 1 if last item was
  221.                                     // a DD
  222.  
  223.     BOOL        fFatal;             // if error is returned and this is TRUE,
  224.                                     // processing is stopped; otherwise the
  225.                                     // error is considered a warning only
  226.  
  227.     ULONG       ulNestingIFDEFs,
  228.                 ulNestingIFNDEFs;
  229. } STATUS, *PSTATUS;
  230.  
  231. /* ******************************************************************
  232.  *
  233.  *   Global variables
  234.  *
  235.  ********************************************************************/
  236.  
  237. TREE        *G_DefinesTreeRoot;
  238. ULONG       G_cDefines = 0;
  239. ULONG       G_ulReplacements = 0;
  240. TREE        *G_LinkIDsTreeRoot;
  241.  
  242. LINKLIST    G_llFiles2Process;
  243.  
  244. ULONG       G_ulVerbosity = 1;
  245.  
  246. BOOL        G_fNoMoveToRoot = FALSE;
  247.  
  248. XSTRING     G_strError,               // string buffer for error msgs
  249.             G_strCrashContext;
  250.  
  251. /* ******************************************************************
  252.  *
  253.  *   misc
  254.  *
  255.  ********************************************************************/
  256.  
  257. /*
  258.  *@@ Error:
  259.  *
  260.  *      ulCategory is:
  261.  *
  262.  *      --  0: command line syntax error.
  263.  *
  264.  *      --  1: HTML syntax warning.
  265.  *
  266.  *      --  2: fatal error.
  267.  */
  268.  
  269. VOID Error(ULONG ulCategory,
  270.            const char *pcszFile,
  271.            ULONG ulLine,
  272.            const char *pcszFunction,
  273.            const char *pcszFormat,     // in: format string (like with printf)
  274.            ...)                        // in: additional stuff (like with printf)
  275. {
  276.     if (G_ulVerbosity <= 1)
  277.         printf("\n");
  278.     if (G_ulVerbosity)
  279.         printf("h2i (%s line %u, %s)",
  280.                pcszFile,
  281.                ulLine,
  282.                pcszFunction);
  283.     else
  284.         printf("h2i");
  285.  
  286.     if (pcszFormat)
  287.     {
  288.         va_list     args;
  289.         switch (ulCategory)
  290.         {
  291.             case 0: printf(": Error in command line syntax. "); break;
  292.             case 1: printf(" HTML warning: "); break;
  293.             case 2: printf(" error: "); break;
  294.         }
  295.         va_start(args, pcszFormat);
  296.         vprintf(pcszFormat, args);
  297.         va_end(args);
  298.     }
  299.  
  300.     if (ulCategory == 0)
  301.         printf("\nType 'h2i -h' for help.\n");
  302.     else
  303.         printf("\n");
  304. }
  305.  
  306. #define H2I_HEADER "h2i V"BLDLEVEL_VERSION" ("__DATE__") (C) 2001-2002 Ulrich Möller"
  307.  
  308. /*
  309.  * PrintHeader:
  310.  *
  311.  *
  312.  */
  313.  
  314. VOID PrintHeader(VOID)
  315. {
  316.     printf(H2I_HEADER "\n");
  317.     printf("  Part of the XWorkplace package.\n");
  318.     printf("  This is free software under the GNU General Public Licence (GPL).\n");
  319.     printf("  Refer to the COPYING file in the XWorkplace installation dir for details.\n");
  320. }
  321.  
  322. /*
  323.  *@@ Explain:
  324.  *
  325.  */
  326.  
  327. VOID Explain(const char *pcszFormat,     // in: format string (like with printf)
  328.              ...)                        // in: additional stuff (like with printf)
  329. {
  330.     if (pcszFormat)
  331.     {
  332.         va_list     args;
  333.         printf("h2i error: ");
  334.         va_start(args, pcszFormat);
  335.         vprintf(pcszFormat, args);
  336.         va_end(args);
  337.         printf("\n");
  338.     }
  339.  
  340.     PrintHeader();
  341.     printf("h2i (for 'html to ipf') translates a large bunch of HTML files into a\n");
  342.     printf("single IPF file, which can then be fed into IBM's ipfc compiler.\n");
  343.     printf("Usage: h2i [-i<include>]... [-v[0|1|2|3]] [-s] [-r] <root.htm>\n");
  344.     printf("with:\n");
  345.     printf("   <root.htm>  the HTML file to start with. This better link to other files.\n");
  346.     printf("   -i<include> include a C header, whose #define statements are parsed\n");
  347.     printf("               to allow for entity references (&ent;).\n");
  348.     printf("   -v[0|1|2|3] specify verbosity level. If not specified, defaults to 1;\n");
  349.     printf("               if -v only is specified, uses 2.\n");
  350.     printf("   -s          show some statistics when done.\n");
  351.     printf("   -r          show only <root.htm> at level 1 in the TOC; otherwise all\n");
  352.     printf("               second-level files are moved to level 1 as well.\n");
  353. }
  354.  
  355. /*
  356.  *@@ fnCompareStrings:
  357.  *      tree comparison func (src\helpers\tree.c).
  358.  *
  359.  *@@added V0.9.9 (2001-04-07) [umoeller]
  360.  */
  361.  
  362. int TREEENTRY fnCompareStrings(ULONG ul1, ULONG ul2)
  363. {
  364.     return (strhicmp((const char *)ul1,
  365.                      (const char *)ul2));
  366. }
  367.  
  368. /* ******************************************************************
  369.  *
  370.  *   Parser helpers
  371.  *
  372.  ********************************************************************/
  373.  
  374. /*
  375.  *@@ AddDefinition:
  376.  *
  377.  */
  378.  
  379. BOOL AddDefinition(PDEFINENODE p)
  380. {
  381.     return (!treeInsert(&G_DefinesTreeRoot,
  382.                         NULL,
  383.                         (TREE*)p,
  384.                         fnCompareStrings));
  385. }
  386.  
  387. /*
  388.  *@@ FindDefinition:
  389.  *
  390.  */
  391.  
  392. PDEFINENODE FindDefinition(const char *pcszIdentifier)
  393. {
  394.     return ((PDEFINENODE)treeFind(G_DefinesTreeRoot,
  395.                                   (ULONG)pcszIdentifier,
  396.                                   fnCompareStrings));
  397. }
  398.  
  399. /*
  400.  *@@ ResolveEntities:
  401.  *
  402.  */
  403.  
  404. APIRET ResolveEntities(PARTICLETREENODE pFile2Process,
  405.                        PXSTRING pstrSource)
  406. {
  407.     if (G_cDefines)
  408.     {
  409.         // we have include files:
  410.         PSZ p = pstrSource->psz;
  411.  
  412.         // extract the entity (between '&' and ';')
  413.         while (p = strchr(p, '&'))
  414.         {
  415.             PSZ p2;
  416.             if (!(p2 = strchr(p, ';')))
  417.                 p++;
  418.             else
  419.             {
  420.                 // find the definition for the entity in
  421.                 // the global list; so make this null-terminated
  422.                 // first
  423.                 *p2 = '\0';
  424.  
  425.                 PDEFINENODE pDef = 0;
  426.                 if (pDef = FindDefinition(p + 1))
  427.                 {
  428.                     if (G_ulVerbosity > 2)
  429.                         printf("\n   found entity \"%s\" --> \"%s\"",
  430.                                p + 1, pDef->pszValue);
  431.                     *p2 = ';';
  432.  
  433.                     // replace the entity string with the
  434.                     // definition string
  435.                     ULONG ulPos = (p - pstrSource->psz),
  436.                           ulReplLen = pDef->ulValueLength;
  437.                     xstrrpl(pstrSource,
  438.                             // first ofs to replace:
  439.                             ulPos,
  440.                             // char count to replace:
  441.                             (p2 + 1) - p,       // include ';'
  442.                             // replacement string:
  443.                             pDef->pszValue,
  444.                             ulReplLen);
  445.  
  446.                     // string buffer might have changed, so
  447.                     // re-adjust source position
  448.                     p = pstrSource->psz + ulPos;
  449.  
  450.                     G_ulReplacements++;
  451.                 }
  452.                 else
  453.                 {
  454.                     // not found:
  455.                     // if it's not one of the standards we'll
  456.                     // replace later, complain
  457.                     if (    (strcmp(p + 1, "gt"))
  458.                          && (strcmp(p + 1, "lt"))
  459.                          && (strcmp(p + 1, "amp"))
  460.                        )
  461.                     {
  462.                         Error(1,
  463.                               __FILE__, __LINE__, __FUNCTION__,
  464.                               "Unknown entity \"%s;\" at line %d in file \"%s\".",
  465.                               p,
  466.                               strhCount(pstrSource->psz, '\n') + 1,
  467.                               (PSZ)pFile2Process->Tree.ulKey);      // filename
  468.                     }
  469.  
  470.                     *p2 = ';';
  471.  
  472.                     p = p2 + 1;
  473.                 }
  474.             }
  475.         }
  476.     }
  477. }
  478.  
  479. /*
  480.  *@@ ConvertEscapes:
  481.  *
  482.  */
  483.  
  484. const char *ConvertEscapes(PXSTRING pstr)
  485. {
  486.     const char *papszSources[] =
  487.         {
  488.             "&",
  489.             "<",
  490.             ">"
  491.         };
  492.     const char *papszTargets[] =
  493.         {
  494.             "&.",
  495.             "<",
  496.             ">"
  497.         };
  498.  
  499.     ULONG ul;
  500.     for (ul = 0;
  501.          ul < ARRAYITEMCOUNT(papszSources);
  502.          ul++)
  503.     {
  504.         ULONG ulOfs = 0;
  505.         while (xstrFindReplaceC(pstr,
  506.                                 &ulOfs,
  507.                                 papszSources[ul],
  508.                                 papszTargets[ul]))
  509.             ;
  510.     }
  511. }
  512.  
  513. /*
  514.  *@@ GetOrCreateArticle:
  515.  *      returns a link ID (resid) for the
  516.  *      specified filename.
  517.  *
  518.  *      If this is called for the first time for
  519.  *      this file name, a new ID is created and
  520.  *      returned. Otherwise the existing one
  521.  *      is returned.
  522.  *
  523.  *      Special flags for ulCurrentLevel:
  524.  *
  525.  *      --  0: create root article (will receive 1 then)
  526.  *
  527.  *      -- -1: do not create if it doesn't exist.
  528.  *
  529.  *      If a file is created, it will intially
  530.  *      carry a resid of -1. If the file has a
  531.  *      RESID attribute to the HTML tag, ParseFile
  532.  *      will reset the resid then. Later we go
  533.  *      through all files and check which ones
  534.  *      are still -1 and assign random values then.
  535.  */
  536.  
  537. PARTICLETREENODE GetOrCreateArticle(const char *pcszFilename,
  538.                                     ULONG ulCurrentLevel,
  539.                                     PARTICLETREENODE pParent)
  540. {
  541.     PARTICLETREENODE pMapping;
  542.  
  543.     // check if we have an article for this file name already
  544.     if (pMapping = (PARTICLETREENODE)treeFind(G_LinkIDsTreeRoot,
  545.                                               (ULONG)pcszFilename,
  546.                                               fnCompareStrings))
  547.         // exists:
  548.         return (pMapping);
  549.     else
  550.         if (ulCurrentLevel != -1)
  551.         {
  552.             // create new one
  553.             if (pMapping = NEW(ARTICLETREENODE))
  554.             {
  555.                 ZERO(pMapping);
  556.  
  557.                 pMapping->Tree.ulKey = (ULONG)strhdup(pcszFilename, NULL);
  558.                 pMapping->ulHeaderLevel = ulCurrentLevel + 1;
  559.                 pMapping->ulResID = -1;         // for now
  560.                 pMapping->pFirstReferencedFrom = pParent;
  561.  
  562.                 xstrInit(&pMapping->strIPF, 0);
  563.                 xstrInit(&pMapping->strTitle, 0);
  564.  
  565.                 if (!treeInsert(&G_LinkIDsTreeRoot,
  566.                                 NULL,
  567.                                 (TREE*)pMapping,
  568.                                 fnCompareStrings))
  569.                 {
  570.                     // this is a new file...
  571.                     // if it's not an HTML link, check if this exists
  572.                     if (access(pcszFilename, 0))
  573.                     {
  574.                         // file does not exist:
  575.                         if (    (strncmp(pcszFilename, "http://", 7))
  576.                              && (strncmp(pcszFilename, "ftp://", 6))
  577.                              && (strncmp(pcszFilename, "mailto:", 7))
  578.                            )
  579.                         {
  580.                             if (G_ulVerbosity)
  581.                                 printf("\n  Warning: Link from \"%s\" to \"%s\" was not found.",
  582.                                        (pParent)
  583.                                            ? (PSZ)pParent->Tree.ulKey // pszFilename,
  584.                                            : "none",
  585.                                        (PSZ)pMapping->Tree.ulKey); // pszFilename,
  586.                         }
  587.  
  588.                         // do not try to load this file!
  589.                         pMapping->ulHeaderLevel = -1; // special flag
  590.                     }
  591.  
  592.                     // since this is new, add it to the list of
  593.                     // things to be processed too
  594.                     lstAppendItem(&G_llFiles2Process,
  595.                                   pMapping);
  596.  
  597.                     return (pMapping);
  598.                 }
  599.             }
  600.         }
  601.  
  602.     return (0);
  603. }
  604.  
  605. /* ******************************************************************
  606.  *
  607.  *   Tag handlers
  608.  *
  609.  ********************************************************************/
  610.  
  611. /*
  612.  *@@ CheckP:
  613.  *      adds a :p. if this was markes as due
  614.  *      in STATUS. When we encounter <P> in HTML,
  615.  *      we never insert :p. directly but only
  616.  *      set the flag in STATUS so we can do
  617.  *      additional checks, mostly for better
  618.  *      <LI> formatting. This gets called from
  619.  *      various locations before appending
  620.  *      other character data.
  621.  */
  622.  
  623. VOID CheckP(PXSTRING pxstrIPF,
  624.             PSTATUS pstat)    // in/out: parser status
  625. {
  626.     if (pstat->fNeedsP)
  627.     {
  628.         // we just had a <P> tag previously:
  629.         if (pstat->fInPre)
  630.             xstrcatc(pxstrIPF, '\n');
  631.         else
  632.             xstrcat(pxstrIPF, "\n:p.\n", 0);
  633.         pstat->ulLineLength = 0;
  634.         pstat->fJustHadSpace = TRUE;
  635.         pstat->fNeedsP = FALSE;
  636.     }
  637. }
  638.  
  639. /*
  640.  *@@ PushList:
  641.  *      pushes the specified list on the list stack.
  642.  */
  643.  
  644. VOID PushList(PSTATUS pstat,    // in/out: parser status
  645.               ULONG ulList)     // in: LIST_* flag
  646. {
  647.     lstAppendItem(&pstat->llListStack,
  648.                   (PVOID)ulList);
  649. }
  650.  
  651. /*
  652.  *@@ CheckListTop:
  653.  *      returns TRUE if the top item on the list
  654.  *      stack matches the given LIST_* flag.
  655.  */
  656.  
  657. BOOL CheckListTop(PSTATUS pstat,    // in: status
  658.                   ULONG ulList)     // in: LIST_* flag
  659. {
  660.     PLISTNODE pNode;
  661.     if (pNode = lstQueryLastNode(&pstat->llListStack))
  662.         if ((ULONG)pNode->pItemData == ulList)
  663.             return (TRUE);
  664.     return (FALSE);
  665. }
  666.  
  667. /*
  668.  *@@ PopList:
  669.  *      removes the top item from the list stack.
  670.  */
  671.  
  672. VOID PopList(PSTATUS pstat) // in/out: parser status
  673. {
  674.     lstRemoveNode(&pstat->llListStack,
  675.                   lstQueryLastNode(&pstat->llListStack));
  676. }
  677.  
  678. typedef PCSZ TAGHANDLER(PARTICLETREENODE pFile2Process, // in: file to process
  679.                         PXSTRING pxstrIPF,  // out: IPF string
  680.                         PSTATUS pstat,      // in/out: parser status
  681.                         BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  682.                         PSZ pszAttrs);      // in: attributes
  683. typedef TAGHANDLER *PTAGHANDLER;
  684.  
  685. /*
  686.  *@@ HandleHTML:
  687.  *
  688.  */
  689.  
  690. PCSZ HandleHTML(PARTICLETREENODE pFile2Process,
  691.                 PXSTRING pxstrIPF,  // out: IPF string
  692.                 PSTATUS pstat,      // in/out: parser status
  693.                 BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  694.                 PSZ pszAttrs)
  695. {
  696.     if (!fClosingTag)
  697.     {
  698.         if (pszAttrs)
  699.         {
  700.             PSZ pszAttrib;
  701.             if (pszAttrib = strhGetTextAttr(pszAttrs,
  702.                                             "GROUP",
  703.                                             NULL))
  704.             {
  705.                 pFile2Process->lGroup = atoi(pszAttrib);
  706.                 free(pszAttrib);
  707.             }
  708.             if (pszAttrib = strhGetTextAttr(pszAttrs,
  709.                                             "WIDTH",
  710.                                             NULL))
  711.             {
  712.                 pFile2Process->pszWidth = pszAttrib;
  713.             }
  714.             if (pszAttrib = strhGetTextAttr(pszAttrs,
  715.                                             "XPOS",
  716.                                             NULL))
  717.             {
  718.                 pFile2Process->pszXPos = pszAttrib;
  719.             }
  720.             if (pszAttrib = strhGetTextAttr(pszAttrs,
  721.                                             "HIDDEN",
  722.                                             NULL))
  723.             {
  724.                 pFile2Process->fHidden = TRUE;
  725.                 free(pszAttrib);
  726.             }
  727.             if (pszAttrib = strhGetTextAttr(pszAttrs,
  728.                                             "RESID",
  729.                                             NULL))
  730.             {
  731.                 pFile2Process->ulResID = atoi(pszAttrib);
  732.                 if (!pFile2Process->ulResID)
  733.                     return ("Invalid RESID in <HTML> tag.");
  734.                 free(pszAttrib);
  735.             }
  736.             if (pszAttrib = strhGetTextAttr(pszAttrs,
  737.                                             "SUBLINKS",
  738.                                             NULL))
  739.             {
  740.                 // @@todo
  741.             }
  742.             if (pszAttrib = strhGetTextAttr(pszAttrs,
  743.                                             "NOSUBLINKS",
  744.                                             NULL))
  745.             {
  746.                 // @@todo
  747.             }
  748.         }
  749.     }
  750.  
  751.     return (0);
  752. }
  753.  
  754. /*
  755.  *@@ HandleHEAD:
  756.  *
  757.  *@@added V0.9.16 (2001-11-22) [umoeller]
  758.  */
  759.  
  760. PCSZ HandleHEAD(PARTICLETREENODE pFile2Process,
  761.                 PXSTRING pxstrIPF,  // out: IPF string
  762.                 PSTATUS pstat,      // in/out: parser status
  763.                 BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  764.                 PSZ pszAttrs)
  765. {
  766.     if (!fClosingTag)
  767.         pstat->fInHead = TRUE;
  768.                 // this flag starts skipping all characters
  769.     else
  770.         pstat->fInHead = FALSE;
  771.                 // stop skipping all characters
  772.  
  773.     return 0;
  774. }
  775.  
  776. /*
  777.  *@@ HandleTITLE:
  778.  *
  779.  */
  780.  
  781. PCSZ HandleTITLE(PARTICLETREENODE pFile2Process,
  782.                  PXSTRING pxstrIPF,  // out: IPF string
  783.                  PSTATUS pstat,      // in/out: parser status
  784.                  BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  785.                  PSZ pszAttrs)
  786. {
  787.     if (!fClosingTag)
  788.     {
  789.         // TITLE tag...
  790.         // we need to remove whitespace from the
  791.         // front and the tail, so play a bit here
  792.         PSZ p3 = pstat->pNextClose + 1;
  793.         while (    *p3
  794.                 && (    (*p3 == ' ')
  795.                      || (*p3 == '\n')
  796.                      || (*p3 == '\t')
  797.                    )
  798.               )
  799.             p3++;
  800.         if (!*p3)
  801.             return ("Incomplete <TITLE>.");
  802.         // find closing title tag
  803.         PSZ p4 = strstr(p3, "</TITLE>");
  804.         if (!p4)
  805.             return ("<TITLE> has no closing tag.");
  806.         else
  807.         {
  808.             // from beginning of title, go backwards
  809.             // until the last non-whitespace
  810.             PSZ p5 = p4 - 1;
  811.             while (    p5 > p3
  812.                     && (    (*p5 == ' ')
  813.                          || (*p5 == '\n')
  814.                          || (*p5 == '\t')
  815.                        )
  816.                   )
  817.                 p5--;
  818.  
  819.             if (p5 > p3)
  820.             {
  821.                 xstrcpy(&pFile2Process->strTitle,
  822.                         p3,
  823.                         p5 - p3 + 1);
  824.                 ResolveEntities(pFile2Process,
  825.                                 &pFile2Process->strTitle);
  826.                 // search on after </TITLE>
  827.                 pstat->pNextClose = p4 + 7;
  828.             }
  829.             else
  830.                 return ("Empty <TITLE> block.");
  831.         }
  832.     }
  833.  
  834.     return (0);
  835. }
  836.  
  837. /*
  838.  *@@ HandleIFDEF:
  839.  *
  840.  */
  841.  
  842. PCSZ HandleIFDEF(PARTICLETREENODE pFile2Process,
  843.                  PXSTRING pxstrIPF,  // out: IPF string
  844.                  PSTATUS pstat,      // in/out: parser status
  845.                  BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  846.                  PSZ pszAttrs)
  847. {
  848.     if (!fClosingTag)
  849.     {
  850.         // IFDEF tag...
  851.         // get the define from the attributes
  852.         PCSZ pDef = pszAttrs;
  853.         while (    (*pDef)
  854.                 && (*pDef == ' ')
  855.               )
  856.             pDef++;
  857.  
  858.         if (*pDef)
  859.         {
  860.             PDEFINENODE pNode;
  861.             if (!(pNode = FindDefinition(pDef)))
  862.             {
  863.                 // definition _not_ found: then we must _skip_ everything
  864.                 PSZ pEndOfSkip;
  865.                 if (pEndOfSkip = strstr(pstat->pNextClose + 1,
  866.                                         "</IFDEF>"))
  867.                 {
  868.                     // search on after </IFDEF>
  869.                     if (G_ulVerbosity > 1)
  870.                         printf("\n<IFDEF '%s'> is FALSE, skipping %d chars",
  871.                                pDef,
  872.                                pEndOfSkip + 8 - pstat->pSource);
  873.                     pstat->pNextClose = pEndOfSkip + 7;
  874.                 }
  875.                 else
  876.                     return ("Cannot find closing </IFDEF> tag");
  877.             }
  878.             else
  879.                 (pstat->ulNestingIFDEFs)++;
  880.         }
  881.     }
  882.     else
  883.         if (pstat->ulNestingIFDEFs)
  884.             (pstat->ulNestingIFDEFs)--;
  885.         else
  886.             return ("Unrelated closing </IFDEF> tag");
  887.  
  888.     return (0);
  889. }
  890.  
  891. /*
  892.  *@@ HandleIFNDEF:
  893.  *
  894.  */
  895.  
  896. PCSZ HandleIFNDEF(PARTICLETREENODE pFile2Process,
  897.                   PXSTRING pxstrIPF,  // out: IPF string
  898.                   PSTATUS pstat,      // in/out: parser status
  899.                   BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  900.                   PSZ pszAttrs)
  901. {
  902.     if (!fClosingTag)
  903.     {
  904.         // IFNDEF tag...
  905.         // get the define from the attributes
  906.         PCSZ pDef = pszAttrs;
  907.         while (    (*pDef)
  908.                 && (*pDef == ' ')
  909.               )
  910.             pDef++;
  911.  
  912.         if (*pDef)
  913.         {
  914.             PDEFINENODE pNode;
  915.             if (pNode = FindDefinition(pDef))
  916.             {
  917.                 // definition _found_: then we must _skip_ everything
  918.                 PSZ pEndOfSkip;
  919.                 if (pEndOfSkip = strstr(pstat->pNextClose + 1,
  920.                                         "</IFNDEF>"))
  921.                 {
  922.                     // search on after </IFNDEF>
  923.                     if (G_ulVerbosity > 1)
  924.                         printf("\n<IFNDEF '%s'> is FALSE, skipping %d chars",
  925.                                pDef,
  926.                                pEndOfSkip + 9 - pstat->pSource);
  927.                     pstat->pNextClose = pEndOfSkip + 8;
  928.                 }
  929.                 else
  930.                     return ("Cannot find closing </IFNDEF> tag");
  931.             }
  932.             else
  933.                 (pstat->ulNestingIFNDEFs)++;
  934.         }
  935.     }
  936.     else
  937.         if (pstat->ulNestingIFNDEFs)
  938.             (pstat->ulNestingIFNDEFs)--;
  939.         else
  940.             return ("Unrelated closing </IFNDEF> tag");
  941.  
  942.     return (0);
  943. }
  944.  
  945. /*
  946.  *@@ HandleP:
  947.  *
  948.  *@@added V0.9.16 (2001-11-22) [umoeller]
  949.  */
  950.  
  951. PCSZ HandleP(PARTICLETREENODE pFile2Process,
  952.              PXSTRING pxstrIPF,
  953.              PSTATUS pstat,    // in/out: parser status
  954.              BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  955.              PSZ pszAttrs)
  956. {
  957.     if (!fClosingTag)
  958.         // mark this only for now since we don't
  959.         // know yet whether a <LI> comes next...
  960.         // if we do <P><LI>, we get huge spaces
  961.         pstat->fNeedsP = TRUE;
  962.  
  963.     return 0;
  964. }
  965.  
  966. /*
  967.  *@@ HandleBR:
  968.  *
  969.  *@@added V0.9.16 (2001-11-22) [umoeller]
  970.  */
  971.  
  972. PCSZ HandleBR(PARTICLETREENODE pFile2Process,
  973.               PXSTRING pxstrIPF,
  974.               PSTATUS pstat,    // in/out: parser status
  975.               BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  976.               PSZ pszAttrs)
  977. {
  978.     if (!fClosingTag)
  979.     {
  980.         if (!pstat->fInTable)
  981.         {
  982.             // IPF cannot handle .br in tables
  983.             xstrcat(pxstrIPF, "\n.br\n", 0);
  984.             pstat->ulLineLength = 0;
  985.         }
  986.     }
  987.  
  988.     return 0;
  989. }
  990.  
  991. /*
  992.  *@@ HandleHR:
  993.  *
  994.  *@@added V0.9.16 (2001-11-22) [umoeller]
  995.  */
  996.  
  997. PCSZ HandleHR(PARTICLETREENODE pFile2Process,
  998.               PXSTRING pxstrIPF,
  999.               PSTATUS pstat,    // in/out: parser status
  1000.               BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1001.               PSZ pszAttrs)
  1002. {
  1003.     if (!fClosingTag)
  1004.     {
  1005.         ULONG ul;
  1006.         xstrcat(pxstrIPF, ":cgraphic.", 0);
  1007.         for (ul = 0; ul < 80; ul++)
  1008.             xstrcatc(pxstrIPF, '_');
  1009.         xstrcat(pxstrIPF, ":ecgraphic.", 0);
  1010.         pstat->fNeedsP = TRUE;
  1011.     }
  1012.     return 0;
  1013. }
  1014.  
  1015. /*
  1016.  *@@ HandleI:
  1017.  *
  1018.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1019.  */
  1020.  
  1021. PCSZ HandleI(PARTICLETREENODE pFile2Process,
  1022.              PXSTRING pxstrIPF,
  1023.              PSTATUS pstat,    // in/out: parser status
  1024.              BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1025.              PSZ pszAttrs)
  1026. {
  1027.     if (!fClosingTag)
  1028.     {
  1029.         if (pstat->fItalics)
  1030.             return ("Nested <I> or <EM> tag.");
  1031.         else
  1032.         {
  1033.             xstrcat(pxstrIPF, ":hp1.", 0);
  1034.             pstat->fItalics = TRUE;
  1035.         }
  1036.     }
  1037.     else
  1038.     {
  1039.         if (!pstat->fItalics)
  1040.             return ("Unrelated closing </I> or </EM> tag.");
  1041.         else
  1042.         {
  1043.             xstrcat(pxstrIPF, ":ehp1.", 0);
  1044.             pstat->fItalics = FALSE;
  1045.         }
  1046.     }
  1047.  
  1048.     return 0;
  1049. }
  1050.  
  1051. /*
  1052.  *@@ HandleB:
  1053.  *
  1054.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1055.  */
  1056.  
  1057. PCSZ HandleB(PARTICLETREENODE pFile2Process,
  1058.              PXSTRING pxstrIPF,
  1059.              PSTATUS pstat,    // in/out: parser status
  1060.              BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1061.              PSZ pszAttrs)
  1062. {
  1063.     if (!fClosingTag)
  1064.     {
  1065.         if (pstat->fBold)
  1066.             return ("Nested <B> or <STRONG> tag.");
  1067.         else
  1068.         {
  1069.             xstrcat(pxstrIPF, ":hp2.", 0);
  1070.             pstat->fBold = TRUE;
  1071.         }
  1072.     }
  1073.     else
  1074.     {
  1075.         if (!pstat->fBold)
  1076.             return ("Unrelated closing </B> or </STRONG> tag.");
  1077.         else
  1078.         {
  1079.             xstrcat(pxstrIPF, ":ehp2.", 0);
  1080.             pstat->fBold = FALSE;
  1081.         }
  1082.     }
  1083.  
  1084.     return 0;
  1085. }
  1086.  
  1087. /*
  1088.  *@@ HandleU:
  1089.  *
  1090.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1091.  */
  1092.  
  1093. PCSZ HandleU(PARTICLETREENODE pFile2Process,
  1094.              PXSTRING pxstrIPF,
  1095.              PSTATUS pstat,    // in/out: parser status
  1096.              BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1097.              PSZ pszAttrs)
  1098. {
  1099.     if (!fClosingTag)
  1100.     {
  1101.         if (pstat->fUnderlined)
  1102.             return ("Nested <U> tag.");
  1103.         else
  1104.         {
  1105.             xstrcat(pxstrIPF, ":hp5.", 0);
  1106.             pstat->fUnderlined = TRUE;
  1107.         }
  1108.     }
  1109.     else
  1110.     {
  1111.         if (!pstat->fUnderlined)
  1112.             return ("Unrelated closing </U> tag.");
  1113.         else
  1114.         {
  1115.             xstrcat(pxstrIPF, ":ehp5.", 0);
  1116.             pstat->fUnderlined = FALSE;
  1117.         }
  1118.     }
  1119.  
  1120.     return 0;
  1121. }
  1122.  
  1123. /*
  1124.  *@@ HandleCODE:
  1125.  *
  1126.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1127.  */
  1128.  
  1129. PCSZ HandleCODE(PARTICLETREENODE pFile2Process,
  1130.                 PXSTRING pxstrIPF,
  1131.                 PSTATUS pstat,    // in/out: parser status
  1132.                 BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1133.                 PSZ pszAttrs)
  1134. {
  1135.     if (!fClosingTag)
  1136.     {
  1137.         if (pstat->fCode)
  1138.             return ("Nested <CODE>, <CITE>, or <TT> tag.");
  1139.         else
  1140.         {
  1141.             xstrcat(pxstrIPF, ":font facename='Courier' size=18x12.", 0);
  1142.             pstat->fCode = TRUE;
  1143.         }
  1144.     }
  1145.     else
  1146.     {
  1147.         if (!pstat->fCode)
  1148.             return ("Unrelated closing </CODE>, </CITE>, or </TT> tag.");
  1149.         else
  1150.         {
  1151.             xstrcat(pxstrIPF, ":font facename=default size=0x0.", 0);
  1152.             pstat->fCode = FALSE;
  1153.         }
  1154.     }
  1155.  
  1156.     return 0;
  1157. }
  1158.  
  1159. /*
  1160.  *@@ HandlePRE:
  1161.  *
  1162.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1163.  */
  1164.  
  1165. PCSZ HandlePRE(PARTICLETREENODE pFile2Process,
  1166.                PXSTRING pxstrIPF,
  1167.                PSTATUS pstat,    // in/out: parser status
  1168.                BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1169.                PSZ pszAttrs)
  1170. {
  1171.     if (!fClosingTag)
  1172.     {
  1173.         if (pstat->fInPre)
  1174.             return ("Nested <PRE> tag.");
  1175.         else
  1176.         {
  1177.             xstrcat(pxstrIPF, "\n:cgraphic.", 0);
  1178.             pstat->fInPre = TRUE;
  1179.         }
  1180.     }
  1181.     else
  1182.     {
  1183.         if (!pstat->fInPre)
  1184.             return ("Unrelated closing </PRE> tag.");
  1185.         else
  1186.         {
  1187.             xstrcat(pxstrIPF, "\n:ecgraphic.", 0);
  1188.             pstat->fInPre = FALSE;
  1189.             pstat->fNeedsP = TRUE;
  1190.         }
  1191.     }
  1192.  
  1193.     return 0;
  1194. }
  1195.  
  1196. /*
  1197.  *@@ HandleUL:
  1198.  *
  1199.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1200.  */
  1201.  
  1202. PCSZ HandleUL(PARTICLETREENODE pFile2Process,
  1203.               PXSTRING pxstrIPF,
  1204.               PSTATUS pstat,    // in/out: parser status
  1205.               BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1206.               PSZ pszAttrs)
  1207. {
  1208.     if (!fClosingTag)
  1209.     {
  1210.         xstrcat(pxstrIPF, "\n:ul compact.", 0);
  1211.         PushList(pstat, LIST_UL);
  1212.         pstat->ulLineLength = 0;
  1213.     }
  1214.     else
  1215.     {
  1216.         if (CheckListTop(pstat, LIST_UL))
  1217.         {
  1218.             xstrcat(pxstrIPF, "\n:eul.", 0);
  1219.             PopList(pstat);
  1220.             pstat->fNeedsP = TRUE;
  1221.         }
  1222.         else
  1223.             return ("Invalid </UL> nesting.");
  1224.     }
  1225.  
  1226.     return 0;
  1227. }
  1228.  
  1229. /*
  1230.  *@@ HandleOL:
  1231.  *
  1232.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1233.  */
  1234.  
  1235. PCSZ HandleOL(PARTICLETREENODE pFile2Process,
  1236.               PXSTRING pxstrIPF,
  1237.               PSTATUS pstat,    // in/out: parser status
  1238.               BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1239.               PSZ pszAttrs)
  1240. {
  1241.     if (!fClosingTag)
  1242.     {
  1243.         xstrcat(pxstrIPF, "\n:ol compact.", 0);
  1244.         PushList(pstat, LIST_OL);
  1245.         pstat->ulLineLength = 0;
  1246.     }
  1247.     else
  1248.     {
  1249.         if (CheckListTop(pstat, LIST_OL))
  1250.         {
  1251.             xstrcat(pxstrIPF, "\n:eol.", 0);
  1252.             PopList(pstat);
  1253.             pstat->fNeedsP = TRUE;
  1254.         }
  1255.         else
  1256.             return ("Invalid </OL> nesting.");
  1257.     }
  1258.  
  1259.     return 0;
  1260. }
  1261.  
  1262. /*
  1263.  *@@ HandleLI:
  1264.  *
  1265.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1266.  */
  1267.  
  1268. PCSZ HandleLI(PARTICLETREENODE pFile2Process,
  1269.               PXSTRING pxstrIPF,
  1270.               PSTATUS pstat,    // in/out: parser status
  1271.               BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1272.               PSZ pszAttrs)
  1273. {
  1274.     if (!fClosingTag)
  1275.     {
  1276.         if (    (CheckListTop(pstat, LIST_UL))
  1277.              || (CheckListTop(pstat, LIST_OL))
  1278.            )
  1279.         {
  1280.             if (pstat->fNeedsP)
  1281.             {
  1282.                 // we just had a <P> previously:
  1283.                 // add a .br instead or we'll get huge
  1284.                 // whitespace
  1285.                 xstrcat(pxstrIPF, "\n.br\n", 0);
  1286.                 pstat->fNeedsP = FALSE;
  1287.             }
  1288.             xstrcat(pxstrIPF, ":li.\n", 0);
  1289.             pstat->ulLineLength = 0;
  1290.         }
  1291.         else
  1292.             return ("<LI> outside list.");
  1293.     }
  1294.  
  1295.     return 0;
  1296. }
  1297.  
  1298. /*
  1299.  *@@ HandleDL:
  1300.  *
  1301.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1302.  */
  1303.  
  1304. PCSZ HandleDL(PARTICLETREENODE pFile2Process,
  1305.               PXSTRING pxstrIPF,
  1306.               PSTATUS pstat,    // in/out: parser status
  1307.               BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1308.               PSZ pszAttrs)
  1309. {
  1310.     if (!fClosingTag)
  1311.     {
  1312.         xstrcat(pxstrIPF, "\n:dl compact break=all.", 0);
  1313.         PushList(pstat, LIST_DL);
  1314.         pstat->ulLineLength = 0;
  1315.     }
  1316.     else
  1317.     {
  1318.         if (CheckListTop(pstat, LIST_DL))
  1319.         {
  1320.             if (!pstat->ulDefinition)
  1321.                 // if the list didn't stop with a DD last,
  1322.                 // we must add one or IPFC will choke
  1323.                 xstrcat(pxstrIPF, "\n:dd.", 0);
  1324.  
  1325.             xstrcat(pxstrIPF, "\n:edl.", 0);
  1326.             PopList(pstat);
  1327.             pstat->fNeedsP = TRUE;
  1328.             pstat->ulDefinition = 0;
  1329.         }
  1330.         else
  1331.             return ("Invalid </DL> nesting.");
  1332.     }
  1333.  
  1334.     return 0;
  1335. }
  1336.  
  1337. /*
  1338.  *@@ HandleDD:
  1339.  *
  1340.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1341.  */
  1342.  
  1343. PCSZ HandleDD(PARTICLETREENODE pFile2Process,
  1344.               PXSTRING pxstrIPF,
  1345.               PSTATUS pstat,    // in/out: parser status
  1346.               BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1347.               PSZ pszAttrs)
  1348. {
  1349.     if (!fClosingTag)
  1350.     {
  1351.         if (!CheckListTop(pstat, LIST_DL))
  1352.             return ("<DD> outside of <DL>.");
  1353.         else
  1354.         {
  1355.             xstrcat(pxstrIPF, "\n:dd.", 0);
  1356.             pstat->ulLineLength = 0;
  1357.             pstat->ulDefinition = 1;
  1358.         }
  1359.     }
  1360.  
  1361.     return 0;
  1362. }
  1363.  
  1364. /*
  1365.  *@@ HandleDT:
  1366.  *
  1367.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1368.  */
  1369.  
  1370. PCSZ HandleDT(PARTICLETREENODE pFile2Process,
  1371.               PXSTRING pxstrIPF,
  1372.               PSTATUS pstat,    // in/out: parser status
  1373.               BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1374.               PSZ pszAttrs)
  1375. {
  1376.     if (!fClosingTag)
  1377.     {
  1378.         if (!CheckListTop(pstat, LIST_DL))
  1379.             return ("<DT> outside of <DL>.");
  1380.         else
  1381.         {
  1382.             xstrcat(pxstrIPF, "\n:dt.", 0);
  1383.             pstat->ulLineLength = 0;
  1384.             pstat->ulDefinition = 0;
  1385.         }
  1386.     }
  1387.  
  1388.     return 0;
  1389. }
  1390.  
  1391. #define START_KEY  "@#!LINK1@#!"
  1392. #define END_KEY    "@#!LINK2@#!"
  1393.  
  1394. /*
  1395.  *@@ HandleA:
  1396.  *
  1397.  *@@changed V0.9.20 (2002-07-06) [umoeller]: added support for cpref etc. toolkit INF variables
  1398.  +@@changed V0.9.20 (2002-07-12) [umoeller]: added support for A HREF="helpfile.hlp#panel"
  1399.  */
  1400.  
  1401. PCSZ HandleA(PARTICLETREENODE pFile2Process,
  1402.              PXSTRING pxstrIPF,
  1403.              PSTATUS pstat,    // in/out: parser status
  1404.              BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1405.              PSZ pszAttrs)
  1406. {
  1407.     if (!fClosingTag)
  1408.     {
  1409.         if (pstat->ulInLink)
  1410.             return ("Nested <A ...> blocks.");
  1411.         else
  1412.         {
  1413.             // check the attribute:
  1414.             // is it HREF?
  1415.             if (pszAttrs)
  1416.             {
  1417.                 PSZ pszAttrib;
  1418.                 if (pszAttrib = strhGetTextAttr(pszAttrs,
  1419.                                                 "AUTO",
  1420.                                                 NULL))
  1421.                 {
  1422.                     PARTICLETREENODE patn
  1423.                         = GetOrCreateArticle(pszAttrib,
  1424.                                              pFile2Process->ulHeaderLevel,
  1425.                                              pFile2Process);
  1426.  
  1427.                     // V0.9.20 (2002-07-12) [umoeller]
  1428.                     xstrPrintf(&pstat->strLinkTag,
  1429.                                // hack in the @#!LINK@#! string for now;
  1430.                                // this is later replaced with the
  1431.                                // resid in ParseFiles
  1432.                                ":link reftype=hd res=" START_KEY "%s" END_KEY " auto dependent.",
  1433.                                pszAttrib);
  1434.  
  1435.                     xstrcats(pxstrIPF,
  1436.                              &pstat->strLinkTag);
  1437.  
  1438.                     pstat->ulInLink = 2;        // special, do not close
  1439.                 }
  1440.                 else if (pszAttrib = strhGetTextAttr(pszAttrs,
  1441.                                                      "HREF",
  1442.                                                      NULL))
  1443.                 {
  1444.                     PSZ pExt;
  1445.                     if (pExt = strhistr(pszAttrib, ".INF"))
  1446.                     {
  1447.                         // special INF cross-link support:
  1448.                         // V0.9.20 (2002-07-06) [umoeller]
  1449.                         // remove .INF and the following # if any;
  1450.                         // this allows us to specify "cpref.inf#func"
  1451.                         // in the sources even though cpref is really
  1452.                         // an environment variable defined by the
  1453.                         // toolkit install
  1454.                         PSZ p3 = pExt + 4;
  1455.                         if (*p3 == '#')
  1456.                         {
  1457.                             *p3 = ' ';
  1458.                         }
  1459.                         strcpy(pExt, p3);
  1460.  
  1461.                         // V0.9.20 (2002-07-12) [umoeller]
  1462.                         xstrPrintf(&pstat->strLinkTag,
  1463.                                    ":link reftype=launch object='view.exe' data='%s'.",
  1464.                                    pszAttrib);
  1465.  
  1466.                         xstrcats(pxstrIPF,
  1467.                                  &pstat->strLinkTag);
  1468.                     }
  1469.                     else if (pExt = strhistr(pszAttrib, ".HLP"))
  1470.                     {
  1471.                         // V0.9.20 (2002-07-06) [umoeller]
  1472.                         // special HLP cross-link support:
  1473.                         // syntax is "helpfile.hlp#panel" here
  1474.                         PSZ p3 = pExt + 4;
  1475.  
  1476.                         if (*p3 == '#')
  1477.                         {
  1478.                             *p3 = '\0';
  1479.  
  1480.                             // V0.9.20 (2002-07-12) [umoeller]
  1481.                             xstrPrintf(&pstat->strLinkTag,
  1482.                                        ":link reftype=hd refid=%d database='%s'.",
  1483.                                        atoi(p3 + 1),
  1484.                                        pszAttrib);
  1485.  
  1486.                             xstrcats(pxstrIPF,
  1487.                                      &pstat->strLinkTag);
  1488.                         }
  1489.                         else
  1490.                             return ("Invalid syntax in <A HREF=\"file.hlp#panel\".");
  1491.                     }
  1492.                     else
  1493.                     {
  1494.                         // no INF:
  1495.                         PARTICLETREENODE patn
  1496.                         = GetOrCreateArticle(pszAttrib,
  1497.                                              pFile2Process->ulHeaderLevel,
  1498.                                              pFile2Process);
  1499.  
  1500.                         // V0.9.20 (2002-07-12) [umoeller]
  1501.                         xstrPrintf(&pstat->strLinkTag,
  1502.                                    // hack in the @#!LINK@#! string for now;
  1503.                                    // this is later replaced with the
  1504.                                    // resid in ParseFiles
  1505.                                    ":link reftype=hd res=" START_KEY "%s" END_KEY ".",
  1506.                                    pszAttrib);
  1507.  
  1508.                         xstrcats(pxstrIPF,
  1509.                                  &pstat->strLinkTag);
  1510.                     }
  1511.  
  1512.                     pstat->ulInLink = 1;        // regular, do close
  1513.  
  1514.                     free(pszAttrib);
  1515.                 }
  1516.                 else
  1517.                     return ("Unknown attribute to <A> tag.");
  1518.             }
  1519.             else
  1520.                 return ("<A> tag has no attributes.");
  1521.         }
  1522.     } // end if (!fClosingTag)
  1523.     else
  1524.     {
  1525.         if (pstat->ulInLink)
  1526.         {
  1527.             if (pstat->ulInLink == 1)
  1528.                 xstrcat(pxstrIPF,
  1529.                         ":elink.",
  1530.                         0);
  1531.             // otherwise 2: that's from A AUTO, do not close
  1532.  
  1533.             pstat->ulInLink = 0;
  1534.         }
  1535.         else
  1536.             return ("Unrelated closing </A> tag.");
  1537.     }
  1538.  
  1539.     return (0);
  1540. }
  1541.  
  1542. /*
  1543.  *@@ HandleIMG:
  1544.  *
  1545.  */
  1546.  
  1547. PCSZ HandleIMG(PARTICLETREENODE pFile2Process,
  1548.                PXSTRING pxstrIPF,
  1549.                PSTATUS pstat,    // in/out: parser status
  1550.                BOOL fClosingTag,   // in: TRUE == closing tag, FALSE == opening tag
  1551.                PSZ pszAttrs)
  1552. {
  1553.     PSZ pszAttrib;
  1554.     if (pszAttrib = strhGetTextAttr(pszAttrs,
  1555.                                     "SRC",
  1556.                                     NULL))
  1557.     {
  1558.         XSTRING str;
  1559.         xstrInitSet(&str, pszAttrib);
  1560.         PSZ p = strrchr(pszAttrib, '.');
  1561.         if (    (!p)
  1562.              || (strlen(p) < 3)
  1563.            )
  1564.             xstrReserve(&str, CCHMAXPATH);
  1565.         strcpy(p, ".bmp");
  1566.  
  1567.         if (access(str.psz, 0))
  1568.             // doesn't exist:
  1569.             Error(1,
  1570.                   __FILE__, __LINE__, __FUNCTION__,
  1571.                   "The bitmap file \"%s\" was not found.",
  1572.                   str.psz);
  1573.  
  1574.         if (pstat->ulInLink == 1)
  1575.             // if we are inside a link, close it first
  1576.             // V0.9.20 (2002-07-12) [umoeller]
  1577.             xstrcat(pxstrIPF,
  1578.                     ":elink.",
  1579.                     0);
  1580.  
  1581.         if (pstat->fNeedsP)
  1582.             // if we had a <P> just before, do not use runin
  1583.             xstrCatf(pxstrIPF,
  1584.                      "\n:artwork name='%s' align=left.",
  1585.                      str.psz);
  1586.         else
  1587.             xstrCatf(pxstrIPF,
  1588.                      "\n:artwork name='%s' runin align=left.",
  1589.                      str.psz);
  1590.  
  1591.         // if we are inside a link, add special tag
  1592.         // :artwork name='img/xfwps_mini.bmp' runin align=left.
  1593.         // :artlink.:link reftype=hd res=58.:eartlink.
  1594.         // V0.9.20 (2002-07-12) [umoeller]
  1595.         if (pstat->ulInLink)
  1596.         {
  1597.             xstrCatf(pxstrIPF,
  1598.                      "\n:artlink.\n%s\n:eartlink.\n",
  1599.                      pstat->strLinkTag.psz);
  1600.                         // e.g. ":link reftype=hd res=58."
  1601.  
  1602.             // do not add another :elink.
  1603.             pstat->ulInLink = 2;
  1604.         }
  1605.  
  1606.         xstrClear(&str);
  1607.  
  1608.         pstat->fNeedsP = FALSE;
  1609.     }
  1610.     else
  1611.         return ("No SRC attribute to <IMG> tag.");
  1612.  
  1613.     return (0);
  1614. }
  1615.  
  1616. typedef struct _HANDLERDEF
  1617. {
  1618.     PCSZ        pcszTag;
  1619.     PTAGHANDLER pHandler;
  1620. } HANDLERDEF, *PHANDLERDEF;
  1621.  
  1622. typedef struct _HANDLERTREENODE
  1623. {
  1624.     TREE        Tree;           // ulkey is used for (ULONG)pcszTag
  1625.     PTAGHANDLER pHandler;
  1626. } HANDLERTREENODE, *PHANDLERTREENODE;
  1627.  
  1628. /*
  1629.  *@@ HandlersList:
  1630.  *      array of all tags we understand with the
  1631.  *      handler func that handles the tag, or NULL
  1632.  *      if the tag is to be silently ignored.
  1633.  *
  1634.  *      We build a string map from this list for
  1635.  *      fast lookup in main().
  1636.  *
  1637.  *@@added V0.9.16 (2001-11-22) [umoeller]
  1638.  */
  1639.  
  1640. HANDLERDEF HandlersList[] =
  1641.     {
  1642.         "P", HandleP,
  1643.         "BR", HandleBR,
  1644.         "LI", HandleLI,
  1645.         "IMG", HandleIMG,
  1646.         "HR", HandleHR,
  1647.         "I", HandleI,
  1648.         "EM", HandleI,
  1649.         "B", HandleB,
  1650.         "STRONG", HandleB,
  1651.         "U", HandleU,
  1652.         "CODE", HandleCODE,
  1653.         "CITE", HandleCODE,
  1654.         "TT", HandleCODE,
  1655.         "A", HandleA,
  1656.         "UL", HandleUL,
  1657.         "OL", HandleOL,
  1658.         "DL", HandleDL,
  1659.         "DT", HandleDT,
  1660.         "DD", HandleDD,
  1661.         "HEAD", HandleHEAD,
  1662.         "PRE", HandlePRE,
  1663.         "HTML", HandleHTML,
  1664.         "TITLE", HandleTITLE,
  1665.         "IFDEF", HandleIFDEF,
  1666.         "IFNDEF", HandleIFNDEF,
  1667.         "BODY", NULL,           // ignore
  1668.         "H1", NULL,             // ignore
  1669.         "H2", NULL,             // ignore
  1670.         "H3", NULL,             // ignore
  1671.         "H4", NULL,             // ignore
  1672.         "H5", NULL,             // ignore
  1673.         "H6", NULL              // ignore
  1674.     };
  1675.  
  1676. TREE *G_HandlersTreeRoot;
  1677.  
  1678. /*
  1679.  *@@ HandleTag:
  1680.  *      HTML tag handler. Gets called from ParseFile
  1681.  *      whenever a '<' character is found in the HTML
  1682.  *      sources. We then look up the tag name in the
  1683.  *      string map we built in main() and call the
  1684.  *      respective handler, if one exists.
  1685.  *
  1686.  *@@changed V0.9.16 (2001-11-22) [umoeller]: major speedup with tag handlers string map
  1687.  */
  1688.  
  1689. const char* HandleTag(PARTICLETREENODE pFile2Process,
  1690.                       PSTATUS pstat)    // in/out: parser status
  1691. {
  1692.     const char *pcszError = NULL;
  1693.     PXSTRING pxstrIPF = &pFile2Process->strIPF;
  1694.  
  1695.     PSZ pStartOfTagName = pstat->pSource + 1;
  1696.  
  1697.     // is this a comment?
  1698.     if (!strncmp(pStartOfTagName, "!--", 3))
  1699.     {
  1700.         // start of comment:
  1701.         // find end of comment
  1702.         PSZ pEnd;
  1703.         if (pEnd = strstr(pStartOfTagName + 3, "-->"))
  1704.         {
  1705.             // found:
  1706.             // search on after end of comment
  1707.             pstat->pSource = pEnd + 2;
  1708.         }
  1709.         else
  1710.         {
  1711.             // end of comment not found:
  1712.             // stop formatting...
  1713.             pcszError = "Terminating comment not found.";
  1714.             pstat->fFatal = TRUE;
  1715.         }
  1716.     }
  1717.     else
  1718.     {
  1719.         // no comment:
  1720.         // find end of tag
  1721.         PSZ     p2 = pStartOfTagName,
  1722.                 // pNextClose = 0,     // receives first '>' after '<'
  1723.                 pNextSpace = 0;     // receives first ' ' after '<'
  1724.         BOOL    fCont = TRUE;
  1725.  
  1726.         pstat->pNextClose = 0;
  1727.  
  1728.         while (fCont)
  1729.         {
  1730.             switch (*p2)
  1731.             {
  1732.                 case ' ':
  1733.                 case '\r':
  1734.                 case '\n':
  1735.                 case '\t':
  1736.                     // store first space after '<'
  1737.                     if (!pNextSpace)
  1738.                         pNextSpace = p2;
  1739.                     *p2 = ' ';
  1740.                 break;
  1741.  
  1742.                 case '>':   // end of tag found:
  1743.                     pstat->pNextClose = p2;
  1744.                     fCont = FALSE;
  1745.                 break;
  1746.  
  1747.                 case '<':
  1748.                     // another opening tag:
  1749.                     // that's an HTML error
  1750.                     return ("Opening < within another tag found.");
  1751.                 break;
  1752.  
  1753.                 case 0:
  1754.                     fCont = FALSE;
  1755.                 break;
  1756.             }
  1757.             p2++;
  1758.         }
  1759.  
  1760.         if (pstat->pNextClose)
  1761.         {
  1762.             // end of tag found:
  1763.             ULONG cbTag;
  1764.             PSZ pszAttrs = NULL;
  1765.  
  1766.             if ((pNextSpace) && (pNextSpace < pstat->pNextClose))
  1767.             {
  1768.                 // we have attributes:
  1769.                 cbTag = pNextSpace - pStartOfTagName;
  1770.                 pszAttrs = strhSubstr(pNextSpace, pstat->pNextClose);
  1771.             }
  1772.             else
  1773.                 cbTag = pstat->pNextClose - pStartOfTagName;
  1774.  
  1775.             if (!cbTag)
  1776.             {
  1777.                 // happens if we have a "<>" in the text:
  1778.                 // just insert the '<>' and go on, we have no tag here
  1779.                 xstrcatc(pxstrIPF, *(pstat->pSource)++);
  1780.                 xstrcatc(pxstrIPF, *(pstat->pSource)++);
  1781.                 pstat->ulLineLength += 2;
  1782.             }
  1783.             else
  1784.             {
  1785.                 PHANDLERTREENODE pHandlerTreeNode;
  1786.                 PTAGHANDLER pHandler = NULL;
  1787.                 BOOL fClosingTag = FALSE;
  1788.  
  1789.                 // attributes come after this
  1790.                 // and go up to pNextClose
  1791.  
  1792.                 // add a null terminator so we can
  1793.                 // use strcmp for testing for tags
  1794.                 CHAR cSaved = *(pStartOfTagName + cbTag);
  1795.                 *(pStartOfTagName + cbTag) = '\0';
  1796.  
  1797.                 // printf("'%s' ", pStartOfTagName);
  1798.  
  1799.                 // tags which can be open or closed
  1800.                 // (e.g. B or /B):
  1801.                 PSZ pStart2 = pStartOfTagName;
  1802.  
  1803.                 if (*pStart2 == '/')
  1804.                 {
  1805.                     // closing tag:
  1806.                     fClosingTag = TRUE;
  1807.                     // use next char for strcmp
  1808.                     pStart2++;
  1809.                 }
  1810.  
  1811.                 // now handle tags
  1812.                 if (pHandlerTreeNode = (PHANDLERTREENODE)treeFind(G_HandlersTreeRoot,
  1813.                                                                   (ULONG)pStart2,
  1814.                                                                   fnCompareStrings))
  1815.                 {
  1816.                     // handler tree node found:
  1817.                     if (pHandlerTreeNode->pHandler)
  1818.                         // tag needs handling code:
  1819.                         pcszError = pHandlerTreeNode->pHandler(pFile2Process,
  1820.                                                                pxstrIPF,
  1821.                                                                pstat,
  1822.                                                                fClosingTag,
  1823.                                                                pszAttrs);
  1824.                 }
  1825.                 else
  1826.                 {
  1827.                     xstrcpy(&G_strError, "", 0);
  1828.                     xstrCatf(&G_strError,
  1829.                                "Unknown tag %s (%s)",
  1830.                                pStartOfTagName,
  1831.                                pStart2);
  1832.                     pcszError = G_strError.psz;
  1833.                 }
  1834.  
  1835.                 // restore char under null terminator
  1836.                 *(pStartOfTagName + cbTag) = cSaved;
  1837.  
  1838.                 // skip the tag, even if unknown
  1839.                 pstat->pSource = pstat->pNextClose;
  1840.  
  1841.                 if (pszAttrs)
  1842.                     free(pszAttrs);
  1843.             }
  1844.         } // end if (pNextClose)
  1845.         else
  1846.         {
  1847.             xstrcatc(pxstrIPF,
  1848.                      *(pstat->pSource));
  1849.             // no closing tag found:
  1850.             // just return, caller will try next char
  1851.             // (probably no tag anyway)
  1852.         }
  1853.     }
  1854.  
  1855.     return (pcszError);
  1856. }
  1857.  
  1858. /*
  1859.  *@@ ParseFile:
  1860.  *      converts the HTML code in the buffer to IPF.
  1861.  *
  1862.  *      This is a complete conversion except:
  1863.  *
  1864.  *      --  automatic resids will be assigned later,
  1865.  *          unless this file has <HTML RESID=xxx> set;
  1866.  *
  1867.  *      --  the :hX. tag will not be added here
  1868.  *          (because it depends on sublinks).
  1869.  *
  1870.  *      This adds files to G_llFiles2Process if they
  1871.  *      are linked to from this file by calling
  1872.  *      GetOrCreateArticle.
  1873.  */
  1874.  
  1875. APIRET ParseFile(PARTICLETREENODE pFile2Process,
  1876.                  PSZ pszBuf)
  1877. {
  1878.     PXSTRING pxstrIPF = &pFile2Process->strIPF;
  1879.  
  1880.     STATUS stat = {0};
  1881.     lstInit(&stat.llListStack, FALSE);
  1882.     xstrInit(&stat.strLinkTag, 0);       // V0.9.20 (2002-07-12) [umoeller]
  1883.     stat.fJustHadSpace = TRUE;
  1884.  
  1885.     // start at beginning of buffer
  1886.     stat.pSource = pszBuf;
  1887.  
  1888.     while (TRUE)
  1889.     {
  1890.         const char *pcszError = NULL;
  1891.         CHAR    c = *(stat.pSource);
  1892.         PSZ     pSaved = stat.pSource;
  1893.  
  1894.         switch (c)
  1895.         {
  1896.             case '<':
  1897.                 pcszError = HandleTag(pFile2Process,
  1898.                                       &stat);
  1899.             break;
  1900.  
  1901.             case ':':
  1902.                 if (!stat.fInHead)
  1903.                 {
  1904.                     CheckP(pxstrIPF, &stat);
  1905.                     xstrcat(pxstrIPF, "&colon.", 7);
  1906.                     stat.ulLineLength += 7;
  1907.                 }
  1908.             break;
  1909.  
  1910.             case '.':
  1911.                 if (!stat.fInHead)
  1912.                 {
  1913.                     CheckP(pxstrIPF, &stat);
  1914.                     xstrcat(pxstrIPF, "&per.", 5);
  1915.                     stat.ulLineLength += 5;
  1916.                 }
  1917.             break;
  1918.  
  1919.             case '\r':
  1920.                 // just skip this
  1921.             break;
  1922.  
  1923.             case '\n':
  1924.             case ' ':
  1925.                 // throw out all line breaks and spaces
  1926.                 // unless we're in PRE mode
  1927.                 if (stat.fInPre)
  1928.                     xstrcatc(pxstrIPF,
  1929.                              c);
  1930.                 else
  1931.                 {
  1932.                     CheckP(pxstrIPF, &stat);
  1933.                     // add a space instead, if we didn't just have one
  1934.                     // dump duplicate spaces
  1935.                     if (!stat.fJustHadSpace)
  1936.                     {
  1937.                         // on spaces, check how long the line is already...
  1938.                         if (stat.ulLineLength > 50)
  1939.                         {
  1940.                             xstrcatc(pxstrIPF, '\n');
  1941.                             stat.ulLineLength = 0;
  1942.                         }
  1943.                         else
  1944.                         {
  1945.                             xstrcatc(pxstrIPF, ' ');
  1946.                             (stat.ulLineLength)++;
  1947.                         }
  1948.                         stat.fJustHadSpace = TRUE;
  1949.                     }
  1950.                 }
  1951.  
  1952.             break;
  1953.  
  1954.             case '\0':
  1955.                 // null terminator reached:
  1956.                 // some safety checks
  1957.                 if (stat.fInHead)
  1958.                     pcszError = "No closing </HEAD> tag found.";
  1959.             break;
  1960.  
  1961.             default:
  1962.                 if (!stat.fInHead)
  1963.                 {
  1964.                     CheckP(pxstrIPF, &stat);
  1965.                     xstrcatc(pxstrIPF,
  1966.                              c);
  1967.                     (stat.ulLineLength)++;
  1968.                     stat.fJustHadSpace = FALSE;
  1969.                 }
  1970.         }
  1971.  
  1972.         if (pcszError)
  1973.         {
  1974.             // get the line count
  1975.             CHAR c = *pSaved;
  1976.             *pSaved = 0;
  1977.             Error(1,
  1978.                   __FILE__, __LINE__, __FUNCTION__,
  1979.                   "file \"%s\", line %d:\n   %s",
  1980.                   (PSZ)pFile2Process->Tree.ulKey,       // file name
  1981.                   strhCount(pszBuf, '\n') + 1,
  1982.                   pcszError);
  1983.             *pSaved = c;
  1984.             if (stat.fFatal)
  1985.                 return (1);
  1986.             // otherwise continue
  1987.         }
  1988.  
  1989.         if (*(stat.pSource))
  1990.             (stat.pSource)++;
  1991.         else
  1992.             break;
  1993.     }
  1994.  
  1995.     ConvertEscapes(&pFile2Process->strTitle);
  1996.     ConvertEscapes(pxstrIPF);
  1997.  
  1998.     return (0);
  1999. }
  2000.  
  2001. /* ******************************************************************
  2002.  *
  2003.  *   File processor
  2004.  *
  2005.  ********************************************************************/
  2006.  
  2007. /*
  2008.  *@@ AppendToMainBuffer:
  2009.  *
  2010.  */
  2011.  
  2012. VOID AppendToMainBuffer(PXSTRING pxstrIPF,
  2013.                         PARTICLETREENODE pFile2Process,
  2014.                         PXSTRING pstrExtras)       // temp buffer for speed
  2015. {
  2016.     if (pFile2Process->ulHeaderLevel == 1)
  2017.     {
  2018.         // root file:
  2019.         xstrCatf(pxstrIPF,
  2020.                    ":title.%s\n",
  2021.                    pFile2Process->strTitle.psz);
  2022.     }
  2023.  
  2024.     xstrCatf(pxstrIPF,
  2025.                "\n.* Source file: \"%s\"\n",
  2026.                (PSZ)pFile2Process->Tree.ulKey); // pszFilename);
  2027.  
  2028.     xstrcpy(pstrExtras, NULL, 0);
  2029.     if (pFile2Process->lGroup)
  2030.         xstrCatf(pstrExtras,
  2031.                    " group=%d",
  2032.                    pFile2Process->lGroup);
  2033.     if (pFile2Process->pszXPos)
  2034.         xstrCatf(pstrExtras,
  2035.                    " x=%s",
  2036.                    pFile2Process->pszXPos);
  2037.     if (pFile2Process->pszWidth)
  2038.         xstrCatf(pstrExtras,
  2039.                    " width=%s",
  2040.                    pFile2Process->pszWidth);
  2041.     if (pFile2Process->fHidden)
  2042.         xstrcpy(pstrExtras, " hide", 0);
  2043.  
  2044.     ULONG ulHeaderLevel = pFile2Process->ulHeaderLevel;
  2045.     // if this is not at root level already,
  2046.     // move down one level (unless -r is specified)
  2047.     if (!G_fNoMoveToRoot)
  2048.         if (ulHeaderLevel > 1)
  2049.             ulHeaderLevel--;
  2050.  
  2051.     xstrCatf(pxstrIPF,
  2052.                ":h%d res=%d%s.%s\n",
  2053.                ulHeaderLevel,
  2054.                pFile2Process->ulResID,
  2055.                (pstrExtras->ulLength)
  2056.                     ? pstrExtras->psz
  2057.                     : "",
  2058.                pFile2Process->strTitle.psz);
  2059.     xstrcat(pxstrIPF,
  2060.             ":p.\n",
  2061.             0);
  2062.  
  2063.     // and the rest of the converted file
  2064.     xstrcats(pxstrIPF,
  2065.              &pFile2Process->strIPF);
  2066.  
  2067.     pFile2Process->fWritten = TRUE;
  2068. }
  2069.  
  2070. /*
  2071.  *@@ DumpArticlesWithParent:
  2072.  *
  2073.  */
  2074.  
  2075. VOID DumpArticlesWithParent(PXSTRING pxstrIPF,
  2076.                             PARTICLETREENODE pParent,
  2077.                             PXSTRING pstrExtras)
  2078. {
  2079.     PLISTNODE pNode = lstQueryFirstNode(&G_llFiles2Process);
  2080.     while (pNode)
  2081.     {
  2082.         PARTICLETREENODE pFile2Process = (PARTICLETREENODE)pNode->pItemData;
  2083.  
  2084.         // rule out special links for now
  2085.         if (    (pFile2Process->ulHeaderLevel != -1)
  2086.              && (!pFile2Process->fWritten)
  2087.            )
  2088.         {
  2089.             if (pFile2Process->pFirstReferencedFrom == pParent)
  2090.             {
  2091.                 if (G_ulVerbosity > 1)
  2092.                     printf("File \"%s\" has resid %d, %d bytes IPF\n",
  2093.                            (PSZ)pFile2Process->Tree.ulKey, // pszFilename,
  2094.                            pFile2Process->ulResID,
  2095.                            pFile2Process->strIPF.ulLength);
  2096.  
  2097.                 AppendToMainBuffer(pxstrIPF,
  2098.                                    pFile2Process,
  2099.                                    pstrExtras);
  2100.  
  2101.                 // recurse with this node
  2102.                 DumpArticlesWithParent(pxstrIPF,
  2103.                                        pFile2Process,
  2104.                                        pstrExtras);
  2105.             }
  2106.  
  2107.             // build a linked list of articles which belong
  2108.             // under this article
  2109.  
  2110.             /* PLISTNODE pSubnode = lstQueryFirstNode(&G_llFiles2Process);
  2111.             while (pSubnode)
  2112.             {
  2113.                 PARTICLETREENODE pSub = (PARTICLETREENODE)pSubnode->pItemData;
  2114.  
  2115.                 if (    (pSub->pFirstReferencedFrom == pFile2Process)
  2116.                      && (pSub->ulHeaderLevel != -1)
  2117.                    )
  2118.                 {
  2119.                     if (G_ulVerbosity > 1)
  2120.                         printf("   loop 2: Writing out %s\n",
  2121.                                pSub->pszFilename);
  2122.                     AppendToMainBuffer(pxstrIPF,
  2123.                                        pSub,
  2124.                                        &strExtras);
  2125.                         // marks the file as "written"
  2126.                 }
  2127.  
  2128.                 pSubnode = pSubnode->pNext;
  2129.             } */
  2130.         }
  2131.  
  2132.         // now for the next main node
  2133.         pNode = pNode->pNext;
  2134.     }
  2135. }
  2136.  
  2137. /*
  2138.  *@@ ProcessFiles:
  2139.  *      loops through all files.
  2140.  *
  2141.  *      When this is called (from main()), G_llFiles2Process
  2142.  *      contains only the one file that was specified on
  2143.  *      the command line. This then calls ParseFile() on
  2144.  *      that file, which will add to the list for every
  2145.  *      A tag that links to another file.
  2146.  *
  2147.  *      After ParseFile() has been called for each such
  2148.  *      file then, we run several additional loops here
  2149.  *      for resid resolution and such.
  2150.  *
  2151.  *      Finally, in the last loop, the IPF code from all
  2152.  *      files is added to the one given XSTRING buffer,
  2153.  *      which will then be written out to disk by the
  2154.  *      caller (main()).
  2155.  */
  2156.  
  2157. APIRET ProcessFiles(PXSTRING pxstrIPF)           // out: one huge IPF file
  2158. {
  2159.     PLISTNODE pNode;
  2160.     APIRET arc = NO_ERROR;
  2161.  
  2162.     /*
  2163.      * loop 1:
  2164.      *
  2165.      */
  2166.  
  2167.     if (G_ulVerbosity > 1)
  2168.         printf("Reading source files...\n");
  2169.  
  2170.     // go thru the list of files to process and
  2171.     // translate them from HTML to IPF...
  2172.     // when this func gets called, this list
  2173.     // contains only the root file, but ParseFile
  2174.     // will add new files to the list, so we just
  2175.     // keep going
  2176.     XSTRING strSource;
  2177.     xstrInit(&strSource, 0);
  2178.  
  2179.     pNode = lstQueryFirstNode(&G_llFiles2Process);
  2180.     while (pNode)
  2181.     {
  2182.         PARTICLETREENODE pFile2Process = (PARTICLETREENODE)pNode->pItemData;
  2183.  
  2184.         // rule out special links for now
  2185.         if (pFile2Process->ulHeaderLevel != -1)
  2186.         {
  2187.             PSZ pszContents = NULL;
  2188.  
  2189.             if (G_ulVerbosity > 1)
  2190.             {
  2191.                 printf("processing %s... ",
  2192.                        (PSZ)pFile2Process->Tree.ulKey); // pszFilename);
  2193.                 fflush(stdout);
  2194.             }
  2195.             else if (G_ulVerbosity)
  2196.             {
  2197.                 printf(".");
  2198.                 fflush(stdout);
  2199.             }
  2200.  
  2201.             xstrcpy(&G_strCrashContext, "", 0);
  2202.             xstrCatf(&G_strCrashContext,
  2203.                        "Processing file %s",
  2204.                        (PSZ)pFile2Process->Tree.ulKey);
  2205.  
  2206.             ULONG cbRead = 0;
  2207.             if (!(arc = doshLoadTextFile((PSZ)pFile2Process->Tree.ulKey, // pszFilename,
  2208.                                          &pszContents,
  2209.                                          &cbRead)))
  2210.             {
  2211.                 xstrset2(&strSource, pszContents, cbRead - 1);
  2212.                 xstrConvertLineFormat(&strSource, CRLF2LF);
  2213.  
  2214.                 ResolveEntities(pFile2Process,
  2215.                                 &strSource);
  2216.  
  2217.                 // now go convert this buffer to IPF
  2218.                 if (!(arc = ParseFile(pFile2Process,
  2219.                                       strSource.psz)))
  2220.                 {
  2221.                 }
  2222.  
  2223.                 pFile2Process->fProcessed = TRUE;
  2224.  
  2225.                 if (G_ulVerbosity > 1)
  2226.                     printf("\n");
  2227.             }
  2228.  
  2229.             if (arc)
  2230.             {
  2231.                 Error(2,
  2232.                       __FILE__, __LINE__, __FUNCTION__,
  2233.                       "Error %d occured reading file \"%s\".",
  2234.                       arc,
  2235.                       (PSZ)pFile2Process->Tree.ulKey); // pszFilename);
  2236.                 arc = 1;
  2237.             }
  2238.         }
  2239.  
  2240.         if (arc)
  2241.             break;
  2242.  
  2243.         // next file (ParseFile has added files to list)
  2244.         pNode = pNode->pNext;
  2245.     }
  2246.  
  2247.     if (!arc)
  2248.     {
  2249.         /*
  2250.          * loop 2:
  2251.          *
  2252.          */
  2253.  
  2254.         // go through the entire file list again
  2255.         // and assign an automatic resid if this
  2256.         // has not been set yet
  2257.  
  2258.         if (G_ulVerbosity > 1)
  2259.             printf("Assigning automatic resids...\n");
  2260.  
  2261.         ULONG ulNextResID = 10000;
  2262.  
  2263.         pNode = lstQueryFirstNode(&G_llFiles2Process);
  2264.         while (pNode)
  2265.         {
  2266.             PARTICLETREENODE pFile2Process = (PARTICLETREENODE)pNode->pItemData;
  2267.  
  2268.             // process special links too
  2269.             if (pFile2Process->ulResID == -1)
  2270.                 pFile2Process->ulResID = ulNextResID++;
  2271.  
  2272.             pNode = pNode->pNext;
  2273.         }
  2274.  
  2275.         if (G_ulVerbosity > 1)
  2276.             printf("  Done, %d resids assigned (%d files had explicit resids)\n",
  2277.                    (ulNextResID - 10000),
  2278.                    lstCountItems(&G_llFiles2Process) - (ulNextResID - 10000));
  2279.  
  2280.         /*
  2281.          * loop 3:
  2282.          *
  2283.          */
  2284.  
  2285.         if (G_ulVerbosity > 1)
  2286.             printf("Replacing link strings...\n");
  2287.  
  2288.         // go through the entire file list again
  2289.         // and in each IPF buffer, replace the
  2290.         // special link strings with the good resids,
  2291.         // which are known by now
  2292.  
  2293.         ULONG ulStartKeyLen = strlen(START_KEY),
  2294.               ulEndKeyLen = strlen(END_KEY);
  2295.  
  2296.         pNode = lstQueryFirstNode(&G_llFiles2Process);
  2297.         while (pNode && !arc)
  2298.         {
  2299.             PARTICLETREENODE pFile2Process = (PARTICLETREENODE)pNode->pItemData;
  2300.  
  2301.             // rule out special links for now
  2302.             if (pFile2Process->ulHeaderLevel != -1)
  2303.             {
  2304.                 PSZ pStart;
  2305.  
  2306.                 if (pStart = pFile2Process->strIPF.psz)
  2307.                         // could be empty V0.9.16 (2001-11-22) [umoeller]
  2308.                 {
  2309.                     PSZ p;
  2310.                     // now, find the special ugly link keys
  2311.                     // we hacked in before; for each string
  2312.                     // target filename now, find the resid
  2313.  
  2314.                     while (p = (strstr(pStart,
  2315.                                        START_KEY)))
  2316.                     {
  2317.                         PSZ pFirst = pFile2Process->strIPF.psz;
  2318.  
  2319.                         PSZ p2 = strstr(p + ulStartKeyLen,
  2320.                                         END_KEY);
  2321.                         if (!p2)
  2322.                         {
  2323.                             Error(2,
  2324.                                   __FILE__, __LINE__, __FUNCTION__,
  2325.                                   "Cannot find second LINK string for \"%s\"\nFile: \"%s\"\n",
  2326.                                   p,
  2327.                                   (PSZ)pFile2Process->Tree.ulKey); // pszFilename);
  2328.                             arc = 1;
  2329.                             break;
  2330.                         }
  2331.  
  2332.                         CHAR cSaved = *p2;
  2333.                         *p2 = '\0';
  2334.  
  2335.                         if (G_ulVerbosity > 2)
  2336.                             printf("   encountered link \"%s\" at %d (len %d), searching resid\n",
  2337.                                    p + ulStartKeyLen,
  2338.                                    p - pFirst,
  2339.                                    p2 - pFirst);
  2340.  
  2341.                         PARTICLETREENODE pTarget = GetOrCreateArticle(p + ulStartKeyLen, // filename
  2342.                                                                       -1,   // do not create
  2343.                                                                       NULL);
  2344.  
  2345.                         if (!pTarget)
  2346.                         {
  2347.                             Error(2,
  2348.                                   __FILE__, __LINE__, __FUNCTION__,
  2349.                                   "Cannot resolve cross reference for \"%s\"\nFile: \"%s\"\n",
  2350.                                   p + ulStartKeyLen,
  2351.                                   (PSZ)pFile2Process->Tree.ulKey); // pszFilename);
  2352.                             arc = 1;
  2353.                             break;
  2354.                         }
  2355.  
  2356.                         *p2 = cSaved;
  2357.  
  2358.                         CHAR szResID[30];
  2359.                         sprintf(szResID, "%d", pTarget->ulResID);
  2360.                         ULONG ulResIDLen = strlen(szResID);
  2361.                         ULONG   ulFirst = p - pFirst,
  2362.                                 cReplace = (p2 + ulStartKeyLen) - p;
  2363.                         if (G_ulVerbosity > 2)
  2364.                             printf("     ofs %d, cReplace %d, replacing with \"%s\"\n",
  2365.                                    ulFirst,
  2366.                                    cReplace,
  2367.                                    szResID);
  2368.                         xstrrpl(&pFile2Process->strIPF,
  2369.                                 // ofs of first char to replace:
  2370.                                 ulFirst,
  2371.                                 // count of chars to replace:
  2372.                                 cReplace, // (p2 + KEYLEN) - p,
  2373.                                 // replace this with the resid
  2374.                                 szResID,
  2375.                                 ulResIDLen);
  2376.  
  2377.                         pStart = pFile2Process->strIPF.psz + ulFirst;
  2378.                     }
  2379.                 }
  2380.             }
  2381.  
  2382.             pNode = pNode->pNext;
  2383.         }
  2384.  
  2385.         if (!arc)
  2386.         {
  2387.             XSTRING strExtras;
  2388.             xstrInit(&strExtras, 0);
  2389.  
  2390.             /*
  2391.              * loop 4:
  2392.              *
  2393.              */
  2394.  
  2395.             if (G_ulVerbosity > 1)
  2396.                 printf("Composing IPF buffer...\n");
  2397.  
  2398.             LINKLIST llSubarticles;
  2399.             lstInit(&llSubarticles, FALSE);
  2400.  
  2401.             // now dump out IPF into one huge buffer...
  2402.             // note, this loop will go thru all files again,
  2403.             // but since we have a second loop within which
  2404.             // marks files as written, this will effectively
  2405.             // only process the root items
  2406.             DumpArticlesWithParent(pxstrIPF,
  2407.                                    NULL,       // root documents only
  2408.                                    &strExtras);
  2409.                     // this will recurse
  2410.  
  2411.             /*
  2412.              * loop 5:
  2413.              *
  2414.              */
  2415.  
  2416.             // add all the special links
  2417.             // to the bottom; these should be the only
  2418.             // unprocessed files, or something went wrong here
  2419.             pNode = lstQueryFirstNode(&G_llFiles2Process);
  2420.             BOOL fFirst = TRUE;
  2421.             XSTRING strEncode;
  2422.             xstrInit(&strEncode, 0);
  2423.             while (pNode)
  2424.             {
  2425.                 PARTICLETREENODE pFile2Process = (PARTICLETREENODE)pNode->pItemData;
  2426.  
  2427.                 // process only the special files now
  2428.                 if (pFile2Process->ulHeaderLevel == -1)
  2429.                 {
  2430.                     if (fFirst)
  2431.                     {
  2432.                         xstrcat(pxstrIPF,
  2433.                                 ":h1 group=99 x=right width=30%.",
  2434.                                 0);
  2435.                         xstrcat(pxstrIPF,
  2436.                                 G_pcszResourcesOnInternetTitle,
  2437.                                 0);
  2438.                         xstrcatc(pxstrIPF, '\n');
  2439.                         xstrcat(pxstrIPF,
  2440.                                 G_pcszResourcesOnInternetBody,
  2441.                                 0);
  2442.                         xstrcatc(pxstrIPF, '\n');
  2443.                         fFirst = FALSE;
  2444.                     }
  2445.  
  2446.                     // encode the strings again because STUPID ipf
  2447.                     // gets confused otherwise
  2448.                     xstrcpy(&strEncode,
  2449.                             (PSZ)pFile2Process->Tree.ulKey, // pszFilename,
  2450.                             0);
  2451.                     ConvertEscapes(&strEncode);
  2452.                     ULONG ulOfs = 0;
  2453.                     while (xstrFindReplaceC(&strEncode,
  2454.                                             &ulOfs,
  2455.                                             ":",
  2456.                                             "&colon."))
  2457.                         ;
  2458.  
  2459.                     xstrCatf(pxstrIPF,
  2460.                                ":h2 res=%d group=98 x=right y=bottom width=60%% height=40%%.%s\n",
  2461.                                pFile2Process->ulResID,
  2462.                                strEncode.psz);
  2463.                     xstrCatf(pxstrIPF,
  2464.                                ":p.:lines align=center."
  2465.                                     "\n%s\n"
  2466.                                     ":p.:link reftype=launch object='netscape.exe' data='%s'.\n"
  2467.                                     "%s\n"
  2468.                                     ":elink.:elines.\n",
  2469.                                G_pcszClickBelow,
  2470.                                (PSZ)pFile2Process->Tree.ulKey, // pszFilename,
  2471.                                strEncode.psz);
  2472.                 }
  2473.                 else
  2474.                     if (!pFile2Process->fProcessed)
  2475.                         Error(2,
  2476.                               __FILE__, __LINE__, __FUNCTION__,
  2477.                               "Strange, file \"%s\" wasn't processed.",
  2478.                               (PSZ)pFile2Process->Tree.ulKey); // pszFilename);
  2479.  
  2480.                 pNode = pNode->pNext;
  2481.             }
  2482.  
  2483.             if (G_ulVerbosity > 1)
  2484.                 printf("\nDone processing files.\n");
  2485.             else
  2486.                 printf("\n");
  2487.         }
  2488.     }
  2489.  
  2490.     return (arc);
  2491. }
  2492.  
  2493. /* ******************************************************************
  2494.  *
  2495.  *   main
  2496.  *
  2497.  ********************************************************************/
  2498.  
  2499. /*
  2500.  *@@ AddDefine:
  2501.  *
  2502.  */
  2503.  
  2504. BOOL AddDefine(const char *pcszDefine)      // in: first char after #define
  2505. {
  2506.     PCSZ    p = pcszDefine;
  2507.     PSZ     pszIdentifier = NULL,
  2508.             pszValue = NULL;
  2509.  
  2510.     /*
  2511.                     printf("testing %c%c%c%c%c%c%c%c%c%c\n",
  2512.                             *p,     *(p+1), *(p+2), *(p+3), *(p+4),
  2513.                             *(p+5), *(p+6), *(p+7), *(p+8), *(p+9));
  2514.     */
  2515.  
  2516.     while ((*p) && (!pszValue))
  2517.     {
  2518.         switch (*p)
  2519.         {
  2520.             // skip comments
  2521.             case '/':
  2522.             case '\r':
  2523.             case '\n':
  2524.                 // forget it
  2525.                     // printf("      quitting\n");
  2526.                 return (FALSE);
  2527.  
  2528.             case ' ':
  2529.             case '\t':
  2530.             break;
  2531.  
  2532.             case '"':
  2533.             {
  2534.                 if (pszIdentifier)
  2535.                 {
  2536.                     /* printf("testing %c%c%c%c%c%c%c%c%c%c\n",
  2537.                             *p,     *(p+1), *(p+2), *(p+3), *(p+4),
  2538.                             *(p+5), *(p+6), *(p+7), *(p+8), *(p+9)); */
  2539.  
  2540.                     // looks like a string constant...
  2541.                     PCSZ pEnd = p + 1;
  2542.                     while (*pEnd && !pszValue)
  2543.                     {
  2544.                         switch (*pEnd)
  2545.                         {
  2546.                             case '\r':
  2547.                             case '\n':
  2548.                                 // forget it
  2549.                                 return (FALSE);
  2550.  
  2551.                             case '"':
  2552.                                 // allow for \" escape
  2553.                                 // V0.9.20 (2002-07-12) [umoeller]
  2554.                                 if (pEnd[-1] != '\\')
  2555.                                     // looks goood!
  2556.                                     pszValue = strhSubstr(p + 1,    // after quote
  2557.                                                           pEnd);
  2558.                                     // printf("-->found \"%s\"\n", pszValue);
  2559.                             break;
  2560.                         }
  2561.  
  2562.                         if ((pEnd) && (*pEnd))
  2563.                             pEnd++;
  2564.                         else
  2565.                             break;
  2566.                     }
  2567.                 }
  2568.                 else
  2569.                     // quote before identifier: don't think so
  2570.                     return (FALSE);
  2571.             }
  2572.             break;
  2573.  
  2574.             default:
  2575.             {
  2576.                 // regular character:
  2577.                 // go to the next space
  2578.                 // this must be the identifier then
  2579.     /*
  2580.     printf("testing %c%c%c%c%c\n",
  2581.             *p, *(p+1), *(p+2), *(p+3), *(p+4));
  2582.             */
  2583.                 PCSZ pEnd = p;          // first non-space character
  2584.                 while (    (*pEnd)
  2585.                         && (*pEnd != ' ')
  2586.                         && (*pEnd != '\t')
  2587.                         && (*pEnd != '\n')
  2588.                       )
  2589.                     pEnd++;
  2590.  
  2591.                 if (    (*pEnd == ' ')
  2592.                      || (*pEnd == '\t')
  2593.                      || ((pszIdentifier) && (*pEnd == '\n'))
  2594.                    )
  2595.                 {
  2596.                     // got it
  2597.                     if (!pszIdentifier)
  2598.                     {
  2599.                         pszIdentifier = strhSubstr(p, pEnd);
  2600.                                 // printf("  got identifier \"%s\"\n", pszIdentifier);
  2601.                     }
  2602.                     else
  2603.                         pszValue = strhSubstr(p, pEnd);
  2604.  
  2605.                     // go on after this
  2606.                     p = pEnd;
  2607.                 }
  2608.                 else
  2609.                     // bull
  2610.                     return (FALSE);
  2611.             }
  2612.             break;
  2613.         }
  2614.  
  2615.         if ((p) && (*p))
  2616.             p++;
  2617.         else
  2618.             return (FALSE);
  2619.     }
  2620.  
  2621.     if (pszValue)
  2622.     {
  2623.         if (!strcmp(pszIdentifier, "H2I_INTERNET_TITLE"))
  2624.         {
  2625.             G_pcszResourcesOnInternetTitle = pszValue;
  2626.         }
  2627.         else if (!strcmp(pszIdentifier, "H2I_INTERNET_BODY"))
  2628.         {
  2629.             G_pcszResourcesOnInternetBody = pszValue;
  2630.         }
  2631.         else if (!strcmp(pszIdentifier, "H2I_INTERNET_CLICK"))
  2632.         {
  2633.             G_pcszClickBelow = pszValue;
  2634.         }
  2635.         else
  2636.         {
  2637.             PDEFINENODE pMapping = NEW(DEFINENODE);
  2638.             pMapping->Tree.ulKey = (ULONG)pszIdentifier;
  2639.  
  2640.             // handle escapes V0.9.20 (2002-07-12) [umoeller]
  2641.             XSTRING strValue;
  2642.             xstrInitSet(&strValue, pszValue);
  2643.  
  2644.             ULONG ulOfs;
  2645.             ulOfs = 0;
  2646.             while (xstrFindReplaceC(&strValue,
  2647.                                     &ulOfs,
  2648.                                     // replace \" with "
  2649.                                     "\\\"",
  2650.                                     "\""))
  2651.                 ;
  2652.             ulOfs = 0;
  2653.             while (xstrFindReplaceC(&strValue,
  2654.                                     &ulOfs,
  2655.                                     // replace \r with carriage return
  2656.                                     "\\r",
  2657.                                     "\r"))
  2658.                 ;
  2659.             ulOfs = 0;
  2660.             while (xstrFindReplaceC(&strValue,
  2661.                                     &ulOfs,
  2662.                                     // replace \n with newline
  2663.                                     "\\n",
  2664.                                     "\n"))
  2665.                 ;
  2666.             ulOfs = 0;
  2667.             while (xstrFindReplaceC(&strValue,
  2668.                                     &ulOfs,
  2669.                                     // replace \\ with backslash
  2670.                                     "\\\\",
  2671.                                     "\\"))
  2672.                 ;
  2673.  
  2674.             pszValue = strValue.psz;
  2675.                 // do not free strValue
  2676.  
  2677.             pMapping->pszValue = pszValue;
  2678.             pMapping->ulValueLength = strlen(pszValue);
  2679.  
  2680.             if (AddDefinition(pMapping))
  2681.             {
  2682.                 if (G_ulVerbosity > 2)
  2683.                     printf("  found #define \"%s\" \"%s\"\n",
  2684.                            pszIdentifier,
  2685.                            pszValue);
  2686.                 G_cDefines++;
  2687.                 return (TRUE);
  2688.             }
  2689.         }
  2690.     }
  2691.  
  2692.     return (FALSE);
  2693. }
  2694.  
  2695. /*
  2696.  *@@ ParseCHeader:
  2697.  *
  2698.  */
  2699.  
  2700. APIRET ParseCHeader(const char *pcszHeaderFile,
  2701.                     PULONG pcDefines)
  2702. {
  2703.     PSZ pszContents;
  2704.     APIRET arc;
  2705.     ULONG cDefines = 0;
  2706.     ULONG cbRead = 0;
  2707.     if (!(arc = doshLoadTextFile(pcszHeaderFile,
  2708.                                  &pszContents,
  2709.                                  &cbRead)))
  2710.     {
  2711.         XSTRING strSource;
  2712.         xstrInitSet2(&strSource, pszContents, cbRead - 1);
  2713.         xstrConvertLineFormat(&strSource, CRLF2LF);
  2714.  
  2715.         PSZ p = strSource.psz;
  2716.  
  2717.         while (TRUE)
  2718.         {
  2719.             switch (*p)
  2720.             {
  2721.                 // skip comments
  2722.                 case '/':
  2723.                     switch (*(p+1))
  2724.                     {
  2725.                         case '/':       // C++ comment
  2726.                             // go to the end of the line, below
  2727.                             p = strhFindEOL(p + 1, NULL);
  2728.                         break;
  2729.  
  2730.                         case '*':       // C comment
  2731.                             p = strstr(p+2, "*/");
  2732.                         break;
  2733.  
  2734.                     }
  2735.                 break;
  2736.  
  2737.                 case '#':
  2738.                     if (!strncmp(p + 1,
  2739.                                  "define",
  2740.                                  6))
  2741.                         if (AddDefine(p + 7))
  2742.                             cDefines++;
  2743.  
  2744.                     // go to the end of the line,
  2745.                     // there can't be anything else
  2746.                     p = strhFindEOL(p + 1, NULL);
  2747.                 break;
  2748.  
  2749.                 case ' ':
  2750.                 case '\t':
  2751.                     // ignore
  2752.                     p++;
  2753.                     continue;
  2754.                 break;
  2755.             }
  2756.  
  2757.             if (p && (*p))
  2758.                 p++;
  2759.             else
  2760.                 break;
  2761.         }
  2762.  
  2763.         xstrClear(&strSource);
  2764.     }
  2765.  
  2766.     if (!arc)
  2767.         *pcDefines = cDefines;
  2768.  
  2769.     return (arc);
  2770. }
  2771.  
  2772. /*
  2773.  *@@ main:
  2774.  *      program entry point.
  2775.  *
  2776.  */
  2777.  
  2778. int main(int argc, char* argv[])
  2779. {
  2780.     int     rc = 0;
  2781.  
  2782.     /*
  2783.      * 1) initialization
  2784.      *
  2785.      */
  2786.  
  2787.     xstrInit(&G_strError, 0);
  2788.     xstrInit(&G_strCrashContext, 0);
  2789.  
  2790.     xstrcpy(&G_strCrashContext, "Startup", 0);
  2791.  
  2792.     TRY_LOUD(excpt1)
  2793.     {
  2794.         CHAR    szRootFile[CCHMAXPATH] = "";
  2795.  
  2796.         BOOL    fShowStatistics = FALSE;
  2797.  
  2798.         treeInit(&G_LinkIDsTreeRoot, NULL);
  2799.         treeInit(&G_DefinesTreeRoot, NULL);
  2800.         lstInit(&G_llFiles2Process, FALSE);
  2801.  
  2802.         LINKLIST llIncludes;
  2803.         lstInit(&llIncludes, TRUE);         // will hold plain -i filenames from strdup
  2804.  
  2805.         /*
  2806.          * 2) parse command line
  2807.          *
  2808.          */
  2809.  
  2810.         xstrcpy(&G_strCrashContext, "Parsing command line", 0);
  2811.  
  2812.         if (argc < 2)
  2813.         {
  2814.             Explain(NULL);
  2815.             rc = 999;
  2816.         }
  2817.         else
  2818.         {
  2819.             SHORT i = 0;
  2820.             while (    (i++ < argc - 1)
  2821.                     && (!rc)
  2822.                   )
  2823.             {
  2824.                 if (argv[i][0] == '-')
  2825.                 {
  2826.                     // option found:
  2827.                     SHORT i2;
  2828.                     for (i2 = 1;
  2829.                          i2 < strlen(argv[i]) && (!rc);
  2830.                          i2++)
  2831.                     {
  2832.                         CHAR cOption = argv[i][i2];
  2833.                         switch (cOption)
  2834.                         {
  2835.                             case 'h':
  2836.                             case '?':
  2837.                                 Explain(NULL);
  2838.                                 rc = 1;
  2839.                             break;
  2840.  
  2841.                             case 's':
  2842.                                 fShowStatistics = TRUE;
  2843.                             break;
  2844.  
  2845.                             case 'v':
  2846.                                 i2++;
  2847.                                 switch (argv[i][i2])
  2848.                                 {
  2849.                                     case '0': G_ulVerbosity = 0; break;
  2850.                                     case '1': G_ulVerbosity = 1; break;     // default also
  2851.                                     case '3': G_ulVerbosity = 3; break;
  2852.                                     case '4': G_ulVerbosity = 4; break;
  2853.                                     default: G_ulVerbosity = 2;
  2854.                                 }
  2855.                             break;
  2856.  
  2857.                             case 'i':
  2858.                                 if (strlen(&argv[i][i2+1]))
  2859.                                 {
  2860.                                     lstAppendItem(&llIncludes,
  2861.                                                   strdup(&argv[i][i2+1]));
  2862.                                 }
  2863.                                 else
  2864.                                 {
  2865.                                     Error(2,
  2866.                                           __FILE__, __LINE__, __FUNCTION__,
  2867.                                           "No filename specified with -i option.");
  2868.                                     rc = 99;
  2869.                                 }
  2870.                                 i2 = 999999999;
  2871.                             break;
  2872.  
  2873.                             case 'r':
  2874.                                 G_fNoMoveToRoot = TRUE;
  2875.                             break;
  2876.  
  2877.                             default:  // unknown option
  2878.                                 Explain("Unknown option '%c'.",
  2879.                                         cOption);
  2880.                                 rc = 999;
  2881.                             break;
  2882.                         }
  2883.                     }
  2884.                 } // end if (argv[i][0] == '-')
  2885.                 else
  2886.                 {
  2887.                     /*
  2888.                      * collect file names:
  2889.                      *
  2890.                      */
  2891.  
  2892.                     if (!szRootFile[0])
  2893.                         strcpy(szRootFile, argv[i]);
  2894.                     else
  2895.                     {
  2896.                         Explain("More than one root file specified.");
  2897.                         rc = 999;
  2898.                     }
  2899.                 } // end elseif (argv[i][0] == '-')
  2900.             }
  2901.         }
  2902.  
  2903.         if (!szRootFile[0] && !rc)
  2904.         {
  2905.             Explain("You have not specified any input file.");
  2906.             rc = 999;
  2907.         }
  2908.  
  2909.         if (!rc)
  2910.         {
  2911.             if (G_ulVerbosity)
  2912.                 PrintHeader();
  2913.  
  2914.             /*
  2915.              * build handlers tree
  2916.              *
  2917.              */
  2918.  
  2919.             treeInit(&G_HandlersTreeRoot, NULL);
  2920.  
  2921.             ULONG ul;
  2922.             for (ul = 0;
  2923.                  ul < ARRAYITEMCOUNT(HandlersList);
  2924.                  ul++)
  2925.             {
  2926.                 PHANDLERTREENODE pNode = NEW(HANDLERTREENODE);
  2927.                 pNode->Tree.ulKey = (ULONG)HandlersList[ul].pcszTag;
  2928.                 pNode->pHandler = HandlersList[ul].pHandler;
  2929.                 treeInsert(&G_HandlersTreeRoot,
  2930.                            NULL,
  2931.                            (TREE*)pNode,
  2932.                            fnCompareStrings);
  2933.             }
  2934.  
  2935.             /*
  2936.              * parse includes
  2937.              *
  2938.              */
  2939.  
  2940.             xstrcpy(&G_strCrashContext, "Parsing include files", 0);
  2941.  
  2942.             PLISTNODE pNode = lstQueryFirstNode(&llIncludes);
  2943.             while (pNode)
  2944.             {
  2945.                 PSZ pszInclude = (PSZ)pNode->pItemData;
  2946.                 ULONG cDefines = 0;
  2947.                 if (!(rc = ParseCHeader(pszInclude,
  2948.                                         &cDefines)))
  2949.                 {
  2950.                     if (G_ulVerbosity)
  2951.                         printf("Found %d valid #define's in \"%s\".\n",
  2952.                                cDefines,
  2953.                                pszInclude);
  2954.                 }
  2955.                 else
  2956.                      Error(2,
  2957.                            __FILE__, __LINE__, __FUNCTION__,
  2958.                            "Error %d opening include file \"%s\".",
  2959.                            rc,
  2960.                            pszInclude);
  2961.  
  2962.                 pNode = pNode->pNext;
  2963.             }
  2964.  
  2965.             /*
  2966.              * finally, convert HTML to IPF
  2967.              *
  2968.              */
  2969.  
  2970.             // main buffer:
  2971.             XSTRING str;
  2972.             xstrInit(&str, 100*1000);
  2973.  
  2974.             xstrcpy(&str,
  2975.                     ":userdoc.\n",
  2976.                     0);
  2977.             xstrcat(&str,
  2978.                     ":docprof toc=12345.\n",   // let heading levels 1-5 appear in TOC
  2979.                     0);
  2980.             xstrcat(&str,
  2981.                     ".* Converted from HTML to IPF by " H2I_HEADER "\n",
  2982.                     0);
  2983.  
  2984.             xstrcpy(&G_strCrashContext, "Reading root article", 0);
  2985.  
  2986.             GetOrCreateArticle(szRootFile,
  2987.                                0,             // current nesting level will be 1 then
  2988.                                NULL);
  2989.  
  2990.             rc = ProcessFiles(&str);
  2991.  
  2992.             xstrcat(&str,
  2993.                     ":euserdoc.",
  2994.                     0);
  2995.  
  2996.             xstrcpy(&G_strCrashContext, "Writing IPF", 0);
  2997.  
  2998.             if (!rc)
  2999.             {
  3000.                 CHAR szOutputFile[CCHMAXPATH];
  3001.                 strcpy(szOutputFile, szRootFile);
  3002.                 PSZ p = strrchr(szOutputFile, '.');
  3003.                 if (p)
  3004.                     strcpy(p, ".ipf");
  3005.                 else
  3006.                     strcat(szOutputFile, ".ipf");
  3007.  
  3008.                 ULONG cbWritten = 0;
  3009.  
  3010.                 XSTRING str2;
  3011.                 xstrInit(&str2, str.ulLength * 2 / 3);
  3012.  
  3013.                 p = str.psz;
  3014.                 CHAR c;
  3015.                 while (c = *p++)
  3016.                 {
  3017.                     if (c == '\n')
  3018.                         xstrcatc(&str2, '\r');
  3019.                     xstrcatc(&str2, c);
  3020.                 }
  3021.                 xstrcatc(&str2, '\0');
  3022.  
  3023.                 if (rc = doshWriteTextFile(szOutputFile,
  3024.                                            str2.psz,
  3025.                                            &cbWritten,
  3026.                                            NULL))
  3027.                     Error(2,
  3028.                           __FILE__, __LINE__, __FUNCTION__,
  3029.                           "Error %d writing output file \"%s\".",
  3030.                           rc,
  3031.                           szOutputFile);
  3032.                 else
  3033.                     if ((fShowStatistics) || (G_ulVerbosity > 1))
  3034.                     {
  3035.                         printf("%d HTML files were processed\n",
  3036.                                lstCountItems(&G_llFiles2Process));
  3037.                         printf("%d #defines were active\n",
  3038.                                G_cDefines);
  3039.                         printf("%d character entities were replaced\n",
  3040.                                G_ulReplacements);
  3041.                         printf("Output file \"%s\" successfully written, %d bytes\n",
  3042.                                 szOutputFile,
  3043.                                 cbWritten);
  3044.                     }
  3045.             }
  3046.         }
  3047.     }
  3048.     CATCH(excpt1)
  3049.     {
  3050.         Error(2,
  3051.               __FILE__, __LINE__, __FUNCTION__,
  3052.               "Exception caught in main(), context: %s",
  3053.               G_strCrashContext.psz);
  3054.         rc = 1;
  3055.     } END_CATCH();
  3056.  
  3057.     return (rc);
  3058. }
  3059.  
  3060.