home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: WPS_PM / WPS_PM.zip / xf083czs.zip / Tools / html2ipf.cmd
OS/2 REXX Batch file  |  1998-11-22  |  53KB  |  1,761 lines

  1. /*--------------------------------------------------------------------*/
  2. /* REXX script to convert a bunch of .html files into os/2 .ipf files */
  3. /*  which can be converted later into .inf files using ipfc compiler  */
  4. /*                                                                    */
  5. /*               Copyright (c) 1997 by FRIENDS software               */
  6. /*                         All Rights Reserved                        */
  7. /*                                                                    */
  8. /* FidoNet: 2:5030/84.5                                               */
  9. /* e-mail:  Andrew Zabolotny <bit@freya.etu.ru>                       */
  10. /*--------------------------------------------------------------------*/
  11.  
  12. /*--------------------------------------------------------------------*/
  13. /* user-customisable section start */
  14.  
  15. /* A command to convert any image file into os/2 bmp format           */
  16. /* This script requires Image Alchemy for os/2, at least demo version */
  17. /* If someone knows of a free proggy which provides at least partial  */
  18. /* or equivalent functionality, please mail me                        */
  19. /* Global.ImageConvert = 'alchemy.exe -o -O -8 <input> <output> >nul'; */
  20.  Global.ImageConvert = 'gbmsize <input> <output>,1.1 >nul';
  21. /* Executable/description of an external WWW browser to launch when   */
  22. /* user selects an URL link. Normally, you shouldn`t change it (even  */
  23. /* if you have Netscape) since WebEx is found on almost every OS/2    */
  24. /* system, and Navigator is not.                                      */
  25.  Global.WWWbrowser = 'netscape.exe*Netscape';
  26. /* Text to be included on the "URL" pages */
  27.  Global.ExternalLinksTitle = 'Zdroje na síti Internet';
  28.  Global.ExternalLinksText = 'Tato kapitola obsahuje vτechny externí odkazy uvedené v této p²íruƒce.'||'0d0a'x,
  29.     'Kaºd∞ odkaz je uveden jako Unified Resource Locator (URL) pro danou zdroj'||'0d0a'x,
  30.     'na síti Internet. Jednoduτe dvakrát klepn╪te na jednom z nich, ƒímº spustíte '||'0d0a'x,
  31.     substr(Global.WWWbrowser, pos('*', Global.WWWbrowser) + 1) 's dan∞m URL.';
  32.  Global.LaunchNetscapeText = 'Poklepáním na odkazu spustíte '||,
  33.     substr(Global.WWWbrowser, pos('*', Global.WWWbrowser) + 1) 's tímto URL:'
  34. /* default book font; use warpsans bold for a nicer-looking books     */
  35. Global.DefaultFont = ':font facename=default size=0x0.';
  36. /* Global.DefaultFont = ':font facename=''WarpSans'' size=9x9.'; */
  37. /* fonts for headings (1 through 6)                                   */
  38.  Global.HeaderFont.1 = ':font facename=''Helv'' size=32x20.';
  39.  Global.HeaderFont.2 = ':font facename=''Helv'' size=20x12.';
  40.  Global.HeaderFont.3 = ':font facename=''Tms Rmn'' size=18x10.'
  41.  Global.HeaderFont.4 = ':font facename=''Tms Rmn'' size=16x8.'
  42.  Global.HeaderFont.5 = ':font facename=''Courier'' size=14x8.'
  43.  Global.HeaderFont.6 = ':font facename=''Helv'' size=14x10.'
  44. /* font for url links (which launches WebExplorer)                    */
  45.  Global.URLinkFont  =  ':font facename=''System VIO'' size=14x8.';
  46. /* proportional font (for <tt>...</tt>                                */
  47.  Global.ProportFont =  ':font facename=''System VIO'' size=14x8.';
  48.  Global.CITEFont  =    ':font facename=''Courier'' size=18x12.';
  49.  
  50. /* end of user-customisable section                                   */
  51. /*--------------------------------------------------------------------*/
  52. '@echo off'
  53.  call rxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
  54.  call SysLoadFuncs
  55.  
  56.  parse arg _cmdLine
  57.  
  58. /***************** hard-coded variables **********************/
  59. /* maximal line length for ipfc :-( */
  60.  Global.maxLineLength = 256;
  61. /* unix end-of-line constant */
  62.  Global.EOL = d2c(10);
  63. /* file extensions and name of handler procedures for these; */
  64. /* all other file extensions will be ignored */
  65.  Global.TypeHandler = '*.HTML doParseHTML *.SHTML doParseHTML *.HTM doParseHTML',
  66.                       '*.HT3 doParseHTML *.HTM3 doParseHTML *.TXT doParseText',
  67.                       '*.TEXT doParseText *.CMD doParseText *.BAT doParseText',
  68.                       '*.GIF doParseImage *.JPG doParseImage *.PNG doParseImage';
  69. /* Set up some global variables */
  70.  Global.Picture.0 = 0;                     /* keep track of embedded Pictures */
  71.  Global.LinkID = 0;                         /* total number of external links */
  72.  Global.URLinks = 0;                               /* keep track of url links */
  73.  Global.Title = '';                                             /* book Title */
  74.  Global.HREF = '';   /* Speedup: keep all encountered HREFs and IMG_SRCs in a */
  75.  Global.IMGSRC = '';    /* string so we can use Pos() and WordPos() functions */
  76.  Global.SubLinks = 0;           /* This stem keeps track of the SUBLINKS tags */
  77.  Global.NoSubLinks = 0;       /* This stem keeps track of the NOSUBLINKS tags */
  78.  
  79. /* Default state for all switches */
  80.  Global.optCO = 1;  /* COlored output */
  81.  Global.optCE = 1;  /* enable CEntering */
  82.  Global.optCH = 0;  /* disable CHecking */
  83.  Global.optP = 1;   /* embed Pictures */
  84.  Global.optS = 1;   /* Sort links */
  85.  Global.optD = 0;   /* Debug log */
  86.  call AnalyseOptions;
  87.  call DefineQuotes;
  88.  
  89.  call ShowHeader;
  90.  if length(_fName) = 0
  91.   then call ShowHelp;
  92.  
  93.  Global.oName = _oName;
  94.  if length(Global.oName) = 0
  95.   then do
  96.         i = lastPos('.', _fName);
  97.         if i > 0
  98.          then Global.oName = left(_fName, i)||'ipf';
  99.          else Global.oName = _fName||'.ipf';
  100.        end;
  101.  call SetColor lCyan;
  102.  if Global.OptCH
  103.   then say 'Checking the integrity of links for '_fName;
  104.   else do
  105.         say 'Output goes into 'Global.oName;
  106.         call SysFileDelete(Global.oName);
  107.        end;
  108.  
  109.  DateTime = Date(n)', 'Time(c);
  110.  call logError ''
  111.  call logError '--- ['DateTime'] conversion started: index file '_fName;
  112.  
  113.  call putline '.*'copies('-', 76)'*';
  114.  call putline '.*'center('Converted by HTML2IPF from '_fName' at 'DateTime, 76)'*';
  115.  call putline '.*'copies('-', 76)'*';
  116.  call putline ':userdoc.';
  117.  call putline ':docprof toc=12345.';
  118.  
  119.  call time 'R'
  120.  call ParseFile _fName, 1;
  121.  do until ResolveLinks(1) = 0;
  122.   Global.Sublinks = 0;
  123.   Global.NoSublinks = 0;/* Include all unresolved sublinks */
  124.  end;
  125.  call ConvertPictures;
  126.  call OutputURLs;
  127.  
  128.  call putline ':euserdoc.';
  129.  
  130.  call SetColor lCyan;
  131.  elapsed = time('E');
  132.  say 'finished; elapsed time = 'elapsed%3600':'elapsed%60':'trunc(elapsed//60,1);
  133.  DateTime = Date(n)', 'Time(c);
  134.  call logError '--- ['DateTime'] conversion finished';
  135.  call Charout ,d2c(27)'[0m';
  136. exit;
  137.  
  138. AnalyseOptions:
  139.  _fName = ''; _oName = '';
  140.  do i = 1 to words(_cmdLine)
  141.   nw = word(_cmdLine, i);
  142.   if left(nw, 1) = '-'
  143.    then do
  144.          nw = translate(substr(nw, 2));
  145.          OptState = pos(right(nw, 1), '-+');
  146.          if OptState > 0
  147.           then nw = left(nw, length(nw) - 1);
  148.           else OptState = 2;
  149.      OptState = OptState - 1;
  150.      select
  151.           when abbrev('COLORS', nw, 2)
  152.            then Global.OptCO = OptState;
  153.           when abbrev('CENTER', nw, 2)
  154.            then Global.OptCE = OptState;
  155.           when abbrev('CHECK', nw, 2)
  156.            then Global.OptCH = OptState;
  157.           when abbrev('SORT', nw, 1)
  158.            then Global.OptS = OptState;
  159.           when abbrev('PICTURES', nw, 1)
  160.            then Global.OptP = OptState;
  161.           when abbrev('DEBUG', nw, 1)
  162.            then Global.OptD = OptState;
  163.           otherwise
  164.            do
  165.             call ShowHeader;
  166.             call SetColor lRed;
  167.             say 'Invalid option in command line: 'word(_cmdLine, i);
  168.             call ShowHelp;
  169.            end;
  170.          end;
  171.         end
  172.    else if length(_fName) = 0
  173.          then _fName = nw
  174.          else
  175.         if length(_oName) = 0
  176.          then _oName = nw
  177.          else do
  178.                call ShowHeader;
  179.                call SetColor lRed;
  180.                say 'Extra filename in command line: 'word(_cmdLine, i);
  181.                call ShowHelp;
  182.               end;
  183.  end;
  184. return;
  185.  
  186. ShowHeader:
  187.  call SetColor white
  188.  say '─┼─ HTML2IPF ─┼─ Version 0.1.0 ─┼─ Copyright (c) 1997 by FRIENDS software ─┼─'
  189. return;
  190.  
  191. ShowHelp:
  192.  call SetColor Yellow
  193.  say 'Usage: HTML2IPF [IndexFilename.HTML] {OutputFilename.IPF} {conversion options}'
  194.  call SetColor lGreen;
  195.  say '[IndexFilename.HTML]'
  196.  call SetColor Green;
  197.  say '└─┤is the "root" .HTML file to start with'
  198.  call SetColor lGreen;
  199.  say '{OutputFilename.IPF}'
  200.  call SetColor Green;
  201.  say '└─┤is the output filename (usually with the .IPF extension)'
  202.  call SetColor lGreen;
  203.  say '{conversion options}'
  204.  call SetColor Green;
  205.  say '└─┬┤are one or more of the following:'
  206.  say '  └┬┬┤-CO{LORS}{+|-}'
  207.  say '   │└┘use (+) or don`t use (-) ansi [c]olors in output'
  208.  say '   ├┬┤-CE{NTER}{+|-}'
  209.  say '   │└┘enable (+) or disable (-) processing <CENTER> tags'
  210.  say '   ├┬┤-CH{ECK}{+|-}'
  211.  say '   │└┘enable (+) or disable (-) checking files only'
  212.  say '   ├┬┤-S{ORT}{+|-}'
  213.  say '   │└┘sort (+) or don`t sort (-) links alphabetically'
  214.  say '   ├┬┤-P{ICTURES}{+|-}'
  215.  say '   │└┘include (+) or don`t include (-) [p]ictures in .IPF file'
  216.  say '   └┬┤-D{EBUG}{+|-}'
  217.  say '    └┘enable (+) or disable (-) [d]ebug logging into HTML2IPF.LOG'
  218.  call SetColor lCyan;
  219.  say 'default HTML2IPF options:'
  220.  call SetColor Cyan;
  221.  say '└─┤-COLORS+ -CENTER+ -CHECK- -SORT+ -PICTURES+ -DEBUG-'
  222. exit(1);
  223.  
  224. ConvertPictures:
  225.  procedure expose Global.;
  226.  if (\Global.optP) | (Global.OptCH) then return;
  227.  do i = 1 to Global.Picture.0
  228.   if stream(Global.Picture.i.dst, 'c', 'Query Exists') = ''
  229.    then call RunCmd Global.ImageConvert, Global.Picture.i.src, Global.Picture.i.dst;
  230.  end;
  231. return;
  232.  
  233. RunCmd:
  234.  parse arg cmd, in, out;
  235.  
  236.  call SetColor lGreen
  237.  ip = pos('<input>', cmd);
  238.  if ip <> 0 then cmd = left(cmd, ip - 1)||in||substr(cmd, ip + 7);
  239.  op = pos('<output>', cmd);
  240.  if op <> 0 then cmd = left(cmd, op - 1)||out||substr(cmd, op + 8);
  241.  cmd;
  242. return;
  243.  
  244. OutputURLs:
  245. /* make a chapter with links to internet locations */
  246.  if Global.URLinks = 0
  247.   then return;
  248.  call putline ':h1 group=99 x=right width=30%.'Global.ExternalLinksTitle;
  249.  call putline Global.DefaultFont;
  250.  call putline ':p.'Global.ExternalLinksText;
  251. /* Sort URLs alphabetically */
  252.  if Global.OptS
  253.   then do i = 1 to Global.URLinks;
  254.         ii = Global.URLinks.i;
  255.         do j = i + 1 to Global.URLinks;
  256.          ji = Global.URLinks.j;
  257.          if Global.LinkID.ji < Global.LinkID.ii
  258.           then do
  259.                 tmp = Global.URLinks.i;
  260.                 Global.URLinks.i = Global.URLinks.j;
  261.                 Global.URLinks.j = tmp;
  262.                 ii = ji;
  263.                end;
  264.         end;
  265.        end;
  266.  if Global.OptCH
  267.   then do
  268.         call SetColor LGreen;
  269.         do i = 1 to Global.URLinks
  270.          j = Global.URLinks.i;
  271.          say 'Unresolved link: 'Global.LinkID.j.RealName;
  272.          call logError '--- Unresolved link: 'Global.LinkID.j.RealName;
  273.         end;
  274.         return;
  275.        end;
  276.  Global.CurrentDir = '';
  277.  do i = 1 to Global.URLinks
  278.   j = Global.URLinks.i;
  279.   call putline ':h2 res='GetLinkID(Global.LinkID.j)' group=98 x=right y=bottom width=60% height=40%.'IPFstring(Global.LinkID.j.RealName);
  280.   call putline Global.DefaultFont;
  281.   call putline ':p.:lines align=center.';
  282.   call putline IPFstring(Global.LaunchNetscapeText);
  283.   call putline Global.URLinkFont;
  284.   call putline ':p.:link reftype=launch object='''left(Global.WWWbrowser, pos('*', Global.WWWbrowser) - 1),
  285.                              ''' data='''Global.LinkID.j.RealName'''.';
  286.   call putline IPFstring(Global.LinkID.j.RealName);
  287.   call putline ':elink.:elines.';
  288.  end;
  289. return;
  290.  
  291. /* Parse a HTML file; called recursively if needed */
  292. ParseFile:
  293.  procedure expose Global.;
  294.  parse arg fName, DeepLevel;
  295.  call SetColor Cyan;
  296.  call charout ,'Parsing 'fName' ...';
  297.  
  298.  Global.CurrentDir = '';
  299.  id = GetLinkID(fName);
  300.  if id > 0 then Global.LinkID.id.Resolved = 1;
  301.  
  302.  tmp = translate(stream(fName, 'c', 'Query Exists'), '/', '\');
  303.  if length(tmp) = 0
  304.   then do
  305.         call SetColor lRed;
  306.         say ' not found';
  307.         call logError '--- file 'fName' not found';
  308.         return;
  309.        end;
  310.  fName = Shorten(tmp);
  311.  Global.CurrentDir = fileSpec('P', translate(fName, '\', '/'));
  312.  Global.CurrentFile = fName;
  313.  call logError '--- Parsing file "'fName'" ...';
  314.  
  315.  Global.Article.linkID = '';                   /* --UM: ID for online help linking */
  316.  Global.Article.Group = 0; /* --UM: IPF group to link to for multiple windows */
  317.  Global.Article.XPos = '';                  /* --UM: IPF window display width */
  318.  Global.Article.Width = '';                 /* --UM: IPF window display width */
  319.  
  320.  Global.Article.Title = '';                                  /* Article Title */
  321.  Global.Article.line.0 = 0;                      /* count of lines in Article */
  322.  Global.Article.Hidden = 0;  /* Is current article hidden from book contents? */
  323.  Global.OpenTag.0 = 0;  /* keep track of open tags to close at end of chapter */
  324.  Global.RefEndTag = '';       /* end tag to put at next end-of-reference <\a> */
  325.  Global.IsParagraph = 0;                   /* We`re inside a <P>...</P> pair? */
  326.  Global.IsTable = 0;               /* We`re inside a <TABLE>...</TABLE> pair? */
  327.  Global.IsCentered = 0;          /* We`re inside a <CENTER>...</CENTER> pair? */
  328.  Global.IsOutputEnabled = 1; /* A global switch to enable/disable text output */
  329.  Global.SkipSpaces = 0;                   /* set to 1 in lists to skip spaces */
  330.  Global.AfterBreak = 0;            /* set to 1 after .br to avoid empty lines */
  331.  call PutToken Global.EOL;                     /* initialize output subsystem */
  332.  Global.AfterBreak = 1;              /* avoid empty lines at start of Article */
  333.  Global.EOF = 0;
  334.  Global.CurFont = Global.DefaultFont;
  335. /* Remember the count of SUBLINKS and NOSUBLINKS to restore it later */
  336.  locSublinks = Global.Sublinks;
  337.  locNoSublinks = Global.NoSublinks;
  338.  
  339.  fExt = max(lastPos('/', fName), lastPos('\', fName));
  340.  if lastPos('.', fName) > fExt
  341.   then fExt = translate(substr(fName, lastPos('.', fName) + 1))
  342.   else fExt = '';
  343.  fExt = wordpos('*.'fExt, Global.TypeHandler);
  344.  if fExt > 0
  345.   then fExt = word(Global.TypeHandler, fExt + 1)
  346.   else do
  347.         call SetColor lRed;
  348.         say ' unknown file type';
  349.         call logError '--- File 'fName': unknown type - ignored';
  350.         return;
  351.        end;
  352.  
  353.  select
  354.   when fExt = 'doParseHTML'  then call doParseHTML;
  355.   when fExt = 'doParseImage' then call doParseImage;
  356.   when fExt = 'doParseText'  then call doParseText;
  357.   otherwise call logError 'Unknown file type handler: 'fExt;
  358.  end;
  359.  call ProgressBar;
  360.  call stream Global.CurrentFile, 'c', 'close';            /* close input file */
  361.  
  362.  if length(Global.Article.Title) = 0
  363.   then Global.Article.Title = IPFstring(filespec('N', translate(fName, '\', '/')));
  364.  if (length(Global.Title) = 0)
  365.   then do
  366.         Global.Title = ':title.'Global.Article.Title;
  367.         call putline Global.Title; IndexFile = 'Y';
  368.        end;
  369.  call putline '.* Source filename: 'fName;
  370.  if id > 0
  371.   then do
  372.         if (Global.Article.Hidden) & (IndexFile \= 'Y')
  373.          then do
  374.                i = max(1, DeepLevel - 1);
  375.                j = ' hide';
  376.                Global.SubLinks = 1; Global.Sublinks.1 = '*';
  377.               end;
  378.          else do
  379.                i = DeepLevel;
  380.                j = '';
  381.               end;
  382.         if (Global.Article.Group > 0) then
  383.             j = j' group='Global.Article.Group;
  384.         if (Global.Article.XPos \= '') then
  385.             j = j' x='Global.Article.XPos;
  386.         if (Global.Article.Width \= '') then
  387.             j = j' width='Global.Article.Width;
  388.         if (Global.Article.linkID \= '') then
  389.             j = j' res='Global.Article.linkID
  390.         else j = j' res='id;
  391.         call putline ':h'i j'.'Global.Article.Title;
  392.        end;
  393.  call putline Global.DefaultFont;
  394.  call putline ':p.';
  395.  do i = 1 to Global.Article.line.0
  396.   call putline Global.Article.line.i;
  397.  end;
  398.  drop Global.Article.;
  399.  
  400.  call SetColor Blue;
  401.  call charout ,' done';
  402.  call CRLF;
  403.  
  404.  call ResolveLinks DeepLevel+1;
  405.  
  406. /* Restore the SUBLINKS and NOSUBLINKS counter */
  407.  Global.Sublinks = locSublinks;
  408.  Global.NoSublinks = locNoSublinks;
  409. return;
  410.  
  411. ResolveLinks:
  412.  procedure expose Global.;
  413.  arg DeepLevel;
  414.  LinkCount = 0;
  415.  Links.0 = 0;
  416.  
  417.  do i = 1 to Global.LinkID
  418.   if (\Global.LinkID.i.Resolved)
  419.    then do
  420.          if Global.SubLinks > 0
  421.           then do
  422.                 do j = 1 to Global.SubLinks
  423.                  if Pos(Global.SubLinks.j, translate(Global.LinkID.i.InitialName)) = 1
  424.                   then do; j = -1; leave; end;
  425.                 end;
  426.                 if j \= -1 then Iterate;
  427.                end;
  428.          do j = 1 to Global.NoSubLinks
  429.           if Pos(Global.NoSubLinks.j, translate(Global.LinkID.i.InitialName)) = 1
  430.            then do; j = -1; leave; end;
  431.          end;
  432.          if j = -1 then Iterate;
  433.          Links.0 = Links.0 + 1; j = Links.0;
  434.          Links.j = Global.LinkID.i.RealName;
  435.          Global.LinkID.i.Resolved = 1;
  436.         end;
  437.  end;
  438.  if Global.OptS
  439.   then call SortLinks 1, Links.0;
  440.  if DeepLevel > 6 then DeepLevel = 6;
  441.  do i = 1 to Links.0
  442.   call ParseFile translate(Links.i, '/', '\'), DeepLevel;
  443.   LinkCount = LinkCount + 1;
  444.  end;
  445.  drop Global.SubLinks.;
  446.  drop Global.NoSubLinks.;
  447. return LinkCount;
  448.  
  449. SortLinks:
  450.  procedure expose Links.;
  451.  arg iLeft, iRight;
  452.  
  453.  Left = iLeft; Right = iRight;
  454.  Middle = (Left + Right) % 2;
  455.  MidVar = Links.Middle;
  456.  do until Left > Right
  457.   do while Links.Left < MidVar;
  458.    Left = Left + 1;
  459.   end;
  460.   do while Links.Right > MidVar;
  461.    Right = Right - 1;
  462.   end;
  463.  
  464.   if Left <= Right
  465.    then do
  466.          tmp = Links.Left;
  467.          Links.Left = Links.Right;
  468.          Links.Right = tmp;
  469.          Left = Left + 1;
  470.          Right = Right - 1;
  471.         end;
  472.  end;
  473.  if iLeft < Right
  474.   then call SortLinks iLeft, Right;
  475.  if Left < iRight
  476.   then call SortLinks Left, iRight;
  477. return;
  478.  
  479. doParseHTML:
  480.  Global.FileContents = '';
  481.  Global.FileSize = chars(fName);                                 /* file size */
  482.  call ParseContents 'EMPTY';
  483. return;
  484.  
  485. doParseText:
  486.  Global.SubLinks = 1;
  487.  Global.SubLinks.1 = '*';           /* A plain text file cannot have sublinks */
  488.  Global.FileSize = chars(fName);                                 /* file size */
  489.  call PutToken ':lines align=left.';
  490.  call SetFont Global.ProportFont; /* draw text using proportional font */
  491.  do while chars(fName) > 0;
  492.   call ProgressBar;
  493.   Global.FileContents = charin(fName,,4096);
  494. /* remove all \0x0d Characters from output stream */
  495.   do until i = 0
  496.    i = pos(d2c(13), Global.FileContents);
  497.    if i > 0 then Global.FileContents = delstr(Global.FileContents, i, 1);
  498.   end;
  499.   call PutText Global.FileContents;
  500.  end;
  501.  call PutToken ':elines.';
  502. return;
  503.  
  504. doParseImage:
  505.  _imgBitmap = GetPictureID(fName);
  506.  if (\Global.optP) | (length(_imgBitmap) <= 1)
  507.   then do
  508.         if Global.optP
  509.          then do
  510.                call SetColor Yellow;
  511.                parse value SysCurPos() with row col;
  512.                if col > 0 then call CRLF;
  513.                say 'Warning: Picture "'Global._imgname'" missing';
  514.                call logError 'Picture "'Global._imgname'" missing';
  515.               end;
  516.         call PutText ':lines align=center.';
  517.         call PutText fName;
  518.         call PutText ':elines.';
  519.        end
  520.   else do
  521.         Global.Picture.0 = Global.Picture.0 + 1;
  522.         i = Global.Picture.0;
  523.         Global.Picture.i.dst = left(_imgBitmap, pos('*', _imgBitmap) - 1);
  524.         Global.Picture.i.src = substr(_imgBitmap, pos('*', _imgBitmap) + 1);
  525.         Global.Picture.i.alt = fName;
  526.         call PutToken ':artwork name='''Global.Picture.i.dst''' align=center.';
  527.        end;
  528. return;
  529.  
  530. ParseContents:
  531.  procedure expose Global.;
  532.  arg TextHandler;
  533.  do until (length(Global.FileContents) = 0) & (Global.EOF)
  534.   Token = GetToken();
  535.   if left(Token, 1) = d2c(0)
  536.    then do
  537.          Token = strip(substr(Token, 2));
  538.       /* assume everything starting with <! is not important */
  539.          if left(Token, 1) = '!'
  540.           then iterate;
  541.          Tag = strip(translate(Token, xrange('A','Z')'_!', xrange('a','z')'-/'));
  542.          TagBreakPos = pos(' ', Tag);
  543.          if TagBreakPos > 0
  544.           then Tag = left(Tag, TagBreakPos - 1);
  545.          TagBreakPos = 0;
  546.          select
  547.           when Tag = 'HTML' then TagBreakPos = doTagHTML();
  548.           when Tag = '!HTML'    then TagBreakPos = doTag!HTML();
  549.           when Tag = 'HEAD' then TagBreakPos = doTagHEAD();
  550.           when Tag = '!HEAD'    then TagBreakPos = doTag!HEAD();
  551.           when Tag = 'BODY' then TagBreakPos = doTagBODY();
  552.           when Tag = '!BODY'    then TagBreakPos = doTag!BODY();
  553.           when Tag = 'META' then TagBreakPos = doTagMETA();
  554.           when Tag = 'TITLE'    then TagBreakPos = doTagTITLE();
  555.           when Tag = '!TITLE'   then TagBreakPos = doTag!TITLE();
  556.           when Tag = 'META' then TagBreakPos = doTagMETA();
  557.           when Tag = 'A'    then TagBreakPos = doTagA();
  558.           when Tag = '!A'   then TagBreakPos = doTag!A();
  559.           when Tag = 'IMG'  then TagBreakPos = doTagIMG();
  560.           when Tag = 'I'    then TagBreakPos = doTagI();
  561.           when Tag = '!I'   then TagBreakPos = doTag!I();
  562.           when Tag = 'B'    then TagBreakPos = doTagB();
  563.           when Tag = '!B'   then TagBreakPos = doTag!B();
  564.           when Tag = 'U'    then TagBreakPos = doTagU();
  565.           when Tag = '!U'   then TagBreakPos = doTag!U();
  566.           when Tag = 'EM'   then TagBreakPos = doTagEM();
  567.           when Tag = '!EM'  then TagBreakPos = doTag!EM();
  568.           when Tag = 'TT'   then TagBreakPos = doTagTT();
  569.           when Tag = '!TT'  then TagBreakPos = doTag!TT();
  570.           when Tag = 'P'    then TagBreakPos = doTagP();
  571.           when Tag = '!P'   then TagBreakPos = doTag!P();
  572.           when Tag = 'H1'   then TagBreakPos = doTagH1();
  573.           when Tag = '!H1'  then TagBreakPos = doTag!H1();
  574.           when Tag = 'H2'   then TagBreakPos = doTagH2();
  575.           when Tag = '!H2'  then TagBreakPos = doTag!H2();
  576.           when Tag = 'H3'   then TagBreakPos = doTagH3();
  577.           when Tag = '!H3'  then TagBreakPos = doTag!H3();
  578.           when Tag = 'H4'   then TagBreakPos = doTagH4();
  579.           when Tag = '!H4'  then TagBreakPos = doTag!H4();
  580.           when Tag = 'H5'   then TagBreakPos = doTagH5();
  581.           when Tag = '!H5'  then TagBreakPos = doTag!H5();
  582.           when Tag = 'H6'   then TagBreakPos = doTagH6();
  583.           when Tag = '!H6'  then TagBreakPos = doTag!H6();
  584.           when Tag = 'OL'   then TagBreakPos = doTagOL();
  585.           when Tag = '!OL'  then TagBreakPos = doTag!OL();
  586.           when Tag = 'UL'   then TagBreakPos = doTagUL();
  587.           when Tag = '!UL'  then TagBreakPos = doTag!UL();
  588.           when Tag = 'LI'   then TagBreakPos = doTagLI();
  589.           when Tag = 'DL'   then TagBreakPos = doTagDL();
  590.           when Tag = '!DL'  then TagBreakPos = doTag!DL();
  591.           when Tag = 'DT'   then TagBreakPos = doTagDT();
  592.           when Tag = 'DD'   then TagBreakPos = doTagDD();
  593.           when Tag = 'BR'   then TagBreakPos = doTagBR();
  594.           when Tag = 'CITE' then TagBreakPos = doTagCITE();
  595.           when Tag = '!CITE'    then TagBreakPos = doTag!CITE();
  596.           when Tag = 'CENTER'   then TagBreakPos = doTagCENTER();
  597.           when Tag = '!CENTER'  then TagBreakPos = doTag!CENTER();
  598.           when Tag = 'PRE'  then TagBreakPos = doTagPRE();
  599.           when Tag = '!PRE' then TagBreakPos = doTag!PRE();
  600.           when Tag = 'META' then TagBreakPos = doTagMETA();
  601.           when Tag = 'MENU' then TagBreakPos = doTagMENU();
  602.           when Tag = '!MENU'    then TagBreakPos = doTag!MENU();
  603.           when Tag = 'CODE' then TagBreakPos = doTagCODE();
  604.           when Tag = '!CODE'    then TagBreakPos = doTag!CODE();
  605.           when Tag = 'STRONG'   then TagBreakPos = doTagSTRONG();
  606.           when Tag = '!STRONG'  then TagBreakPos = doTag!STRONG();
  607.           when Tag = 'ADDRESS'  then TagBreakPos = doTagADDRESS();
  608.           when Tag = '!ADDRESS' then TagBreakPos = doTag!ADDRESS();
  609.           when Tag = 'HR'   then TagBreakPos = doTagHR();
  610.           when Tag = 'TABLE'    then TagBreakPos = doTagTABLE();
  611.           when Tag = '!TABLE'   then TagBreakPos = doTag!TABLE();
  612.           when Tag = 'TR'   then TagBreakPos = doTagTR();
  613.           when Tag = '!TR'  then TagBreakPos = doTag!TR();
  614.           when Tag = 'TH'   then TagBreakPos = doTagTH();
  615.           when Tag = '!TH'  then TagBreakPos = doTag!TH();
  616.           when Tag = 'TD'   then TagBreakPos = doTagTD();
  617.           when Tag = '!TD'  then TagBreakPos = doTag!TD();
  618.           when Tag = 'BLOCKQUOTE'then TagBreakPos = doTagBLOCKQUOTE();
  619.           when Tag = '!BLOCKQUOTE'then TagBreakPos = doTag!BLOCKQUOTE();
  620.           otherwise call logError 'Unexpected tag <'Token'>';
  621.          end;
  622.          if TagBreakPos then leave;
  623.         end;
  624.    else select
  625.          when TextHandler = 'EMPTY' then call doTextEMPTY;
  626.          when TextHandler = 'HEAD'  then call doTextHEAD;
  627.          when TextHandler = 'BODY'  then call doTextBODY;
  628.     end;
  629.  end;
  630. return;
  631.  
  632. ParseTag:
  633.  procedure expose Global.;
  634.  parse arg Tag;
  635.  parse var Tag Prefix Tag
  636.  Prefix = translate(Prefix);
  637.  do while length(Tag) > 0
  638.   parse value translate(Tag, ' ', Global.EOL) with subTag '=' Tag;
  639.   Tag = strip(Tag, 'leading');
  640.   if left(Tag, 1) = '"'
  641.    then parse var Tag '"' subTagValue '"' Tag
  642.    else parse var Tag subTagValue Tag;
  643.   subTag = translate(strip(subTag));
  644.   subTagValue = strip(subTagValue);
  645.   select
  646.    when Prefix = 'A'
  647.     then select
  648.           when subTag = 'AUTO'      then call doTagA_AUTO;
  649.           when subTag = 'HREF'      then call doTagA_HREF;
  650.           when subTag = 'NAME'      then call doTagA_NAME;
  651.           otherwise call logError 'Unexpected subTag 'subTag'="'subTagValue'"';
  652.          end;
  653.    when Prefix = 'IMG'
  654.     then select
  655.       when subTag = 'SRC'       then call doTagIMG_SRC;
  656.       when subTag = 'ALT'       then call doTagIMG_ALT;
  657.       when subTag = 'ALIGN'     then call doTagIMG_ALIGN;
  658.       when subTag = 'WIDTH'     then call doTagIMG_WIDTH;
  659.       when subTag = 'HEIGHT'    then call doTagIMG_HEIGHT;
  660.           otherwise call logError 'Unexpected subTag 'subTag'="'subTagValue'"';
  661.          end;
  662.    when Prefix = 'HTML'
  663.     then select
  664.       when subTag = 'HIDDEN'    then call doTagHTML_HIDDEN;
  665.       when subTag = 'SUBLINKS'  then call doTagHTML_SUBLINKS;
  666.       when subTag = 'NOSUBLINKS'    then call doTagHTML_NOSUBLINKS;
  667.  
  668.       when subTag = 'ID'        then call doTagHTML_ID;         /* --UM */
  669.       when subTag = 'GROUP'     then call doTagHTML_GROUP;      /* --UM */
  670.       when subTag = 'WIDTH'     then call doTagHTML_WIDTH;      /* --UM*/
  671.       when subTag = 'XPOS'      then call doTagHTML_XPOS;       /* --UM*/
  672.           otherwise call logError 'Unexpected subTag 'subTag'="'subTagValue'"';
  673.          end;
  674.   end;
  675.  end;
  676. return;
  677.  
  678. doTagHTML:
  679.  call ParseTag Token;
  680.  call ParseContents 'EMPTY';
  681. return 0;
  682.  
  683. doTag!HTML:
  684. return 1;
  685.  
  686. doTagHTML_ID:                           /* --UM */
  687.  Global.Article.linkID = SubtagValue;
  688. return 0;
  689.  
  690. doTagHTML_GROUP:                        /* --UM */
  691.  Global.Article.Group = SubtagValue;
  692. return 0;
  693.  
  694. doTagHTML_WIDTH:                        /* --UM */
  695.  Global.Article.Width = SubtagValue;
  696. return 0;
  697.  
  698. doTagHTML_XPOS:                         /* --UM */
  699.  Global.Article.XPos = SubtagValue;
  700. return 0;
  701.  
  702. doTagHTML_HIDDEN:
  703.  Global.Article.Hidden = 1;
  704. return 0;
  705.  
  706. doTagHTML_SUBLINKS:
  707.  Global.SubLinks = Global.SubLinks + 1;
  708.  i = Global.SubLinks;
  709.  Global.SubLinks.i = translate(SubTagValue);
  710. return 0;
  711.  
  712. doTagHTML_NOSUBLINKS:
  713.  Global.NoSubLinks = Global.NoSubLinks + 1;
  714.  i = Global.NoSubLinks;
  715.  Global.NoSubLinks.i = translate(SubTagValue);
  716. return 0;
  717.  
  718. doTagHEAD:
  719.  Global.grabTitle = 0;
  720.  call ParseContents 'HEAD';
  721. return 0;
  722.  
  723. doTag!HEAD:
  724.  Global.grabTitle = 0;
  725. return 1;
  726.  
  727. doTagBODY:
  728.  Global.grabTitle = 0;
  729.  call ParseContents 'BODY';
  730. return 0;
  731.  
  732. doTag!BODY:
  733. return 1;
  734.  
  735. doTagTITLE:
  736.  Global.grabTitle = 1;
  737.  Global.Article.Title = '';
  738. return 0;
  739.  
  740. doTag!TITLE:
  741.  Global.grabTitle = 0;
  742. return 0;
  743.  
  744. doTagCITE:
  745.  call PutToken Global.CITEFont;
  746. return 0;
  747.  
  748. doTag!CITE:
  749.  call PutToken Global.DefaultFont;
  750. return 0;
  751.  
  752. doTagI:
  753.  call PutToken ":hp1.";
  754. return 0;
  755.  
  756. doTag!I:
  757.  call PutToken ":ehp1.";
  758. return 0;
  759.  
  760. doTagB:
  761. doTagSTRONG:
  762.  call PutToken ':hp2.';
  763. return 0;
  764.  
  765. doTag!B:
  766. doTag!STRONG:
  767.  call PutToken ':ehp2.';
  768. return 0;
  769.  
  770. doTagU:
  771.  call PutToken ':hp5.';
  772. return 0;
  773.  
  774. doTag!U:
  775.  call PutToken ':ehp5.';
  776. return 0;
  777.  
  778. doTagEM:
  779.  call PutToken ':hp3.';
  780. return 0;
  781.  
  782. doTag!EM:
  783.  call PutToken ':ehp3.';
  784. return 0;
  785.  
  786. doTagCODE:
  787. doTagTT:
  788.  call SetFont Global.ProportFont;
  789. return 0;
  790.  
  791. doTag!CODE:
  792. doTag!TT:
  793.  call SetFont Global.DefaultFont;
  794. return 0;
  795.  
  796. doTagBLOCKQUOTE:
  797. doTagP:
  798.  call NewLine;
  799.  call PutToken ':p.';
  800.  Global.IsParagraph = 1;
  801. return 0;
  802.  
  803. doTag!BLOCKQUOTE:
  804. doTag!P:
  805.  call NewLine;
  806.  Global.IsParagraph = 0;
  807. return 0;
  808.  
  809. doTagBR:
  810.  if Global.IsTable
  811.   then return 0; /* IPFC does not allow .br`s in tables */
  812.  Global.AfterBreak = 0;
  813.  call NewLine;
  814.  call PutToken '.br';
  815.  call NewLine;
  816.  Global.AfterBreak = 1;
  817.  if doCheckTag(':eul.') | doCheckTag(':edl.') | doCheckTag(':eol.')
  818.   then Global.SkipSpaces = 1;
  819. return 0;
  820.  
  821. doTagPRE:
  822.  call NewLine;
  823.  call PutToken ':cgraphic.';
  824. return 0;
  825.  
  826. doTag!PRE:
  827.  call PutToken ':ecgraphic.';
  828. return 0;
  829.  
  830. doTagH_begin:
  831.  arg i;
  832.  call NewLine;
  833.  if \Global.IsTable
  834.   then do; call PutToken '.br'; call NewLine; end;
  835.  call SetFont Global.HeaderFont.i;
  836.  if \Global.IsTable
  837.   then do; call NewLine; call PutToken '.br'; end;
  838.  call NewLine;
  839.  Global.AfterBreak = 1;
  840. return;
  841.  
  842. doTagH1:
  843.  call doTagH_begin 1;
  844. return 0;
  845.  
  846. doTagH2:
  847.  call doTagH_begin 2;
  848. return 0;
  849.  
  850. doTagH3:
  851.  call doTagH_begin 3;
  852. return 0;
  853.  
  854. doTagH4:
  855.  call doTagH_begin 4;
  856. return 0;
  857.  
  858. doTagH5:
  859.  call doTagH_begin 5;
  860. return 0;
  861.  
  862. doTagH6:
  863.  call doTagH_begin 6;
  864. return 0;
  865.  
  866. doTag!H1:
  867. doTag!H2:
  868. doTag!H3:
  869. doTag!H4:
  870. doTag!H5:
  871. doTag!H6:
  872.  call SetFont Global.DefaultFont;
  873.  if \Global.IsTable
  874.   then do
  875.         call NewLine; call PutToken '.br'; call NewLine;
  876.         call NewLine; call PutToken '.br';
  877.        end;
  878.  call NewLine;
  879.  Global.AfterBreak = 1;
  880. return 0;
  881.  
  882. doTagHR:
  883.  call NewLine;
  884.  call PutToken ':cgraphic.'copies('─', 80)':ecgraphic.';
  885.  call doTagBR;
  886. return 0;
  887.  
  888. doTagOL:
  889.  if Global.IsTable
  890.   then return 0;
  891.  call doOpenOL;
  892. return 0;
  893.  
  894. doTag!OL:
  895.  if Global.IsTable
  896.   then return 0;
  897.  call NewLine;
  898.  call doCloseTag ':eol.';
  899.  call doTagBR;              /* UM */
  900. return 0;
  901.  
  902. doTagMENU:
  903. doTagUL:
  904.  if Global.IsTable
  905.   then return 0;
  906.  call doOpenUL;
  907. return 0;
  908.  
  909. doTag!MENU:
  910. doTag!UL:
  911.  if Global.IsTable
  912.   then return 0;
  913.  call NewLine;
  914.  call doCloseTag ':eul.';
  915.  call doTagBR;              /* UM */
  916. return 0;
  917.  
  918. doTagLI:
  919.  if Global.IsTable
  920.   then return 0;
  921.  if (doCheckTag(':eul.') = 0) & (doCheckTag(':eol.') = 0)
  922.   then call doOpenUL;
  923.  call NewLine;
  924.  call PutToken ':li.';
  925.  Global.SkipSpaces = 1;
  926. return 0;
  927.  
  928. doTagDL:
  929.  if Global.IsTable
  930.   then return 0;
  931.  call doOpenDL;
  932. return 0;
  933.  
  934. doTag!DL:
  935.  if Global.IsTable
  936.   then return 0;
  937.  call NewLine;
  938.  if \Global.DLDescDefined
  939.   then call doTagDD;
  940.  call doCloseTag ':edl.';
  941. return 0;
  942.  
  943. doTagDT:
  944.  if Global.IsTable
  945.   then return 0;
  946.  if doCheckTag(':edl.') = 0
  947.   then call doOpenDL;
  948.  call NewLine; call PutToken ':dt.';
  949.  Global.SkipSpaces = 1;
  950.  Global.DLTermDefined = 1;
  951.  Global.DLDescDefined = 0;
  952. return 0;
  953.  
  954. doTagDD:
  955.  if Global.IsTable
  956.   then return 0;
  957.  if doCheckTag(':edl.') = 0
  958.   then call doOpenDL;
  959.  call NewLine;
  960.  if \Global.DLTermDefined
  961.   then call doTagDT;
  962.  call PutToken ':dd.';
  963.  Global.SkipSpaces = 1;
  964.  Global.DLTermDefined = 0;
  965.  Global.DLDescDefined = 1;
  966. return 0;
  967.  
  968. doTagA:
  969.  call CloseRef;
  970.  call ParseTag Token;
  971. return 0;
  972.  
  973. doTag!A:
  974.  call CloseRef;
  975. return 0;
  976.  
  977. doTagA_HREF:
  978.  i = GetLinkID(subTagValue);
  979.  if i > 0 then do /* changed, --UM */
  980.     _data = translate(Global.LinkID.i)
  981.     if (pos('.INF', _data) > 0) then do
  982.         if (pos('#', _data) > 0) then
  983.             _data = translate(Global.LinkID.i, " ", "#")
  984.         else _data = Global.LinkID.i
  985.         call PutToken ":link reftype=launch object='view.exe' data='"_data"'.";
  986.         Global.CurLink = i;
  987.         Global.RefEndTag = ':elink.'||Global.RefEndTag;
  988.     end
  989.     else do
  990.         call PutToken ':link reftype=hd res='i'.';
  991.         Global.CurLink = i;
  992.         Global.RefEndTag = ':elink.'||Global.RefEndTag;
  993.     end;
  994.   end
  995. return 0;
  996.  
  997. doTagA_AUTO: /* new, --UM */
  998.  i = GetLinkID(subTagValue);
  999.  if i > 0 then do /* changed, UM */
  1000.     call PutToken ':link reftype=hd res='i' auto dependent.';
  1001.     Global.CurLink = i;
  1002.   end
  1003. return 0;
  1004.  
  1005. doTagA_NAME:
  1006.  /* ignore */
  1007. return 0;
  1008.  
  1009. doTagIMG:
  1010.  Global._altName = 'missing Picture';
  1011.  Global._imgName = '';
  1012.  if Global.IsCentered /* Choose default picture alignment */
  1013.   then Global._imgAlign = 'center';
  1014.   else Global._imgAlign = 'left';
  1015.  call ParseTag Token;
  1016.  _imgBitmap = GetPictureID(Global._imgName);
  1017.  if (\Global.optP) | (length(_imgBitmap) <= 1),
  1018.     | Global.IsTable       /* Since IPF does not allow pictures in tables :-( */
  1019.   then do
  1020.         if Global.optP & \Global.IsTable
  1021.          then do
  1022.                call SetColor Yellow;
  1023.                parse value SysCurPos() with row col;
  1024.                if col > 0 then call CRLF;
  1025.                say 'Warning: Picture "'Global._imgName'" missing';
  1026.                call logError 'Picture "'Global._imgName'" missing';
  1027.               end;
  1028.         call PutText ' 'Global._altName' ';
  1029.        end
  1030.   else do
  1031.         if pos(':elink.', Global.RefEndTag) > 0
  1032.          then do /* image is a link */
  1033.                call PutToken ':elink.';
  1034.               end;
  1035.         if Global.IsParagraph
  1036.          then call PutToken Global.EOL;
  1037.         Global.Picture.0 = Global.Picture.0 + 1;
  1038.         i = Global.Picture.0;
  1039.         Global.Picture.i.dst = left(_imgBitmap, pos('*', _imgBitmap) - 1);
  1040.         Global.Picture.i.src = substr(_imgBitmap, pos('*', _imgBitmap) + 1);
  1041.         Global.Picture.i.alt = Global._altName;
  1042.         call PutToken ':artwork name='''Global.Picture.i.dst''' align='Global._imgAlign;
  1043.         if Global.IsParagraph
  1044.          then call PutToken ' runin.';
  1045.          else call PutToken '.';
  1046.         if pos(':elink.', Global.RefEndTag) > 0
  1047.          then do /* image is a link */
  1048.                call PutToken ':artlink.:link reftype=hd res='Global.CurLink'.:eartlink.';
  1049.                call PutToken ':link reftype=hd res='Global.CurLink'.';
  1050.               end;
  1051.        end;
  1052. return 0;
  1053.  
  1054. doTagIMG_ALIGN:
  1055.  if pos('<'translate(subTagValue)'>', '<LEFT><RIGHT><CENTER>') > 0
  1056.   then Global._imgAlign = subTagValue;
  1057. return 0;
  1058.  
  1059. doTagIMG_SRC:
  1060.  Global._imgName = subTagValue;
  1061. return 0;
  1062.  
  1063. doTagIMG_ALT:
  1064.  Global._altName = subTagValue;
  1065. return 0;
  1066.  
  1067. doTagIMG_WIDTH:
  1068. doTagIMG_HEIGHT:
  1069. /* nop */
  1070. return 0;
  1071.  
  1072. doTagADDRESS:
  1073. /* nop */
  1074. return 0;
  1075.  
  1076. doTag!ADDRESS:
  1077. /* nop */
  1078. return 0;
  1079.  
  1080. doTagMETA:
  1081. /* nop */
  1082. return 0;
  1083.  
  1084. doTagCENTER:
  1085.  if \Global.OptCE
  1086.   then return 0;
  1087.  Global.IsCentered = 1;
  1088.  call PutToken ':lines align=center.';
  1089. return 0;
  1090.  
  1091. doTag!CENTER:
  1092.  if \Global.OptCE
  1093.   then return 0;
  1094.  if Global.IsCentered
  1095.   then do
  1096.         Global.IsCentered = 0;
  1097.         call NewLine;
  1098.         call PutToken ':elines.';
  1099.        end;
  1100. return 0;
  1101.  
  1102. doTagTABLE:
  1103.  Global.Table.WasCentered = Global.IsCentered;
  1104.  if Global.IsCentered
  1105.   then call doTag!CENTER;
  1106.  call NewLine;
  1107.  Global.AfterBreak = 0;
  1108.  call PutToken '.* table';
  1109.  Global.Table.Begin = Global.Article.Line.0;
  1110.  call NewLine;
  1111.  Global.Table.Width = 0;
  1112.  Global.Table.MaxWidth = 0;
  1113.  Global.AfterBreak = 1;
  1114.  Global.IsTable = 1;
  1115.  Global.IsOutputEnabled = 0;
  1116. return 0;
  1117.  
  1118. doTag!TABLE:
  1119.  call NewLine;
  1120.  if (Global.IsTable)
  1121.   then do
  1122.         i = Global.Table.Begin;
  1123.         if Global.Table.MaxWidth > 0
  1124.          then ColWidth = (79 - Global.Table.MaxWidth) % Global.Table.MaxWidth
  1125.          else tableCols = 78;
  1126.         tableCols = '';
  1127.         do j = 1 to Global.Table.MaxWidth
  1128.          tableCols = tableCols' 'ColWidth;
  1129.         end;
  1130.         if \Global.OptCH
  1131.          then Global.Article.Line.i = ':table cols='''substr(tableCols, 2)'''.';
  1132.         call PutToken ':etable.';
  1133.        end;
  1134.  Global.Table.Begin = 0;
  1135.  Global.IsTable = 0;
  1136.  Global.IsOutputEnabled = 1;
  1137.  if Global.Table.WasCentered
  1138.   then call doTagCENTER;
  1139. return 0;
  1140.  
  1141. doTagTR:
  1142.  call PutToken ':row.';
  1143.  call PutToken Global.EOL;
  1144.  Global.IsOutputEnabled = 0;
  1145. return 0;
  1146.  
  1147. doTag!TR:
  1148.  call CloseRef;
  1149.  if Global.Table.Width > Global.Table.MaxWidth
  1150.   then Global.Table.MaxWidth = Global.Table.Width;
  1151.  Global.Table.Width = 0;
  1152. return 0;
  1153.  
  1154. doTagTH:
  1155.  Global.IsOutputEnabled = 1;
  1156.  Global.Table.Width = Global.Table.Width + 1;
  1157.  call PutToken ':c.'; call doTagU;
  1158. return 0;
  1159.  
  1160. doTag!TH:
  1161.  call CloseRef;
  1162.  call doTag!U;
  1163. return 0;
  1164.  
  1165. doTagTD:
  1166.  Global.IsOutputEnabled = 1;
  1167.  Global.Table.Width = Global.Table.Width + 1;
  1168.  call PutToken ':c.';
  1169. return 0;
  1170.  
  1171. doTag!TD:
  1172.  call CloseRef;
  1173. return 0;
  1174.  
  1175. doTextEMPTY:
  1176.  Token = translate(Token, ' ', xrange(d2c(0),d2c(31)));
  1177.  if length(strip(Token)) > 0
  1178.   then call logError 'Unexpected text 'Token;
  1179. return;
  1180.  
  1181. doTextHEAD:
  1182.  if Global.grabTitle = 1
  1183.   then Global.Article.Title = Global.Article.Title||IPFstring(translate(Token, '  ', d2c(9)d2c(10)))
  1184.   else call dotextempty;
  1185. return;
  1186.  
  1187. doTextBODY:
  1188.  call PutText Token;
  1189. return;
  1190.  
  1191. doOpenOL:
  1192.  call NewLine;
  1193.  call doOpenTag ':ol compact.',':eol.';
  1194. return;
  1195.  
  1196. doOpenUL:
  1197.  call NewLine;
  1198.  call doOpenTag ':ul compact.',':eul.';
  1199. return;
  1200.  
  1201. doOpenDL:
  1202.  call NewLine;
  1203.  call doOpenTag ':dl compact break=all.', ':edl.';
  1204.  Global.DLTermDefined = 0;
  1205.  Global.DLDescDefined = 0;
  1206. return;
  1207.  
  1208. CloseRef:
  1209.  call PutToken Global.RefEndTag;
  1210.  Global.RefEndTag = '';
  1211. return;
  1212.  
  1213. /* recursive Tags management */
  1214. doOpenTag:
  1215.  parse arg ot, ct;
  1216.  call PutToken ot;
  1217.  Global.OpenTag.0 = Global.OpenTag.0 + 1;
  1218.  i = Global.OpenTag.0;
  1219.  Global.OpenTag.i = ct;
  1220.  Global.OpenTag.i.open = ot;
  1221. return;
  1222.  
  1223. doCloseTag:
  1224.  parse arg bottom;
  1225.  if length(bottom) = 0
  1226.   then i = 1
  1227.   else do i = Global.OpenTag.0 to 0 by -1
  1228.         if bottom = Global.OpenTag.i
  1229.          then leave;
  1230.        end;
  1231.  if i > 0
  1232.   then do
  1233.         call NewLine;
  1234.         do j = Global.OpenTag.0 to i by -1
  1235.          call PutToken Global.OpenTag.j;
  1236.          call PutToken Global.EOL;
  1237.         end;
  1238.         Global.OpenTag.0 = i - 1;
  1239.         return 1;
  1240.        end;
  1241. return 0;
  1242.  
  1243. doCheckTag:
  1244.  parse arg SearchArg;
  1245.  do i = Global.OpenTag.0 to 1 by -1
  1246.   if pos(SearchArg, Global.OpenTag.i) > 0
  1247.    then return 1;
  1248.  end;
  1249. return 0;
  1250.  
  1251. /* Set the current font in output stream */
  1252. SetFont:
  1253.  parse arg Font;
  1254.  if Global.IsTable
  1255.   then return;
  1256.  if Global.CurFont = Font
  1257.   then return;
  1258.  Global.CurFont = Font;
  1259.  call PutToken Font;
  1260. return;
  1261.  
  1262. /* Get id number depending of link value (<A HREF=...>) */
  1263. /* Returns 0 if link belongs to same page (alas, IPF doesn`t permit this...) */
  1264. GetLinkID:
  1265.  procedure expose Global.;
  1266.  parse arg link;
  1267.  
  1268.  InitialLink = link;
  1269.  if (pos('#', link) > 0) & (pos('.INF', translate(link)) = 0)
  1270.   then link = left(link, pos('#', link) - 1);
  1271.  if length(link) = 0
  1272.   then return 0;
  1273.  link = FindFile(link);
  1274.  ulink = translate(link);
  1275.  i = wordpos(ulink, Global.HREF);
  1276.  if i > 0 then return i;
  1277.  Global.LinkID = Global.LinkID + 1;
  1278.  i = Global.LinkID;
  1279.  Global.LinkID.i = ulink;
  1280.  Global.LinkID.i.RealName = link;
  1281.  Global.LinkID.i.InitialName = InitialLink;
  1282.  Global.HREF = Global.HREF||ulink||' ';
  1283.  if (pos(".INF", translate(link)) > 0 ) then do /* inserted, UM */
  1284.         Global.LinkID.i.Resolved = 1;
  1285.  end
  1286.  else if (length(stream(link, 'c', 'query exists')) = 0)
  1287.   then do
  1288.         Global.LinkID.i.Resolved = 1;
  1289.         Global.URLinks = Global.URLinks + 1;
  1290.         j = Global.URLinks;
  1291.         Global.URLinks.j = i;
  1292.         parse var link prot ':' location;
  1293.         if (length(location) = 0) | (pos('/', prot) > 0)
  1294.          then Global.LinkID.i.RealName = filespec('N', translate(link, '\', '/'))
  1295.        end;
  1296.   else Global.LinkID.i.Resolved = 0;
  1297. return i;
  1298.  
  1299. /* transform image extension into .bmp */
  1300. GetPictureID:
  1301.  procedure expose Global.;
  1302.  parse arg PictName;
  1303.  
  1304.  PictName = FindFile(PictName);
  1305.  if length(stream(PictName, 'c', 'query exists')) > 0
  1306.   then do
  1307.         tmp = PictName;
  1308.         i = lastPos('.', tmp);
  1309.         if i > 0
  1310.          then PictName = left(tmp, i)||'bmp';
  1311.          else PictName = tmp||'.bmp';
  1312.        end
  1313.   else do
  1314.         tmp = '';
  1315.         PictName = '';
  1316.        end;
  1317. return PictName||'*'||tmp;
  1318.  
  1319. /* Actively search for file on all possible paths */
  1320. FindFile:
  1321.  parse arg fName;
  1322.  
  1323.  ifName = fName;
  1324.  parse var fName prot ':' location;
  1325.  if (length(location) > 0) & (pos('/', prot) = 0)
  1326.   then fName = location;
  1327.  tmp = '';
  1328.  do while length(fName) > 0
  1329.   do while pos(left(fName, 1), '/\') > 0
  1330.    fName = substr(fName, 2);
  1331.   end;
  1332.   if length(fName) = 0
  1333.    then leave;
  1334.   tmp = stream(fName, 'c', 'query exists');
  1335.   if length(tmp) > 0 then return Shorten(tmp);
  1336.   tmp = stream(Global.CurrentDir||fName, 'c', 'query exists');
  1337.   if length(tmp) > 0 then return Shorten(tmp);
  1338.   tmp1 = Pos('/', fName);
  1339.   tmp2 = Pos('\', fName);
  1340.   if (tmp2 < tmp1) & (tmp2 > 0)
  1341.    then tmp = tmp2
  1342.    else tmp = tmp1;
  1343.   if tmp > 0
  1344.    then fName = substr(fName, tmp)
  1345.    else fName = '';
  1346.  end;
  1347. return ifName;
  1348.  
  1349. /* return next Token (a Tag or a text string) from input stream */
  1350. GetToken:
  1351.  procedure expose Global.;
  1352.  if (length(Global.FileContents) < 512) & (\Global.EOF)
  1353.   then
  1354. GetData:
  1355.        do
  1356.      /* read next chunk of file */
  1357.         Global.FileContents = Global.FileContents||charin(Global.CurrentFile,,1024);
  1358.         call ProgressBar;
  1359.      /* remove all \0x0d Characters from input stream */
  1360.         do until i = 0
  1361.          i = pos('0D'x, Global.FileContents);
  1362.          if i > 0 then Global.FileContents = delstr(Global.FileContents, i, 1);
  1363.         end;
  1364.         Global.EOF = (chars(Global.CurrentFile) = 0);
  1365.        end;
  1366.  
  1367.  i = pos('<', Global.FileContents);
  1368.  if (i = 0)
  1369.   then if (\Global.EOF)
  1370.         then signal GetData;
  1371.         else do
  1372.               i = length(Global.FileContents) + 1;
  1373.               if i = 1 then return '';
  1374.              end;
  1375.  if (i = 1)
  1376.   then do
  1377.         j = pos('>', Global.FileContents);
  1378.         if (j = 0)
  1379.          then if \Global.EOF
  1380.                then signal GetData;
  1381.                else j = length(Global.FileContents) + 1;
  1382.         Token = '00'x||substr(Global.FileContents, 2, j - 2);
  1383.         Global.FileContents = substr(Global.FileContents, j + 1);
  1384.        end
  1385.   else do
  1386.         Token = NoQuotes(left(Global.FileContents, i - 1));
  1387.         Global.FileContents = substr(Global.FileContents, i);
  1388.        end;
  1389. return Token;
  1390.  
  1391. /* put an IPF Token into output stream */
  1392. PutToken:
  1393.  procedure expose Global.;
  1394.  parse arg Output;
  1395.  
  1396.  if Global.OptCH then return;
  1397.  
  1398.  if Output = Global.EOL
  1399.   then if Global.AfterBreak
  1400.         then Global.AfterBreak = 0;
  1401.         else do
  1402.               Global.Article.line.0 = Global.Article.line.0 + 1;
  1403.               i = Global.Article.line.0;
  1404.               Global.Article.line.i = '';
  1405.              end;
  1406.   else do
  1407.         Global.AfterBreak = 0;
  1408.         i = Global.Article.line.0;
  1409.         if length(Global.Article.line.i) + length(Output) > Global.maxLineLength
  1410.          then do; call PutToken Global.EOL; i = Global.Article.line.0; end;
  1411.         Global.Article.line.i = Global.Article.line.i||Output;
  1412.        end;
  1413. return;
  1414.  
  1415. /* Put an text string into Output stream */
  1416. /* If EOLs are present, string is subdivided */
  1417. PutText:
  1418.  procedure expose Global.;
  1419.  parse arg Output;
  1420.  
  1421.  if Global.OptCH then return;
  1422.  
  1423.  if (Global.IsTable) & (\Global.IsOutputEnabled)
  1424.   then return; /* Skip everything out of :c. ... :c. or :row. tags */
  1425.  
  1426.  if Global.SkipSpaces
  1427.   then Output = strip(strip(Output, 'leading'), 'leading', d2c(9));
  1428.  
  1429.  do while length(Output) > 0
  1430.   EOLpos = pos(Global.EOL, Output);
  1431.   if EOLpos > 0
  1432.    then do
  1433.          if EOLpos > 1
  1434.           then _text_ = left(Output, EOLpos - 1);
  1435.          Output = substr(Output, EOLpos + 1);
  1436.          if EOLpos > 1
  1437.           then call PutText _text_;
  1438.          call PutToken Global.EOL;
  1439.         end;
  1440.    else do
  1441.          Global.SkipSpaces = 0;
  1442.         /* replace tab Characters with needed number of spaces */
  1443.          curpos = -1;
  1444.          do forever
  1445.           tabpos = pos(d2c(9), Output);
  1446.           if tabpos = 0 then leave;
  1447.           if curpos = -1 /* effective position not yet computed? */
  1448.            then do
  1449.                  i = Global.Article.line.0;
  1450.                  tmpS = Global.Article.line.i;
  1451.                  curpos = 0;
  1452.                  do while length(tmpS) > 0
  1453.                   if pos(left(tmpS, 1), '&:.') > 0
  1454.                    then do
  1455.                          EOLpos = pos('.', tmpS, 2);
  1456.                          if EOLpos = 0 then leave;
  1457.                          if left(tmpS, 1) = '&'
  1458.                           then tmpS = substr(tmpS, EOLpos);
  1459.                           else do; tmpS = substr(tmpS, EOLpos + 1); iterate; end;
  1460.                         end;
  1461.                   curpos = curpos + 1; tmpS = substr(tmpS, 2);
  1462.                  end;
  1463.                 end;
  1464.           Output = left(Output, tabpos - 1)||copies(' ',
  1465.            ,8 - (curpos + tabpos - 1)//8)||substr(Output, tabpos + 1);
  1466.          end;
  1467.          Output = IPFstring(Output);
  1468.         /* SubDivide Output string if it is too long */
  1469.          i = Global.Article.line.0;
  1470.          do while length(Global.Article.line.i) + length(Output) > Global.maxLineLength
  1471.           EOLpos = Global.maxLineLength - length(Global.Article.line.i);
  1472.           j = EOLpos;
  1473.           do while (EOLpos > 0)
  1474.            if (c2d(substr(Output, EOLpos, 1)) <= 32) then Leave;
  1475.            EOLpos = EOLpos - 1;
  1476.           end;
  1477.           if (EOLpos = 0) & (length(Global.Article.line.i) = 0)
  1478.            then do /* Line cannot be split on word-bound :-( */
  1479.                  EOLpos = j;
  1480.                  _text_ = left(Output, EOLpos - 1);
  1481.                  Output = substr(Output, EOLpos);
  1482.                 end
  1483.            else do
  1484.                  if EOLpos > 1
  1485.                   then _text_ = left(Output, EOLpos - 1)
  1486.                   else _text_ = '';
  1487.                  Output = substr(Output, EOLpos + 1);
  1488.                 end;
  1489.           Global.Article.line.i = Global.Article.line.i||_text_;
  1490.           call PutToken Global.EOL;
  1491.           i = Global.Article.line.0;
  1492.          end;
  1493.          Global.Article.line.i = Global.Article.line.i||Output;
  1494.          Output = '';
  1495.         end;
  1496.  end;
  1497.  Global.AfterBreak = 0;
  1498. return;
  1499.  
  1500. PutLine:
  1501.  parse arg str;
  1502.  if Global.OptCH then return;
  1503.  call lineout Global.oName, str;
  1504. return;
  1505.  
  1506. NewLine:
  1507.  nli = Global.Article.line.0;
  1508.  if length(Global.Article.line.nli) > 0
  1509.   then do; call PutToken Global.EOL; return 1; end;
  1510. return 0;
  1511.  
  1512. IPFstring:
  1513.  parse arg ins;
  1514.  return ChangeStr(d2c(0), ,
  1515.          ChangeStr(':', ,
  1516.           ChangeStr('&', ,
  1517.            ChangeStr('.', ins, d2c(0)'per.'), ,
  1518.           '&.'), ,
  1519.          '&colon.'), ,
  1520.         '&');
  1521. /*
  1522.  ins = StrReplace('.', d2c(0)'per.', ins);
  1523.  ins = StrReplace('&', '&.', ins);
  1524.  ins = StrReplace(':', '&colon.', ins);
  1525. return StrReplace(d2c(0), '&', ins);
  1526. */
  1527. ChangeStr:
  1528.  procedure expose Global.;
  1529.  parse arg src,var,trg;
  1530.  curpos = 1;
  1531.  do forever
  1532.   curpos = pos(src, var, curpos);
  1533.   if curpos = 0 then leave;
  1534.   var = left(var, curpos - 1)||trg||substr(var, curpos + 1);
  1535.   curpos = curpos + length(trg);
  1536.  end;
  1537. return var;
  1538.  
  1539. Shorten:
  1540.  parse arg fName;
  1541.  fName = translate(stream(fName, 'c', 'query exists'), '/', '\');
  1542.  tmp = translate(Directory(), '/', '\');
  1543.  if Pos(tmp, fName) = 1
  1544.   then return substr(fName, length(tmp) + 2);
  1545.  if substr(fName, 2, 1) = ':'
  1546.   then return substr(fName, 3);
  1547. return fName;
  1548.  
  1549. logError:
  1550.  procedure expose Global.;
  1551.  if Global.optD
  1552.   then do
  1553.         parse arg line;
  1554.         call lineout 'HTML2IPF.log', line;
  1555.        end;
  1556. return;
  1557.  
  1558. CRLF:
  1559.  parse value SysTextScreenSize() with maxrow maxcol;
  1560.  parse value SysCurPos() with row col;
  1561.  call charout ,copies(' ', maxcol-col);
  1562. return;
  1563.  
  1564. ProgressBar:
  1565.  parse value SysCurPos() with row col;
  1566.  if col > 79 - 18 then say '';
  1567.  Rest = ((Global.FileSize - chars(Global.CurrentFile)) * 16) % Global.FileSize;
  1568.  call setcolor lcyan; call charOut ,'[';
  1569.  call setcolor white; call charOut ,copies('█', Rest)copies('▒', 16-Rest);
  1570.  call setcolor lcyan; call charOut ,']'copies('08'x, 18);
  1571. return;
  1572.  
  1573. SetColor:
  1574.  arg col;
  1575.  col = ColorNo(col);
  1576.  if \Global.optCO then return;
  1577.  
  1578.  if col = -1 then return -1;
  1579.  if col > 7
  1580.   then col = '1;3'col-8;
  1581.   else col = '0;3'col;
  1582.  call Charout ,d2c(27)'['col'm';
  1583. return 0;
  1584.  
  1585. ColorNo:
  1586.  arg colname;
  1587.  if substr(colname, 1, 1) = 'L'
  1588.   then do
  1589.         colname = right(colname, length(colname) - 1);
  1590.         light = 8;
  1591.        end
  1592.   else light = 0;
  1593.  select
  1594.   when abbrev('BLACK', colname, 3)
  1595.    then return light + 0;
  1596.   when abbrev('BLUE', colname, 3)
  1597.    then return light + 4;
  1598.   when abbrev('GREEN', colname, 3)
  1599.    then return light + 2;
  1600.   when abbrev('CYAN', colname, 3)
  1601.    then return light + 6;
  1602.   when abbrev('RED', colname, 3)
  1603.    then return light + 1;
  1604.   when abbrev('MAGENTA', colname, 3)
  1605.    then return light + 5;
  1606.   when abbrev('BROWN', colname, 3)
  1607.    then return light + 3;
  1608.   when abbrev('GRAY', colname, 3)
  1609.    then return light + 7;
  1610.   when abbrev('DGRAY', colname, 3)
  1611.    then return 8;
  1612.   when abbrev('YELLOW', colname, 3)
  1613.    then return 11;
  1614.   when abbrev('WHITE', colname, 3)
  1615.    then return 15;
  1616.  end;
  1617.  return -1;
  1618.  
  1619. /* these constants have been ripped from    */
  1620. /* HTM2txt v 1.0, mar.11,1997 by otto räder */
  1621. DefineQuotes:
  1622. /* --------------------------------------------- */
  1623. /* constants contributed by tremro@digicom.qc.ca */
  1624. /* --------------------------------------------- */
  1625.  Global.Quotes = ,
  1626.   "COPY   (C)",
  1627.   "SPACE  0x20",
  1628.   "QUOT   0x22",
  1629.   "AMP    0x00",
  1630.   "LT     <",
  1631.   "GT     >",
  1632.   "NBSP   0x20",
  1633.   "#160   0x20",
  1634.   "IEXCL  0xA1",
  1635.   "CENT   0xA2",
  1636.   "POUND  0xA3",
  1637.   "CURREN 0xA4",
  1638.   "YEN    0xA5",
  1639.   "BRVBAR 0xA6",
  1640.   "SECT   0xA7",
  1641.   "UML    0xA8",
  1642.   "COPY   0xA9",
  1643.   "ORDF   0xAA",
  1644.   "LAQNO  0xAB",
  1645.   "NOT    0xAC",
  1646.   "SHY    0xAD",
  1647.   "REG    0xAE",
  1648.   "HIBAR  0xAF",
  1649.   "DEG    0xB0",
  1650.   "PLUSMN 0xB1",
  1651.   "SUP2   0xB2",
  1652.   "SUP3   0xB3",
  1653.   "ACUTE  0xB4",
  1654.   "MICRO  0xB4",
  1655.   "PARA   0xB6",
  1656.   "MIDDOT 0xB7",
  1657.   "CEDIL  0xB8",
  1658.   "SUP1   0xB9",
  1659.   "ORDM   0xBA",
  1660.   "RAQUO  0xBB",
  1661.   "FRAC14 0xBC",
  1662.   "FRAC12 0xBD",
  1663.   "FRAC34 0xBE",
  1664.   "IQUEST 0xBF",
  1665.   "AGRAVE 0xC0",
  1666.   "AACUTE 0xC1",
  1667.   "ACIRC  0xC2",
  1668.   "ATILDE 0xC3",
  1669.   "AUML   0xC4",
  1670.   "ARING  0xC5",
  1671.   "AELIG  0xC6",
  1672.   "CCEDIL 0xC7",
  1673.   "EGRAVE 0xC8",
  1674.   "EACUTE 0xC9",
  1675.   "ECIRC  0xCA",
  1676.   "EUML   0xCB",
  1677.   "IGRAVE 0xCC",
  1678.   "IACUTE 0xCD",
  1679.   "ICIRC  0xCE",
  1680.   "IUML   0xCF",
  1681.   "ETH    0xD0",
  1682.   "NTILDE 0xD1",
  1683.   "OGRAVE 0xD2",
  1684.   "OACUTE 0xD3",
  1685.   "OCIRC  0xD4",
  1686.   "OTILDE 0xD5",
  1687.   "OUML   0xD6",
  1688.   "TIMES  0xD7",
  1689.   "OSLASH 0xD8",
  1690.   "UGRAVE 0xD9",
  1691.   "UACUTE 0xDA",
  1692.   "UCIRC  0xDB",
  1693.   "UUML   0xDC",
  1694.   "YACUTE 0xDD",
  1695.   "THORN  0xDE",
  1696.   "SZLIG  0xDF",
  1697.   "AGRAVE 0xE0",
  1698.   "AACUTE 0xE1",
  1699.   "ACIRC  0xE2",
  1700.   "ATILDE 0xE3",
  1701.   "AUML   0xE4",
  1702.   "ARING  0xE5",
  1703.   "AELIG  0xE6",
  1704.   "CCEDIL 0xE7",
  1705.   "EGRAVE 0xE8",
  1706.   "EACUTE 0xE9",
  1707.   "ECIRC  0xEA",
  1708.   "EUML   0xEB",
  1709.   "IGRAVE 0xEC",
  1710.   "IACUTE 0xED",
  1711.   "ICIRC  0xEE",
  1712.   "IUML   0xEF",
  1713.   "ETH    0xF0",
  1714.   "NTILDE 0xF1",
  1715.   "OGRAVE 0xF2",
  1716.   "OACUTE 0xF3",
  1717.   "OCIRC  0xF4",
  1718.   "OTILDE 0xF5",
  1719.   "OUML   0xF6",
  1720.   "DIVIDE 0xF7",
  1721.   "OSLASH 0xF8",
  1722.   "UGRAVE 0xF9",
  1723.   "UACUTE 0xFA",
  1724.   "UCIRC  0xFB",
  1725.   "UUML   0xFC",
  1726.   "YACUTE 0xFD",
  1727.   "THORN  0xFE",
  1728.   "YUML   0xFF";
  1729. return;
  1730.  
  1731. /* substitute quoted Characters */
  1732. NoQuotes:
  1733.  parse arg text;
  1734.  
  1735.  sPos = 1;
  1736.  do forever
  1737.   qPos = pos('&', text, sPos);
  1738.   if qPos = 0 then leave;
  1739.   parse var text _head '&' Token ';' _tail
  1740.  
  1741.   wordN = wordpos(translate(Token), Global.Quotes);
  1742.   if wordN = 0
  1743.    then do
  1744.          if (left(Token, 1)='#') & (datatype(substr(Token, 2), 'num'))
  1745.           then do
  1746.                 Token = substr(Token,2);
  1747.                 Token = d2c(Token);
  1748.                end
  1749.           else do
  1750.                 Token=d2c(0)||Token||';'
  1751.                end
  1752.         end
  1753.    else do
  1754.          Token = word(Global.Quotes, wordN + 1);
  1755.          if left(Token, 2)="0x" then Token=x2c(substr(Token, 3));
  1756.         end
  1757.   sPos = length(_head) + length(Token) + 1;
  1758.   text = _head||Token||_tail;
  1759.  end;
  1760. return translate(text, '&', d2c(0));
  1761.