-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhcv_chunkmap.cc
283 lines (262 loc) · 10.2 KB
/
hcv_chunkmap.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/****************************************************************
* file hcv_chunkmap.cc
*
* Description:
* Parsing a file containing literal strings for customization
* for https://github.com/bstarynk/helpcovid
* see its file CUSTOMIZATION.md
*
* Author(s):
* © Copyright 2020
* Basile Starynkevitch <[email protected]>
* Abhishek Chakravarti <[email protected]>
*
*
* License:
* This HELPCOVID program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "hcv_header.hh"
extern "C" const char hcv_chunkmap_gitid[] = HELPCOVID_GITID;
extern "C" const char hcv_chunkmap_date[] = __DATE__;
static constexpr unsigned hcv_max_chunkmap_size = 512*1024;
static constexpr unsigned hcv_max_chunkmap_linelen = 65536;
extern "C" std::mutex hcv_globchunkmap_mtx;
extern "C" std::map<std::string,std::string> hcv_globchunk_map;
/// see documentation in CUSTOMIZATION.md
std::map<std::string,std::string>
hcv_parse_chunk_map(const std::string& filepath)
{
#define HCVSTATE_ENDLABEL_LEN 32
std::map<std::string,std::string> resultmap;
HCV_DEBUGOUT("hcv_parse_chunk_map start filepath=" << filepath);
if (access(filepath.c_str(), R_OK))
HCV_FATALOUT("hcv_parse_chunk_map cannot access " << filepath);
// https://en.wikipedia.org/wiki/X_Macro trick
#define HCVSTATE_XMACRO(Mac) \
Mac(_NONE), \
Mac(SKIPSPACES), \
Mac(HEADERLINE), \
Mac(INSIDECHUNK)
// define HCVCHUNKSTATE__NONE, etc...
enum hcvchunkstate_en
{
#define HCVSTATE_XDEFINE(Nam) HCVCHUNKSTATE_##Nam
HCVSTATE_XMACRO(HCVSTATE_XDEFINE)
#undef HCVSTATE_XDEFINE
};
const char*const statenames[] =
{
#define HCVSTATE_XNAME(Nam) #Nam
HCVSTATE_XMACRO(HCVSTATE_XNAME),
#undef HCVSTATE_XNAME
nullptr
};
enum hcvchunkstate_en state = HCVCHUNKSTATE__NONE;
std::ifstream inp(filepath);
int lincnt = 0;
long off = 0;
state = HCVCHUNKSTATE_SKIPSPACES;
std::string chunkname;
std::string chunkbody;
for (std::string linbuf; (off=inp.tellg()), std::getline(inp, linbuf); )
{
char endstrbuf[HCVSTATE_ENDLABEL_LEN]; // that size appears in sscanf below
memset(endstrbuf, 0, sizeof(endstrbuf));
lincnt++;
if (lincnt >= (int)hcv_max_chunkmap_linelen)
HCV_FATALOUT("hcv_parse_chunk_map " << filepath << " has too many lines " << lincnt);
if (off > hcv_max_chunkmap_size)
HCV_FATALOUT("hcv_parse_chunk_map " << filepath << " has too many bytes " << off);
HCV_DEBUGOUT("hcv_parse_chunk_map line#" << lincnt
<< " state=" << statenames[state] << " line:" << linbuf);
bool allspaces = false;
switch (state)
{
case HCVCHUNKSTATE_SKIPSPACES:
{
chunkname.clear();
chunkbody.clear();
if (linbuf.empty())
continue;
// lines starting with # are comments
if (linbuf[0] == '#')
continue;
// line with only spaces should be skipped
allspaces = true;
for (auto c : linbuf)
if (!isspace(c))
{
allspaces = false;
break;
};
if (allspaces)
continue;
if (linbuf.size() >= 3
&& linbuf[0] == '!'
&& isalpha(linbuf[1]))
{
state = HCVCHUNKSTATE_HEADERLINE;
goto parse_headerline;
}
else
HCV_FATALOUT("hcv_parse_chunk_map, in file " << filepath
<< ", line# " << lincnt
<< " is unexpected:" << linbuf);
};
break;
////
case HCVCHUNKSTATE_HEADERLINE:
parse_headerline:
{
char chunknamebuf[64];
memset (chunknamebuf, 0, sizeof(chunknamebuf));
int endpos= -1;
if (linbuf.size() >= 3
&& linbuf[0] == '!'
&& isalpha(linbuf[1]))
{
if (sscanf(linbuf.c_str(),
"!%60[A-Za-z0-9_]'%n", chunknamebuf, &endpos) >= 1
&& endpos>2 && linbuf[endpos-1]=='\'')
{
/**
* single-line chunk line such as
* !FOO'some chunk on single line up to end-of-line
**/
chunkbody.assign(linbuf.c_str()+endpos);
chunkname.assign(chunknamebuf);
HCV_DEBUGOUT("hcv_parse_chunk_map line#" << lincnt
<< ", file:" << filepath
<< ", single line chunk " << chunkname
<< ":" << chunkbody);
resultmap.insert({chunkname,chunkbody});
state = HCVCHUNKSTATE_SKIPSPACES;
continue;
}
else if (sscanf(linbuf.c_str(),
"!%60[A-Za-z0-9_]\"%30[A-Za-z0-9_](%n",
chunknamebuf, endstrbuf, &endpos) >= 2
&& endpos>4 && isalpha(endstrbuf[0])
&& linbuf[endpos-1]=='(')
{
/**
* multi-line chunk line starting with
* !MULTIBAR"abc(
**/
chunkname.assign(chunknamebuf);
chunkbody.clear();
if ((int)linbuf.size() > endpos
&& !isspace(linbuf[endpos]))
chunkbody.assign(linbuf.c_str()+endpos);
HCV_DEBUGOUT("hcv_parse_chunk_map line#" << lincnt
<< ", file:" << filepath
<< ", multiline line chunk " << chunkname
<< (chunkbody.empty()?" eol":" starting with:")
<< chunkbody);
state = HCVCHUNKSTATE_INSIDECHUNK;
}
else goto badheaderline;
}
else
badheaderline:
HCV_FATALOUT("hcv_parse_chunk_map line#" << lincnt
<< " bad header line state=" << statenames[state]
<< " line:" << linbuf);
}
break;
////
case HCVCHUNKSTATE_INSIDECHUNK:
/**
* Inside a chunk started with something like !MULTIBAR"abc(
*
* which should be ended by )abc"
**/
{
int endpos= -1;
char parsendstrbuf[HCVSTATE_ENDLABEL_LEN]; // that size appears in sscanf below
memset (parsendstrbuf, 0, sizeof(parsendstrbuf));
if (sscanf(linbuf.c_str(), ")%30[A-Za-z0-9_] %n", parsendstrbuf, &endpos) >= 1
&& endpos>2 && isalpha(parsendstrbuf[0])
&& !strncmp(parsendstrbuf, endstrbuf, HCVSTATE_ENDLABEL_LEN))
{
// end of chunk
resultmap.insert({chunkname,chunkbody});
state = HCVCHUNKSTATE_SKIPSPACES;
continue;
}
else
{
chunkbody += linbuf;
#warning should hcv_parse_chunk_map add a newline here to chunkbody?
continue;
}
//
}
// not implemented
////
#warning very incomplete hcv_parse_chunk_map
case HCVCHUNKSTATE__NONE:
HCV_FATALOUT("hcv_parse_chunk_map line#" << lincnt
<< " unimplemented state=" << statenames[state]
<< " line:" << linbuf);
};
}; // end for linbuf....
HCV_DEBUGOUT("hcv_parse_chunk_map end filepath=" << filepath
<< " with " << resultmap.size() << " keys");
if (state != HCVCHUNKSTATE_SKIPSPACES)
HCV_SYSLOGOUT(LOG_WARNING, "hcv_parse_chunk_map line#" << lincnt
<< " of " << filepath
<< " is not a space line, in state " << statenames[state]
<< std::endl);
return resultmap;
} // end hcv_parse_chunk_map
////////////////////////////////////////////////////////////////
/******* global chunk map *********/
std::mutex hcv_globchunkmap_mtx;
std::map<std::string,std::string> hcv_globchunk_map;
void
hcv_add_chunkmap_file(const std::string& filepath)
{
if (filepath.empty())
HCV_FATALOUT("hcv_add_chunkmap_file got empty path");
if (access(filepath.c_str(), R_OK))
HCV_FATALOUT("hcv_add_chunkmap_file got unreadable file "
<< Glib::shell_quote(filepath));
if (!isalnum(filepath[filepath.size()-1]))
HCV_FATALOUT("hcv_add_chunkmap_file with file path "
<< Glib::shell_quote(filepath) << " not ending with letter or digit");
HCV_DEBUGOUT("hcv_add_chunkmap_file should parse "
<< Glib::shell_quote(filepath));
std::map<std::string,std::string> gotmap = hcv_parse_chunk_map(filepath);
HCV_DEBUGOUT("hcv_add_chunkmap_file parsed "
<< Glib::shell_quote(filepath)
<< " with " << gotmap.size() << " entries.");
std::lock_guard<std::mutex> gu(hcv_globchunkmap_mtx);
hcv_globchunk_map.merge(gotmap);
HCV_DEBUGOUT("hcv_add_chunkmap_file merged "
<< Glib::shell_quote(filepath)
<< " so cumulating " << gotmap.size() << " entries.");
} // end hcv_add_chunkmap_file
const std::string
hcv_get_chunkmap_entry(const std::string&chunkname)
{
if (chunkname.empty())
return "";
std::lock_guard<std::mutex> gu(hcv_globchunkmap_mtx);
auto it = hcv_globchunk_map.find(chunkname);
if (it != hcv_globchunk_map.end())
return it->second;
return "";
} // end hcv_get_chunkmap_entry
/// end of file hcv_chunkmap.cc