summaryrefslogtreecommitdiff
path: root/src/qalculate-helper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qalculate-helper.cpp')
-rw-r--r--src/qalculate-helper.cpp228
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
33using std::string;
34using std::stringstream;
35using std::getline;
36using std::vector;
37using std::string_view;
38using std::size_t;
39
40#if __cplusplus >= 201703L
41#include <string_view>
42
43static 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
49const char TYPE_MESSAGE = 1;
50const char TYPE_RESULT = 2;
51
52const char RESULT_APPROXIMATION_NO = 1;
53const char RESULT_APPROXIMATION_YES = 2;
54
55const char LEVEL_INFO = 1;
56const char LEVEL_WARNING = 2;
57const char LEVEL_ERROR = 3;
58const char LEVEL_UNKNOWN = 4;
59const char SEPARATOR = 0;
60
61#define COMMAND_UPDATE "update"
62
63const unsigned long MODE_PRECISION = 1 << 0;
64const unsigned long MODE_EXACT = 1 << 1;
65const unsigned long MODE_NOCOLOR = 1 << 2;
66
67const int ETIMEOUT = 102;
68const int ECANTFETCH = 103;
69
70static 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
101static bool mode_set(unsigned long mode, unsigned long test) {
102 return mode & test;
103}
104
105static 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
120MathStructure 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
126void 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
142static 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
150static 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
168static 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
188static 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
196static 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
205int 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