text; $pos_act =& $task->pos_act; if (strlen($text) < $pos_act) return TAGPARSER_RET_ERR; // in_code state if($task->in_code) { // seek [/code] only // ERROR: code NOT caseinsensitive! Correct core logic! // throw this into interpreter part! $checkpos = fb_stripos($text, '[/code]', $pos_act); if($checkpos!==FALSE) { $pos_act = $checkpos; return TAGPARSER_RET_OK; } $checkpos = fb_stripos($text, '[/code:1]', $pos_act); if($checkpos!==FALSE) { $pos_act = $checkpos; return TAGPARSER_RET_OK; } // temporarily close code tag if not exists $task->in_code = 0; return TAGPARSER_RET_ERR; } // in_noparse state if($task->in_noparse) { // seek [/noparse] only $checkpos = fb_stripos($text, '[/noparse]', $pos_act); // ERROR: noparse NOT caseinsensitive! Correct core logic! // throw this into interpreter part! if($checkpos!==FALSE) { $pos_act = $checkpos; return TAGPARSER_RET_OK; } return TAGPARSER_RET_ERR; } // parse tag_start in regular case // need text, pos_act, [length] $checkpos = fb_stripos($text, $this->tag_start, $pos_act); if($checkpos!==FALSE) { $pos_act = $checkpos; return TAGPARSER_RET_OK; } return TAGPARSER_RET_ERR; } function UnEscape(&$task) { # Check if current tag is escaped, unescape # RET: 0 continue, $text =& $task->text; $pos_act =& $task->pos_act; $nextchar = substr($text, $pos_act+1, 1); if($nextchar==$this->tag_start) { // escaped tagstart: remove escape! // abc[[def => pos_act@3, [@3, [@4 => abc[def if($task->dry) { // omit this tag $pos_act += 2; return TAGPARSER_RET_REPLACED; } $text = substr($text, 0, $pos_act).substr($text, $pos_act+1); $pos_act += 1; return TAGPARSER_RET_REPLACED; } // no escape return TAGPARSER_RET_NOTHING; } function ParseTag(&$tag, &$task) { # Parse Tag content and create construct # logs error itself to task # RET: # TAGPARSER_RET_OK continue # TAGPARSER_RET_ERR tag parse error, skip // this is a real char-by-char parser! $text =& $task->text; $pos =& $task->pos_act; $pos_start = $pos; $tagname = ''; $nowkey = ''; $nowval = ''; $arr = array(); $quot=''; // ' or " $isesc = FALSE; // escape \ used for quotes! $isend = FALSE; // TRUE if endtag $mode = BBCODE_PARSE_START; // scan through string #echo 'POS:'.$pos."\n"; $textlen = strlen($text); while(++$pos<$textlen) { $char = substr($text, $pos, 1); #echo 'CHAR:'.$mode.':'.$char."\n"; // missing tag end, overflow prevention! if($char===FALSE) { // cancel this tag $err = new ParserErrorContext('parser.err.tag.parsetag.endless'); $err->GrabContext($task); $task->ErrorPush($err); unset($err); //opt $tag->tag_end = $pos+1; return TAGPARSER_RET_ERR; } // modes if($mode==BBCODE_PARSE_START) { // switch to name in any case $mode=BBCODE_PARSE_NAME; if($char=='/') { $isend = TRUE; continue; } // continue on name totally } if($mode==BBCODE_PARSE_NAME) { if($char==$this->tag_end || $char=='=' || $char==' ') { if($tagname=='') { // cancel this nameless tag $err = new ParserErrorContext('parser.err.tag.parsetag.noname'); $err->GrabContext($task); $task->ErrorPush($err); unset($err); //opt $tag->tag_end = $pos+1; return TAGPARSER_RET_ERR; } } if($char==$this->tag_end) { break; } if($char=='=') { $mode = BBCODE_PARSE_EQUAL; $nowkey .= 'default'; continue; } if($char==' ') { $mode = BBCODE_PARSE_SPACE; continue; } // build tagname $tagname .= strtolower($char); #echo 'TAG:'.$tagname."\n"; continue; } if($mode==BBCODE_PARSE_SPACE) { if($char==' ') { continue; // eat up spaces } if($char==$this->tag_end) { break; } $nowkey .= strtolower($char); $mode=BBCODE_PARSE_KEY_OR_END; continue; } if($mode==BBCODE_PARSE_KEY_OR_END) { if($char=='=') { $mode = BBCODE_PARSE_EQUAL; continue; } if($char==$this->tag_end) { $arr[$nowkey] = TRUE; break; } if($char==' ') { $arr[$nowkey] = TRUE; $nowkey = ''; $mode = BBCODE_PARSE_SPACE; continue; } $nowkey .= strtolower($char); } if($mode==BBCODE_PARSE_EQUAL) { $quot=''; // quotescan " if($char=='"') { $quot='"'; $mode = BBCODE_PARSE_VALQUOT; continue; } // quotescan ' if($char=='\'') { $quot='\''; $mode = BBCODE_PARSE_VALQUOT; continue; } if($char==' ') { $arr[$nowkey] = TRUE; $nowkey = ''; $mode = BBCODE_PARSE_SPACE; continue; } if($char==$this->tag_end) { $arr[$nowkey] = TRUE; break; } $nowval .= $char; $mode=BBCODE_PARSE_VAL; continue; } if($mode==BBCODE_PARSE_VALQUOT) { if($isesc) { $nowval .= $char; $isesc = FALSE; continue; } if($char=='\\') { // ONE backspace #echo 'ESCAPE'."\n"; $isesc = TRUE; continue; } if($char==$quot) { $arr[$nowkey] = $nowval; $nowkey = $nowval = ''; $mode = BBCODE_PARSE_SPACE; continue; } $nowval .= $char; continue; //opt } if($mode==BBCODE_PARSE_VAL) { if($char==' ') { $arr[$nowkey] = $nowval; $nowkey = $nowval = ''; $mode = BBCODE_PARSE_SPACE; continue; } if($char==$this->tag_end) { $arr[$nowkey] = $nowval; break; } $nowval .= $char; continue; //opt } } // end position points to tag closing #$pos_end = $pos; // create tag object if($isend) { // no reference! #echo 'TAGEND:'.$tagname.':'.$pos_start.":".$pos_end."\n"; $tag = new ParserEventTagEnd($pos_start, $pos, $tagname); } else { // no reference! #echo 'TAG:'.$tagname.':'.$pos_start.":".$pos_end."\n"; $tag = new ParserEventTag($pos_start, $pos, $tagname); $tag->setOptions($arr); } // parser position after tag: this is next char $pos++; return TAGPARSER_RET_OK; } function CheckTag(&$task, $tag) { # Parse Tag content and create construct # RET: 0 OK, errorinfo // check tagname regexp // $tag->name // dropping [*] here would lead to remove! so it goes to stack! /* $err = new ParserErrorContext('parser.err.tag.check'); $err->GrabContext($task, $tag); $task->ErrorPush($err); */ // optionally check tag return TAGPARSER_RET_OK; } } class BBCodeParserTask extends ParserTask { # stateful task for parser runs # parse state in_code in [code] var $in_code = FALSE; # parse state in_noparse in [noparse] var $in_noparse = FALSE; } ?>