diff options
Diffstat (limited to 'src/qalculate-helper.cpp')
-rw-r--r-- | src/qalculate-helper.cpp | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/src/qalculate-helper.cpp b/src/qalculate-helper.cpp new file mode 100644 index 0000000..3356fb2 --- /dev/null +++ b/src/qalculate-helper.cpp | |||
@@ -0,0 +1,228 @@ | |||
1 | //SPDX-License-Identifier: GPL-3.0 | ||
2 | /* | ||
3 | * qalculate-helper.cpp | ||
4 | * Copyright (C) 2024 Marko Zajc | ||
5 | * | ||
6 | * This program is free software: you can redistribute it and/or modify it under the | ||
7 | * terms of the GNU General Public License as published by the Free Software | ||
8 | * Foundation, version 3. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY | ||
11 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A | ||
12 | * PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with this | ||
15 | * program. If not, see <https://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <config.h> | ||
19 | #include <exchange_update_exception.h> | ||
20 | #include <libqalculate/Calculator.h> | ||
21 | #include <libqalculate/includes.h> | ||
22 | #include <libqalculate/MathStructure.h> | ||
23 | #include <security_util.h> | ||
24 | #include <timeout_exception.h> | ||
25 | #include <cstddef> | ||
26 | #include <cstdio> | ||
27 | #include <cstdlib> | ||
28 | #include <cstring> | ||
29 | #include <sstream> | ||
30 | #include <string> | ||
31 | #include <vector> | ||
32 | |||
33 | using std::string; | ||
34 | using std::stringstream; | ||
35 | using std::getline; | ||
36 | using std::vector; | ||
37 | using std::string_view; | ||
38 | using std::size_t; | ||
39 | |||
40 | #if __cplusplus >= 201703L | ||
41 | #include <string_view> | ||
42 | |||
43 | static bool ends_with(string_view str, string_view suffix) { | ||
44 | return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; | ||
45 | } | ||
46 | |||
47 | #endif | ||
48 | |||
49 | const char TYPE_MESSAGE = 1; | ||
50 | const char TYPE_RESULT = 2; | ||
51 | |||
52 | const char RESULT_APPROXIMATION_NO = 1; | ||
53 | const char RESULT_APPROXIMATION_YES = 2; | ||
54 | |||
55 | const char LEVEL_INFO = 1; | ||
56 | const char LEVEL_WARNING = 2; | ||
57 | const char LEVEL_ERROR = 3; | ||
58 | const char LEVEL_UNKNOWN = 4; | ||
59 | const char SEPARATOR = 0; | ||
60 | |||
61 | #define COMMAND_UPDATE "update" | ||
62 | |||
63 | const unsigned long MODE_PRECISION = 1 << 0; | ||
64 | const unsigned long MODE_EXACT = 1 << 1; | ||
65 | const unsigned long MODE_NOCOLOR = 1 << 2; | ||
66 | |||
67 | const int ETIMEOUT = 102; | ||
68 | const int ECANTFETCH = 103; | ||
69 | |||
70 | static MathStructure evaluate_single(Calculator *calc, const EvaluationOptions &eo, unsigned long line_number, | ||
71 | const string &expression) { | ||
72 | MathStructure result; | ||
73 | if (!calc->calculate(&result, calc->unlocalizeExpression(expression), TIMEOUT_CALC, eo)) | ||
74 | throw timeout_exception(); | ||
75 | |||
76 | const CalculatorMessage *message; | ||
77 | while ((message = CALCULATOR->message())) { | ||
78 | putchar(TYPE_MESSAGE); | ||
79 | switch (message->type()) { | ||
80 | case MESSAGE_INFORMATION: | ||
81 | putchar(LEVEL_INFO); | ||
82 | break; | ||
83 | case MESSAGE_WARNING: | ||
84 | putchar(LEVEL_WARNING); | ||
85 | break; | ||
86 | case MESSAGE_ERROR: | ||
87 | putchar(LEVEL_ERROR); | ||
88 | break; | ||
89 | default: | ||
90 | putchar(LEVEL_UNKNOWN); | ||
91 | break; | ||
92 | } | ||
93 | printf("line %lu: ", line_number); | ||
94 | fputs(message->c_message(), stdout); | ||
95 | putchar(SEPARATOR); | ||
96 | calc->nextMessage(); | ||
97 | } | ||
98 | return result; | ||
99 | } | ||
100 | |||
101 | static bool mode_set(unsigned long mode, unsigned long test) { | ||
102 | return mode & test; | ||
103 | } | ||
104 | |||
105 | static void set_precision(Calculator *calc, unsigned long mode, EvaluationOptions &eo, PrintOptions &po) { | ||
106 | int precision = PRECISION_DEFAULT; | ||
107 | |||
108 | if (mode_set(mode, MODE_EXACT)) { | ||
109 | eo.approximation = APPROXIMATION_EXACT; | ||
110 | po.number_fraction_format = FRACTION_DECIMAL_EXACT; | ||
111 | |||
112 | } else if (mode_set(mode, MODE_PRECISION)) { | ||
113 | precision = PRECISION_HIGH; | ||
114 | po.indicate_infinite_series = false; | ||
115 | } | ||
116 | |||
117 | calc->setPrecision(precision); | ||
118 | } | ||
119 | |||
120 | MathStructure evaluate_all(const vector<string> &expressions, const EvaluationOptions &eo, Calculator *calc) { | ||
121 | for (size_t i = 0; i < expressions.size() - 1; ++i) | ||
122 | evaluate_single(calc, eo, i + 1, expressions[i]); | ||
123 | return evaluate_single(calc, eo, expressions.size(), expressions.back()); | ||
124 | } | ||
125 | |||
126 | void print_result(Calculator *calc, const MathStructure &result_struct, const PrintOptions &po, int mode, | ||
127 | bool approximate) { | ||
128 | string result = calc->print(result_struct, TIMEOUT_PRINT, po, false, mode_set(mode, MODE_NOCOLOR) ? 0 : 1, | ||
129 | TAG_TYPE_TERMINAL); | ||
130 | |||
131 | if (ends_with(result, calc->timedOutString())) { | ||
132 | throw timeout_exception(); | ||
133 | |||
134 | } else { | ||
135 | putchar(TYPE_RESULT); | ||
136 | putchar(approximate ? RESULT_APPROXIMATION_YES : RESULT_APPROXIMATION_NO); | ||
137 | fputs(result.c_str(), stdout); | ||
138 | } | ||
139 | putchar(SEPARATOR); | ||
140 | } | ||
141 | |||
142 | static EvaluationOptions get_evaluationoptions() { | ||
143 | EvaluationOptions eo; | ||
144 | eo.approximation = APPROXIMATION_TRY_EXACT; | ||
145 | eo.parse_options.unknowns_enabled = false; | ||
146 | eo.sync_units = false; | ||
147 | return eo; | ||
148 | } | ||
149 | |||
150 | static PrintOptions get_printoptions(int base, bool &approximate) { | ||
151 | PrintOptions po; | ||
152 | po.base = base; | ||
153 | po.number_fraction_format = FRACTION_DECIMAL; | ||
154 | po.interval_display = INTERVAL_DISPLAY_PLUSMINUS; | ||
155 | po.use_unicode_signs = true; | ||
156 | //po.min_decimals = MIN_DECIMALS; | ||
157 | po.time_zone = TIME_ZONE_UTC; | ||
158 | po.abbreviate_names = true; | ||
159 | po.spell_out_logical_operators = true; | ||
160 | po.allow_non_usable = true; | ||
161 | po.show_ending_zeroes = false; | ||
162 | //po.preserve_precision = true; | ||
163 | //po.restrict_to_parent_precision = false; | ||
164 | po.is_approximate = &approximate; | ||
165 | return po; | ||
166 | } | ||
167 | |||
168 | static void evaluate(Calculator *calc, const vector<string> &expressions, unsigned int mode, int base) { | ||
169 | calc->setExchangeRatesWarningEnabled(false); | ||
170 | calc->loadExchangeRates(); | ||
171 | calc->loadGlobalDefinitions(); | ||
172 | |||
173 | bool approximate = false; | ||
174 | |||
175 | PrintOptions po = get_printoptions(base, approximate); | ||
176 | EvaluationOptions eo = get_evaluationoptions(); | ||
177 | set_precision(calc, mode, eo, po); | ||
178 | |||
179 | calc->setMessagePrintOptions(po); | ||
180 | |||
181 | do_seccomp(); | ||
182 | |||
183 | auto result_struct = evaluate_all(expressions, eo, calc); | ||
184 | |||
185 | print_result(calc, result_struct, po, mode, approximate); | ||
186 | } | ||
187 | |||
188 | static void update() { | ||
189 | if (!CALCULATOR->canFetch()) | ||
190 | throw exchange_update_exception(); | ||
191 | |||
192 | CALCULATOR->fetchExchangeRates(TIMEOUT_UPDATE); | ||
193 | // for some reason this returns false even when it's successful so I'm not going to check it | ||
194 | } | ||
195 | |||
196 | static vector<string> parseExpressions(stringstream input) { | ||
197 | vector<string> result; | ||
198 | string expression; | ||
199 | while (std::getline(input, expression, '\n')) | ||
200 | result.push_back(expression); | ||
201 | |||
202 | return result; | ||
203 | } | ||
204 | |||
205 | int main(int argc, char **argv) { | ||
206 | do_setuid(); | ||
207 | |||
208 | if (argc < 2) | ||
209 | return 1; | ||
210 | |||
211 | auto *calc = new Calculator(true); | ||
212 | do_defang_calculator(calc); | ||
213 | try { | ||
214 | if (argc == 2) { | ||
215 | if (strcmp(argv[1], COMMAND_UPDATE) == 0) | ||
216 | update(); | ||
217 | else | ||
218 | return 1; | ||
219 | } else { | ||
220 | evaluate(calc, parseExpressions(stringstream(argv[1])), std::strtoul(argv[2], nullptr, 10), | ||
221 | std::strtol(argv[3], nullptr, 10)); | ||
222 | } | ||
223 | |||
224 | } catch (const qalculate_exception &e) { | ||
225 | return e.getCode(); | ||
226 | } | ||
227 | } | ||
228 | |||