Skip to content
API Documentation
BastionGuard™
main.cpp
Go to the documentation of this file.
1/*
2 * BastionGuard™
3 * Copyright (C) 2025–2026 Calogero Scarnà
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 *
17 * BastionGuard™ is a trademark of Calogero Scarnà.
18 * The BastionGuard™ name and branding are not licensed under the GPL.
19 */
20
21
22#include "wizard_window.hpp"
23#include "StyleProvider.hpp"
24#include "resource.hpp"
25#include <gtkmm.h>
26#include <fstream>
27#include <clocale>
28#include <glib/gi18n.h>
29#include <glib.h>
30#include <filesystem>
31#include <system_error>
32#include <unistd.h>
33#include <cerrno>
34#include <cstring>
35#include <iostream>
36
37#ifndef LOCALEDIR
38#error "LOCALEDIR non definita per questo target"
39#endif
40
41
42namespace fs = std::filesystem;
43
44static bool bastionguard_mo_exists(const std::string& lang) {
45 fs::path mo = fs::path(LOCALEDIR) / lang / "LC_MESSAGES" / "BastionGuard.mo";
46 std::error_code ec;
47 return fs::exists(mo, ec) && !ec;
48}
49
51 const std::string lang_conf = Glib::get_home_dir() + "/.config/BastionGuard/lang.conf";
52 std::ifstream f(lang_conf);
53
54 auto normalize = [](std::string s) {
55 auto ltrim = [](std::string& x){ x.erase(0, x.find_first_not_of(" \t\r\n")); };
56 auto rtrim = [](std::string& x){ x.erase(x.find_last_not_of(" \t\r\n") + 1); };
57 ltrim(s); rtrim(s);
58 auto dot = s.find('.');
59 if (dot != std::string::npos) s = s.substr(0, dot);
60 auto at = s.find('@');
61 if (at != std::string::npos) s = s.substr(0, at);
62 return s;
63 };
64
65 if (f.is_open()) {
66 std::string line;
67 while (std::getline(f, line)) {
68 if (line.empty() || line[0] == '#') continue;
69 auto pos = line.find('=');
70 if (pos == std::string::npos) continue;
71
72 std::string key = line.substr(0, pos);
73 std::string val = line.substr(pos + 1);
74 key = normalize(key);
75 val = normalize(val);
76
77 if ((key == "LANGUAGE" || key == "LC_MESSAGES" || key == "LANG") && !val.empty()) {
78 auto colon = val.find(':');
79 std::string first = (colon == std::string::npos) ? val : val.substr(0, colon);
80
81 if (bastionguard_mo_exists(first))
82 return first;
83
84 // fallback: se "it_IT" non esiste, prova "it"
85 auto us = first.find('_');
86 if (us != std::string::npos) {
87 std::string base = first.substr(0, us);
88 if (bastionguard_mo_exists(base))
89 return base;
90 }
91 }
92 }
93 }
94
95 if (bastionguard_mo_exists("en_US")) return "en_US";
96 if (bastionguard_mo_exists("en")) return "en";
97 return "C";
98}
99
100
101static bool load_lang_conf()
102{
103 const std::string lang_conf = Glib::get_home_dir() + "/.config/BastionGuard/lang.conf";
104 std::ifstream f(lang_conf);
105 if (!f.is_open())
106 return false;
107
108 std::string line;
109 bool any = false;
110
111 while (std::getline(f, line)) {
112 if (line.empty() || line[0] == '#')
113 continue;
114
115 auto pos = line.find('=');
116 if (pos == std::string::npos)
117 continue;
118
119 std::string key = line.substr(0, pos);
120 std::string val = line.substr(pos + 1);
121
122 auto ltrim = [](std::string& s){
123 s.erase(0, s.find_first_not_of(" \t\r\n"));
124 };
125 auto rtrim = [](std::string& s){
126 s.erase(s.find_last_not_of(" \t\r\n") + 1);
127 };
128 ltrim(key); rtrim(key);
129 ltrim(val); rtrim(val);
130
131 if (!key.empty() && !val.empty()) {
132 setenv(key.c_str(), val.c_str(), 1);
133 any = true;
134 }
135 }
136
137 return any;
138}
139
140int main(int argc, char* argv[]) {
141
142 const std::string lang_conf = Glib::get_home_dir() + "/.config/BastionGuard/lang.conf";
143 {
144 std::error_code ec;
145 if (!fs::exists(lang_conf, ec) || ec) {
146 std::string cfgdir = Glib::get_home_dir() + "/.config/BastionGuard";
147 fs::create_directories(cfgdir, ec);
148 std::ofstream out(lang_conf, std::ios::out | std::ios::trunc);
149 if (out.is_open()) {
150 out << "LANG=en_US.UTF-8\n";
151 out << "LC_ALL=en_US.UTF-8\n";
152 out << "LANGUAGE=en_US\n";
153 out.close();
154 std::cout << "[Lang] ✅ Creato lang.conf di default: " << lang_conf << std::endl;
155 }
156 }
157 }
158
159 std::ifstream f(lang_conf);
160 if (f.is_open()) {
161 std::string line;
162 while (std::getline(f, line)) {
163 auto pos = line.find('=');
164 if (pos != std::string::npos) {
165 std::string key = line.substr(0, pos);
166 std::string val = line.substr(pos + 1);
167 if (!key.empty() && !val.empty())
168 setenv(key.c_str(), val.c_str(), 1);
169 }
170 }
171 f.close();
172 std::cout << "[Lang] Config caricata da " << lang_conf << std::endl;
173 } else {
174 std::cerr << "[Lang] ⚠️ Impossibile aprire lang.conf, forzo inglese di default" << std::endl;
175 }
176
177 const char* lang_env = std::getenv("LANG");
178 if (!lang_env || std::string(lang_env).empty())
179 setenv("LANG", "en_US.UTF-8", 1);
180
181 const char* lc_all_env = std::getenv("LC_ALL");
182 if (!lc_all_env || std::string(lc_all_env).empty())
183 setenv("LC_ALL", "en_US.UTF-8", 1);
184
185 const char* language_env = std::getenv("LANGUAGE");
186 if (!language_env || std::string(language_env).empty())
187 setenv("LANGUAGE", "en_US", 1);
188
189 const char* applied = setlocale(LC_ALL, "");
190 if (!applied) applied = setlocale(LC_ALL, "en_US.UTF-8");
191 if (!applied) applied = setlocale(LC_ALL, "en_US");
192 if (!applied) applied = setlocale(LC_ALL, "C.UTF-8");
193 if (!applied) applied = setlocale(LC_ALL, "C");
194
195
196 bindtextdomain("BastionGuard", LOCALEDIR);
197 bind_textdomain_codeset("BastionGuard", "UTF-8");
198 textdomain("BastionGuard");
199
200 auto app = Gtk::Application::create("org.BastionGuard.wizard");
201 load_style();
202
203 auto* wizard = new BastionGuard::wizard::WizardWindow();
204 app->add_window(*wizard);
205
206 wizard->signal_finished().connect([&app, wizard]() {
207 wizard->hide();
208 app->remove_window(*wizard);
209 delete wizard;
210
211 // 7) Lancia BastionGuard-first-run e aspetta che finisca
212 const std::string first_run_app = "/usr/bin/BastionGuard-first-run";
213 pid_t pid = fork();
214
215 if (pid == 0) {
216 execl(first_run_app.c_str(), first_run_app.c_str(), nullptr);
217 std::cerr << "[Wizard] ❌ Impossibile lanciare BastionGuard-first-run: "
218 << strerror(errno) << std::endl;
219 _exit(1);
220
221 } else if (pid > 0) {
222 Glib::signal_child_watch().connect([&app](GPid child_pid, int status) {
223 g_spawn_close_pid(child_pid);
224
225 const std::string main_app = "/usr/bin/BastionGuard";
226 std::cout << "[Wizard] 🚀 Lancio: " << main_app << std::endl;
227 execl(main_app.c_str(), main_app.c_str(), nullptr);
228
229 std::cerr << "[Wizard] ❌ Impossibile lanciare BastionGuard: "
230 << strerror(errno) << std::endl;
231 app->quit();
232 }, pid);
233
234 } else {
235 std::cerr << "[Wizard] ❌ fork() fallita: " << strerror(errno) << std::endl;
236 app->quit();
237 }
238 });
239
240 wizard->present();
241 return app->run(argc, argv);
242}
243
int main(int argc, char *argv[])
static bool load_lang_conf()
#define LOCALEDIR
void load_style()
static bool bastionguard_mo_exists(const std::string &lang)
Definition main.cpp:44
static std::string pick_wizard_language_from_localedir()
Definition main.cpp:50