text: // Copyright 2017 Open Source Robotics Foundation, Inc.
  //
  // 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.
  
  #ifdef __cplusplus
  extern "C"
  {
  #endif
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include "rcutils/error_handling.h"
  #include "rcutils/format_string.h"
  #include "rcutils/logging_macros.h"
  #include "rcutils/split.h"
  #include "rcutils/types.h"
  
  rcutils_ret_t
  rcutils_split(
    const char * str,
    char delimiter,
    rcutils_allocator_t allocator,
    rcutils_string_array_t * string_array)
  {
    if (NULL == string_array) {
      RCUTILS_SET_ERROR_MSG("string_array is null");
      return RCUTILS_RET_INVALID_ARGUMENT;
    }
    if (NULL == str || '\0' == *str) {
      *string_array = rcutils_get_zero_initialized_string_array();
      return RCUTILS_RET_OK;
    }
    string_array->allocator = allocator;
  
    size_t string_size = strlen(str);
  
    // does it start with a delimiter?
    size_t lhs_offset = 0;
    if (str[0] == delimiter) {
      lhs_offset = 1;
    }
  
    // does it end with a delimiter?
    size_t rhs_offset = 0;
    if (str[string_size - 1] == delimiter) {
      rhs_offset = 1;
    }
  
    string_array->size = 1;
    for (size_t i = lhs_offset; i < string_size - rhs_offset; ++i) {
      if (str[i] == delimiter) {
        ++string_array->size;
      }
    }
    // TODO(wjwwood): refactor this function so it can use rcutils_string_array_init() instead
    string_array->data = allocator.allocate(string_array->size * sizeof(char *), allocator.state);
    if (NULL == string_array->data) {
      goto fail;
    }
  
    size_t token_counter = 0;
    size_t lhs = 0 + lhs_offset;
    size_t rhs = 0 + lhs_offset;
    for (; rhs < string_size - rhs_offset; ++rhs) {
      if (str[rhs] == delimiter) {
        // in case we have two consecutive delimiters
        // we ignore these and diminish the size of the array
        if (rhs - lhs < 1) {
          --string_array->size;
          string_array->data[string_array->size] = NULL;
        } else {
          // +2 (1+1) because lhs is index, not actual position
          // and nullterminating
          string_array->data[token_counter] =
            allocator.allocate((rhs - lhs + 2) * sizeof(char), allocator.state);
          if (NULL == string_array->data[token_counter]) {
            string_array->size = token_counter;
            goto fail;
          }
          snprintf(string_array->data[token_counter], (rhs - lhs + 1), "%s", str + lhs);
          ++token_counter;
        }
        lhs = rhs;
        ++lhs;
      }
    }
  
    // take care of trailing token
    if (rhs - lhs < 1) {
      --string_array->size;
      string_array->data[string_array->size] = NULL;
    } else {
      string_array->data[token_counter] =
        allocator.allocate((rhs - lhs + 2) * sizeof(char), allocator.state);
      snprintf(string_array->data[token_counter], (rhs - lhs + 1), "%s", str + lhs);
    }
  
    return RCUTILS_RET_OK;
  
  fail:
    if (rcutils_string_array_fini(string_array) != RCUTILS_RET_OK) {
      RCUTILS_SAFE_FWRITE_TO_STDERR("failed to finalize string array during error handling: ");
      RCUTILS_SAFE_FWRITE_TO_STDERR(rcutils_get_error_string().str);
      RCUTILS_SAFE_FWRITE_TO_STDERR("\n");
      rcutils_reset_error();
    }
  
    RCUTILS_SET_ERROR_MSG("unable to allocate memory for string array data");
    return RCUTILS_RET_ERROR;
  }
  
  rcutils_ret_t
  rcutils_split_last(
    const char * str,
    char delimiter,
    rcutils_allocator_t allocator,
    rcutils_string_array_t * string_array)
  {
    if (NULL == str || strlen(str) == 0) {
      *string_array = rcutils_get_zero_initialized_string_array();
      return RCUTILS_RET_OK;
    }
  
    size_t string_size = strlen(str);
  
    // does it start with a delimiter?
    size_t lhs_offset = 0;
    if (str[0] == delimiter) {
      lhs_offset = 1;
    }
  
    // does it end with a delimiter?
    size_t rhs_offset = 0;
    if (str[string_size - 1] == delimiter) {
      rhs_offset = 1;
    }
  
    size_t found_last = string_size;
    for (size_t i = lhs_offset; i < string_size - rhs_offset; ++i) {
      if (str[i] == delimiter) {
        found_last = i;
      }
    }
  
    rcutils_ret_t result_error;
    if (found_last == string_size) {
      rcutils_ret_t ret = rcutils_string_array_init(string_array, 1, &allocator);
      if (ret != RCUTILS_RET_OK) {
        result_error = ret;
        goto fail;
      }
      string_array->data[0] =
        allocator.allocate((found_last - lhs_offset + 2) * sizeof(char), allocator.state);
      if (NULL == string_array->data[0]) {
        result_error = RCUTILS_RET_BAD_ALLOC;
        goto fail;
      }
      snprintf(string_array->data[0], found_last - lhs_offset + 1, "%s", str + lhs_offset);
    } else {
      rcutils_ret_t ret = rcutils_string_array_init(string_array, 2, &allocator);
      if (ret != RCUTILS_RET_OK) {
        result_error = ret;
        goto fail;
      }
  
      /*
       * The extra +1 after 'found_last' is to compensate its position
       * found_last is the index of the the last position the delimiter was found
       * and not the actual position in terms of counting from 1
       */
      size_t inner_rhs_offset = (str[found_last - 1] == delimiter) ? 1 : 0;
      string_array->data[0] = allocator.allocate(
        (found_last + 1 - lhs_offset - inner_rhs_offset + 1) * sizeof(char),
        allocator.state);
      if (NULL == string_array->data[0]) {
        result_error = RCUTILS_RET_BAD_ALLOC;
        goto fail;
      }
      snprintf(
        string_array->data[0], found_last + 1 - lhs_offset - inner_rhs_offset,
        "%s", str + lhs_offset);
  
      string_array->data[1] = allocator.allocate(
        (string_size - found_last - rhs_offset + 1) * sizeof(char),
        allocator.state);
      if (NULL == string_array->data[1]) {
        result_error = RCUTILS_RET_BAD_ALLOC;
        goto fail;
      }
      snprintf(
        string_array->data[1], string_size - found_last - rhs_offset, "%s",
        str + found_last + 1);
    }
  
    return RCUTILS_RET_OK;
  
  fail:
    if (rcutils_string_array_fini(string_array) != RCUTILS_RET_OK) {
      RCUTILS_LOG_ERROR(
        "failed to clean up on error (leaking memory): '%s'", rcutils_get_error_string().str);
    }
    return result_error;
  }
  
  #ifdef __cplusplus
  }
  #endif
  