<commit_before>/*
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you 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 "ts/ts.h"
#include "ts/remap.h"
#include "ink_defs.h"

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

static const char PLUGIN_NAME[] = "conf_remap";

// This makes the plugin depend on the version of traffic server installed, but that's
// OK, since this plugin is distributed only with the "core" (it's a core piece).
#define MAX_OVERRIDABLE_CONFIGS TS_CONFIG_LAST_ENTRY


// Class to hold a set of configurations (one for each remap rule instance)
struct RemapConfigs
{
  struct Item {
    TSOverridableConfigKey _name;
    TSRecordDataType _type;
    TSRecordData _data;
    int _data_len; // Used when data is a string
  };

  RemapConfigs()
    : _current(0)
  {
    memset(_items, 0, sizeof(_items));
  };

  bool parse_file(const char *fn);

  Item _items[MAX_OVERRIDABLE_CONFIGS];
  int _current;
};

// Helper functionfor the parser
inline TSRecordDataType
str_to_datatype(const char* str)
{
  TSRecordDataType type = TS_RECORDDATATYPE_NULL;

  if (!str || !*str)
    return TS_RECORDDATATYPE_NULL;

  if (!strcmp(str, "INT")) {
    type = TS_RECORDDATATYPE_INT;
  } else if (!strcmp(str, "STRING")) {
    type = TS_RECORDDATATYPE_STRING;
  }

  return type;
}

// Config file parser, somewhat borrowed from P_RecCore.i
bool
RemapConfigs::parse_file(const char* fn)
{
  int line_num = 0;
  TSFile file;
  char buf[8192];
  TSOverridableConfigKey name;
  TSRecordDataType type, expected_type;

  if (!fn || ('\0' == *fn))
    return false;

  if (NULL == (file = TSfopen(fn, "r"))) {
    TSError("%s: could not open config file %s", PLUGIN_NAME, fn);
    return false;
  }

  TSDebug(PLUGIN_NAME, "loading configuration file %s", fn);

  while (NULL != TSfgets(file, buf, sizeof(buf))) {
    char *ln, *tok;
    char *s = buf;

    ++line_num; // First line is #1 ...
    while (isspace(*s))
      ++s;
    tok = strtok_r(s, " \t", &ln);

    // check for blank lines and comments
    if ((!tok) || (tok && ('#' == *tok)))
      continue;

    if (strncmp(tok, "CONFIG", 6)) {
      TSError("%s: file %s, line %d: non-CONFIG line encountered", PLUGIN_NAME, fn, line_num);
      continue;
    }

    // Find the configuration name
    tok = strtok_r(NULL, " \t", &ln);
    if (TSHttpTxnConfigFind(tok, -1, &name, &expected_type) != TS_SUCCESS) {
      TSError("%s: file %s, line %d: no records.config name given", PLUGIN_NAME, fn, line_num);
      continue;
    }
    
    // Find the type (INT or STRING only)
    tok = strtok_r(NULL, " \t", &ln);
    if (TS_RECORDDATATYPE_NULL == (type = str_to_datatype(tok))) {
      TSError("%s: file %s, line %d: only INT and STRING types supported", PLUGIN_NAME, fn, line_num);
      continue;
    }

    if (type != expected_type) {
      TSError("%s: file %s, line %d: mismatch between provide data type, and expected type", PLUGIN_NAME, fn, line_num);
      continue;
    }

    // Find the value (which depends on the type above)
    if (ln) {
      while (isspace(*ln))
        ++ln;
      if ('\0' == *ln) {
        tok = NULL;
      } else {
        tok = ln;
        while (*ln != '\0')
          ++ln;
        --ln;
        while (isspace(*ln) && (ln > tok))
          --ln;
        ++ln;
        *ln = '\0';
      }
    } else {
      tok = NULL;
    }
    if (!tok) {
      TSError("%s: file %s, line %d: the configuration must provide a value", PLUGIN_NAME, fn, line_num);
      continue;
    }

    // Now store the new config
    switch (type) {
    case TS_RECORDDATATYPE_INT:
      _items[_current]._data.rec_int = strtoll(tok, NULL, 10);
      break;
    case TS_RECORDDATATYPE_STRING:
      _items[_current]._data.rec_string = TSstrdup(tok);
      _items[_current]._data_len = strlen(tok);
      break;
    default:
      TSError("%s: file %s, line %d: type not support (unheard of)", PLUGIN_NAME, fn, line_num);
      continue;
      break;
    }
    _items[_current]._name = name;
    _items[_current]._type = type;
    ++_current;
  }

  TSfclose(file);
  return (_current > 0);
}


///////////////////////////////////////////////////////////////////////////////<commit_msg>config_remap: accept file paths relative to SYSCONFIGDIR<commit_after>/*
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you 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 "ts/ts.h"
#include "ts/remap.h"
#include "ink_defs.h"

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <string>

static const char PLUGIN_NAME[] = "conf_remap";

// This makes the plugin depend on the version of traffic server installed, but that's
// OK, since this plugin is distributed only with the "core" (it's a core piece).
#define MAX_OVERRIDABLE_CONFIGS TS_CONFIG_LAST_ENTRY


// Class to hold a set of configurations (one for each remap rule instance)
struct RemapConfigs
{
  struct Item {
    TSOverridableConfigKey _name;
    TSRecordDataType _type;
    TSRecordData _data;
    int _data_len; // Used when data is a string
  };

  RemapConfigs()
    : _current(0)
  {
    memset(_items, 0, sizeof(_items));
  };

  bool parse_file(const char *filename);

  Item _items[MAX_OVERRIDABLE_CONFIGS];
  int _current;
};

// Helper functionfor the parser
inline TSRecordDataType
str_to_datatype(const char* str)
{
  TSRecordDataType type = TS_RECORDDATATYPE_NULL;

  if (!str || !*str)
    return TS_RECORDDATATYPE_NULL;

  if (!strcmp(str, "INT")) {
    type = TS_RECORDDATATYPE_INT;
  } else if (!strcmp(str, "STRING")) {
    type = TS_RECORDDATATYPE_STRING;
  }

  return type;
}

// Config file parser, somewhat borrowed from P_RecCore.i
bool
RemapConfigs::parse_file(const char* filename)
{
  int line_num = 0;
  TSFile file;
  char buf[8192];
  TSOverridableConfigKey name;
  TSRecordDataType type, expected_type;

  std::string path;

  if (!filename || ('\0' == *filename))
    return false;

  if (*filename == '/') {
    // Absolute path, just use it.
    path = filename;
  } else {
    // Relative path. Make it relative to the configuration directory.
    path = TSConfigDirGet();
    path += "/";
    path += filename;
  }

  if (NULL == (file = TSfopen(path.c_str(), "r"))) {
    TSError("%s: could not open config file %s", PLUGIN_NAME, path.c_str());
    return false;
  }

  TSDebug(PLUGIN_NAME, "loading configuration file %s", path.c_str());

  while (NULL != TSfgets(file, buf, sizeof(buf))) {
    char *ln, *tok;
    char *s = buf;

    ++line_num; // First line is #1 ...
    while (isspace(*s))
      ++s;
    tok = strtok_r(s, " \t", &ln);

    // check for blank lines and comments
    if ((!tok) || (tok && ('#' == *tok)))
      continue;

    if (strncmp(tok, "CONFIG", 6)) {
      TSError("%s: file %s, line %d: non-CONFIG line encountered", PLUGIN_NAME, path.c_str(), line_num);
      continue;
    }

    // Find the configuration name
    tok = strtok_r(NULL, " \t", &ln);
    if (TSHttpTxnConfigFind(tok, -1, &name, &expected_type) != TS_SUCCESS) {
      TSError("%s: file %s, line %d: no records.config name given", PLUGIN_NAME, path.c_str(), line_num);
      continue;
    }
    
    // Find the type (INT or STRING only)
    tok = strtok_r(NULL, " \t", &ln);
    if (TS_RECORDDATATYPE_NULL == (type = str_to_datatype(tok))) {
      TSError("%s: file %s, line %d: only INT and STRING types supported", PLUGIN_NAME, path.c_str(), line_num);
      continue;
    }

    if (type != expected_type) {
      TSError("%s: file %s, line %d: mismatch between provide data type, and expected type", PLUGIN_NAME, path.c_str(), line_num);
      continue;
    }

    // Find the value (which depends on the type above)
    if (ln) {
      while (isspace(*ln))
        ++ln;
      if ('\0' == *ln) {
        tok = NULL;
      } else {
        tok = ln;
        while (*ln != '\0')
          ++ln;
        --ln;
        while (isspace(*ln) && (ln > tok))
          --ln;
        ++ln;
        *ln = '\0';
      }
    } else {
      tok = NULL;
    }
    if (!tok) {
      TSError("%s: file %s, line %d: the configuration must provide a value", PLUGIN_NAME, path.c_str(), line_num);
      continue;
    }

    // Now store the new config
    switch (type) {
    case TS_RECORDDATATYPE_INT:
      _items[_current]._data.rec_int = strtoll(tok, NULL, 10);
      break;
    case TS_RECORDDATATYPE_STRING:
      _items[_current]._data.rec_string = TSstrdup(tok);
      _items[_current]._data_len = strlen(tok);
      break;
    default:
      TSError("%s: file %s, line %d: type not support (unheard of)", PLUGIN_NAME, path.c_str(), line_num);
      continue;
      break;
    }
    _items[_current]._name = name;
    _items[_current]._type = type;
    ++_current;
  }

  TSfclose(file);
  return (_current > 0);
}


///////////////////////////////////////////////////////////////////////////////
{'fraction_non_alphanumeric': 0.13927276291286877, 'fraction_numerical': 0.004116436342252279, 'mean_word_length': 2.782060785767235, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3200750', 'n_tokens_mistral': 3482, 'n_tokens_neox': 3359, 'n_words': 1066}