Bug Summary

File:state.cpp
Warning:line 212, column 10
Although the value stored to 'r' is used in the enclosing expression, the value is never actually read from 'r'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name state.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/tmp/build/transfuse/transfuse-0.6.3+g73~cf552955/src -resource-dir /usr/lib/llvm-16/lib/clang/16 -D HAS_FS -I /tmp/build/transfuse/transfuse-0.6.3+g73~cf552955/src -I /usr/include/libxml2 -D NDEBUG -internal-isystem /usr/lib/llvm-16/bin/../include/c++/v1 -internal-isystem /usr/lib/llvm-16/lib/clang/16/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-missing-field-initializers -Wno-deprecated -Wno-unused-parameter -std=c++20 -fdebug-compilation-dir=/tmp/build/transfuse/transfuse-0.6.3+g73~cf552955/src -ferror-limit 19 -fvisibility-inlines-hidden -fgnuc-version=4.2.1 -fno-implicit-modules -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/build/transfuse/scan-build/2024-09-11-135321-72641-1 -x c++ /tmp/build/transfuse/transfuse-0.6.3+g73~cf552955/src/state.cpp
1/*
2* Copyright (C) 2020 Tino Didriksen <mail@tinodidriksen.com>
3*
4* This program is free software: you can redistribute it and/or modify
5* it under the terms of the GNU General Public License as published by
6* the Free Software Foundation, either version 3 of the License, or
7* (at your option) any later version.
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 <http://www.gnu.org/licenses/>.
16*/
17
18#include "state.hpp"
19#include "shared.hpp"
20#include "base64.hpp"
21#include <xxhash.h>
22#include <sqlite3.h>
23#include <array>
24#include <map>
25#include <stdexcept>
26
27// SQLite use is completely contained in this file and hidden from the rest of the codebase
28// Only begin() and commit() hint at there being a database for storage, but they would also be useful for other storage backends
29
30namespace Transfuse {
31
32inline auto sqlite3_exec(sqlite3* db, const char* sql) {
33 return ::sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
34}
35
36struct sqlite3_stmt_h {
37 sqlite3_stmt*& operator()() {
38 return s;
39 }
40
41 operator sqlite3_stmt*() {
42 return s;
43 }
44
45 void reset() {
46 if (s) {
47 sqlite3_reset(s);
48 }
49 }
50
51 void clear() {
52 if (s) {
53 sqlite3_finalize(s);
54 }
55 s = nullptr;
56 }
57
58 ~sqlite3_stmt_h() {
59 clear();
60 }
61
62protected:
63 sqlite3_stmt* s = nullptr;
64};
65
66enum Stmt {
67 info_sel,
68 info_ins,
69 style_ins,
70 style_sel,
71 num_stmts
72};
73
74struct State::impl {
75 std::string name;
76 std::string format;
77 std::string stream;
78 std::string tmp_s;
79
80 std::map<std::string, std::map<std::string, std::pair<std::string, std::string>>> styles;
81
82 sqlite3* db = nullptr;
83 std::array<sqlite3_stmt_h, num_stmts> stmts;
84
85 auto& stm(Stmt s) {
86 return stmts[s];
87 }
88
89 ~impl() {
90 for (auto& stm : stmts) {
91 stm.clear();
92 }
93 sqlite3_close(db);
94 }
95};
96
97State::State(fs::path tmpdir, bool ro)
98 : tmpdir(tmpdir)
99 , s(std::make_unique<impl>())
100{
101 if (sqlite3_initialize() != SQLITE_OK0) {
102 throw std::runtime_error("sqlite3_initialize() errored");
103 }
104
105 int flags = ro ? (SQLITE_OPEN_READONLY0x00000001) : (SQLITE_OPEN_READWRITE0x00000002 | SQLITE_OPEN_CREATE0x00000004);
106 if (sqlite3_open_v2((tmpdir / "state.sqlite3").string().c_str(), &s->db, flags, nullptr) != SQLITE_OK0) {
107 throw std::runtime_error(concat("sqlite3_open_v2() error: ", sqlite3_errmsg(s->db)));
108 }
109
110 // All the write operations and writing prepared statements
111 if (!ro) {
112 if (sqlite3_exec(s->db, "CREATE TABLE IF NOT EXISTS info (key TEXT PRIMARY KEY NOT NULL, value TEXT NOT NULL)") != SQLITE_OK0) {
113 throw std::runtime_error(concat("sqlite3 error while creating info table: ", sqlite3_errmsg(s->db)));
114 }
115
116 if (sqlite3_exec(s->db, "CREATE TABLE IF NOT EXISTS styles (tag TEXT NOT NULL, hash TEXT NOT NULL, otag TEXT NOT NULL, ctag TEXT NOT NULL, PRIMARY KEY (tag, hash))") != SQLITE_OK0) {
117 throw std::runtime_error(concat("sqlite3 error while creating inlines table: ", sqlite3_errmsg(s->db)));
118 }
119
120 if (sqlite3_prepare_v2(s->db, "INSERT OR REPLACE INTO info (key, value) VALUES (:key, :value)", -1, &s->stm(info_ins)(), nullptr) != SQLITE_OK0) {
121 throw std::runtime_error(concat("sqlite3 error preparing insert into info table: ", sqlite3_errmsg(s->db)));
122 }
123
124 if (sqlite3_prepare_v2(s->db, "INSERT OR REPLACE INTO styles (tag, hash, otag, ctag) VALUES (:tag, :hash, :otag, :ctag)", -1, &s->stm(style_ins)(), nullptr) != SQLITE_OK0) {
125 throw std::runtime_error(concat("sqlite3 error preparing insert into styles table: ", sqlite3_errmsg(s->db)));
126 }
127 }
128
129 // All the reading prepared statements
130 if (sqlite3_prepare_v2(s->db, "SELECT value FROM info WHERE key = :key", -1, &s->stm(info_sel)(), nullptr) != SQLITE_OK0) {
131 throw std::runtime_error(concat("sqlite3 error preparing select from info table: ", sqlite3_errmsg(s->db)));
132 }
133
134 if (sqlite3_prepare_v2(s->db, "SELECT tag, hash, otag, ctag FROM styles", -1, &s->stm(style_sel)(), nullptr) != SQLITE_OK0) {
135 throw std::runtime_error(concat("sqlite3 error preparing select from styles table: ", sqlite3_errmsg(s->db)));
136 }
137}
138
139State::~State() {
140}
141
142void State::begin() {
143 if (sqlite3_exec(s->db, "BEGIN") != SQLITE_OK0) {
144 throw std::runtime_error(concat("sqlite3 error while beginning transaction: ", sqlite3_errmsg(s->db)));
145 }
146}
147
148void State::commit() {
149 if (sqlite3_exec(s->db, "COMMIT") != SQLITE_OK0) {
150 throw std::runtime_error(concat("sqlite3 error while committing transaction: ", sqlite3_errmsg(s->db)));
151 }
152}
153
154void State::name(std::string_view val) {
155 info("name", val);
156 s->name = val;
157}
158
159std::string_view State::name() {
160 if (s->name.empty()) {
161 s->name = info("name");
162 }
163 return s->name;
164}
165
166void State::format(std::string_view val) {
167 info("format", val);
168 s->format = val;
169}
170
171std::string_view State::format() {
172 if (s->format.empty()) {
173 s->format = info("format");
174 }
175 return s->format;
176}
177
178void State::stream(std::string_view val) {
179 info("stream", val);
180 s->stream = val;
181}
182
183std::string_view State::stream() {
184 if (s->stream.empty()) {
185 s->stream = info("stream");
186 }
187 return s->stream;
188}
189
190void State::info(std::string_view key, std::string_view val) {
191 s->stm(info_ins).reset();
192 if (sqlite3_bind_text(s->stm(info_ins), 1, key.data(), SI(key.size()), SQLITE_STATIC((sqlite3_destructor_type)0)) != SQLITE_OK0) {
193 throw std::runtime_error(concat("sqlite3 error trying to bind text for key: ", sqlite3_errmsg(s->db)));
194 }
195 if (sqlite3_bind_text(s->stm(info_ins), 2, val.data(), SI(val.size()), SQLITE_STATIC((sqlite3_destructor_type)0)) != SQLITE_OK0) {
196 throw std::runtime_error(concat("sqlite3 error trying to bind text for value: ", sqlite3_errmsg(s->db)));
197 }
198 if (sqlite3_step(s->stm(info_ins)) != SQLITE_DONE101) {
199 throw std::runtime_error(concat("sqlite3 error inserting into info table: ", sqlite3_errmsg(s->db)));
200 }
201}
202
203std::string State::info(std::string_view key) {
204 std::string rv;
205
206 s->stm(info_sel).reset();
207 if (sqlite3_bind_text(s->stm(info_sel), 1, key.data(), SI(key.size()), SQLITE_STATIC((sqlite3_destructor_type)0)) != SQLITE_OK0) {
208 throw std::runtime_error(concat("sqlite3 error trying to bind text for key: ", sqlite3_errmsg(s->db)));
209 }
210
211 int r = 0;
212 while ((r = sqlite3_step(s->stm(info_sel))) == SQLITE_ROW100) {
Although the value stored to 'r' is used in the enclosing expression, the value is never actually read from 'r'
213 rv = reinterpret_cast<const char*>(sqlite3_column_text(s->stm(info_sel), 0));
214 }
215
216 return rv;
217}
218
219xmlChar_view State::style(xmlChar_view _name, xmlChar_view _otag, xmlChar_view _ctag) {
220 auto name = x2s(_name);
221 auto otag = x2s(_otag);
222 auto ctag = x2s(_ctag);
223
224 // Make sure that empty opening or closing tag still causes a difference
225 s->tmp_s.assign(otag.begin(), otag.end());
226 s->tmp_s += TFI_HASH_SEP"\xee\x80\x90";
227 s->tmp_s += ctag;
228 auto h32 = XXH32(s->tmp_s.data(), s->tmp_s.size(), 0);
229 base64_url(s->tmp_s, h32);
230
231 s->stm(style_ins).reset();
232 if (sqlite3_bind_text(s->stm(style_ins), 1, name.data(), SI(name.size()), SQLITE_STATIC((sqlite3_destructor_type)0)) != SQLITE_OK0) {
233 throw std::runtime_error(concat("sqlite3 error trying to bind text for tag: ", sqlite3_errmsg(s->db)));
234 }
235 if (sqlite3_bind_text(s->stm(style_ins), 2, s->tmp_s.data(), SI(s->tmp_s.size()), SQLITE_STATIC((sqlite3_destructor_type)0)) != SQLITE_OK0) {
236 throw std::runtime_error(concat("sqlite3 error trying to bind text for hash: ", sqlite3_errmsg(s->db)));
237 }
238 if (sqlite3_bind_text(s->stm(style_ins), 3, otag.data(), SI(otag.size()), SQLITE_STATIC((sqlite3_destructor_type)0)) != SQLITE_OK0) {
239 throw std::runtime_error(concat("sqlite3 error trying to bind text for otag: ", sqlite3_errmsg(s->db)));
240 }
241 if (sqlite3_bind_text(s->stm(style_ins), 4, ctag.data(), SI(ctag.size()), SQLITE_STATIC((sqlite3_destructor_type)0)) != SQLITE_OK0) {
242 throw std::runtime_error(concat("sqlite3 error trying to bind text for ctag: ", sqlite3_errmsg(s->db)));
243 }
244 if (sqlite3_step(s->stm(style_ins)) != SQLITE_DONE101) {
245 throw std::runtime_error(concat("sqlite3 error inserting into styles table: ", sqlite3_errmsg(s->db)));
246 }
247
248 return s2x(s->tmp_s);
249}
250
251std::pair<std::string_view, std::string_view> State::style(std::string_view tag, std::string_view hash) {
252 if (s->styles.empty()) {
253 std::string t;
254 std::string h;
255 std::string o;
256 std::string c;
257 s->stm(style_sel).reset();
258 int r = 0;
259 while ((r = sqlite3_step(s->stm(style_sel))) == SQLITE_ROW100) {
260 t = reinterpret_cast<const char*>(sqlite3_column_text(s->stm(style_sel), 0));
261 h = reinterpret_cast<const char*>(sqlite3_column_text(s->stm(style_sel), 1));
262 o = reinterpret_cast<const char*>(sqlite3_column_text(s->stm(style_sel), 2));
263 c = reinterpret_cast<const char*>(sqlite3_column_text(s->stm(style_sel), 3));
264 s->styles[t][h] = std::make_pair(o, c);
265 }
266 }
267
268 s->tmp_s.assign(tag.begin(), tag.end());
269 auto t = s->styles.find(s->tmp_s);
270 if (t == s->styles.end()) {
271 return {};
272 }
273
274 s->tmp_s.assign(hash.begin(), hash.end());
275 auto oc = t->second.find(s->tmp_s);
276 if (oc == t->second.end()) {
277 return {};
278 }
279 return { oc->second.first, oc->second.second };
280}
281
282}