Beispiel #1
0
String StringUtil::SqlDecode(CStrRef input) {
  if (input.empty()) return input;
  int len = input.size();
  char *ret = string_stripslashes(input, len);
  return String(ret, len, AttachString);
}
Beispiel #2
0
bool ini_on_update_long(CStrRef value, void *p) {
  if (p) {
    *((int64_t*)p) = value.toInt64();
  }
  return true;
}
Beispiel #3
0
Variant ZendPack::pack(CStrRef fmt, CArrRef argv) {
  /* Preprocess format into formatcodes and formatargs */
  vector<char> formatcodes;
  vector<int> formatargs;
  int argc = argv.size();

  const char *format = fmt.c_str();
  int formatlen = fmt.size();
  int currentarg = 0;
  for (int i = 0; i < formatlen; ) {
    char code = format[i++];
    int arg = 1;

    /* Handle format arguments if any */
    if (i < formatlen) {
      char c = format[i];

      if (c == '*') {
        arg = -1;
        i++;
      }
      else if (c >= '0' && c <= '9') {
        arg = atoi(&format[i]);

        while (format[i] >= '0' && format[i] <= '9' && i < formatlen) {
          i++;
        }
      }
    }

    /* Handle special arg '*' for all codes and check argv overflows */
    switch ((int) code) {
      /* Never uses any args */
    case 'x':
    case 'X':
    case '@':
      if (arg < 0) {
        throw_invalid_argument("Type %c: '*' ignored", code);
        arg = 1;
      }
      break;

      /* Always uses one arg */
    case 'a':
    case 'A':
    case 'h':
    case 'H':
      if (currentarg >= argc) {
        throw_invalid_argument("Type %c: not enough arguments", code);
        return false;
      }

      if (arg < 0) {
        arg = argv[currentarg].toString().size();
      }

      currentarg++;
      break;

      /* Use as many args as specified */
    case 'c':
    case 'C':
    case 's':
    case 'S':
    case 'i':
    case 'I':
    case 'l':
    case 'L':
    case 'n':
    case 'N':
    case 'v':
    case 'V':
    case 'f':
    case 'd':
      if (arg < 0) {
        arg = argc - currentarg;
      }

      currentarg += arg;

      if (currentarg > argc) {
        throw_invalid_argument("Type %c: too few arguments", code);
        return false;
      }
      break;

    default:
      throw_invalid_argument("Type %c: unknown format code", code);
      return false;
    }

    formatcodes.push_back(code);
    formatargs.push_back(arg);
  }

  if (currentarg < argc) {
    throw_invalid_argument("%d arguments unused", (argc - currentarg));
  }

  int outputpos = 0, outputsize = 0;
  /* Calculate output length and upper bound while processing*/
  for (int i = 0; i < (int)formatcodes.size(); i++) {
    int code = (int) formatcodes[i];
    int arg = formatargs[i];

    switch ((int) code) {
    case 'h':
    case 'H':
      INC_OUTPUTPOS((arg + (arg % 2)) / 2,1);  /* 4 bit per arg */
      break;

    case 'a':
    case 'A':
    case 'c':
    case 'C':
    case 'x':
      INC_OUTPUTPOS(arg,1);    /* 8 bit per arg */
      break;

    case 's':
    case 'S':
    case 'n':
    case 'v':
      INC_OUTPUTPOS(arg,2);    /* 16 bit per arg */
      break;

    case 'i':
    case 'I':
      INC_OUTPUTPOS(arg,sizeof(int));
      break;

    case 'l':
    case 'L':
    case 'N':
    case 'V':
      INC_OUTPUTPOS(arg,4);    /* 32 bit per arg */
      break;

    case 'f':
      INC_OUTPUTPOS(arg,sizeof(float));
      break;

    case 'd':
      INC_OUTPUTPOS(arg,sizeof(double));
      break;

    case 'X':
      outputpos -= arg;

      if (outputpos < 0) {
        throw_invalid_argument("Type %c: outside of string", code);
        outputpos = 0;
      }
      break;

    case '@':
      outputpos = arg;
      break;
    }

    if (outputsize < outputpos) {
      outputsize = outputpos;
    }
  }

  String s = String(outputsize, ReserveString);
  char *output = s.mutableSlice().ptr;
  outputpos = 0;
  currentarg = 0;

  /* Do actual packing */
  for (int i = 0; i < (int)formatcodes.size(); i++) {
    int code = (int) formatcodes[i];
    int arg = formatargs[i];
    String val;
    const char *s;
    int slen;

    switch ((int) code) {
    case 'a':
    case 'A':
      memset(&output[outputpos], (code == 'a') ? '\0' : ' ', arg);
      val = argv[currentarg++].toString();
      s = val.c_str();
      slen = val.size();
      memcpy(&output[outputpos], s, (slen < arg) ? slen : arg);
      outputpos += arg;
      break;

    case 'h':
    case 'H': {
      int nibbleshift = (code == 'h') ? 0 : 4;
      int first = 1;
      const char *v;

      val = argv[currentarg++].toString();
      v = val.data();
      slen = val.size();
      outputpos--;
      if(arg > slen) {
        throw_invalid_argument
          ("Type %c: not enough characters in string", code);
        arg = slen;
      }

      while (arg-- > 0) {
        char n = *v++;

        if (n >= '0' && n <= '9') {
          n -= '0';
        } else if (n >= 'A' && n <= 'F') {
          n -= ('A' - 10);
        } else if (n >= 'a' && n <= 'f') {
          n -= ('a' - 10);
        } else {
          throw_invalid_argument("Type %c: illegal hex digit %c", code, n);
          n = 0;
        }

        if (first--) {
          output[++outputpos] = 0;
        } else {
          first = 1;
        }

        output[outputpos] |= (n << nibbleshift);
        nibbleshift = (nibbleshift + 4) & 7;
      }

      outputpos++;
      break;
    }

    case 'c':
    case 'C':
      while (arg-- > 0) {
        pack(argv[currentarg++], 1, byte_map, &output[outputpos]);
        outputpos++;
      }
      break;

    case 's':
    case 'S':
    case 'n':
    case 'v': {
      int *map = machine_endian_short_map;

      if (code == 'n') {
        map = big_endian_short_map;
      } else if (code == 'v') {
        map = little_endian_short_map;
      }

      while (arg-- > 0) {
        pack(argv[currentarg++], 2, map, &output[outputpos]);
        outputpos += 2;
      }
      break;
    }

    case 'i':
    case 'I':
      while (arg-- > 0) {
        pack(argv[currentarg++], sizeof(int), int_map, &output[outputpos]);
        outputpos += sizeof(int);
      }
      break;

    case 'l':
    case 'L':
    case 'N':
    case 'V': {
      int *map = machine_endian_int32_map;

      if (code == 'N') {
        map = big_endian_int32_map;
      } else if (code == 'V') {
        map = little_endian_int32_map;
      }

      while (arg-- > 0) {
        pack(argv[currentarg++], 4, map, &output[outputpos]);
        outputpos += 4;
      }
      break;
    }

    case 'f': {
      float v;

      while (arg-- > 0) {
        v = argv[currentarg++].toDouble();
        memcpy(&output[outputpos], &v, sizeof(v));
        outputpos += sizeof(v);
      }
      break;
    }

    case 'd': {
      double v;

      while (arg-- > 0) {
        v = argv[currentarg++].toDouble();
        memcpy(&output[outputpos], &v, sizeof(v));
        outputpos += sizeof(v);
      }
      break;
    }

    case 'x':
      memset(&output[outputpos], '\0', arg);
      outputpos += arg;
      break;

    case 'X':
      outputpos -= arg;

      if (outputpos < 0) {
        outputpos = 0;
      }
      break;

    case '@':
      if (arg > outputpos) {
        memset(&output[outputpos], '\0', arg - outputpos);
      }
      outputpos = arg;
      break;
    }
  }

  return s.setSize(outputpos);
}
Beispiel #4
0
CVarRef ArrayData::getNotFound(CStrRef k) {
  raise_notice("Undefined index: %s", k.data());
  return null_variant;
}
Beispiel #5
0
bool BZ2File::open(CStrRef filename, CStrRef mode) {
  assert(m_bzFile == nullptr);

  return m_innerFile->open(filename, mode) &&
    (m_bzFile = BZ2_bzdopen(dup(m_innerFile->fd()), mode.data()));
}
Beispiel #6
0
int64_t f_furchash_hphp_ext(CStrRef key, int len, int nPart) {
  len = std::max(std::min(len, key.size()), 0);
  return furc_hash(key.data(), len, nPart);
}
Beispiel #7
0
String StringUtil::Base64Decode(CStrRef input, bool strict /* = false */) {
  int len = input.size();
  char *ret = string_base64_decode(input.data(), len, strict);
  return String(ret, len, AttachString);
}
Beispiel #8
0
void Debugger::UnregisterSandbox(CStrRef id) {
  s_debugger.unregisterSandbox(id.get());
}
Beispiel #9
0
String StringUtil::HexDecode(CStrRef input) {
  if (input.empty()) return input;
  int len = input.size();
  char *ret = string_hex2bin(input, len);
  return String(ret, len, AttachString);
}
Beispiel #10
0
String StringUtil::Base64Encode(CStrRef input) {
  int len = input.size();
  char *ret = string_base64_encode(input.data(), len);
  return String(ret, len, AttachString);
}
Beispiel #11
0
String StringUtil::QuotedPrintableDecode(CStrRef input) {
  if (input.empty()) return input;
  int len = input.size();
  char *ret = string_quoted_printable_decode(input, len, false);
  return String(ret, len, AttachString);
}
Beispiel #12
0
String StringUtil::HtmlEncodeExtra(CStrRef input, QuoteStyle quoteStyle,
                                   const char *charset, bool nbsp,
                                   Array extra) {
  if (input.empty()) return input;

  assert(charset);
  int flags = STRING_HTML_ENCODE_UTF8;
  if (nbsp) {
    flags |= STRING_HTML_ENCODE_NBSP;
  }
  if (RuntimeOption::Utf8izeReplace) {
    flags |= STRING_HTML_ENCODE_UTF8IZE_REPLACE;
  }
  if (!*charset || strcasecmp(charset, "UTF-8") == 0) {
  } else if (strcasecmp(charset, "ISO-8859-1") == 0) {
    flags &= ~STRING_HTML_ENCODE_UTF8;
  } else {
    throw NotImplementedException(charset);
  }

  const AsciiMap *am;
  AsciiMap tmp;

  switch (quoteStyle) {
    case FBUtf8Only:
      am = &mapNothing;
      flags |= STRING_HTML_ENCODE_HIGH;
      break;
    case FBUtf8:
      am = &mapBothQuotes;
      flags |= STRING_HTML_ENCODE_HIGH;
      break;
    case BothQuotes:
      am = &mapBothQuotes;
      break;
    case DoubleQuotes:
      am = &mapDoubleQuotes;
      break;
    case NoQuotes:
      am = &mapNoQuotes;
      break;
    default:
      am = &mapNothing;
      raise_error("Unknown quote style: %d", (int)quoteStyle);
  }

  if (quoteStyle != FBUtf8Only && extra.toBoolean()) {
    tmp = *am;
    am = &tmp;
    for (ArrayIter iter(extra); iter; ++iter) {
      String item = iter.second().toString();
      char c = item.data()[0];
      tmp.map[c & 64 ? 1 : 0] |= 1uLL << (c & 63);
    }
  }

  int len = input.size();
  char *ret = string_html_encode_extra(input, len,
                                       (StringHtmlEncoding)flags, am);
  if (!ret) {
    raise_error("HtmlEncode called on too large input (%d)", len);
  }
  return String(ret, len, AttachString);
}
Beispiel #13
0
String StringUtil::RegExEncode(CStrRef input) {
  if (input.empty()) return input;
  int len = input.size();
  char *ret = string_quotemeta(input, len);
  return String(ret, len, AttachString);
}
Beispiel #14
0
bool ini_on_update_real(CStrRef value, void *p) {
  if (p) {
    *((double*)p) = value.toDouble();
  }
  return true;
}
Beispiel #15
0
String StringUtil::ROT13(CStrRef input) {
  if (input.empty()) return input;
  return String(string_rot13(input, input.size()), input.size(), AttachString);
}
Beispiel #16
0
bool ini_on_update_string(CStrRef value, void *p) {
  if (p) {
    *((std::string*)p) = std::string(value.data(), value.size());
  }
  return true;
}
Beispiel #17
0
int64 StringUtil::CRC32(CStrRef input) {
  return string_crc32(input, input.size());
}
Beispiel #18
0
DebuggerProxyPtr Debugger::GetProxy() {
  CStrRef sandboxId = g_context->getSandboxId();
  return s_debugger.findProxy(sandboxId.get());
}
Beispiel #19
0
String StringUtil::SHA1(CStrRef input, bool raw /* = false */) {
  int len;
  char *ret = string_sha1(input, input.size(), raw, len);
  return String(ret, len, AttachString);
}
Beispiel #20
0
bool f_hash_update(CObjRef context, CStrRef data) {
  HashContext *hash = context.getTyped<HashContext>();
  hash->ops->hash_update(hash->context, (unsigned char *)data.data(),
                         data.size());
  return true;
}
Beispiel #21
0
String StringUtil::Reverse(CStrRef input) {
  if (input.empty()) return input;
  int len = input.size();
  return String(string_reverse(input.data(), len), len, AttachString);
}
Beispiel #22
0
int64_t f_hphp_murmurhash(CStrRef key, int len, int seed) {
  len = std::max(std::min(len, key.size()), 0);
  return murmur_hash_64A(key.data(), len, seed);
}
Beispiel #23
0
void StringBuffer::append(CStrRef s) {
  // REGISTER_MUTATED() is called by data()
  append(s.data(), s.size());
}
Beispiel #24
0
Variant ZendPack::unpack(CStrRef fmt, CStrRef data) {
  const char *format = fmt.c_str();
  int formatlen = fmt.size();
  const char *input = data.c_str();
  int inputlen = data.size();
  int inputpos = 0;

  Array ret;
  while (formatlen-- > 0) {
    char type = *(format++);
    char c;
    int arg = 1, argb;
    const char *name;
    int namelen;
    int size=0;

    /* Handle format arguments if any */
    if (formatlen > 0) {
      c = *format;

      if (c >= '0' && c <= '9') {
        arg = atoi(format);

        while (formatlen > 0 && *format >= '0' && *format <= '9') {
          format++;
          formatlen--;
        }
      } else if (c == '*') {
        arg = -1;
        format++;
        formatlen--;
      }
    }

    /* Get of new value in array */
    name = format;
    argb = arg;

    while (formatlen > 0 && *format != '/') {
      formatlen--;
      format++;
    }

    namelen = format - name;

    if (namelen > 200)
      namelen = 200;

    switch ((int) type) {
      /* Never use any input */
    case 'X':
      size = -1;
      break;

    case '@':
      size = 0;
      break;

    case 'a':
    case 'A':
      size = arg;
      arg = 1;
      break;

    case 'h':
    case 'H':
      size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg;
      arg = 1;
      break;

      /* Use 1 byte of input */
    case 'c':
    case 'C':
    case 'x':
      size = 1;
      break;

      /* Use 2 bytes of input */
    case 's':
    case 'S':
    case 'n':
    case 'v':
      size = 2;
      break;

      /* Use sizeof(int) bytes of input */
    case 'i':
    case 'I':
      size = sizeof(int);
      break;

      /* Use 4 bytes of input */
    case 'l':
    case 'L':
    case 'N':
    case 'V':
      size = 4;
      break;

      /* Use sizeof(float) bytes of input */
    case 'f':
      size = sizeof(float);
      break;

      /* Use sizeof(double) bytes of input */
    case 'd':
      size = sizeof(double);
      break;

    default:
      throw_invalid_argument("Invalid format type %c", type);
      return false;
    }

    /* Do actual unpacking */
    for (int i = 0; i != arg; i++ ) {
      /* Space for name + number, safe as namelen is ensured <= 200 */
      char n[256];

      if (arg != 1 || namelen == 0) {
        /* Need to add element number to name */
        snprintf(n, sizeof(n), "%.*s%d", namelen, name, i + 1);
      } else {
        /* Truncate name to next format code or end of string */
        snprintf(n, sizeof(n), "%.*s", namelen, name);
      }

      if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
        throw_invalid_argument("Type %c: integer overflow", type);
        inputpos = 0;
      }

      if ((inputpos + size) <= inputlen) {
        switch ((int) type) {
        case 'a':
        case 'A': {
          char pad = (type == 'a') ? '\0' : ' ';
          int len = inputlen - inputpos; /* Remaining string */

          /* If size was given take minimum of len and size */
          if ((size >= 0) && (len > size)) {
            len = size;
          }

          size = len;

          /* Remove padding chars from unpacked data */
          while (--len >= 0) {
            if (input[inputpos + len] != pad)
              break;
          }

          ret.set(String(n, CopyString),
                  String(input + inputpos, len + 1, CopyString));
          break;
        }

        case 'h':
        case 'H': {
          int len = (inputlen - inputpos) * 2;  /* Remaining */
          int nibbleshift = (type == 'h') ? 0 : 4;
          int first = 1;
          char *buf;
          int ipos, opos;

          /* If size was given take minimum of len and size */
          if (size >= 0 && len > (size * 2)) {
            len = size * 2;
          }

          if (argb > 0) {
            len -= argb % 2;
          }

          String s = String(len, ReserveString);
          buf = s.mutableSlice().ptr;

          for (ipos = opos = 0; opos < len; opos++) {
            char c = (input[inputpos + ipos] >> nibbleshift) & 0xf;

            if (c < 10) {
              c += '0';
            } else {
              c += 'a' - 10;
            }

            buf[opos] = c;
            nibbleshift = (nibbleshift + 4) & 7;

            if (first-- == 0) {
              ipos++;
              first = 1;
            }
          }

          s.setSize(len);
          ret.set(String(n, CopyString), s);
          break;
        }

        case 'c':
        case 'C': {
          int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0;
          ret.set(String(n, CopyString),
                  unpack(&input[inputpos], 1, issigned, byte_map));
          break;
        }

        case 's':
        case 'S':
        case 'n':
        case 'v': {
          int issigned = 0;
          int *map = machine_endian_short_map;

          if (type == 's') {
            issigned = input[inputpos + (machine_little_endian ? 1 : 0)] &
              0x80;
          } else if (type == 'n') {
            map = big_endian_short_map;
          } else if (type == 'v') {
            map = little_endian_short_map;
          }

          ret.set(String(n, CopyString),
                  unpack(&input[inputpos], 2, issigned, map));
          break;
        }

        case 'i':
        case 'I': {
          int32_t v = 0;
          int issigned = 0;

          if (type == 'i') {
            issigned = input[inputpos + (machine_little_endian ?
                                         (sizeof(int) - 1) : 0)] & 0x80;
          } else if (sizeof(int32_t) > 4 &&
                     (input[inputpos + machine_endian_int32_map[3]]
                      & 0x80) == 0x80) {
            v = ~INT_MAX;
          }

          v |= unpack(&input[inputpos], sizeof(int), issigned, int_map);
          if (type == 'i') {
            ret.set(String(n, CopyString), v);
          } else {
            uint64_t u64 = uint32_t(v);
            ret.set(String(n, CopyString), u64);
          }
          break;
        }

        case 'l':
        case 'L':
        case 'N':
        case 'V': {
          int issigned = 0;
          int *map = machine_endian_int32_map;
          int32_t v = 0;

          if (type == 'l' || type == 'L') {
            issigned = input[inputpos + (machine_little_endian ? 3 : 0)]
              & 0x80;
          } else if (type == 'N') {
            issigned = input[inputpos] & 0x80;
            map = big_endian_int32_map;
          } else if (type == 'V') {
            issigned = input[inputpos + 3] & 0x80;
            map = little_endian_int32_map;
          }

          if (sizeof(int32_t) > 4 && issigned) {
            v = ~INT_MAX;
          }

          v |= unpack(&input[inputpos], 4, issigned, map);
          if (type == 'l') {
            ret.set(String(n, CopyString), v);
          } else {
            uint64_t u64 = uint32_t(v);
            ret.set(String(n, CopyString), u64);
          }
          break;
        }

        case 'f': {
          float v;

          memcpy(&v, &input[inputpos], sizeof(float));
          ret.set(String(n, CopyString), (double)v);
          break;
        }

        case 'd': {
          double v;

          memcpy(&v, &input[inputpos], sizeof(double));
          ret.set(String(n, CopyString), v);
          break;
        }

        case 'x':
          /* Do nothing with input, just skip it */
          break;

        case 'X':
          if (inputpos < size) {
            inputpos = -size;
            i = arg - 1;    /* Break out of for loop */

            if (arg >= 0) {
              throw_invalid_argument("Type %c: outside of string", type);
            }
          }
          break;

        case '@':
          if (arg <= inputlen) {
            inputpos = arg;
          } else {
            throw_invalid_argument("Type %c: outside of string", type);
          }

          i = arg - 1;  /* Done, break out of for loop */
          break;
        }

        inputpos += size;
        if (inputpos < 0) {
          if (size != -1) { /* only print warning if not working with * */
            throw_invalid_argument("Type %c: outside of string", type);
          }
          inputpos = 0;
        }
      } else if (arg < 0) {
        /* Reached end of input for '*' repeater */
        break;
      } else {
        throw_invalid_argument
          ("Type %c: not enough input, need %d, have %d",
           type, size, inputlen - inputpos);
        return false;
      }
    }

    formatlen--; /* Skip '/' separator, does no harm if inputlen == 0 */
    format++;
  }
