<?php
$er 
error_reporting(E_ALL & ~E_NOTICE);
#StructuredText parser, by Keith Devens, version .99g.
#http://www.keithdevens.com/software/
#See release notes and To Do at http://www.keithdevens.com/software/markup_history.php
#
#This code is not being developed anymore. It still works and is still in use on my site,
# but I've begun development of version 2, which will replace this version when its released.
# Because this code is monolithic and not all that great, I don't even necessarily want my name
# associated with it - so, I declare it public domain. Do what you want with it, remove my name
# if you release it with something else (please) :), etc.

if(isset($old_error_reporting)) error_reporting($old_error_reporting);

define("MARKUP_STATE_NOTHING",    0);
define("MARKUP_STATE_LIST",       1);
define("MARKUP_STATE_BLOCKQUOTE"2);
define("MARKUP_STATE_PARA",       3);
define("MARKUP_STATE_TABLE",      4);
define("MARKUP_STATE_HR",         5);
define("MARKUP_STATE_DIRECTIVE",  6);
define("MARKUP_STATE_HEADING",    7);
define("MARKUP_STATE_PRE",        8);
define("MARKUP_STATE_RAW",        9);

global 
$markup_list_types$markup_list_subtypes$markup_escape_chars;

$markup_list_types    = array("*" => "ul""+" => "ul""#" => "ol");
$markup_list_subtypes = array("1" => "decimal""a" => "lower-alpha""A" => "upper-alpha""i" => "lower-roman""I" => "upper-roman""~" => "none");
$markup_escape_chars = array('\\*'=>"*"'\\_'=>'_''\\='=>'=''\\-'=>'-'"\\&quot;"=>"&quot;""\\]" => "]","\\[" => "[",'\\]'=>']'"\\`"=>"`");
function 
make_image($prefix$image$alt$alignment ""){
    
$output .= '<img ';
    if(
$alignment){
        
$alignment strtolower($alignment);
        if(
$alignment == "r"){
            
$output .= 'style="float: right" ';
        }elseif(
$alignment == "l"){
            
$output .= 'style="float: left" ';
        }elseif(
$alignment == 'c'){
            
$output .= 'style="display: block; margin-left: auto; margin-right: auto" ';
        }
    }
    return 
$output."src=\"$prefix$image\" alt=\"$alt\" />";
}
function 
long_url_cut($escaped_url$max_len){
    
$url html_entity_decode($escaped_url);
    return 
'<a href="'.$escaped_url.'">'.
        ((
$len strlen($url)) > $max_len ?
            
htmlspecialchars(substr($url,0,ceil($max_len/2)).'...'.substr($url,$len-(floor($max_len/2)-3))) :
            
$escaped_url)
        .
'</a>';
}
function & 
markup($str$flags = array()){
    
$er error_reporting(E_ALL & ~E_NOTICE);
    global 
$markup_list_types$markup_escape_chars;
    static 
$regexes$styles$searches$replaces$extensions;
    
$previous_list_stack = array();
    
$footnotes = array();
    
$heading_count = array(0,0,0,0,0,0);
    
$heading_master_count 0;
    
$heading_current_count 0;
    
$footnote_number 1;
    
$debug 0;

    
$extensions = array("code"=>"markup_code");

    
#set missing flags:
    
if(!isset($flags['only_get_title']))    {$flags['only_get_title']    = false;}
    if(!isset(
$flags['page_number']))       {$flags['page_number']       = 0;} #zero means get all pages
    
if(!isset($flags['get_page_info']))     {$flags['get_page_info']     = false;}
    
#Location offset is the directory offset from your root. This allows you to not have to be at the root of your
    # web space for you to use absolute links. By default it is set to "", which means the root.
    
if(!isset($flags['location_offset']))   {$flags['location_offset']   = '';}
    
#Main location is the permanent location of this document.
    #It's used for lead-ins.
    
if(!isset($flags['main_location']))     {$flags['main_location']     = '';}
    if(!isset(
$flags['footnotes_heading'])) {$flags['footnotes_heading'] = "Footnotes";} #Maybe someone would want "References"
    
if(!isset($flags['leadin_cont_text']))  {$flags['leadin_cont_text']  = "Read more";}
    if(!isset(
$flags["output_directly"]))   {$flags['output_directly']   = false;}

    if(!isset(
$flags['autonumber_headings'])){$flags['autonumber_headings'] = false;}
    if(!isset(
$flags['enable_raw_html']))   {$flags['enable_raw_html']   = true;}
    if(!isset(
$flags['enable_abs_links']))  {$flags['enable_abs_links']  = false;}
    if(!isset(
$flags['enable_TOC']))        {$flags['enable_TOC']        = true;}
    if(!isset(
$flags['enable_headings']))   {$flags['enable_headings']   = true;}
    if(!isset(
$flags['enable_images']))     {$flags['enable_images']     = true;}
    if(!isset(
$flags['enable_lists']))      {$flags['enable_lists']      = true;}
    if(!isset(
$flags['enable_tables']))     {$flags['enable_tables']     = true;}
    if(!isset(
$flags['enable_styles']))     {$flags['enable_styles']     = true;}
    if(!isset(
$flags['enable_mono']))       {$flags['enable_mono']       = true;}
    if(!isset(
$flags['enable_pre']))        {$flags['enable_pre']        = true;}
    if(!isset(
$flags['enable_hr']))         {$flags['enable_hr']         = true;}
    if(!isset(
$flags['enable_smileys']))    {$flags['enable_smileys']    = true;}
    if(!isset(
$flags['enable_comments']))   {$flags['enable_comments']   = true;}
    if(!isset(
$flags['enable_leadin']))     {$flags['enable_leadin']     = true;}
    if(!isset(
$flags['enable_footnotes']))  {$flags['enable_footnotes']  = true;}
    if(!isset(
$flags['enable_extensions'])) {$flags['enable_extensions'] = true;}
    if(!isset(
$flags['enable_settings']))   {$flags['enable_settings']   = true;}

    if(!isset(
$flags['show_leadin']))       {$flags['show_leadin']       = false;}

    if(!isset(
$flags['max_url_length']))    {$flags['max_url_length']    = 80;}

#    if(!isset($flags['link_back_headers'])) {$flags['link_back_headers'] = true;}

    
if($flags["enable_abs_links"]){
        
$url_prefix "http://$_SERVER[HTTP_HOST]$flags[location_offset]";
        
$flags["main_location"] = $url_prefix.$flags["main_location"];
    }else{
        
$url_prefix $flags['location_offset'];
    }

    
#----------------------------------------------------------------------------------
    # Inline Styles
    #----------------------------------------------------------------------------------
    
if(!$regexes){
        
$t '\/\\\\.:&;!?%0-9A-Za-z=+_^\\[\\]\\\'\\"\\$'#characters to match
        
$e '&*=`_\s-.:;,?!)(<>\\[\\]'#characters to exclude
        
$regexes = array();
        
$searches = array();
        
$regexes[] = "/(?<=^|[>$e])(?<!\\\\)`([$t<-].*?[$t>-]?)`/"#kbd
        
$styles[] = '<kbd>$1</kbd>';
        
$regexes[] = "/(?<=^|[>$e])(?<!\\\\)\*{2}([$t<-].*?[$t>-]?)\*{2}(?=[<$e]|$)/"#strong
        
$styles[] = '<strong>$1</strong>';
        
$regexes[] = "/(?<=^|\*{2}|[>$e])(?<!\\\\)\*([$t<-].*?[$t>-]?)\*(?=\*{2}|[<$e]|$)/"#emphasis
        
$styles[] = '<em>$1</em>';
        
$regexes[] = "/(?<=^|[>$e])(?<!\\\\)_([$t<*-].*?[$t>*-]?)_(?=[<$e]|$)/"#underline
        
$styles[] = '<em style="font-style: normal; text-decoration: underline">$1</em>';

    
#    $regexes[] = "/(?<=^|[>$e])(?<!\\\\)(-1,2})([$t<*].*?[$t>*])\\1(?=[<$e]|$)/"; #strikeout
        
$regexes[] = "/(?<=^|[>$e])(?<!\\\\)-{2}([$t<*].*?[$t>*])-{2}(?=[<$e]|$)/"#strikeout
        
$styles[] = '<strike>$1</strike>';

#        if($flags["enable_mono"]){
#            $regexes[] = "/(?<=^|[>$e])(?<!\\\\)=([$t<*-].*?[$t>*-])=(?=[<$e]|$)/"; #monospace
#            $styles[] = '<tt>$1</tt>';
#        }
        
if($flags["enable_styles"]){
    
#        $regexes[] = '/(?<=\\W|)(?<!\\\\)\$([A-Za-z<].*?[>A-Za-z0-9;])\$([A-Za-z<].*?[>A-Za-z])\$(?=\\W|)/'; #any style
            
$regexes[] = "/(?<=\\W|)(?<!\\\\)\\$([A-Za-z<].*?[>A-Za-z0-9;])(?<!\\\\)\\$([<$t].*?[$t>])(?<!\\\\)\\$(?=\\W|)/"#any style
            
$styles[] = '<span style="$1; display: inline; position: static">$2</span>';
        }
        if(
$flags["enable_images"]){
            
$regexes[] = "/img([lLrRcC]?)[:=](?<!\\\\)&quot;(.+?)(?<!\\\\)&quot;(?:[:=]?(?<!\\\\)&quot;(.+?)(?<!\\\\)&quot;)?/e"#images in the format img:"url":"alttext"
            
$styles[] = "make_image('$url_prefix', '$2', '$3', '$1')";
        }

#        $regexes[] = "/(?:(?<=^|[\s({]))(\w+:\/\/[\w.,\/?+~&=_:;#$%{}-]+[^\s.?!:,()<>[\]'\"&]+?)/e"; #urls
#        $styles[] = '<a href="$1">$1</a>';

        
$regexes[] = "/(?<=^|[\s({])(\w+:\/\/[\w.,\/?+~&=_:;#$%{}()'-|]+[^\s.?!:,()<>[\]'\"&]+?)/e"#urls
        
$styles[] = 'long_url_cut(\'$1\','.$flags['max_url_length'].');';

#        #email addresses
#        $regexes[] = "/(([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?))/e";
#        $styles[] = '"<a href=\"mailto:" . cryptemail("$1") . "\">" . cryptemail("$1") . "</a>"';
    
}

    
#----------------------------------------------------------------------------------
    # End Inline Styles
    #----------------------------------------------------------------------------------
    #----------------------------------------------------------------------------------
    # Simple string replacements
    #----------------------------------------------------------------------------------
    
if(!$searches){
        
$searches $replaces = array();
        if(
$flags["enable_smileys"]){
            
$searches[] = ':)';
            
$replaces[] = '<img class="smiley" src="/images/smiley_side.gif" alt="Smiley" />';
            
$searches[] = ':-D';
            
$replaces[] = '<img class="smiley" src="/images/smiley_happy.gif" alt="Smiley (big smile)" />';
            
$searches[] = ':D';
            
$replaces[] = '<img class="smiley" src="/images/smiley_happy.gif" alt="Smiley (big smile)" />';
            
$searches[] = ' :P';
            
$replaces[] = '<img class="smiley" src="/images/smiley_tongue.gif" alt="Smiley sticking out its tongue" />';
            
$searches[] = ':(';
            
$replaces[] = '<img class="smiley" src="/images/smiley_frown.gif" alt="Smiley frowning" />';
            
$searches[] = '&gt;:|';
            
$replaces[] = '<img class="smiley" src="/images/smiley_mad.gif" alt="Smiley mad" />';
            
$searches[] = '&gt;:(';
            
$replaces[] = '<img class="smiley" src="/images/smiley_mad.gif" alt="Smiley mad" />';
            
$searches[] = ':|';
            
$replaces[] = '<img class="smiley" src="/images/smiley_indifferent.gif" alt="Smiley indifferent" />';
            
$searches[] = ' ;)';
            
$replaces[] = ' <img class="smiley" src="/images/smiley_wink_animated.gif" alt="Smiley winking" />';
            
$searches[] = ' :/';
            
$replaces[] = ' <img class="smiley" src="/images/smiley_ohwell.gif" alt="Smiley \'oh well\'" />';
            
$searches[] = ' :\\';
            
$replaces[] = ' <img class="smiley" src="/images/smiley_ohwell.gif" alt="Smiley \'oh well\'" />';
        }
    }
    
#----------------------------------------------------------------------------------
    # End Simple string replacements
    #----------------------------------------------------------------------------------

    #split the $str into separate lines if it's not already
    #find out if it's faster to replace all crlf's with \n's first, then explode
    #rather than split according to a regex
    
if(!is_array($str)){
        
$str_arr preg_split('/\n|(?:\r\n?)/'$str);
    }else{
        
$str_arr = &$str;
        for(
$n 0$n<count($str_arr);$n++)
            
$str_arr[$n] = chop($str_arr[$n]); #make sure there are no line returns
    
}

    
$line_count count($str_arr);
    if(!
$line_count) return ''#noop
    
$n 0;

    
#----------------------------------------------------------------------------------
    # Preprocessing of directives
    #----------------------------------------------------------------------------------
    
if($flags["get_page_info"]){ #if you just want page information
     #improve this to a generic "get structure" flag that will return title, all page info, and all headings in each page
        
$pages = array();
        for(
$n 0$n<$line_count$n++)
            if(
substr($str_arr[$n], 05) == "?page")
                
$pages[] = trim(substr($str_arr[$n], 5));
        return 
$pages;
    }
    if(
substr($str_arr[0], 06) == "?title"){ #the title has to be the first line in the entire document
        
$title htmlspecialchars(trim(substr($str_arr[0], 6)));
        
$str_arr[0] = "<h3 class=\"st-markup\">$title</h3>\n\n";
        
$n++;
    }
    if(
$flags["only_get_title"]) return $title;
    if(
$flags["page_number"] > 0){
        
#get the title for the first page
        
$page_count 1;
        if(
$flags["page_number"] == 1){
            if(
substr($str_arr[$n], 05) == "?page"){ #check the first line to see if it has a page
                
$page_title trim(substr($str_arr[$n], 5));
                
$n++;
            }
        }else{
            
$n++; #skip past the first line of the file, because whether it's a ?page directive or not, it's in the first page.
            
while($n $line_count and $page_count $flags["page_number"]){
                if(
substr($str_arr[$n], 05) == "?page"$page_count++; #found a new page.
                
$n++;
            }
            
$page_title trim(substr($str_arr[$n], 5));
        }
        
$m $n;
        
#I have to go to the end to find out where to stop
        
while($m $line_count and substr($str_arr[$m], 05) != "?page"$m++;
        
#slice the array from $n (the first line of the content after the page declaration)
        # to $line_count - $m spaces before the end. $line_count - $m will be the remaining spaces in the array
        
$str_arr array_slice($str_arr$n, -($line_count-$m));
        
$line_count $m-$n;
        
$n 0;
    }
    
#----------------------------------------------------------------------------------
    # End Preprocessing of directives
    #----------------------------------------------------------------------------------
    
$list_level 0;
    for(; 
$n<$line_count$n++){
        
$line $str_arr[$n];
        
$previous_state $current_state;
        
$current_state $prepend $append '';

        if(
$line{0} == ";" and $flags["enable_raw_html"]){
            
$line substr($line1)."\n";
            
$current_state 'raw_html';
        }else{
            if(
substr($line0,2) != '?!' or !$flags['enable_extensions']){ #if it's not an extension
                
$line = &htmlspecialchars($line);
                
#first, simple string replacements, preferred over regexes if possible (they're faster)
                
$line = &str_replace($searches$replaces$line);
                
$line = &preg_replace($regexes$styles$line);
            }
        }

        
#First, processing to be done regardless of the other contents of the line.
        # This ensures that even if the line is a blockquote, for instance, links will still
        # be linked.

/*        $quote_position = 6; # no sense in starting within the first seven characters
        #from the beginning of the line: &quot;A&quot;=&quot;url&quot; is the minimal link.
        #Count the characters and you'll see why I want to start at position seven
        while(($quote_position = strpos($line, "&quot;=&quot;", $quote_position+1)) !=== false){
            if($line{$quote_position-1} == "\\"){
                continue;
            }else{
            }
*/

        
if(($quote_position strpos($line'&quot;')) !== false){ #if there's a quote on the line
            
$m 0;
            
#there's got to be a better way to do this....
            # Find the "=", split on the equals, and look for a quote forward and back from there.
            # Try that later, see if the algorithm is simpler
            
while($quote_position !== false){
                if(
$quote_position == or ($quote_position and $line{$quote_position-1} != '\\')){
                    
#handle escaped quotes
                    
$quotes[$m++] = $quote_position;
                }
                
$quote_position strpos($line'&quot;'$quote_position+6);
            }
            
$num_quotes $m;
            
$begin 0;
            
$line_copy '';
            for(
$m=1$m<$num_quotes$m++){
                if(
substr($line$quotes[$m]+67) == '=&quot;' or substr($line$quotes[$m]+67) == ':&quot;'){
                    
#if the character after the quote was an equals sign and another quote
                    #there'll always be a quote before it, so as long as there's another quote after it...
                    
if($m+!= $num_quotes){ #there have to be two quotes left!
                        #make a link
                        
$url      substr($line$quotes[$m+1]+6, ($quotes[$m+2] - $quotes[$m+1])-6);
                        if(
$url_prefix and $url and $url{0} != '/' and !strstr($url"://"))
                            
$url $url_prefix $url;
                        
$linktext substr($line$quotes[$m-1]+6, ($quotes[$m]   - $quotes[$m-1])-6);
                        
$quotestring "<a href=\"$url\">$linktext</a>";
                        
$line_copy .= substr($line$begin$quotes[$m-1] - $begin) . $quotestring;
                        
$begin $quotes[$m+2] + 6;
                    }
                }
            }
            
$line $line_copy.substr($line$begin);
            unset(
$quotes);
        }

        
#look for footnotes
        
if($flags['enable_footnotes']){
            
$footnote_positions = array();
            
$close_footnote_position 0;
            
$line_length strlen($line);
            while(
                
$close_footnote_position $line_length
                
and (($open_footnote_position strpos($line"[#", ($close_footnote_position 1))) !== false)){
                if(
$line{$open_footnote_position-1} == "\\"){
                    
#it's escaped
                    
$close_footnote_position $open_footnote_position+2;
                    continue;
                }else{
                    
$temp_pos $open_footnote_position+2;
                    while((
$close_footnote_position strpos($line"]"$temp_pos)) !== false){
                        if(
$line{$close_footnote_position-1} == "\\"){$temp_pos $close_footnote_position+1; continue;} #escaped

                        
$footnote_length $close_footnote_position - ($open_footnote_position+2);
                        
$full_footnote substr($line$open_footnote_position+2$footnote_length);
                        list(
$footnote$footnote_text) = explode(" "$full_footnote2);
                        if(!
$footnote_text) break; #if it's not a valid footnote

                        #autonumber footnotes
                        
$footnote = !$footnote $footnote_number++ : normalize_ids($footnote"fn_");
                        
$footnotes[$footnote] = $footnote_text;
                        
$replacement_text "<sup><a id=\"fnp$footnote\" href=\"#fn$footnote\">[$footnote]</a></sup>";
                        
$replacement_length strlen($replacement_text);
                        
$line substr_replace($line$replacement_text$open_footnote_position$footnote_length 3); # the 3 is plus the opening bracket and # and the closing bracket
                        
$close_footnote_position $open_footnote_position $replacement_length 1;
                        
$line_length $line_length - ($footnote_length $replacement_length);
                        break; 
#we want to stop once we've found the close footnote position that isn't escaped
                    
}
                }
            }
        }
        
#----------------------------------------------------------------------------------
        # Determine our state
        #----------------------------------------------------------------------------------
        
if($current_state == 'raw_html'){}
        elseif(
preg_match('/^---(?:(\d{1,2})([lLrR])?)?$/'$line$matches)){
            
$current_state MARKUP_STATE_HR;
        }elseif(
preg_match('/^((?:(?:[*+]~?|#[1aAiI]?)\s*)+)\s?(.*)$/'$line$matches)){
            
$current_state MARKUP_STATE_LIST;
            
preg_match_all('/[*+]~?|#[1aAiI]?/'$matches[1], $list_begins);
        }elseif(
substr($line,0,4) == '&gt;'){
            
$current_state MARKUP_STATE_BLOCKQUOTE;
            
$prepend $previous_state == MARKUP_STATE_BLOCKQUOTE "<br />\n" '<blockquote class="st-markup"><p>';
        }elseif(
substr($line,0,1) == '|' and substr($line,-1) == '|'){
            
$current_state MARKUP_STATE_TABLE;
            if(
$previous_state != MARKUP_STATE_TABLE){$prepend '<table border="1" class="st-markup">'."\n";}
        }elseif(
$line{0} == '!'){
            
$current_state MARKUP_STATE_HEADING;
        }elseif(
$line{0} == "="){
            
$current_state MARKUP_STATE_PRE;
            if(
$previous_state != MARKUP_STATE_PRE){$prepend '<pre class="st-markup">';}
        }elseif(
$line{0} == '?'){
            
$current_state MARKUP_STATE_DIRECTIVE;
        }elseif(!
$line){
            
$current_state MARKUP_STATE_NOTHING;
        }else{
            
$current_state MARKUP_STATE_PARA;
            
$prepend $previous_state == MARKUP_STATE_PARA "<br />\n" '<p class="st-markup">';
        }

        if(
$debug){if(!$previous_state){echo "<table border=\"1\"><tr><th>Line Number</th><th>Current State</th><th>Previous State</th></tr>\n";}
        echo 
"<tr><td>".($n 1)."</td><td>$current_state</td><td>$previous_state</td></tr>\n";}

        
#these are all states that can potentially continue until the next line.
        #Make sure you correctly close any of the preceding states
        
if(    $previous_state == MARKUP_STATE_LIST  and $current_state != MARKUP_STATE_LIST) {$prepend end_lists(count($list_begins[0]), $list_begins[0]).$prepend$previous_list_stack = array();}
        elseif(
$previous_state == MARKUP_STATE_TABLE and $current_state != MARKUP_STATE_TABLE){$prepend "</table>\n\n$prepend";}
        elseif(
$previous_state == MARKUP_STATE_PRE   and $current_state != MARKUP_STATE_PRE)  {$prepend "</pre>\n\n$prepend";}
        elseif(
$previous_state == MARKUP_STATE_PARA  and $current_state != MARKUP_STATE_PARA) {$prepend "</p>\n\n$prepend";}
        elseif(
$previous_state == MARKUP_STATE_BLOCKQUOTE and $current_state != MARKUP_STATE_BLOCKQUOTE){$prepend "</p></blockquote>\n\n$prepend";}
        
#----------------------------------------------------------------------------------
        # Finish determining our state
        #----------------------------------------------------------------------------------

        
if($current_state == MARKUP_STATE_LIST and $flags['enable_lists']){ #lists
            
$line list_item($previous_list_stack$list_begins[0], $matches[2]);
        }elseif(
$current_state == MARKUP_STATE_TABLE and $flags["enable_tables"]){ #tables
            
$cells explode("|"substr($line1,-1));
            
$line "\t<tr>";
            for(
$m 0$m count($cells); $m++){
                
$colspan 1;
                
$cell $cells[$m];
                while(isset(
$cells[$m+1]) and $cells[$m+1] == ''){ #if the next cell is empty
                    
$colspan++;
                    
$m++;
                }
                
$line .= "<td".($colspan " colspan=\"$colspan\"" '').">$cell</td>";
            }
            
$line .= "</tr>\n";
        }elseif(
$current_state == MARKUP_STATE_HEADING and $flags["enable_headings"]){ #heading
            
if(!$headings[$heading_current_count]){
                
$headings[$heading_current_count] = extract_heading_info($line$flags["autonumber_headings"], $heading_count$heading_current_count);
            }
            
$line "<h{$headings[$heading_current_count][0]} id=\"{$headings[$heading_current_count][2]}\" class=\"st-markup\">{$headings[$heading_current_count][3]} {$headings[$heading_current_count][1]}</h{$headings[$heading_current_count][0]}>\n\n";
            
$heading_current_count++;
        }elseif(
$current_state == MARKUP_STATE_HR and $flags["enable_hr"]){
            
$num $matches[1]; #left over from the test above
            
if($num){ #if there are one or two numeric characters characters
                
if(strlen($num) == 1$num .= "0";
                
$line "<hr class=\"st-markup\" style=\"width: $num%";
                if(
$matches[2])
                    
$line .= strtoupper($matches[2]) == 'L' '; float: left' '; float:right';
                
$line .= "\" />\n\n";
            }else
                
$line "<hr class=\"st-markup\" />\n\n";
        }elseif(
$current_state == MARKUP_STATE_BLOCKQUOTE){
            
$line trim(substr($line4));
#        }elseif(($imgloc = strpos($line, "img")) !== false){ #images (finish this later)
        
}elseif($current_state == MARKUP_STATE_PRE and $flags["enable_pre"]){ #block preformatted text
            #also remove one preceeding space if it exists
            
$line substr($line, ($line{1} == " " 1))."\n";
        }elseif(
$current_state == MARKUP_STATE_DIRECTIVE){ #directives
            
if($line{1} == '!' and $flags["enable_extensions"] and $line{2} != '/'){ #it's the beginning of an extension
                
$pos strpos($line':');
                
$args = array();
                if(
$pos){
                    
$extension_name substr($line2$pos 2);
                    
$escapes = array('\\&'=>'&''\\='=>'=''\\\\'=>'\\');
                    foreach(
preg_split("/(?<!\\\\)&/"substr($line$pos+1)) as $temparg){
                        list(
$key$value) = preg_split("/(?<!\\\\)=/"$temparg);
                        
$key strtr($key$escapes);
                        
$value strtr($value$escapes);
                        
$args[$key] = $value;
                    }
                }else{
                    
$extension_name 'markup_'.substr($line2);
                }
                
$function_name "markup_$extension_name";
                if(
function_exists($function_name)){
                    
$new_n $function_name($str_arr$n+1$args);
                    
$line '';
                }else{
                    
$line "<strong>extension '$extension_name' doesn't exist</strong>";
                }
            }elseif(
$line == "?page"){ #new page (this will only happen if $flags["page_number"] is 0)
                
$line "<strong>" trim(substr($line5)) . "</strong><br />\n";
            }elseif(
$line == "?tableofcontents" and $flags["enable_TOC"]){ #print out the table of contents, generated from headings
                
if(!$flags["enable_headings"]){
                    
$line '';
                }else{
                    if(!
$headings_complete){
                        
$heading_master_count $heading_current_count;
                        for(
$m $n+1;$m<$line_count;$m++){
                            
#finish keeping track of the headings
                            
if($str_arr[$m]{0} == '!'){
                                
$headings[$heading_master_count] = extract_heading_info($str_arr[$m], $flags['autonumber_headings'], $heading_count$heading_master_count);
                                
$heading_master_count++;
                            }
                        }
                        
$headings_complete true;
                    }
                    
$line = !$headings '' generate_TOC($headings$flags['autonumber_headings'] ? array('*~') : array('#1''#A''#i''#I'));
                }
            }elseif(
substr($line,1,6) == 'leadin' and $flags['enable_leadin']){
                if(!
$flags['show_leadin']){
                    if(
$flags['main_location']){
                        
$temp trim(substr($line7));
                        
$line "<p><a href=\"$flags[main_location]\">".($temp $temp $flags["leadin_cont_text"])."</a></p>";
                    }else{
                        
$line "<p>... continued at permanent location.</p>";
                    }
                    
$str_arr array_slice($str_arr0$n+1); #slice out the rest.
                    #I wonder which is faster - the slice, or just setting all future elements to blank or undef?
                
}else{
                    
$line '';
                }
            }elseif(
substr($line,1,9) == "footnotes" and $flags['enable_footnotes']){
                if(
count($footnotes)) $line .= print_footnotes($footnotes$flags["footnotes_heading"]);
            }elseif(
substr($line18) == 'settings' and $flags['enable_settings']){
                
ksort($flags);
                
$line "<p style=\"margin: 0; font-weight: bold; font-size: larger; text-decoration: underline\">Settings:</p>\n<table>\n";
                while(list(
$key$value) = each($flags)){
                    
$line .= "\t<tr><th style=\"padding-right: 20px; text-align: left\">$key</th><td>";
                    
$value is_bool($value) ? ($value "true" "false") : "'$value'";
                    
$line .= "$value</td></tr>\n";
                }
                
$line .= "</table>\n\n";
            }
        }

        
#----------------------------------------------------------------------------------
        # Handle all escapes
        #----------------------------------------------------------------------------------
        
if($current_state != 'raw_html')
            
$line = &strtr($line$markup_escape_chars);
        if(
substr($line,0,5) == '\\&gt;'){$line = &substr_replace($line"&gt;"05);}
        if(
$line{0} == '\\' and strspn($line{1}, "!#|;=?$"))
            
$line substr_replace($line,$line{1},0,2);

        if(
$flags["output_directly"]){
            echo 
$prepend.$line.$append;
            if(
$new_n)
                for(
$m=$n+1$m<$new_n$m++)
                    echo 
$str_arr[$m];
        }else
            
$str_arr[$n] = $prepend $line $append;

        if(
$new_n){
            
$n $new_n;
            unset(
$new_n);
        }
    }
    
$line '';
    
#end any remaining states
    
if(    $current_state == MARKUP_STATE_LIST) {$line .= end_lists(count($list_begins[0]), $list_begins[0]); $previous_list_stack = array();}
    elseif(
$current_state == MARKUP_STATE_PRE)  {$line "</pre>\n\n";}
    elseif(
$current_state == MARKUP_STATE_TABLE){$line "</table>\n\n";}
    elseif(
$current_state == MARKUP_STATE_PARA) {$line "</p>\n\n";}
    elseif(
$current_state == MARKUP_STATE_BLOCKQUOTE) {$line "</p></blockquote>\n\n";}

#    return "<div style=\"border: thin black solid\">" . join("", $str_arr) . "</div>";
    
if($debug) echo "</table>";

    
#if there are any footnotes left at the end
    
if(count($footnotes)) $line .= print_footnotes($footnotes$flags["footnotes_heading"]);

    if(
$flags["output_directly"]){
        echo 
$line;
        return 
'';
    }else{
        
$str_arr[] = $line;
        return 
join(''$str_arr);
    }
    
error_reporting($er);
}

#----------------------------------------------------------------------------------
# HELPER FUNCTIONS
#----------------------------------------------------------------------------------

function print_footnotes(&$footnotes$footnotes_heading){
    global 
$markup_escape_chars;
    
$return "<p class=\"st-markup\"><em style=\"font-style: normal; text-decoration: underline\">".$footnotes_heading.":</em><br />\n";
    while(list(
$footnote$footnote_text) = each($footnotes))
        
$return .= "<a id=\"fn$footnote\" href=\"#fnp$footnote\">[$footnote]</a>: ".strtr($footnote_text$markup_escape_chars)."<br />\n";
    
$return .= "</p>\n\n";
    
$footnotes NULL#reset
    
return $return;
}
function 
extract_heading_info($line$autonumber_headings, &$heading_count$heading_current_count){
    
$ex_count strspn($line"!"); #number of exclamation points at beginning of line
    
$heading trim(substr($line$ex_count));
    
$heading_id normalize_ids($heading"h$heading_current_count");
    
$heading_number '';
    if(
$autonumber_headings){
        for(
$j=0$j<$ex_count$j++){
            if(
$ex_count-== $j){
                
$heading_count[$j]++;
                for(
$k=$j+1;$k<6;$k++)
                    
$heading_count[$k] = 0;
            }
        }
        
$heading_number join("."array_slice($heading_count0, ($ex_count == $ex_count)));
    }
    return array(
$ex_count$heading$heading_id$heading_number);
}
function 
print_list_item($depth$text$end_list_first false){
    return (
$end_list_first "</li>\n" "").str_repeat("\t"$depth)."<li>$text";
}
function 
start_lists(&$previous_list_stack$new_list_stack){
#function start_lists($current_depth, $list_stack, &$previous_list_stack, $text){
    
global $markup_list_types$markup_list_subtypes;
    
#copy from list stack to previous list stack, starting at current depth
    
$output '';
    
$new_list_level count($new_list_stack);
    
$n count($previous_list_stack);
    if(
$n 0$output .= "\n";
    for(; 
$n<$new_list_level$n++){
        
$list_type $new_list_stack[$n];
        
$output .= str_repeat("\t"$n).'<'.$markup_list_types[$list_type{0}]." class=\"st-markup\"".
            (
$list_type{1} ? " style=\"list-style-type: {$markup_list_subtypes[$list_type{1}]}\"" "").
        
">\n";
        if(
$n+$new_list_level)
            
$output .= str_repeat("\t"$n) . "<li>\n";
        
$previous_list_stack[$n] = $list_type;
    }
    return 
$output;
}
function 
end_lists($how_many, &$previous_list_stack){
    global 
$markup_list_types;
    
#pop $how_many lists of the previous_list_stack
    
$output "</li>\n";
    
$previous_count count($previous_list_stack);
    
$n 0;
    while(
$n++ < $how_many){
        
$previous_count--;
        
$list_type array_pop($previous_list_stack);
        
$list_type $list_type{0};
        
$output .= str_repeat("\t"$previous_count) . "</$markup_list_types[$list_type]>\n";
        if(
$previous_count 0)
            
$output .= str_repeat("\t"$previous_count) . "</li>\n";
    }
    if(
$previous_count == 0$output .= "\n";
    return 
$output;
}
function 
list_item(&$previous_list_stack$new_list_stack$text){
    
$previous_depth count($previous_list_stack);
    
$new_depth      count($new_list_stack);
    
$line '';

    if(
$previous_depth $new_depth){
        
#we're going down in levels
        
$line .= end_lists($previous_depth $new_depth$previous_list_stack);
        
$ended_lists true;
    }
    
#now, new depth will be >= previous depth
    
if($previous_list_stack == $new_list_stack){
        
#if the new list spec was a "subset" of the other, then you're good to just print out a list item
        
$line .= print_list_item($new_depth$text, ($ended_lists false true));
    }else{
        
#the new list is different than the old list
        #start at the beginning of both lists (since they're now the same size)
        # and go until you find a difference
        
$n 0;
        while(
$n $previous_depth){
            if(
$previous_list_stack[$n] != $new_list_stack[$n]){
                
#you found the start of the differences
                #pop the old list until you're at the current level
                
$line .= end_lists($previous_depth $n$previous_list_stack);
                
#now you can just add on the new list stack stuff
                
break;
            }else{
                
$n++;
            }
        }
        
$line .= start_lists($previous_list_stack$new_list_stack).print_list_item($new_depth$text);
    }
    return 
$line;
}
function 
generate_TOC(&$headings$style){
    
$toc '';
    
$style_length count($style);
    if(!
$style_length or !is_array($style))
        
$style = array("*","*","*","*","*","*");
    elseif(
$style_length<6)
        for(
$n 0$style_length 6$style_length++,$n++)
            
$style[$style_length] = $style[$n];

    
$prev_heading_level 0;
    
$heading_level 0;
    
$previous_list_stack = array();
    foreach(
$headings as $heading){
        list(
$heading_level$heading_text$heading_id$heading_number) = $heading;
        
$heading_text "<a href=\"#$heading_id\">$heading_text</a>";

        if(
$heading_level $prev_heading_level){
            
$toc .= start_lists($previous_list_stackarray_slice($style0$heading_level));
            
$toc .= print_list_item($heading_level"$heading_number $heading_text");
        }elseif(
$heading_level == $prev_heading_level){
            
$toc .= print_list_item($heading_level"$heading_number $heading_text"true);
        }else{
            
$toc .= end_lists($prev_heading_level $heading_level$previous_list_stack);
            
$toc .= print_list_item($heading_level"$heading_number $heading_text");
        }
        
$prev_heading_level $heading_level;
    }
    return 
$toc.end_lists($prev_heading_level$previous_list_stack);
}
function 
normalize_ids($id$prefix){
    
# "Normalizes" an id so that it only contains valid characters.
    # http://www.w3.org/TR/html4/types.html#type-name
    #ID and NAME tokens must begin with a letter ([A-Za-z])
    # and may be followed by any number of letters,
    # digits ([0-9]), hyphens ("-"), underscores ("_"), colons (":"), and periods (".").
    
return "$prefix_".preg_replace("/[^A-Za-z0-9_:.-]/""-"$id);
}

function 
markup_code(&$document$position$args){
    
$count count($document);
    if(
$position $count){ #if we're not already at the end of the document
        
$line_count 0;
        
$source '';
        while(
$position $count and $document[$position] != '?!/code'){
            
$line_count++;
            
$source .= ($args["line_numbers"] ? sprintf("%2d: "$line_count) : "").$document[$position]."\n";
            
$document[$position] = ''#clear each line as we see it
            
$position++;
        }
        if(
$args['language'] == 'php'){
            
$source str_replace(
                array(
'<font color="''</font>''&nbsp;'),
                array(
'<span style="color: ''</span>''&#160;'),
                
highlight_string($sourcetrue)
            );
        }elseif(
$args['language'] == 'html' or $args['language'] == 'xml'){
            
$source '<code>' nl2br(preg_replace(
                array(
'/ /''/\t/'"/&lt;(\/?\w+?)((?:&#160;|\s).*?)?&gt;/sS"'/(&lt;!--.*?--&gt;)/sS'),
                array(
'&#160;''&#160;&#160;&#160;&#160;''<span style="color: #c00">&lt;<span style="color: #00c">$1</span><span style="color: #060">$2</span>&gt;</span>''<span style="color: #f90">$1</span>'),
                    
htmlspecialchars($source))) . '</code>' "\n\n";
        }else{
            
$source '<code>' nl2br(str_replace(array(" ""\t"), array("&#160;""&#160;&#160;&#160;&#160;"), htmlspecialchars($source))) . '</code>';
            
#$source = '<textarea name="code" class="c#:nogutter:nocontrols" cols="60" rows="10">
            #2    ... some code here ...
            #3    </textarea>
        
}
        
$document[$position] = $source;
        
$position++;
    }
    return 
$position;
}

function 
markup_js_highlight(){
    global 
$MARKUP_LANGUAGES;

    
$mapping = array(
        
'csharp' => 'CSharp',
        
'delphi' => 'Delphi',
        
'javascript' => 'JScript',
        
'jscript' => 'JScript',
        
'js' => 'JScript',
        
'python' => 'Python',
        
'sql' => 'Sql',
        
'vb' => 'Vb',
    );

    
#if($markup
}
#----------------------------------------------------------------------------------
# END OF HELPER FUNCTIONS
#----------------------------------------------------------------------------------

error_reporting($er);
?>