fcshDaemon.php 日本語対応

"以前の記事" で作成した fcshDaemon.php を日本語に対応させました。

課題

コーディングの相違を吸収する必要があった。cygwin のコードは ShiftJIS(コードページ932)、flex+fcsh もShiftJIS。phpUTF-8 なので、日本語を使うとなると変換が必要になる。具体的には、preg_match が動作しないということ。環境は、windowsXP+cygwin+meadow+ant+flex4+AIR2.0 などなど。

対処

  1. fcshDaemon.phpUTF-8 で作成する。
  2. mb_internal_encoding("UTF-8"); をコールする。
  3. $result=mb_convert_encoding( $result, "UTF-8", "SJIS" ); でコードをShiftJISからUTF-8に変更する。
  4. jvm.config (flex_sdk/bin/jvm.config) をNON-LOCALEにする。
  5. my.php.ini に extension=php_mbstring.dll を加える。
jvm.config 該当部分
#java.args=-Xmx384m -Dsun.io.useCanonCaches=false -Duser.language=en -Duser.region=US
#java.args=-Xmx384m -Dsun.io.useCanonCaches=false -Duser.language=ja -Duser.region=JP
java.args=-Xmx384m -Dsun.io.useCanonCaches=false
my.php.ini

これはタグ含めて4行で全部。このiniファイルを使うことで、php.exe が速やかな動作をする。

[PHP]
extension_dir = "C:\xampp\php\ext\"
extension=php_sockets.dll
extension=php_mbstring.dll
fcshDaemon.php
#! php -q

<?php
// $Revision: 206 $
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();

/*
 * 日本語を使うからこれが大事
 * ...と言いながら、このファイルは UTF-8 で
 * 作成しているので無くても構わない。
 */
mb_internal_encoding('UTF-8');

/*
 * IP address and port number of daemon
 */
$daemon_address ='127.0.0.1';
$daemon_port    =10000;

/*
 * process ID of fcsh
 */
$fcsh_process   =null;

/*
 * prompt string of fcsh 
 */
$fcsh_prompt ="(fcsh) "; //最後の空白は要ります

/*
 * a compile command list and a compile target list
 *
 * $command_list ... $command_list["instruction"][0] ="instruction";
 *                   $command_list["instruction"][1] ="replaced instruction"
 * $target_list  ... $target_list["taget number"] ="instruction"
 *
 * $command_list is storage of a replaced instruction.
 * $target_list is storage of compile target.
 */
$command_list =array();
$target_list  =array(); 

/****************************************
 *           プログラム本体
 */
echo "eggtoothcroc's private fcsh wrapper ".'($Revision: 206 $)'."\n";
echo "Copyleft (c) 2009 eggtoothcroc. All lefts reserved.\n";
echo "\n";
echo "     ... waiting a connection request thru socket.\n\n";

/*
 * create a daemon socket
 */
