+
+# wfMerge attempts to merge differences between three texts.
+# Returns true for a clean merge and false for failure or a conflict.
+
+function wfMerge( $old, $mine, $yours, &$result ){
+ global $wgDiff3;
+
+ # This check may also protect against code injection in
+ # case of broken installations.
+ if(! file_exists( $wgDiff3 ) ){
+ return false;
+ }
+
+ # Make temporary files
+ $td = "/tmp/";
+ $oldtextFile = fopen( $oldtextName = tempnam( $td, "merge-old-" ), "w" );
+ $mytextFile = fopen( $mytextName = tempnam( $td, "merge-mine-" ), "w" );
+ $yourtextFile = fopen( $yourtextName = tempnam( $td, "merge-your-" ), "w" );
+
+ fwrite( $oldtextFile, $old ); fclose( $oldtextFile );
+ fwrite( $mytextFile, $mine ); fclose( $mytextFile );
+ fwrite( $yourtextFile, $yours ); fclose( $yourtextFile );
+
+ # Check for a conflict
+ $cmd = wfEscapeShellArg( $wgDiff3 ) . " -a --overlap-only " .
+ wfEscapeShellArg( $mytextName ) . " " .
+ wfEscapeShellArg( $oldtextName ) . " " .
+ wfEscapeShellArg( $yourtextName );
+ $handle = popen( $cmd, "r" );
+
+ if( fgets( $handle ) ){
+ $conflict = true;
+ } else {
+ $conflict = false;
+ }
+ pclose( $handle );
+
+ # Merge differences
+ $cmd = wfEscapeShellArg( $wgDiff3 ) . " -a -e --merge " .
+ wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName );
+ $handle = popen( $cmd, "r" );
+ $result = "";
+ do {
+ $data = fread( $handle, 8192 );
+ if ( strlen( $data ) == 0 ) {
+ break;
+ }
+ $result .= $data;
+ } while ( true );
+ pclose( $handle );
+ unlink( $mytextName ); unlink( $oldtextName ); unlink( $yourtextName );
+ return ! $conflict;
+}
+
+function wfVarDump( $var )
+{
+ global $wgOut;
+ $s = str_replace("\n","<br>\n", var_export( $var, true ) . "\n");
+ if ( headers_sent() || !@is_object( $wgOut ) ) {
+ print $s;
+ } else {
+ $wgOut->addHTML( $s );
+ }
+}
+
+# Provide a simple HTTP error.
+function wfHttpError( $code, $label, $desc ) {
+ global $wgOut;
+ $wgOut->disable();
+ header( "HTTP/1.0 $code $label" );
+ header( "Status: $code $label" );
+ $wgOut->sendCacheControl();
+
+ # Don't send content if it's a HEAD request.
+ if( $_SERVER['REQUEST_METHOD'] == 'HEAD' ) {
+ header( "Content-type: text/plain" );
+ print "$desc\n";
+ }
+}
+
+# Converts an Accept-* header into an array mapping string values to quality factors
+function wfAcceptToPrefs( $accept, $def = "*/*" ) {
+ # No arg means accept anything (per HTTP spec)
+ if( !$accept ) {
+ return array( $def => 1 );
+ }
+
+ $prefs = array();
+
+ $parts = explode( ",", $accept );
+
+ foreach( $parts as $part ) {
+ # FIXME: doesn't deal with params like 'text/html; level=1'
+ @list( $value, $qpart ) = explode( ";", $part );
+ if( !isset( $qpart ) ) {
+ $prefs[$value] = 1;
+ } elseif( preg_match( '/q\s*=\s*(\d*\.\d+)/', $qpart, $match ) ) {
+ $prefs[$value] = $match[1];
+ }
+ }
+
+ return $prefs;
+}
+
+/* private */ function mimeTypeMatch( $type, $avail ) {
+ if( array_key_exists($type, $avail) ) {
+ return $type;
+ } else {
+ $parts = explode( '/', $type );
+ if( array_key_exists( $parts[0] . '/*', $avail ) ) {
+ return $parts[0] . '/*';
+ } elseif( array_key_exists( '*/*', $avail ) ) {
+ return '*/*';
+ } else {
+ return NULL;
+ }
+ }
+}
+
+# FIXME: doesn't handle params like 'text/plain; charset=UTF-8'
+# XXX: generalize to negotiate other stuff
+function wfNegotiateType( $cprefs, $sprefs ) {
+ $combine = array();
+
+ foreach( array_keys($sprefs) as $type ) {
+ $parts = explode( '/', $type );
+ if( $parts[1] != '*' ) {
+ $ckey = mimeTypeMatch( $type, $cprefs );
+ if( $ckey ) {
+ $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
+ }
+ }
+ }
+
+ foreach( array_keys( $cprefs ) as $type ) {
+ $parts = explode( '/', $type );
+ if( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) {
+ $skey = mimeTypeMatch( $type, $sprefs );
+ if( $skey ) {
+ $combine[$type] = $sprefs[$skey] * $cprefs[$type];
+ }
+ }
+ }
+
+ $bestq = 0;
+ $besttype = NULL;
+
+ foreach( array_keys( $combine ) as $type ) {
+ if( $combine[$type] > $bestq ) {
+ $besttype = $type;
+ $bestq = $combine[$type];
+ }
+ }
+
+ return $besttype;
+}
+