text: // Copyright 2012 Google Inc. All Rights Reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #include "syzygy/trace/parse/parse_utils.h"
  
  #include "base/logging.h"
  #include "syzygy/common/buffer_parser.h"
  
  namespace trace {
  namespace parser {
  
  namespace {
  
  using ::common::BinaryBufferReader;
  
  bool ParseString(BinaryBufferReader* reader, std::wstring* output) {
    DCHECK(reader != NULL);
  
    const wchar_t* string = NULL;
    size_t length = 0;
    if (!reader->ReadString(&string, &length)) {
      LOG(ERROR) << "Failed to parse string from TraceFileHeader blob.";
      return false;
    }
  
    if (output != NULL)
      *output = string;
  
    return true;
  }
  
  bool ParseEnvironmentStrings(BinaryBufferReader* reader,
                               TraceEnvironmentStrings* env_strings) {
    DCHECK(reader != NULL);
  
    if (env_strings != NULL)
      env_strings->clear();
  
    // Parse the environment string.
    size_t env_string_count = 0;
    while (true) {
      const wchar_t* string = NULL;
      size_t length = 0;
      if (!reader->ReadString(&string, &length)) {
        LOG(ERROR) << "Failed to parse environment strings from TraceFileHeader.";
        return false;
      }
  
      if (length == 0 && env_string_count > 0)
        return true;
  
      // Parse this environment string by splitting it at the first '=' sign.
      if (env_strings != NULL) {
        // If we don't find a '=' we assume the whole thing is a key. This is
        // actually strictly invalid, but no harm done.
        const wchar_t* split = ::wcschr(string, L'=');
        if (split != NULL) {
          env_strings->push_back(std::make_pair(
              std::wstring(string, split - string),
              std::wstring(split + 1)));
        } else {
          env_strings->push_back(std::make_pair(std::wstring(string),
                                                std::wstring()));
        }
      }
  
      ++env_string_count;
    }
  }
  
  }  // namespace
  
  bool ParseEnvironmentStrings(const wchar_t* env_string,
                               TraceEnvironmentStrings* env_strings) {
    DCHECK(env_string != NULL);
    DCHECK(env_strings != NULL);
  
    // Search for the double zero termination.
    size_t i = 2;
    while (true) {
      if (env_string[i - 2] == 0 && env_string[i - 1] == 0)
        break;
  
      ++i;
    }
  
    BinaryBufferReader reader(env_string, sizeof(env_string[0]) * i);
    return ParseEnvironmentStrings(&reader, env_strings);
  }
  
  bool ParseTraceFileHeaderBlob(const TraceFileHeader& header,
                                std::wstring* module_path,
                                std::wstring* command_line,
                                TraceEnvironmentStrings* env_strings) {
    if (header.header_size < offsetof(TraceFileHeader, blob_data)) {
      LOG(ERROR) << "Invalid header size.";
      return false;
    }
  
    size_t blob_length = header.header_size -
        offsetof(TraceFileHeader, blob_data);
  
    BinaryBufferReader reader(header.blob_data, blob_length);
  
    if (!ParseString(&reader, module_path))
      return false;
    if (!ParseString(&reader, command_line))
      return false;
    if (!ParseEnvironmentStrings(&reader, env_strings))
      return false;
  
    if (reader.RemainingBytes() > 0) {
      LOG(ERROR) << "TraceFileHeader blob contains extra data.";
      return false;
    }
  
    return true;
  }
  
  }  // namespace parser
  }  // namespace trace
  