Beispiel #25
0
bool IniSetting::Get(CStrRef name, String &value) {
  if (name == s_error_reporting) {
    value = String((int64_t)g_context->getErrorReportingLevel());
    return true;
  }
  if (name == s_memory_limit) {
    int64_t v = g_context->getRequestMemoryMaxBytes();
    if (v == INT64_MAX) v = -1;
    value = String(v);
    return true;
  }
  if (name == s_max_execution_time || name == s_maximum_execution_time) {
    int64_t timeout = ThreadInfo::s_threadInfo.getNoCheck()->
      m_reqInjectionData.getTimeout();
    value = String(timeout);
    return true;
  }
  if (name == s_hphp_build_id) {
    value = String(RuntimeOption::BuildId);
    return true;
  }
  if (name == s_hphp_compiler_version) {
    value = String(getHphpCompilerVersion());
    return true;
  }
  if (name == s_hphp_compiler_id) {
    value = String(getHphpCompilerId());
    return true;
  }
  if (name == s_arg_separator_output) {
    value = g_context->getArgSeparatorOutput();
    return true;
  }
  if (name == s_upload_max_filesize) {
    int uploadMaxFilesize = VirtualHost::GetUploadMaxFileSize() / (1 << 20);
    value = String(uploadMaxFilesize) + "M";
    return true;
  }
  if (name == s_post_max_size) {
    int postMaxSize = VirtualHost::GetMaxPostSize();
    value = String(postMaxSize);
    return true;
  }
  if (name == s_log_errors) {
    value = g_context->getLogErrors() ? s_1 : s_0;
    return true;
  }
  if (name == s_error_log) {
    value = g_context->getErrorLog();
    return true;
  }
  if (name == s_notice_frequency) {
    value = String((int64_t)RuntimeOption::NoticeFrequency);
    return true;
  }
  if (name == s_warning_frequency) {
    value = String((int64_t)RuntimeOption::WarningFrequency);
    return true;
  }
  if (name == s_include_path) {
    value = g_context->getIncludePath();
    return true;
  }

  DefaultMap::iterator iter = s_global_ini.find(name.data());
  if (iter != s_global_ini.end()) {
    value = iter->second;
    return true;
  }

  return false;
}
Beispiel #26
0
bool ArrayData::IsValidKey(CStrRef k) {
  return IsValidKey(k.get());
}
Beispiel #27
0
Variant StringUtil::Explode(CStrRef input, CStrRef delimiter,
                            int limit /* = 0x7FFFFFFF */) {
  if (delimiter.empty()) {
    throw_invalid_argument("delimiter: (empty)");
    return false;
  }

  Array ret(Array::Create());

  if (input.empty()) {
    if (limit >= 0) {
      ret.append("");
    }
    return ret;
  }

  if (limit > 1) {
    int pos = input.find(delimiter);
    if (pos < 0) {
      ret.append(input);
    } else {
      int len = delimiter.size();
      int pos0 = 0;
      do {
        ret.append(input.substr(pos0, pos - pos0));
        pos += len;
        pos0 = pos;
      } while ((pos = input.find(delimiter, pos)) >= 0 && --limit > 1);

      if (pos0 <= input.size()) {
        ret.append(input.substr(pos0));
      }
    }
  } else if (limit < 0) {
    int pos = input.find(delimiter);
    if (pos >= 0) {
      vector<int> positions;
      int len = delimiter.size();
      int pos0 = 0;
      int found = 0;
      do {
        positions.push_back(pos0);
        positions.push_back(pos - pos0);
        pos += len;
        pos0 = pos;
        found++;
      } while ((pos = input.find(delimiter, pos)) >= 0);

      if (pos0 <= input.size()) {
        positions.push_back(pos0);
        positions.push_back(input.size() - pos0);
        found++;
      }
      int iMax = (found + limit) << 1;
      for (int i = 0; i < iMax; i += 2) {
        ret.append(input.substr(positions[i], positions[i+1]));
      }
    } // else we have negative limit and delimiter not found
  } else {
    ret.append(input);
  }

  return ret;
}