-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhelp.cpp
311 lines (245 loc) · 7.95 KB
/
help.cpp
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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#include "help.h"
#include "stringfunc.h" // For no_caps()
#include "window.h" // For debugmsg()
// _type and _text default to ""
Help_article::Help_article(std::string _name, std::string _type,
std::string _text)
{
name = _name;
type = _type;
// Call set_text() so it can set up links
set_text(_text);
}
void Help_article::set_text(std::string _text)
{
text = _text;
// Search for links.
size_t next_link = 0;
do { // while (next_link != std::string::npos)
next_link = text.find("<link=", next_link);
if (next_link != std::string::npos) {
size_t target_start = next_link + 6;
size_t target_stop = text.find(">", target_start);
size_t link_stop = text.find("</link>", target_stop);
next_link = link_stop; // Next iteration will search after this point.
// Make sure our link is well-formed!
if (target_stop == std::string::npos) {
debugmsg("Unclosed <link> tag in help article '%s'.", name.c_str());
return;
} else if (link_stop == std::string::npos) {
debugmsg("<link> tag without matching </link> in help article '%s'.",
name.c_str());
return;
}
std::string link_target =
text.substr(target_start, target_stop - target_start);
std::string link_text =
text.substr(target_stop + 1, link_stop - target_stop - 1);
// Check if we already have that link.
bool has_link = false;
for (int i = 0; !has_link && i < links.size(); i++) {
if (no_caps(links[i].target) == no_caps(link_target)) {
has_link = true;
}
}
if (!has_link) {
links.push_back( Help_link( link_target, link_text ) );
}
} // if (next_link != std::string::npos)
} while (next_link != std::string::npos);
// Now we need to replace all the instances of "<link=...>" with "<c=ltblue>"
// and "</link>" with "<c=/>"
size_t open_link;
do {
open_link = text.find("<link");
if (open_link != std::string::npos) {
size_t link_end = text.find(">", open_link);
size_t length = link_end - open_link + 1;
text.replace(open_link, length, "<c=ltblue>");
}
} while (open_link != std::string::npos);
size_t close_link;
do {
close_link = text.find("</link>");
if (close_link != std::string::npos) {
text.replace(close_link, 7, "<c=/>");
}
} while (close_link != std::string::npos);
}
bool Help_article::is_redirect()
{
return !redirect.empty();
}
Help_database::Help_database()
{
}
Help_database::~Help_database()
{
for (std::map<std::string,Help_article*>::iterator it = articles.begin();
it != articles.end();
it++) {
delete (it->second);
}
}
bool Help_database::add_article(Help_article article)
{
Help_article* new_article = new Help_article;
*new_article = article;
return add_article(new_article);
}
bool Help_database::add_article(Help_article* article)
{
if (article->name.empty()) {
debugmsg("Attempted to add a nameless article to the help database!");
return false;
}
std::string lookup_name = no_caps(article->name);
if (articles.count(lookup_name)) {
debugmsg("Attempted to add article '%s' to the help database, but it \
already exists!", lookup_name.c_str());
return false;
}
articles[lookup_name] = article;
std::string category = no_caps(article->type);
return true;
}
void Help_database::process_categories()
{
categories.clear(); // Just in case
for (std::map<std::string,Help_article*>::iterator it = articles.begin();
it != articles.end();
it++) {
Help_article* article = it->second;
std::string category = no_caps(article->type);
if (!category.empty()) { // Redirect articles have no category
// Add the article to the category list, too
if (categories.count(category) == 0) {
// Make a new vector
std::vector<Help_article*> tmpvec;
tmpvec.push_back(article);
categories[category] = tmpvec;
} else {
categories[category].push_back(article);
}
}
}
}
int Help_database::num_articles()
{
return articles.size();
}
int Help_database::num_categories()
{
return categories.size();
}
// non_exact and content both default to true
std::vector<Help_result> Help_database::search(std::string term,
bool non_exact, bool content)
{
term = no_caps(term);
std::vector<Help_result> all_matches, title_matches, text_matches;
// First, check for an exact match.
if (articles.count(term)) {
Help_result exact_match( get_article_name(term), get_article_type(term),
HELP_RESULT_EXACT );
all_matches.push_back( exact_match );
}
// Next, check for articles where the title is a *partial* match, or the text
// matches.
if (non_exact || content) {
for (std::map<std::string,Help_article*>::iterator it = articles.begin();
it != articles.end();
it++) {
std::string lookup_name = it->first;
std::string* text = &(it->second->text);
if (it->first != term) { // Skip the exact match we already used.
if (non_exact && lookup_name.find(term) != std::string::npos) {
Help_result title_match( it->second->name, it->second->type,
HELP_RESULT_TITLE, it->second->is_redirect());
title_matches.push_back( title_match );
} else if (content && text->find(term) != std::string::npos) {
Help_result text_match( it->second->name, it->second->type,
HELP_RESULT_TEXT, it->second->is_redirect() );
text_matches.push_back( text_match );
}
}
}
// Now, put it all together in the same vector.
for (int i = 0; i < title_matches.size(); i++) {
all_matches.push_back( title_matches[i] );
}
for (int i = 0; i < text_matches.size(); i++) {
all_matches.push_back( text_matches[i] );
}
} // if (non_exact || content)
return all_matches;
}
std::string Help_database::get_article_name(std::string term)
{
term = no_caps(term);
if (articles.count(term)) {
return articles[term]->name;
}
return std::string();
}
std::string Help_database::get_article_type(std::string term)
{
// Use get_article() in case there's a redirect.
Help_article* article = get_article(term);
if (article) {
return article->type;
}
return std::string();
}
Help_article* Help_database::get_article(std::string term)
{
term = no_caps(term);
if (articles.count(term)) {
Help_article* ret = articles[term];
if (!ret->redirect.empty()) {
Help_article* redirect = get_article(ret->redirect);
// There may be multiple redirects!
while (redirect && !redirect->redirect.empty()) {
ret = redirect;
redirect = get_article(redirect->redirect);
}
// If redirect eventually becomes null, we'll use the last-non-null article
if (redirect) {
return redirect;
}
}
return ret;
}
return NULL;
}
std::vector<std::string> Help_database::get_categories()
{
std::vector<std::string> ret;
for (std::map< std::string,std::vector<Help_article*> >::iterator it =
categories.begin();
it != categories.end();
it++) {
ret.push_back( capitalize(it->first) );
}
return ret;
}
std::vector<Help_article*> Help_database::get_articles_in_category(std::string
category)
{
category = no_caps(category);
if (categories.count(category) == 0) { // Category does not exist!
return std::vector<Help_article*>();
}
return categories[category];
}
std::vector<std::string> Help_database::get_titles_in_category(std::string
category)
{
category = no_caps(category);
std::vector<std::string> ret;
std::vector<Help_article*> article_list = get_articles_in_category(category);
for (int i = 0; i < article_list.size(); i++) {
ret.push_back( article_list[i]->name );
}
return ret;
}