private function formatMercurialArguments($command, array $arguments) { $spec = DiffusionMercurialWireProtocol::getCommandArgs($command); $out = array(); // Mercurial takes normal arguments like this: // // name <length(value)> // value $has_star = false; foreach ($spec as $arg_key) { if ($arg_key == '*') { $has_star = true; continue; } if (isset($arguments[$arg_key])) { $value = $arguments[$arg_key]; $size = strlen($value); $out[] = "{$arg_key} {$size}\n{$value}"; unset($arguments[$arg_key]); } } if ($has_star) { // Mercurial takes arguments for variable argument lists roughly like // this: // // * <count(args)> // argname1 <length(argvalue1)> // argvalue1 // argname2 <length(argvalue2)> // argvalue2 $count = count($arguments); $out[] = "* {$count}\n"; foreach ($arguments as $key => $value) { if (in_array($key, $spec)) { // We already added this argument above, so skip it. continue; } $size = strlen($value); $out[] = "{$key} {$size}\n{$value}"; } } return implode('', $out); }
protected function decodeStream($data) { $this->buffer .= $data; $out = array(); $messages = array(); while (true) { if ($this->state == 'command') { $this->initializeState(); // We're reading a command. It looks like: // // <command> $line = $this->readProtocolLine(); if ($line === null) { break; } $this->command = $line; $this->state = 'arguments'; } else { if ($this->state == 'arguments') { // Check if we're still waiting for arguments. $args = DiffusionMercurialWireProtocol::getCommandArgs($this->command); $have = array_select_keys($this->arguments, $args); if (count($have) == count($args)) { // We have all the arguments. Emit a message and read the next // command. $messages[] = $this->newMessageAndResetState(); } else { // We're still reading arguments. They can either look like: // // <name> <length(value)> // <value> // ... // // ...or like this: // // * <count> // <name1> <length(value1)> // <value1> // ... $line = $this->readProtocolLine(); if ($line === null) { break; } list($arg, $size) = explode(' ', $line, 2); $size = (int) $size; if ($arg != '*') { $this->expectBytes = $size; $this->argumentName = $arg; $this->state = 'value'; } else { $this->arguments['*'] = array(); $this->expectArgumentCount = $size; $this->state = 'argv'; } } } else { if ($this->state == 'value' || $this->state == 'argv-value') { // We're reading the value of an argument. We just need to wait for // the right number of bytes to show up. $bytes = $this->readProtocolBytes(); if ($bytes === null) { break; } if ($this->state == 'argv-value') { $this->arguments['*'][$this->argumentName] = $bytes; $this->state = 'argv'; } else { $this->arguments[$this->argumentName] = $bytes; $this->state = 'arguments'; } } else { if ($this->state == 'argv') { // We're reading a variable number of arguments. We need to wait for // the arguments to arrive. if ($this->expectArgumentCount) { $line = $this->readProtocolLine(); if ($line === null) { break; } list($arg, $size) = explode(' ', $line, 2); $size = (int) $size; $this->expectBytes = $size; $this->argumentName = $arg; $this->state = 'argv-value'; $this->expectArgumentCount--; } else { $this->state = 'arguments'; } } else { if ($this->state == 'data-length') { $line = $this->readProtocolLine(); if ($line === null) { break; } $this->expectBytes = (int) $line; if (!$this->expectBytes) { $messages[] = $this->newDataMessage(''); $this->initializeState(); } else { $this->state = 'data-bytes'; } } else { if ($this->state == 'data-bytes') { $bytes = substr($this->buffer, 0, $this->expectBytes); $this->buffer = substr($this->buffer, strlen($bytes)); $this->expectBytes -= strlen($bytes); $messages[] = $this->newDataMessage($bytes); if (!$this->expectBytes) { // We've finished reading this chunk, so go read the next chunk. $this->state = 'data-length'; } else { // We're waiting for more data, and have read everything available // to us so far. break; } } else { throw new Exception("Bad parser state '{$this->state}'!"); } } } } } } } return $messages; }