diff --git a/.gitignore b/.gitignore
index 0ad02d1..a9bb76d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ out/
./cmake-build*/
./build/
./out/
+test/
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/editor.xml b/.idea/editor.xml
new file mode 100644
index 0000000..bf1be31
--- /dev/null
+++ b/.idea/editor.xml
@@ -0,0 +1,527 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..0b76fe5
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..83b63b4
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sql-converter.iml b/.idea/sql-converter.iml
new file mode 100644
index 0000000..f08604b
--- /dev/null
+++ b/.idea/sql-converter.iml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..961de5a
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9650fed..889e0c9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,7 +49,7 @@ macro(blt_add_project name source type)
project(sql-converter)
endmacro()
-project(sql-converter VERSION 0.0.2)
+project(sql-converter VERSION 0.0.3)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
option(ENABLE_UBSAN "Enable the ub sanitizer" OFF)
diff --git a/lib/blt b/lib/blt
index 8c88c62..04a5c01 160000
--- a/lib/blt
+++ b/lib/blt
@@ -1 +1 @@
-Subproject commit 8c88c6296f817d7c86d5089b4050fac258eb337a
+Subproject commit 04a5c01704bbf126ef623d57af61359cdf3d659c
diff --git a/src/main.cpp b/src/main.cpp
index 6f2b004..3326dec 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,327 @@
-#include
+#define BLT_DISABLE_TRACE
+#include
+#include
+#include
+#include
+#include
+#include
-int main()
+enum class case_t
{
- std::cout << "Hello World!" << std::endl;
+ UPPERCASE, LOWERCASE
+};
+
+blt::hashset_t sqlite_keywords{
+ "ABORT",
+ "ACTION",
+ "ADD",
+ "AFTER",
+ "ALL",
+ "ALTER",
+ "ALWAYS",
+ "ANALYZE",
+ "AND",
+ "AS",
+ "ASC",
+ "ATTACH",
+ "AUTOINCREMENT",
+ "BEFORE",
+ "BEGIN",
+ "BETWEEN",
+ "BY",
+ "CASCADE",
+ "CASE",
+ "CAST",
+ "CHECK",
+ "COLLATE",
+ "COLUMN",
+ "COMMIT",
+ "CONFLICT",
+ "CONSTRAINT",
+ "CREATE",
+ "CROSS",
+ "CURRENT",
+ "CURRENT_DATE",
+ "CURRENT_TIME",
+ "CURRENT_TIMESTAMP",
+ "DATABASE",
+ "DEFAULT",
+ "DEFERRABLE",
+ "DEFERRED",
+ "DELETE",
+ "DESC",
+ "DETACH",
+ "DISTINCT",
+ "DO",
+ "DROP",
+ "EACH",
+ "ELSE",
+ "END",
+ "ESCAPE",
+ "EXCEPT",
+ "EXCLUDE",
+ "EXCLUSIVE",
+ "EXISTS",
+ "EXPLAIN",
+ "FAIL",
+ "FILTER",
+ "FIRST",
+ "FOLLOWING",
+ "FOR",
+ "FOREIGN",
+ "FROM",
+ "FULL",
+ "GENERATED",
+ "GLOB",
+ "GROUP",
+ "GROUPS",
+ "HAVING",
+ "IF",
+ "IGNORE",
+ "IMMEDIATE",
+ "IN",
+ "INDEX",
+ "INDEXED",
+ "INITIALLY",
+ "INNER",
+ "INSERT",
+ "INSTEAD",
+ "INTERSECT",
+ "INTO",
+ "IS",
+ "ISNULL",
+ "JOIN",
+ "KEY",
+ "LAST",
+ "LEFT",
+ "LIKE",
+ "LIMIT",
+ "MATCH",
+ "NATURAL",
+ "NO",
+ "NOT",
+ "NOTHING",
+ "NOTNULL",
+ "NULL",
+ "NULLS",
+ "OF",
+ "OFFSET",
+ "ON",
+ "OR",
+ "ORDER",
+ "OTHERS",
+ "OUTER",
+ "OVER",
+ "PARTITION",
+ "PLAN",
+ "PRAGMA",
+ "PRECEDING",
+ "PRIMARY",
+ "QUERY",
+ "RAISE",
+ "RANGE",
+ "RECURSIVE",
+ "REFERENCES",
+ "REGEXP",
+ "REINDEX",
+ "RELEASE",
+ "RENAME",
+ "REPLACE",
+ "RESTRICT",
+ "RIGHT",
+ "ROLLBACK",
+ "ROW",
+ "ROWS",
+ "SAVEPOINT",
+ "SELECT",
+ "SET",
+ "TABLE",
+ "TEMP",
+ "TEMPORARY",
+ "THEN",
+ "TIES",
+ "TO",
+ "TRANSACTION",
+ "TRIGGER",
+ "UNBOUNDED",
+ "UNION",
+ "UNIQUE",
+ "UPDATE",
+ "USING",
+ "VACUUM",
+ "VALUES",
+ "VIEW",
+ "VIRTUAL",
+ "WHEN",
+ "WHERE",
+ "WINDOW",
+ "WITH",
+ "WITHOUT",
+ "RETURNING"
+};
+
+blt::logging::status_progress_bar_t progress;
+
+double number_of_files = 1;
+double processed_files = 0;
+
+void process_file(std::optional search, const std::string& file, case_t c)
+{
+ auto file_data = blt::fs::getFile(file);
+ size_t last_pos = 0;
+ size_t pos = 0;
+
+ if (!search)
+ search = "\"";
+
+ while ((pos = file_data.find(*search, last_pos)) != std::string::npos)
+ {
+ const auto begin = file_data.find('"', pos);
+ size_t end = begin;
+ for (size_t i = begin + 1; i < file_data.size(); i++)
+ {
+ if (file_data[i] == '"' && file_data[i - 1] != '\\')
+ {
+ end = i;
+ break;
+ }
+ }
+ if (end == begin)
+ {
+ BLT_WARN("Unable to process SQL statement, at char pos {}, local string looks like '{}'", pos, std::string_view(
+ file_data.data() + std::max(static_cast(pos) - 10, 0l), std::min(20ul, file_data.size() - pos - 10)));
+ last_pos = end + 1;
+ continue;
+ }
+
+ size_t space_pos = file_data.find(' ', begin);
+ size_t last_space = space_pos;
+
+ auto substr = blt::string::toUpperCase(file_data.substr(begin + 1, space_pos - begin - 1));
+ BLT_TRACE("Found substring '{}' at {} until {}", substr, begin + 1, space_pos - 1);
+
+ if (sqlite_keywords.contains(substr))
+ {
+ switch (c)
+ {
+ case case_t::UPPERCASE:
+ file_data.replace(begin + 1, substr.size(), substr);
+ break;
+ case case_t::LOWERCASE:
+ file_data.replace(begin + 1, substr.size(), blt::string::toLowerCase(substr));
+ break;
+ }
+ }
+
+ while (((space_pos = file_data.find(' ', last_space + 1)) != std::string::npos) && space_pos < end)
+ {
+ substr = blt::string::toUpperCase(file_data.substr(last_space + 1, space_pos - last_space - 1));
+ BLT_TRACE("Found substring '{}' at {} until {}", substr, last_space + 1, last_space + 1 + substr.size());
+
+ if (sqlite_keywords.contains(substr))
+ {
+ switch (c)
+ {
+ case case_t::UPPERCASE:
+ file_data.replace(last_space + 1, substr.size(), substr);
+ break;
+ case case_t::LOWERCASE:
+ file_data.replace(last_space + 1, substr.size(), blt::string::toLowerCase(substr));
+ break;
+ }
+ }
+
+ last_space = space_pos;
+ }
+
+ substr = blt::string::toUpperCase(file_data.substr(last_space + 1, end - last_space - 1));
+ BLT_TRACE("Found substring '{}' at {} until {}", substr, last_space + 1, last_space + 1 + substr.size());
+
+ if (sqlite_keywords.contains(substr))
+ {
+ switch (c)
+ {
+ case case_t::UPPERCASE:
+ file_data.replace(last_space + 1, substr.size(), substr);
+ break;
+ case case_t::LOWERCASE:
+ file_data.replace(last_space + 1, substr.size(), blt::string::toLowerCase(substr));
+ break;
+ }
+ }
+
+ BLT_DEBUG("Processed SQL statement at pos {}", pos);
+ last_pos = end + 1;
+ }
+ ++processed_files;
+ progress.set_progress(processed_files / number_of_files);
+ std::ofstream out(file, std::ios::out);
+ out.write(file_data.data(), static_cast(file_data.size()));
+ BLT_INFO("Processed file {}", file);
+}
+
+void process_directory(const std::optional& search, const std::string& dir, case_t c)
+{
+ std::vector files_to_process;
+ std::vector directories_to_process;
+ directories_to_process.push_back(dir);
+ while (!directories_to_process.empty())
+ {
+ const auto local_dir = directories_to_process.back();
+ directories_to_process.pop_back();
+ BLT_DEBUG("Processing directory '{}'", local_dir);
+ for (auto& file : std::filesystem::directory_iterator(local_dir))
+ {
+ if (file.is_directory())
+ directories_to_process.push_back(file.path().string());
+ else
+ files_to_process.push_back(file.path().string());
+ }
+ }
+ number_of_files = static_cast(files_to_process.size());
+ for (const auto& file : files_to_process)
+ process_file(search, file, c);
+}
+
+int main(const int argc, const char** argv)
+{
+ blt::logging::status_bar_t status;
+ blt::logging::get_global_config().add_injector(status.add(progress));
+
+ blt::argparse::argument_parser_t parser{"Rename SQL statements to be of a case."};
+ parser.with_help();
+ parser.with_version();
+ parser.add_flag("-r", "--recursive").set_dest("-r").make_flag().set_help("Treat path as a directory and recursively iterate through it.");
+ parser.add_flag("-u", "--uppercase").set_dest("-u").make_flag().set_help("Make SQL statements uppercase. This is the default option");
+ parser.add_flag("-l", "--lowercase").set_dest("-l").make_flag().set_help("Make SQL statements lowercase.");
+ parser.add_positional("path").set_help("Path to the file or directory to process.");
+ parser.add_positional("search").set_help(
+ "Search string for parser to lock onto for SQL replacement. "
+ "This should be everything BEFORE the first \" (Do not include the double quote)").set_required(false);
+
+ const auto args = parser.parse(argc, argv);
+
+ if (args.get("-u") && args.get("-l"))
+ {
+ BLT_ERROR("Cannot use both uppercase and lowercase flags at the same time");
+ return 1;
+ }
+
+ auto c = case_t::UPPERCASE;
+
+ if (args.get("-u"))
+ c = case_t::UPPERCASE;
+ if (args.get("-l"))
+ c = case_t::LOWERCASE;
+
+ BLT_INFO("Running on path '{}'", args.get("path"));
+
+ std::optional search;
+ if (args.contains("search"))
+ search = args.get("search");
+
+ if (args.get("-r"))
+ process_directory(search, args.get("path"), c);
+ else
+ process_file(search, args.get("path"), c);
}