summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/administration.hpp2
-rw-r--r--include/importer.hpp6
-rw-r--r--include/strops.hpp5
-rw-r--r--src/administration.cpp25
-rw-r--r--src/administration_reader.cpp1
-rw-r--r--src/administration_writer.cpp14
-rw-r--r--src/ai_providers/DeepSeek.cpp3
-rw-r--r--src/ai_providers/openAI.cpp15
-rw-r--r--src/importer.cpp8
-rw-r--r--src/logger.cpp9
-rw-r--r--src/strops.cpp44
-rw-r--r--src/ui/imgui_extensions.cpp19
-rw-r--r--src/ui/ui_contacts.cpp9
-rw-r--r--src/ui/ui_earnings.cpp1
-rw-r--r--src/ui/ui_expenses.cpp6
-rw-r--r--src/ui/ui_invoices.cpp6
-rw-r--r--src/ui/ui_projects.cpp7
-rw-r--r--src/ui/ui_settings.cpp21
-rw-r--r--tests/test_helper.cpp3
19 files changed, 111 insertions, 93 deletions
diff --git a/include/administration.hpp b/include/administration.hpp
index 4cd83ee..75f5b52 100644
--- a/include/administration.hpp
+++ b/include/administration.hpp
@@ -341,6 +341,8 @@ typedef enum
{
AI_PROVIDER_OPENAI = 0,
AI_PROVIDER_DEEPSEEK = 1,
+
+ AI_PROVIDER_END,
} ai_provider;
typedef struct
diff --git a/include/importer.hpp b/include/importer.hpp
index fde297a..cb24cf8 100644
--- a/include/importer.hpp
+++ b/include/importer.hpp
@@ -23,9 +23,10 @@
#define I_ERR_FAILED_QUERY 2
#define I_ERR_FAILED_IMPORT 3
+typedef uint32_t i_err;
+
namespace importer {
- typedef uint32_t i_err;
typedef enum
{
@@ -47,6 +48,7 @@ namespace importer {
typedef struct
{
+ char* provider_name;
bool (*upload_file)(char* file_path, char* file_id, size_t file_id_len);
bool (*query_with_file)(char* query, size_t query_length, char* file_id, char** response);
} ai_provider_impl;
@@ -55,5 +57,5 @@ namespace importer {
const char* status_to_string(status status);
invoice_request* ai_document_to_invoice(char* file_path);
-
+ ai_provider_impl get_ai_provider_implementation(ai_provider provider);
} \ No newline at end of file
diff --git a/include/strops.hpp b/include/strops.hpp
index 3a85543..5dc7368 100644
--- a/include/strops.hpp
+++ b/include/strops.hpp
@@ -23,10 +23,15 @@ namespace strops {
size_t copy(char *dst, const char *src, size_t size);
char* contains(char* a, char* b);
bool equals(const char* a, const char* b);
+
+ s32 format_va(char* s, size_t n, const char* format, va_list args);
+ s32 format(char* s, size_t n, const char* format, ...);
+
void replace(char *buf, size_t buf_size, const char *search, const char *replace);
void replace_int32(char *buf, size_t buf_size, const char *search, s32 number);
void replace_int64(char *buf, size_t buf_size, const char *search, s64 number);
void replace_float(char *buf, size_t buf_size, const char *search, float number, int decimals);
+
bool is_prefixed(const char *pre, const char *str);
char* get_filename(const char* path);
diff --git a/src/administration.cpp b/src/administration.cpp
index 1de99b4..a3d3c2f 100644
--- a/src/administration.cpp
+++ b/src/administration.cpp
@@ -16,7 +16,6 @@
#define _CRT_SECURE_NO_WARNINGS
-#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
@@ -61,7 +60,7 @@ static void create_default_tax_rates()
#define ADD_BRACKET(_country, _rate, _code)\
{\
tax_rate* tb = (tax_rate*)malloc(sizeof(tax_rate));\
- snprintf(tb->id, sizeof(tb->id), "T/%d", create_id());\
+ strops::format(tb->id, sizeof(tb->id), "T/%d", create_id());\
memcpy(tb->country_code, _country, sizeof(tb->country_code));\
tb->rate = _rate;\
memcpy(tb->category_code, _code, sizeof(tb->category_code));\
@@ -224,7 +223,7 @@ static void create_default_cost_centers()
#define ADD_COSTCENTER(_description, _code)\
{\
cost_center* tb = (cost_center*)malloc(sizeof(cost_center));\
- snprintf(tb->id, sizeof(tb->id), "E/%d", create_id());\
+ strops::format(tb->id, sizeof(tb->id), "E/%d", create_id());\
memcpy(tb->description, _description, sizeof(tb->description));\
memcpy(tb->code, _code, sizeof(tb->code));\
list_append(&g_administration.cost_centers, tb);\
@@ -529,7 +528,7 @@ void administration::create_income_statement(income_statement* statement)
quarter.report_count = 0;
quarter.is_empty = 1;
quarter.profit = 0.0f;
- snprintf(quarter.quarter_str, MAX_LEN_SHORT_DESC, "%dQ%d", quarter.quarter+1, quarter.year);
+ strops::format(quarter.quarter_str, MAX_LEN_SHORT_DESC, "%dQ%d", quarter.quarter+1, quarter.year);
project_count = administration::project_count();
project* project_buffer = (project*)malloc(sizeof(project)*project_count);
@@ -913,7 +912,7 @@ contact administration::contact_create_empty()
{
contact result;
memset(&result, 0, sizeof(contact));
- snprintf(result.id, sizeof(result.id), "C/%d", create_id());
+ strops::format(result.id, sizeof(result.id), "C/%d", create_id());
return result;
}
@@ -1100,7 +1099,7 @@ project administration::project_create_empty()
result.start_date = time(NULL);
result.start_date -= (result.start_date % 86400);
result.end_date = 0;
- snprintf(result.id, sizeof(result.id), "P/%d", create_id());
+ strops::format(result.id, sizeof(result.id), "P/%d", create_id());
return result;
}
@@ -1111,8 +1110,8 @@ tax_rate administration::tax_rate_create_empty()
{
tax_rate result;
memset(&result, 0, sizeof(tax_rate));
- snprintf(result.id, sizeof(result.id), "T/%d", create_id());
- snprintf(result.category_code, sizeof(result.category_code), "S"); // S = standard rate.
+ strops::format(result.id, sizeof(result.id), "T/%d", create_id());
+ strops::format(result.category_code, sizeof(result.category_code), "S"); // S = standard rate.
return result;
}
@@ -1125,7 +1124,7 @@ a_err administration::tax_rate_get_by_shorthandle(tax_rate* buffer, char* handle
tax_rate c = *(tax_rate *)list_iterator_next(&g_administration.tax_rates);
char compare_str[20];
- snprintf(compare_str, 20, "%s/%.2f", c.country_code, c.rate);
+ strops::format(compare_str, 20, "%s/%.2f", c.country_code, c.rate);
if (strcmp(compare_str, handle) == 0)
{
*buffer = c;
@@ -1273,7 +1272,7 @@ cost_center administration::cost_center_create_empty()
{
cost_center cc;
memset(&cc, 0, sizeof(cost_center));
- snprintf(cc.id, sizeof(cc.id), "E/%d", create_id());
+ strops::format(cc.id, sizeof(cc.id), "E/%d", create_id());
return cc;
}
@@ -1447,8 +1446,8 @@ invoice administration::invoice_create_empty()
{
invoice result;
memset(&result, 0, sizeof(invoice));
- snprintf(result.id, sizeof(result.id), "I/%d", create_id());
- snprintf(result.sequential_number, sizeof(result.id), "INV%010d", create_sequence_number());
+ strops::format(result.id, sizeof(result.id), "I/%d", create_id());
+ strops::format(result.sequential_number, sizeof(result.id), "INV%010d", create_sequence_number());
result.issued_at = time(NULL);
result.issued_at -= (result.issued_at % 86400);
@@ -1951,7 +1950,7 @@ a_err administration::billing_item_add_to_invoice(invoice* invoice, billing_item
if (!tb) return A_ERR_GENERIC;
memcpy(tb, &item, sizeof(billing_item));
- snprintf(tb->id, sizeof(tb->id), "B/%d", create_id());
+ strops::format(tb->id, sizeof(tb->id), "B/%d", create_id());
strops::copy(tb->currency, invoice->currency, MAX_LEN_CURRENCY); // Set billing item currency to invoice currency.
administration_recalculate_billing_item_totals(tb);
diff --git a/src/administration_reader.cpp b/src/administration_reader.cpp
index 7400caf..e39ae8c 100644
--- a/src/administration_reader.cpp
+++ b/src/administration_reader.cpp
@@ -16,7 +16,6 @@
#include <malloc.h>
#include <stdio.h>
-#include <stdlib.h>
#include <stdint.h>
#include <zip.h>
#include <xml.h>
diff --git a/src/administration_writer.cpp b/src/administration_writer.cpp
index b6dbb8c..040b842 100644
--- a/src/administration_writer.cpp
+++ b/src/administration_writer.cpp
@@ -131,7 +131,7 @@ static bool delete_entry_by_name(char* entry)
bool administration_writer::delete_entry(char* id)
{
char final_path[50];
- snprintf(final_path, 50, "%s.xml", id);
+ strops::format(final_path, 50, "%s.xml", id);
return delete_entry_by_name(final_path);
}
@@ -299,7 +299,7 @@ static void _add_document_to_zip(invoice* inv)
if (strlen(doc->copy_path) == 0 && strlen(doc->original_path) != 0)
{
char copy_path[MAX_LEN_PATH];
- snprintf(copy_path, MAX_LEN_PATH, "documents/%s%s", inv->sequential_number, _get_file_extension(doc->original_path));
+ strops::format(copy_path, MAX_LEN_PATH, "documents/%s%s", inv->sequential_number, _get_file_extension(doc->original_path));
FILE* orig_file = fopen(doc->original_path, "rb");
if (orig_file == NULL) {
@@ -517,7 +517,7 @@ bool administration_writer::save_invoice_blocking(invoice inv)
//// Write to Disk.
char final_path[50];
- snprintf(final_path, 50, "%s.xml", inv.id);
+ strops::format(final_path, 50, "%s.xml", inv.id);
int final_length = (int)strlen(file_content);
if (!xml_parse_document((uint8_t*)file_content, final_length)) result = 0;
@@ -576,7 +576,7 @@ bool administration_writer::save_project_blocking(project project)
//// Write to Disk.
char final_path[50];
- snprintf(final_path, 50, "%s.xml", project.id);
+ strops::format(final_path, 50, "%s.xml", project.id);
int final_length = (int)strlen(file_content);
if (!xml_parse_document((uint8_t*)file_content, final_length)) result = 0;
@@ -624,7 +624,7 @@ bool administration_writer::save_cost_center_blocking(cost_center cost)
//// Write to Disk.
char final_path[50];
- snprintf(final_path, 50, "%s.xml", cost.id);
+ strops::format(final_path, 50, "%s.xml", cost.id);
int final_length = (int)strlen(file_content);
if (!xml_parse_document((uint8_t*)file_content, final_length)) result = 0;
@@ -673,7 +673,7 @@ bool administration_writer::save_tax_rate_blocking(tax_rate rate)
//// Write to Disk.
char final_path[50];
- snprintf(final_path, 50, "%s.xml", rate.id);
+ strops::format(final_path, 50, "%s.xml", rate.id);
int final_length = (int)strlen(file_content);
if (!xml_parse_document((uint8_t*)file_content, final_length)) result = 0;
@@ -733,7 +733,7 @@ bool administration_writer::save_contact_blocking(contact c)
strops::replace(file_content, buf_length, "{{CONTACT_REGION}}", c.address.region);
char final_path[50];
- snprintf(final_path, 50, "%s.xml", c.id);
+ strops::format(final_path, 50, "%s.xml", c.id);
int final_length = (int)strlen(file_content);
if (!xml_parse_document((uint8_t*)file_content, final_length)) result = 0;
diff --git a/src/ai_providers/DeepSeek.cpp b/src/ai_providers/DeepSeek.cpp
index 5a378dd..4dd4e0a 100644
--- a/src/ai_providers/DeepSeek.cpp
+++ b/src/ai_providers/DeepSeek.cpp
@@ -50,7 +50,7 @@ static bool _DeepSeek_query_with_file(char* query, size_t query_length, char* fi
size_t body_size = file_size + QUERY_BUFFER_SIZE;
char* body = (char*)malloc(body_size);
- snprintf(body, body_size,
+ strops::format(body, body_size,
"{\"model\":\"deepseek-reasoner\", \"messages\": [ { \"role\": \"user\", \"content\": \"%s\" } ] }", query_escaped);
httplib::Headers headers;
@@ -117,6 +117,7 @@ static bool _DeepSeek_upload_file(char* file_path, char* file_id, size_t file_id
}
importer::ai_provider_impl _deepseek_api_provider = {
+ "DeekSeek",
_DeepSeek_upload_file,
_DeepSeek_query_with_file,
}; \ No newline at end of file
diff --git a/src/ai_providers/openAI.cpp b/src/ai_providers/openAI.cpp
index bbe9f8f..c4526ef 100644
--- a/src/ai_providers/openAI.cpp
+++ b/src/ai_providers/openAI.cpp
@@ -39,7 +39,7 @@ static bool _openAI_query_with_file(char* query, size_t query_length, char* file
size_t body_size = query_length + 200;
char* body = (char*)malloc(body_size);
- snprintf(body, body_size,
+ strops::format(body, body_size,
"{\"model\":\"gpt-5-nano\", \"input\": [ { \"role\": \"user\", \"content\": [ { \"type\": \"input_file\", \"file_id\": \"%s\" }, "
"{ \"type\": \"input_text\", \"text\": \"%s\" } ] } ] }", file_id, query_escaped);
@@ -90,7 +90,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l
//cli.enable_server_certificate_verification(false);
char body[512];
- snprintf(body, sizeof(body), "{\"filename\":\"%s\",\"purpose\":\"user_data\", \"bytes\": %d, \"mime_type\": \"application/pdf\", \"expires_after\": { \"anchor\": \"created_at\", \"seconds\": 3600 } }", filename, sz);
+ strops::format(body, sizeof(body), "{\"filename\":\"%s\",\"purpose\":\"user_data\", \"bytes\": %d, \"mime_type\": \"application/pdf\", \"expires_after\": { \"anchor\": \"created_at\", \"seconds\": 3600 } }", filename, sz);
httplib::Headers headers;
headers.insert(std::make_pair("Authorization", std::string("Bearer ") + api_key));
@@ -111,7 +111,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l
char *buffer = (char*)malloc(part_size);
char completion_body[1048];
- snprintf(completion_body, sizeof(completion_body), "{\"part_ids\": [");
+ strops::format(completion_body, sizeof(completion_body), "{\"part_ids\": [");
int part_number = 0;
while (1) {
@@ -128,7 +128,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l
};
char path[256];
- snprintf(path, sizeof(path), "/v1/uploads/%s/parts?part_number=%d", upload_id, part_number);
+ strops::format(path, sizeof(path), "/v1/uploads/%s/parts?part_number=%d", upload_id, part_number);
httplib::Result part_res = cli.Post(path, part_headers, items);
@@ -142,15 +142,15 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l
else {
char part_id[128];
strops::get_json_value(part_res->body.c_str(), "id", part_id, sizeof(part_id));
- if (part_number == 0) snprintf(completion_body+strlen(completion_body), sizeof(completion_body)-strlen(completion_body), "\"%s\"", part_id);
- if (part_number != 0) snprintf(completion_body+strlen(completion_body), sizeof(completion_body)-strlen(completion_body), ", \"%s\"", part_id);
+ if (part_number == 0) strops::format(completion_body+strlen(completion_body), sizeof(completion_body)-strlen(completion_body), "\"%s\"", part_id);
+ if (part_number != 0) strops::format(completion_body+strlen(completion_body), sizeof(completion_body)-strlen(completion_body), ", \"%s\"", part_id);
}
logger::info("Uploaded part %d\n", part_number);
part_number++;
}
- snprintf(completion_body+strlen(completion_body), sizeof(completion_body)-strlen(completion_body), "]}");
+ strops::format(completion_body+strlen(completion_body), sizeof(completion_body)-strlen(completion_body), "]}");
free(buffer);
fclose(orig_file);
@@ -174,6 +174,7 @@ static bool _openAI_upload_file(char* file_path, char* file_id, size_t file_id_l
}
importer::ai_provider_impl _chatgpt_api_provider = {
+ "OpenAI",
_openAI_upload_file,
_openAI_query_with_file,
}; \ No newline at end of file
diff --git a/src/importer.cpp b/src/importer.cpp
index 3f81672..8b5cc5c 100644
--- a/src/importer.cpp
+++ b/src/importer.cpp
@@ -33,10 +33,8 @@
extern importer::ai_provider_impl _chatgpt_api_provider;
extern importer::ai_provider_impl _deepseek_api_provider;
-importer::ai_provider_impl _ai_get_impl()
+importer::ai_provider_impl importer::get_ai_provider_implementation(ai_provider provider)
{
- ai_provider provider = administration::get_ai_service().provider;
-
switch(provider)
{
case AI_PROVIDER_OPENAI: return _chatgpt_api_provider;
@@ -50,7 +48,7 @@ importer::ai_provider_impl _ai_get_impl()
static int _ai_document_to_invoice_t(void *arg) {
importer::invoice_request* request = (importer::invoice_request*)arg;
char* file_path = request->file_path;
- importer::ai_provider_impl impl = _ai_get_impl();
+ importer::ai_provider_impl impl = importer::get_ai_provider_implementation(administration::get_ai_service().provider);
request->status = importer::status::IMPORT_UPLOADING_FILE;
@@ -179,7 +177,7 @@ const char* importer::status_to_string(importer::status status)
return "";
}
-const char* importer::error_to_string(importer::i_err error)
+const char* importer::error_to_string(i_err error)
{
switch(error)
{
diff --git a/src/logger.cpp b/src/logger.cpp
index 38d17b7..df0ae91 100644
--- a/src/logger.cpp
+++ b/src/logger.cpp
@@ -17,7 +17,10 @@
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
+
#include "timer.h"
+
+#include "strops.hpp"
#include "logger.hpp"
namespace logger {
@@ -26,7 +29,7 @@ namespace logger {
static void log_message(const char* fmt, ImVec4 color, va_list args)
{
- vsnprintf(g_log.history[g_log.write_cursor], logger::MAX_LEN_LOG_TXT, fmt, args);
+ strops::format_va(g_log.history[g_log.write_cursor], logger::MAX_LEN_LOG_TXT, fmt, args);
g_log.colors[g_log.write_cursor] = color;
tick_t ms_since_epoch = timer_system();
@@ -42,14 +45,14 @@ namespace logger {
localtime_r(&seconds, &tm_time);
#endif
- snprintf(time_buf, 50, "%02d:%02d %02d.%03d",
+ strops::format(time_buf, 50, "%02d:%02d %02d.%03d",
tm_time.tm_hour,
tm_time.tm_min,
tm_time.tm_sec,
milliseconds);
char tmp[logger::MAX_LEN_LOG_TXT];
- snprintf(tmp, logger::MAX_LEN_LOG_TXT, "[%s] %s", time_buf, g_log.history[g_log.write_cursor]);
+ strops::format(tmp, logger::MAX_LEN_LOG_TXT, "[%s] %s", time_buf, g_log.history[g_log.write_cursor]);
tmp[logger::MAX_LEN_LOG_TXT-1] = 0;
memcpy(g_log.history[g_log.write_cursor], tmp, logger::MAX_LEN_LOG_TXT);
diff --git a/src/strops.cpp b/src/strops.cpp
index 356ce5b..6142c01 100644
--- a/src/strops.cpp
+++ b/src/strops.cpp
@@ -15,7 +15,6 @@
*/
#include <stdlib.h>
-#include <string.h>
#include <ctype.h>
#include <stdio.h>
@@ -51,17 +50,32 @@ namespace strops {
char* contains(char* haystack, char* needle)
{
do {
- const char* h = haystack;
- const char* n = needle;
- while (tolower((unsigned char) *h) == tolower((unsigned char ) *n) && *n) {
- h++;
- n++;
- }
- if (*n == 0) {
- return (char *) haystack;
+ const char* h = haystack;
+ const char* n = needle;
+ while (tolower((unsigned char) *h) == tolower((unsigned char ) *n) && *n) {
+ h++;
+ n++;
+ }
+ if (*n == 0) {
+ return (char *) haystack;
}
- } while (*haystack++);
- return 0;
+ } while (*haystack++);
+ return 0;
+ }
+
+ s32 format_va(char* s, size_t n, const char* format, va_list args)
+ {
+ s32 result = vsnprintf(s, n, format, args);
+ return result;
+ }
+
+ s32 format(char* s, size_t n, const char* format, ...)
+ {
+ va_list args;
+ va_start (args, format);
+ s32 result = vsnprintf(s, n, format, args);
+ va_end (args);
+ return result;
}
void replace(char *buf, size_t buf_size, const char *search, const char *replace)
@@ -99,28 +113,28 @@ namespace strops {
void replace_int32(char *buf, size_t buf_size, const char *search, s32 number)
{
char num_buf[200];
- snprintf(num_buf, 200, "%d", number);
+ strops::format(num_buf, 200, "%d", number);
strops::replace(buf, buf_size, search, num_buf);
}
void replace_int64(char *buf, size_t buf_size, const char *search, s64 number)
{
char num_buf[200];
- snprintf(num_buf, 200, "%lld", number);
+ strops::format(num_buf, 200, "%lld", number);
strops::replace(buf, buf_size, search, num_buf);
}
void replace_float(char *buf, size_t buf_size, const char *search, float number, int decimals)
{
char num_buf[200];
- snprintf(num_buf, 200, "%.*f", decimals, number);
+ strops::format(num_buf, 200, "%.*f", decimals, number);
strops::replace(buf, buf_size, search, num_buf);
}
char* get_json_value(const char *json, const char *key, char *out, size_t out_size, int skip)
{
char pattern[128];
- snprintf(pattern, sizeof(pattern), "\"%s\"", key);
+ strops::format(pattern, sizeof(pattern), "\"%s\"", key);
const char *pos = strstr(json, pattern);
while(skip > 0) {
pos = strstr(pos+1, pattern);
diff --git a/src/ui/imgui_extensions.cpp b/src/ui/imgui_extensions.cpp
index 5a00648..4e8270a 100644
--- a/src/ui/imgui_extensions.cpp
+++ b/src/ui/imgui_extensions.cpp
@@ -1,4 +1,3 @@
-#include <stdio.h>
#include <stdlib.h>
#include "ui.hpp"
@@ -93,7 +92,7 @@ namespace ImGui
for (int x = 0; x < config::country_count; x++)
{
char locale_str[20];
- snprintf(locale_str, 20, "country.%s", config::country_codes[x]);
+ strops::format(locale_str, 20, "country.%s", config::country_codes[x]);
countries[x] = locale::get(locale_str);
}
@@ -227,7 +226,7 @@ namespace ImGui
for (int i = 0; i < autocomplete_count; i++)
{
autocomplete_strings[i] = (char*)malloc(200);
- snprintf(autocomplete_strings[i], 200, "%s (%s %s)", autocomplete_list[i].name, autocomplete_list[i].address.address1, autocomplete_list[i].address.address2);
+ strops::format(autocomplete_strings[i], 200, "%s (%s %s)", autocomplete_list[i].name, autocomplete_list[i].address.address1, autocomplete_list[i].address.address2);
}
int autocomplete_index = ImGui::TextInputWithAutocomplete(locale::get("contact.form.fullname"),
@@ -355,10 +354,10 @@ namespace ImGui
{
if (strcmp(selected_tax_rate->country_code, "00") == 0) {
char category_code_desc[MAX_LEN_LONG_DESC];
- snprintf(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", selected_tax_rate->category_code);
- snprintf(rate_str_buf, 40, "%s", locale::get(category_code_desc));
+ strops::format(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", selected_tax_rate->category_code);
+ strops::format(rate_str_buf, 40, "%s", locale::get(category_code_desc));
}
- else snprintf(rate_str_buf, 40, "%s/%.1f%%", selected_tax_rate->country_code, selected_tax_rate->rate);
+ else strops::format(rate_str_buf, 40, "%s/%.1f%%", selected_tax_rate->country_code, selected_tax_rate->rate);
}
if (has_error) {
@@ -374,10 +373,10 @@ namespace ImGui
if (strcmp(buffer[n].country_code, "00") == 0) {
char category_code_desc[MAX_LEN_LONG_DESC];
- snprintf(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", buffer[n].category_code);
- snprintf(rate_str_buf, 40, "%s", locale::get(category_code_desc));
+ strops::format(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", buffer[n].category_code);
+ strops::format(rate_str_buf, 40, "%s", locale::get(category_code_desc));
}
- else snprintf(rate_str_buf, 40, "%s/%.1f%%", buffer[n].country_code, buffer[n].rate);
+ else strops::format(rate_str_buf, 40, "%s/%.1f%%", buffer[n].country_code, buffer[n].rate);
if (ImGui::Selectable(rate_str_buf, is_selected)) {
selected_tax_rate_index = n;
@@ -458,7 +457,7 @@ namespace ImGui
const char* items[] = { option1, option2 };
char ID[MAX_LEN_LONG_DESC];
- snprintf(ID, MAX_LEN_LONG_DESC, "Mode##%p", buffer);
+ strops::format(ID, MAX_LEN_LONG_DESC, "Mode##%p", buffer);
if (ImGui::BeginCombo(ID, items[*buffer])) {
for (int n = 0; n < 2; n++) {
bool is_selected = (n == (int)*buffer);
diff --git a/src/ui/ui_contacts.cpp b/src/ui/ui_contacts.cpp
index 03fcee0..7d585c9 100644
--- a/src/ui/ui_contacts.cpp
+++ b/src/ui/ui_contacts.cpp
@@ -14,9 +14,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <stdio.h>
-#include <stdlib.h>
-
#include "strops.hpp"
#include "config.hpp"
#include "ui.hpp"
@@ -163,7 +160,7 @@ static void draw_contact_list()
ImGui::TableSetColumnIndex(3);
char btn_name[20];
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.view"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.view"), i);
if (ImGui::Button(btn_name)) {
active_contact = c;
current_view_state = ui::view_state::VIEW_EXISTING;
@@ -171,14 +168,14 @@ static void draw_contact_list()
ImGui::SameLine();
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.change"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.change"), i);
if (ImGui::Button(btn_name)) {
active_contact = c;
current_view_state = ui::view_state::EDIT_EXISTING;
}
ImGui::SameLine();
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.delete"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.delete"), i);
if (ImGui::Button(btn_name)) {
selected_for_removal = c;
ImGui::OpenPopup("ConfirmDeletePopup");
diff --git a/src/ui/ui_earnings.cpp b/src/ui/ui_earnings.cpp
index 5e04a61..1ceabbe 100644
--- a/src/ui/ui_earnings.cpp
+++ b/src/ui/ui_earnings.cpp
@@ -14,7 +14,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <stdio.h>
#include <stdlib.h>
#include "ui.hpp"
diff --git a/src/ui/ui_expenses.cpp b/src/ui/ui_expenses.cpp
index e4441ef..8812273 100644
--- a/src/ui/ui_expenses.cpp
+++ b/src/ui/ui_expenses.cpp
@@ -244,7 +244,7 @@ static void draw_expenses_list()
ImGui::TableSetColumnIndex(6);
char btn_name[20];
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.view"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.view"), i);
if (ImGui::Button(btn_name)) {
active_invoice = c;
current_view_state = ui::view_state::VIEW_EXISTING;
@@ -252,14 +252,14 @@ static void draw_expenses_list()
ImGui::SameLine();
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.change"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.change"), i);
if (ImGui::Button(btn_name)) {
active_invoice = administration::invoice_create_copy(&c); // We create a copy because of billing item list pointers.
current_view_state = ui::view_state::EDIT_EXISTING;
}
ImGui::SameLine();
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.delete"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.delete"), i);
if (ImGui::Button(btn_name)) {
selected_for_removal = c;
ImGui::OpenPopup("ConfirmDeletePopup");
diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp
index 191cd7d..4dd44e8 100644
--- a/src/ui/ui_invoices.cpp
+++ b/src/ui/ui_invoices.cpp
@@ -341,7 +341,7 @@ static void draw_invoices_list()
ImGui::TableSetColumnIndex(6);
char btn_name[20];
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.view"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.view"), i);
if (ImGui::Button(btn_name)) {
active_invoice = c;
current_view_state = ui::view_state::VIEW_EXISTING;
@@ -351,14 +351,14 @@ static void draw_invoices_list()
if (c.status == invoice_status::INVOICE_CONCEPT)
{
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.change"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.change"), i);
if (ImGui::Button(btn_name)) {
active_invoice = administration::invoice_create_copy(&c); // We create a copy because of billing item list pointers.
current_view_state = ui::view_state::EDIT_EXISTING;
}
ImGui::SameLine();
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.delete"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.delete"), i);
if (ImGui::Button(btn_name)) {
selected_for_removal = c;
ImGui::OpenPopup("ConfirmDeletePopup");
diff --git a/src/ui/ui_projects.cpp b/src/ui/ui_projects.cpp
index f9f494d..66cc336 100644
--- a/src/ui/ui_projects.cpp
+++ b/src/ui/ui_projects.cpp
@@ -21,6 +21,7 @@
#include "administration.hpp"
#include "administration_writer.hpp"
#include "locales.hpp"
+#include "strops.hpp"
static ui::view_state current_view_state = ui::view_state::LIST_ALL;
static project selected_for_cancellation;
@@ -139,7 +140,7 @@ static void draw_project_list()
ImGui::TableSetColumnIndex(3);
char btn_name[20];
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.view"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.view"), i);
if (ImGui::Button(btn_name)) {
active_project = c;
current_view_state = ui::view_state::VIEW_EXISTING;
@@ -148,14 +149,14 @@ static void draw_project_list()
if (c.state == project_state::PROJECT_RUNNING)
{
ImGui::SameLine();
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.change"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.change"), i);
if (ImGui::Button(btn_name)) {
active_project = c;
current_view_state = ui::view_state::EDIT_EXISTING;
}
ImGui::SameLine();
- snprintf(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.cancel"), i);
+ strops::format(btn_name, sizeof(btn_name), "%s##%d", locale::get("form.cancel"), i);
if (ImGui::Button(btn_name)) {
selected_for_cancellation = c;
ImGui::OpenPopup("ConfirmCancelProject");
diff --git a/src/ui/ui_settings.cpp b/src/ui/ui_settings.cpp
index cb6cdf5..8177f07 100644
--- a/src/ui/ui_settings.cpp
+++ b/src/ui/ui_settings.cpp
@@ -15,13 +15,13 @@
*/
#include <stdlib.h>
-#include <stdio.h>
#include "strops.hpp"
#include "ui.hpp"
#include "imgui.h"
#include "administration.hpp"
#include "locales.hpp"
+#include "importer.hpp"
#include "administration_writer.hpp"
extern void draw_contact_form(contact* buffer, bool viewing_only = false);
@@ -103,7 +103,7 @@ static void draw_vat_rates()
ImGui::TableNextRow();
char locale_buf[20];
- snprintf(locale_buf, sizeof(locale_buf), "country.%s", c.country_code);
+ strops::format(locale_buf, sizeof(locale_buf), "country.%s", c.country_code);
ImGui::TableSetColumnIndex(0);
ImGui::Text(locale::get(locale_buf));
@@ -112,7 +112,7 @@ static void draw_vat_rates()
{
ImGui::SameLine();
char btn_name[20];
- snprintf(btn_name, sizeof(btn_name), "+##%d",i);
+ strops::format(btn_name, sizeof(btn_name), "+##%d",i);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0));
if (ImGui::Button(btn_name, ImVec2(20,20))) {
is_adding_item = true;
@@ -131,7 +131,7 @@ static void draw_vat_rates()
ImGui::TableSetColumnIndex(0);
char category_code_desc[MAX_LEN_LONG_DESC];
- snprintf(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", c.category_code);
+ strops::format(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", c.category_code);
ImGui::Text(can_be_modified ? "" : locale::get(category_code_desc));
@@ -338,18 +338,17 @@ static void draw_services()
// AI service
if (ImGui::CollapsingHeader(locale::get("settings.services.ai_service")))
{
- // TODO: get this from iterator over ai_get_impl
- char* services[2] = {
- "OpenAI",
- "DeepSeek",
- };
+ char* ai_service_names[AI_PROVIDER_END];
+ for (u32 i = 0; i < AI_PROVIDER_END; i++) {
+ ai_service_names[i] = importer::get_ai_provider_implementation((ai_provider)i).provider_name;
+ }
- if (ImGui::BeginCombo(locale::get("settings.services.ai_service.provider"), services[new_service.provider]))
+ if (ImGui::BeginCombo(locale::get("settings.services.ai_service.provider"), ai_service_names[new_service.provider]))
{
for (u32 n = 0; n < 2; n++)
{
bool is_selected = n == (uint32_t)new_service.provider;
- if (ImGui::Selectable(services[n], is_selected)) {
+ if (ImGui::Selectable(ai_service_names[n], is_selected)) {
new_service.provider = (ai_provider)n;
}
}
diff --git a/tests/test_helper.cpp b/tests/test_helper.cpp
index f0b0d66..b74916a 100644
--- a/tests/test_helper.cpp
+++ b/tests/test_helper.cpp
@@ -1,6 +1,5 @@
#define _CRT_SECURE_NO_WARNINGS
-#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
@@ -174,7 +173,7 @@ static bool _test_peppol_file(char* id)
zip_extract(test_file_path, extract_dir, 0, 0);
char command[200];
- snprintf(command, 200, "java -jar libs\\schxslt-cli.jar -d %s\\%s.xml -s %s -o %s > NUL 2>&1",
+ strops::format(command, 200, "java -jar libs\\schxslt-cli.jar -d %s\\%s.xml -s %s -o %s > NUL 2>&1",
extract_dir, id, validation_file, result_file);
system(command);