Advertisement
miken32

rmate.php

Jan 27th, 2012
335
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.00 KB | None | 0 0
  1. #!/usr/bin/php
  2. <?php
  3. /*
  4.     rmate.php by Michael Newton <http://mike.eire.ca/>
  5.     Based on rmate Ruby script included with TextMate 2.0
  6.     TextMate is © MacroMates Ltd., 2011
  7. */
  8. //hide errors that will pop up if the file is missing
  9. $err = error_reporting(E_ALL & ~E_WARNING);
  10. if (!(include 'Console/CommandLine.php')) {
  11.     msg('This program requires the PEAR package Console_CommandLine to be installed!');
  12.     exit(1);
  13. }
  14. error_reporting($err);
  15.  
  16. define('DEFAULT_HOST', 'localhost');
  17. define('DEFAULT_PORT', 52698);
  18.  
  19. class Settings {
  20.     function __construct() {
  21.         self::read_disk_settings();
  22.         self::parse_cli_options();
  23.     }
  24.  
  25.     function read_disk_settings() {
  26.  
  27.     }
  28.  
  29.     function parse_cli_options() {
  30.         $options = array('description'=>'Uses an SSH tunnel to open the specified file in a remote instance of TextMate.', 'version'=>'1.3php (2011-10-18)');
  31.         $parser = new Console_CommandLine($options);
  32.         $parser->addOption('host', array(
  33.             'long_name' => '--host',
  34.             'description' => 'Connect to host. Use "auto" to detect the host from SSH. Defaults to "' . DEFAULT_HOST . '" if not specified, or use the RMATE_HOST environment variable.',
  35.             'action' => 'StoreString',
  36.             'default' => getenv('RMATE_HOST') ? getenv('RMATE_HOST') : DEFAULT_HOST,
  37.             'help_name' => 'name',
  38.         ));
  39.  
  40.         $parser->addOption('port', array(
  41.             'short_name' => '-p',
  42.             'long_name' => '--port',
  43.             'description' => 'Port number to use for connection. Defaults to ' . DEFAULT_PORT . ' if not specified, or use the RMATE_PORT environment variable.',
  44.             'action' => 'StoreInt',
  45.             'default' => getenv('RMATE_PORT') ? (int)getenv('RMATE_PORT') : DEFAULT_PORT,
  46.             'help_name' => '#',
  47.         ));
  48.  
  49.         $parser->addOption('wait', array(
  50.             'short_name' => '-w',
  51.             'long_name' => '--wait',
  52.             'description' => 'Wait for file to be closed by TextMate.',
  53.             'action' => 'StoreTrue',
  54.             'default' => false,
  55.         ));
  56.  
  57.         $parser->addOption('force', array(
  58.             'short_name' => '-f',
  59.             'long_name' => '--force',
  60.             'description' => 'Open even if the file is not writable.',
  61.             'action' => 'StoreTrue',
  62.             'default' => false,
  63.         ));
  64.  
  65.         $parser->addOption('verbose', array(
  66.             'short_name' => '-v',
  67.             'long_name' => '--verbose',
  68.             'description' => 'Verbose logging messages. Use twice for even more verbosity.',
  69.             'action' => 'Counter',
  70.         ));
  71.  
  72.         $parser->addArgument('file', array(
  73.             'description' => 'The file to open',
  74.         ));
  75.  
  76.         try {
  77.             $result = $parser->parse();
  78.             $this->host = $result->options['host'];
  79.             if ($this->host === 'auto' && getenv('SSH_CONNECTION')) {
  80.                 list($this->host,) = explode(' ', getenv('SSH_CONNECTION'), 2);
  81.             }
  82.             $this->port = $result->options['port'];
  83.             $this->wait = $result->options['wait'];
  84.             $this->force = $result->options['force'];
  85.             $this->verbose = $result->options['verbose'];
  86.             $this->files = is_array($result->args) ? $result->args : array($result->args);
  87.         }
  88.         catch (Exception $exc) {
  89.             $parser->displayError($exc->getMessage());
  90.         }
  91.     }
  92. }
  93.  
  94. class Command {
  95.     public $command = '';
  96.     public $variables = array();
  97.     public $data = null;
  98.     public $size = null;
  99.  
  100.     function __construct($name) {
  101.         $this->command = $name;
  102.     }
  103.  
  104.     function set($key, $value) {
  105.         $this->variables[$key] = $value;
  106.     }
  107.  
  108.     function get($key = null) {
  109.         return $key === null ? $this->variables : $this->variables[$key];
  110.     }
  111.  
  112.     function read_file($path) {
  113.         $this->size = filesize($path);
  114.         $this->data = file_get_contents($path);
  115.     }
  116.  
  117.     function send($socket) {
  118.         global $settings;
  119.  
  120.         fwrite($socket, "$this->command\n");
  121.         foreach($this->variables as $k=>$v) {
  122.             if ($v === true) $v = 'yes';
  123.             msg("Sending $k: $v", 2);
  124.             fwrite($socket, "$k: $v\n");
  125.         }
  126.         if ($this->data) {
  127.             msg("Sending $this->size bytes of data", 2);
  128.             fwrite($socket, "data: $this->size\n");
  129.             fwrite($socket, "$this->data\n");
  130.         }
  131.     }
  132. }
  133.  
  134. function msg($message, $verbose = 0) {
  135.     global $settings;
  136.  
  137.     if ($verbose === 0 || ($verbose > 0 && $verbose <= $settings->verbose)) {
  138.         fwrite(STDERR, trim($message) . "\n");
  139.     }
  140. }
  141.  
  142. function handle_save($socket, $variables, $data) {
  143.     global $settings;
  144.  
  145.     $path = $variables['token'];
  146.     msg("Saving $path", 1);
  147.     if ($result = file_exists($path)) {
  148.         copy($path, "$path~");
  149.         if ($result = file_put_contents($path, $data)) {
  150.             unlink("$path~");
  151.         }
  152.     }
  153.     if ($result === false) {
  154.         msg("Save failed! Backup in $path~");
  155.     }
  156. }
  157.  
  158. function handle_close($socket, $variables, $data) {
  159.     global $settings;
  160.  
  161.     $path = $variables['token'];
  162.     msg("Closed $path", 1);
  163. }
  164.  
  165. function handle_cmd($socket) {
  166.     global $settings;
  167.  
  168.     $cmd = trim(fgets($socket));
  169.     msg("Command: $cmd", 2);
  170.  
  171.     $variables = array();
  172.     $data = '';
  173.     while (($line = fgets($socket)) !== false) {
  174.         $line = trim($line);
  175.         if (empty($line)) {
  176.             break;
  177.         }
  178.         list($name, $value) = explode(': ', $line);
  179.         if ($name === 'data') {
  180.             $data .= fread($socket, $value);
  181.         }
  182.         else {
  183.             msg("Received $name: $value", 2);
  184.             $variables[$name] = $value;
  185.         }
  186.     }
  187.  
  188.     if ($cmd === 'save') {
  189.         handle_save($socket, $variables, $data);
  190.     }
  191.     elseif ($cmd === 'close') {
  192.         handle_close($socket, $variables, $data);
  193.     }
  194.     else {
  195.         msg("Received unknown command $cmd, exiting");
  196.         exit(1);
  197.     }
  198. }
  199.  
  200. function connect_and_handle_commands($host, $port, $commands) {
  201.     global $settings;
  202.  
  203.     $stream = stream_socket_client("$settings->host:$settings->port");
  204.     if ($stream === false) {
  205.         msg("Unable to connect to $settings->host on port $settings->port, exiting");
  206.         exit(1);
  207.     }
  208.     $server_info = trim(fgets($stream));
  209.     msg("Connect: '$server_info'", 1);
  210.     foreach($commands as $cmd) {
  211.         $cmd->send($stream);
  212.     }
  213.     fwrite($stream, ".\n");
  214.     while (!feof($stream)) {
  215.         handle_cmd($stream);
  216.     }
  217.     fclose($stream);
  218.     msg("Done", 1);
  219. }
  220.  
  221. $settings = new Settings;
  222. $cmds = array();
  223. foreach($settings->files as $file) {
  224.     if (!is_writable($file) || !file_exists($file)) {
  225.         $err = file_exists($file) ? 'is not writable' : 'does not exist';
  226.         if (!$settings->force) {
  227.             msg("File $file $err, use --force to open anyway.");
  228.             die(1);
  229.         }
  230.         else {
  231.             msg("File $file $err. Opening anyway.");
  232.         }
  233.     }
  234.     $cmd = new Command('open');
  235.     $cmd->set('display-name', php_uname('n') . ':' . $file);
  236.     $cmd->set('real-path', realpath($file));
  237.     $cmd->set('data-on-save', true);
  238.     $cmd->set('re-activate', true);
  239.     $cmd->set('token', $file);
  240.     if (file_exists($file)) {
  241.         $cmd->read_file($file);
  242.     }
  243.     else {
  244.         $cmd->set('data', 0);
  245.     }
  246.     $cmds[] = $cmd;
  247. }
  248.  
  249. if (!$settings->wait && function_exists('pcntl_fork')) {
  250.     $pid = pcntl_fork();
  251.     if ($pid > 0) {
  252.         //we are the parent
  253.         exit(0);
  254.     }
  255.     elseif ($pid === -1) {
  256.         //we are still the parent, there is no child
  257.         msg("Error while forking, use --wait to prevent forking.");
  258.         exit(1);
  259.     }
  260.     elseif ($pid === 0) {
  261.         //we are the child
  262.         connect_and_handle_commands($settings->host, $settings->port, $cmds);
  263.     }
  264. }
  265. else {
  266.     if (!$settings->wait && !function_exists('pcntl_fork')) {
  267.         msg("Unable to fork, ensure that pcntl_* functions are enabled.");
  268.     }
  269.     connect_and_handle_commands($settings->host, $settings->port, $cmds);
  270. }
  271. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement