home *** CD-ROM | disk | FTP | other *** search
/ CGI How-To / CGI HOW-TO.iso / chap5 / 5_1 / parse_pl / parsehtm.pl < prev    next >
Encoding:
Perl Script  |  1996-06-15  |  9.1 KB  |  408 lines

  1. #!/usr/local/bin/perl
  2.  
  3. # This package uses the global file handle htmlFile
  4. # There are two global assoc. arrays, endTags & handlerDict
  5.  
  6. # parseHtml takes one argument, a filename
  7. # and returns the parsed html in a string
  8.  
  9. sub parseHtml
  10. {
  11.     # Declare variables to hold the arguments
  12.     local($fileName) = @_;
  13.  
  14.     # Declare a variable to store the return value
  15.     local($retVal);
  16.  
  17.     # Open the file
  18.     open(htmlFile,$fileName);
  19.  
  20.     # If the file opened, call the parser on it
  21.     $retVal = &mainHtmlParser("",0) if htmlFile;
  22.  
  23.     # Close the file
  24.     close(htmlFile);
  25.  
  26.     # Return the string parsed from the file
  27.     return $retVal;
  28. }
  29.  
  30. # mainHtmlParser takes several arguments
  31. # This subroutine can either take a stop string, or a stop char
  32. # it reads the file htmlFile until either the end of file
  33. # the stopstring or the stop char is encountered.
  34. #
  35. # mainHtmlParser returns a string filtered from the file.
  36. # The filters are tag handlers and a default handler.
  37. # Handlers should take 5 arguments for:
  38. #
  39. # tagString - The string containing the tag
  40. # argString - Any data between the tag and end tag
  41. # endString - The end tag
  42. # tagDict - The dictionary created using dictForTag
  43. # userData - The user data argument
  44. #
  45. # Handlers are registered in the global dictionary
  46. # handlerDict.
  47. #
  48. # If the tag has a matching end tag like <HTML> and </HTML>
  49. # then the tag should be registered in the global
  50. # %endTags array, with the value equal to its end tag.
  51. #
  52. # If the tag needs the data up to the end of the line, like
  53. # OPTION, then if should appear in %endTags with the value
  54. # "eol".
  55. #
  56. # Handlers should return the string to replace the tag with.
  57. #
  58. # The default is used for text that wasn't part of a tag.
  59. # Tags are denoted by <text>.
  60. # As plain text is encountered the handler registered under
  61. # the string "DEFAULT" is called.
  62.  
  63. sub mainHtmlParser
  64. {
  65.     # Declare locals to store the arguments
  66.     local($stopStr,$stopChar) = @_;
  67.  
  68.     # Declare several local variables
  69.     local($char,$inTag,$tmpBuffer,$mainBuffer);
  70.  
  71.     # Initialize the main buffer, this is what is returned
  72.     $mainBuffer = "";
  73.     
  74.     # $inTag is used to denote when we are inside <>'s
  75.     $inTag = 0;
  76.  
  77.     # Loop until the end of the file, or
  78.     # we encounter the stop string or stop character.
  79.     do
  80.     {
  81.     
  82.         # Get the next character from the file.
  83.         # This is not the most effecient method of reading a file
  84.         # But makes our code cleaner
  85.         
  86.         $char = getc(htmlFile);
  87.         
  88.         # Check if we are at the start of a tag
  89.         if($char eq "<")
  90.         {
  91.             # Dont allow any tags inside other tags
  92.             if($inTag)
  93.             {
  94.                 die "This is an invalid html file.\n";
  95.             }
  96.             else
  97.             {
  98.                 # Denote that we are in a tag
  99.                 $inTag = 1;
  100.                 
  101.                 # If we were reading plain text
  102.                 if($tmpBuffer)
  103.                 {
  104.                     # Handle the plain text
  105.                     $mainBuffer .= &handlePlainText($tmpBuffer);
  106.             
  107.                     # Reset the tmp buffer
  108.                     $tmpBuffer = "";
  109.                 }
  110.                 
  111.                 # Start the new tmp buffer
  112.                 $tmpBuffer = "<";
  113.             }
  114.         }
  115.         elsif($char eq ">") # Check if we are at the end of a tag
  116.         {
  117.             # Dont allow end tags without start tags
  118.             if(! $inTag)
  119.             {
  120.                 die "This is an invalid html file.\n";
  121.             }
  122.             else
  123.             {
  124.                 # Denote the end of the tag
  125.                 $inTag = 0;
  126.  
  127.                 # Finish the tmp buffer
  128.                 $tmpBuffer .= ">";
  129.  
  130.                 # See if we are at the stop string
  131.                 if($stopStr && ($tmpBuffer =~ /$stopStr/i))
  132.                 {
  133.                     return $mainBuffer;#we have read to the stop string
  134.                 }
  135.                 else
  136.                 {
  137.                     # If not handle the tag, and keep reading
  138.                     $tmpBuffer = &handleTag($tmpBuffer);
  139.                     
  140.                     # Add the tmp buffer to the main buffer
  141.                     $mainBuffer .= $tmpBuffer;
  142.                     
  143.                     # Reset the tmp buffer
  144.                     $tmpBuffer = "";
  145.                 }
  146.             }
  147.         }
  148.         elsif(eof(htmlFile) 
  149.           || ($stopChar && ($char eq $stopChar))) # check for stopchar
  150.         {
  151.         
  152.             # Dont allow the parsing to end inside a tag
  153.             if($inTag)
  154.             {
  155.                 die "This is an invalid html file.\n";
  156.             }
  157.             else
  158.             {
  159.                 # Add the character to the tmp buffer
  160.                 $tmpBuffer .= $char if (!eof(htmlFile));
  161.         
  162.                 # Add the tmp buffer to the main buffer,
  163.                 # after handling it.
  164.                 $mainBuffer .= &handlePlainText($tmpBuffer);
  165.  
  166.                 # Reset the tmp buffer
  167.                 $tmpBuffer = "";
  168.             }
  169.             
  170.             # We are at the end of the file, or found
  171.             # the stop string, so return the main buffer
  172.             return $mainBuffer; 
  173.         }
  174.         else # If nothing else add the character to the tmp buffer
  175.         {
  176.             $tmpBuffer .= $char;
  177.         }
  178.  
  179.     }
  180.     until(eof(htmlFile));
  181.  
  182.     # Return the main buffer
  183.     return $mainBuffer;
  184. }
  185.  
  186. #
  187. # handleTag actualy handles the tags for mainHtml parser
  188.  
  189. sub handleTag
  190. {
  191.     # Declare local variables for the argument, as well
  192.     # as the other required locals.
  193.     
  194.     local($tagString) = @_;
  195.     local(%tagDict,$endTag,$handler,$argString);
  196.     local($evalString);
  197.  
  198.     # Create an associative array containing the data for the
  199.     # tag string.
  200.     
  201.     %tagDict = &dictForTag($tagString);
  202.  
  203.     # Look for an end tag. These are registered in the %endTags
  204.     # global associative array.
  205.     
  206.     $endTag = $endTags{$tagDict{"TAG"}};
  207.  
  208.     # Look for a handler subroutine for the tag.
  209.     # These are registered in the %handlerDict global
  210.     # associative array.
  211.     
  212.     $handler = $handlerDict{$tagDict{"TAG"}};
  213.     
  214.     # If no handler is found, treat the tag as plain text, and
  215.     # return the parsed data.
  216.     
  217.     if(!($handler))
  218.     {
  219.         $tagString = &handlePlainText($tagString);
  220.  
  221.         return $tagString;
  222.     }
  223.  
  224.     # If the tag wants the data to the end of the line
  225.     # use mainHtmlParser to read to the end of the line, then
  226.     # call the tag's handler subroutine with the data to the
  227.     # end of the line.
  228.     
  229.     if($endTag eq "eol")    # Tag that needs data to eol
  230.     {
  231.         $argString = &mainHtmlParser("","\n");
  232.     
  233.         $evalString = "&".$handler.'($tagString,$argString,0,%tagDict);';
  234.     }
  235.     elsif($endTag)        # Tag with an end tag
  236.     {
  237.         # Use mainHtmlParser to read any text, up to
  238.         # the end tag. Remove the end tag from the sting.
  239.         
  240.         $argString = &mainHtmlParser($endTag,0);
  241.         $argString =~ s/<.*>$//; # Remove the end tag
  242.  
  243.         # Call the tag's handler
  244.         $evalString = "&".$handler.'($tagString,$argString,$endTag,%tagDict);';
  245.     }
  246.     else            # General unary tag
  247.     {
  248.         #For unary tags, simply call the handler.
  249.         $evalString = "&".$handler.'($tagString,0,0,%tagDict);';
  250.     }
  251.     
  252.     $tagString = eval($evalString);
  253.  
  254.     # Return the parsed text.
  255.     return $tagString;
  256. }
  257.  
  258. # handlePlainText actually handles plain text for htmlMainParser
  259.  
  260. sub handlePlainText
  261. {
  262.     # Declare the locals
  263.     
  264.     local($plainString) = @_;
  265.     local($handler,$evalString);
  266.     
  267.     # Look for a default handler for plain text
  268.     $handler = $handlerDict{"DEFAULT"};
  269.  
  270.     #If there is a handler, call it and catch the return value.
  271.     
  272.     if($handler)
  273.     {
  274.         $evalString = "&".$handler.'($plainString,0,0,0);';
  275.         $plainString = eval($evalString);    
  276.     }
  277.  
  278.     # Return either the text passed in, or the parsed text if there
  279.     # was a default handler.
  280.     
  281.     return $plainString;
  282. }
  283.  
  284. # Creates an associative array for a tag string
  285.  
  286. sub dictForTag
  287. {
  288.     # Declare locals
  289.     local($tagString) = @_;
  290.     local(%tagDict,$key);
  291.  
  292.     # Look for the tag
  293.     # Remove it from the tag string
  294.     # Capitalize the tag, and put it into the dict
  295.     # with the key, TAG
  296.     # If no tag is found, then this is not a tag string.
  297.     
  298.     if(($tagString =~ s/^<(\w*)[\s>]//) && $1)
  299.     {
  300.         ($key = $1) =~ tr/a-z/A-Z/; # Make the tag upper case
  301.  
  302.         $tagDict{"TAG"} = $key;
  303.     }
  304.     elsif(($tagString =~ s/^<!--(\w*)[\s>]//) && $1)
  305.     {
  306.         ($key = $1) =~ tr/a-z/A-Z/; # Make the tag upper case
  307.  
  308.         $tagDict{"TAG"} = $key;
  309.     }
  310.     else
  311.     {
  312.         return %tagDict;
  313.     }
  314.  
  315.     # Find all of the tag's key/value attrubutes
  316.     # Remove them from the tag string.
  317.     
  318.     while($tagString =~ s/(\w*)\s*=\s*\"([^\"]*)\"//)
  319.     {
  320.  
  321.         if($1)
  322.         {
  323.             ($key = $1) =~ tr/a-z/A-Z/;  # Make upper case
  324.         
  325.             if($2)
  326.             {
  327.             $tagDict{$key} = $2;    # Add the key to the dict
  328.             }
  329.             else
  330.             {
  331.             $tagDict{$key} = "";
  332.             }
  333.         }
  334.     }
  335.  
  336.     # Find the single attributes
  337.     # and remove them from the string.
  338.     while($tagString =~ s/\s+(\w*)[\s>]*//)
  339.     {
  340.         if($1)
  341.         {
  342.             ($key = $1) =~ tr/a-z/A-Z/;  # Make upper case
  343.             $tagDict{$key} = $key;    # Add to the dict
  344.         }
  345.     }
  346.     
  347.     return %tagDict;
  348. }
  349.  
  350. # Creates a string from a tag dictionary
  351.  
  352. sub stringForTagDict
  353. {
  354.     # Declare locals
  355.     local(%tagDict) = @_;
  356.     local($tagString);
  357.  
  358.     # If there was a tag dictionary passed in
  359.     if(%tagDict)
  360.     {
  361.         #If the tag dictionary has a TAG in it, build the tag string
  362.         if($tagDict{"TAG"})
  363.         {
  364.             # Start the string with a < and the tag
  365.             
  366.             $tagString .= "<";
  367.             $tagString .= $tagDict{"TAG"};
  368.             
  369.             # Add the keys to the string
  370.             
  371.             foreach $key (keys %tagDict)
  372.             {
  373.                 # Ignore TAG, we already added it
  374.                 
  375.                 if($key eq "TAG")
  376.                 {
  377.                     next;
  378.                 }
  379.                 elsif($key eq $tagDict{$key}) # unary attribute
  380.                 {
  381.                     $tagString .= " ";
  382.                     $tagString .= $key;
  383.                 }
  384.                 elsif($tagDict{$key}) #key/value attributes
  385.                 {
  386.                     $tagString .= " ";
  387.                        $tagString .= $key;
  388.                     $tagString .= "= \"";
  389.                     $tagString .= $tagDict{$key};
  390.                     $tagString .= "\"";
  391.                 }
  392.             }
  393.             
  394.             #Close the tag string
  395.             $tagString .= ">";
  396.         }
  397.     }
  398.  
  399.     #Return the tag string
  400.     return $tagString;
  401. }
  402.  
  403. 1;
  404.  
  405.  
  406.  
  407.  
  408.