if( ($daemon_sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false ){
  echo __FILE__ . "(" . __LINE__ ."):  Error: ".socket_strerror(socket_last_error());
  exit(-1);
 }

/*
 * put a daemon socket into net 
 */
if( @socket_bind($daemon_sock, $daemon_address, $daemon_port) === false ){
  echo __FILE__ . "(" . __LINE__ ."):  Error: "
    .socket_strerror(socket_last_error($daemon_sock));
  socket_close($daemon_sock);
  exit(-1);
 }

do{
  // wait one client
  if( @socket_listen($daemon_sock, 1) === false ){
    echo __FILE__ . "(" . __LINE__ ."):  Error: "
      .socket_strerror(socket_last_error($daemon_sock));
    socket_close($daemon_sock);
    exit(-1);
  }
  
  // accept a client
  if( ($client_sock = @socket_accept($daemon_sock)) === false ){
    echo __FILE__ . "(" . __LINE__ ."):  Error: "
      .socket_strerror(socket_last_error($daemon_sock));
    socket_close($daemon_sock);
    exit(-1);
  }
  
  if( !is_resource($fcsh_process) ){
     // at the first acceptance a fcsh will be executed
    list($fcsh_process, 
	 $fcsh_to, $fcsh_from, $fcsh_error) =fcsh_open();
    if( !is_resource($fcsh_process) ){
      $mssg = __FILE__ . "(" . __LINE__ ."):  Error: cannot execute fcsh.exe";
      reply($client_sock, $mssg);
      echo $mssg;
      socket_shutdown($client_sock);
      socket_close($client_sock);
      socket_close($daemon_sock);
      exit(-1);
    }
    // read a message from fcsh and echo 
    fcsh_read($fcsh_from,$client_sock);
  }else{
    /*
     * send a "(fcsh) " to the client
     */
    reply($client_sock, $fcsh_prompt);
  }

  /*
   * read a instruction from the client
   */
  $read =array( $client_sock );
  $write =NULL;
  $except =NULL;

  $num_changed =socket_select($read,$write,$except,5);
  if( $num_changed == 0 ){
    /*
     * we don't need to wait for 5 seconds, it's enough in 1 second.
     * The time between a prompt and a reply to/from the client is
     * only dozens of milliseconds.
     */
    echo __FILE__ . "(" . __LINE__ ."):  Error: Nobody makes me happy...bye";
    break;
  }
  $command =socket_read($client_sock,1000);

  // if clear
  purge( &$command_list, &$target_list, $command );

  // if replace ( mxmlc => compile (id) )
  $command =reget( &$command_list, $command );

  // send a instruction to fcsh
  fcsh_write( $fcsh_to, $command );  

  // if quit
  if( quit( $client_sock, $command ) ) break;

  // read a result of compilation and echo
  $result =fcsh_read( $fcsh_from, $client_sock );

  // replace an instruction ( mxmlc => compc (id) )
  remake( &$command_list, &$target_list, $command, $result );

 }while(true);

/*
 * the end of program
 */
fclose( $fcsh_to );
fclose( $fcsh_from );
fclose( $fcsh_error );

proc_close($fcsh_process);

socket_shutdown($client_sock);
socket_close($client_sock);
socket_close($daemon_sock);

1;


/**********************************************************
 *                functions
 */

/*
 * fcsh_read
 *
 */
function fcsh_read( $pipe, $client_sock )
{
  global $fcsh_prompt;

  $rdbuf  ="";
  $rdline ="";

  do{
    $c =fread($pipe,1);
    $rdline .=$c;
    if( $c=="\n" ){
      echo $rdline;
      reply($client_sock, $rdline);
      $rdbuf .=$rdline;
      $rdline ="";
    }else if( $c==" " && strpos($rdline,$fcsh_prompt)!==false ){
      echo $rdline;
      reply($client_sock, $rdline);
      $rdbuf .=$rdline;
      break;
    }
  }while(true);

  return $rdbuf;
}

/*
 * fcsh_write
 *
 */
function fcsh_write( $pipe, $command )
{
  echo $command;
  fwrite($pipe,$command);
}

/*
 * fcsh_open
 *
 * execute fcsh, and join stderr and stdout
 */
function fcsh_open()
{
  $descriptorspec
    =array(
	   0 => array("pipe", "r"), // stdin
	   1 => array("pipe", "w"), // stdout
	   2 => array("pipe", "w")  // stderr
	   );
  
  $process =proc_open( 'fcsh.exe 2>&1', $descriptorspec, $pipes );

  return array( $process, $pipes[0], $pipes[1], $pipes[2] );
}

/*
 * reget
 *
 * replace a command ( mxmlc => compile (id) )
 */
function reget( $command_list, $command )
{
  if( !array_key_exists($command,$command_list) ){
    $command_list[$command] =array( $command, $command );
  }
  return $command_list[$command][1];
}

/*
 * remake
 *
 * replace a command if new assignment is appeared
 *
 */
function remake( $command_list, $target_list, $command, $result )
{
  //日本語($result)がSHIFTJISでやってくる
  $result =mb_convert_encoding( $result, "UTF-8", "SJIS" );

  if( preg_match( "/fcsh: Assigned (\d+) as the compile target id/", 
		  $result, $chunks ) ||
      preg_match( "/fcsh : コンパイルターゲット ID として (\d+) を割り当てました/", 
		  $result, $chunks )  ){
    // コンパイル命令の割り当てが見つかった
    $command_list[$command][1] ="compile ".$chunks[1]."\n";
    $target_list[$chunks[1]] =$command;
  }
}

/*
 * purge (clear)
 *
 * clear the target
 */
function purge( $command_list, $target_list, $command )
{
  if( preg_match( "/clear *(\d*)/", $command, $chunks ) ){
    if( $chunks[1]==NULL ){
      /* 
       * if no target id specified, clear all 
       */
      $command_list =array();
      $target_list  =array();
    }else{
      /* 
       * clear a specified target if exists
       */
      if( !array_key_exists($chunks[1],$target_list) ) return;

      $clear_target =$target_list[$chunks[1]];
      unset( $command_list[$clear_target] );
      unset( $target_list[$chunks[1]] );
    }
  }
}

/*
 * quit
 */
function quit( $client_sock, $command )
{
  global $fcsh_prompt;

  if( preg_match( "/quit */", $command ) ){
    reply($client_sock, $fcsh_prompt);    
    return true;
  }
  return false;
}

/*
 * reply
 *
 * reply to a client
 */
function reply( $client_sock, $command )
{
  $read   =NULL;
  $write  =array( $client_sock );
  $except =NULL;

  $num_changed =socket_select($read,$write,$except,5);
  if( $num_changed == 0 ){
    echo __FILE__ . "(" . __LINE__ ."):  Error: write is disabled.";
    return false;
  }

  socket_write($client_sock,$command,strlen($command));
  return true;
}

?>

以上です。以前の記事のソース一式に fcshDaemon.php と my.php.ini を上書きするだけ。