1 : /*
2 : * Merge different vocabularies together and create the tag and facet indexes
3 : *
4 : * Copyright (C) 2003-2006 Enrico Zini <enrico@debian.org>
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 2 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, write to the Free Software
18 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 : */
20 :
21 :
22 : #include <ept/debtags/maint/vocabularymerger.h>
23 : #include <ept/debtags/maint/debdbparser.h>
24 :
25 : #include <cassert>
26 : #include <cstring>
27 :
28 : using namespace std;
29 : using namespace tagcoll;
30 :
31 : namespace ept {
32 : namespace debtags {
33 :
34 1432 : static void writeDebStyleField(FILE* out, const string& name, const string& val) throw ()
35 : {
36 1432 : fprintf(out, "%s: ", name.c_str());
37 :
38 : // Properly escape newlines
39 1432 : bool was_nl = false;
40 56225 : for (string::const_iterator s = val.begin(); s != val.end(); s++)
41 54793 : if (was_nl)
42 : // \n\n -> \n .\n
43 738 : if (*s == '\n')
44 : {
45 120 : fputc(' ', out);
46 120 : fputc('.', out);
47 120 : fputc(*s, out);
48 : }
49 : // \n([^ \t]) -> \n \1
50 618 : else if (*s != ' ' && *s != '\t')
51 : {
52 618 : fputc(' ', out);
53 618 : fputc(*s, out);
54 618 : was_nl = false;
55 : }
56 : // \n[ \t] goes unchanged
57 : else
58 : {
59 0 : fputc(*s, out);
60 0 : was_nl = false;
61 : }
62 : else
63 54055 : if (*s == '\n')
64 : {
65 618 : fputc(*s, out);
66 618 : was_nl = true;
67 : }
68 : else
69 53437 : fputc(*s, out);
70 :
71 1432 : fputc('\n', out);
72 1432 : }
73 :
74 1865 : VocabularyMerger::TagData& VocabularyMerger::FacetData::obtainTag(const std::string& name)
75 : {
76 1865 : std::map<std::string, TagData>::iterator i = tags.find(name);
77 1865 : if (i == tags.end())
78 : {
79 : // Create the tag if it's missing
80 1245 : pair<std::map<std::string, TagData>::iterator, bool> res = tags.insert(make_pair<std::string, TagData>(name, TagData()));
81 1245 : i = res.first;
82 1245 : i->second.name = name;
83 : }
84 1865 : return i->second;
85 : }
86 :
87 1953 : VocabularyMerger::FacetData& VocabularyMerger::obtainFacet(const std::string& name)
88 : {
89 1953 : std::map<std::string, FacetData>::iterator i = facets.find(name);
90 1953 : if (i == facets.end())
91 : {
92 : // Create the facet if it's missing
93 62 : pair<std::map<std::string, FacetData>::iterator, bool> res = facets.insert(make_pair<std::string, FacetData>(name, FacetData()));
94 62 : i = res.first;
95 62 : i->second.name = name;
96 : }
97 1953 : return i->second;
98 : }
99 :
100 1865 : VocabularyMerger::TagData& VocabularyMerger::obtainTag(const std::string& fullname)
101 : {
102 1865 : size_t p = fullname.find("::");
103 1865 : if (p == string::npos)
104 : {
105 28 : FacetData& facet = obtainFacet("legacy");
106 28 : return facet.obtainTag(fullname);
107 : } else {
108 1837 : FacetData& facet = obtainFacet(fullname.substr(0, p));
109 3674 : return facet.obtainTag(fullname.substr(p + 2));
110 : }
111 : }
112 :
113 :
114 7 : void VocabularyMerger::read(tagcoll::input::Input& input)
115 : {
116 7 : DebDBParser parser(input);
117 7 : DebDBParser::Record record;
118 :
119 1967 : while (parser.nextRecord(record))
120 : {
121 1953 : DebDBParser::Record::const_iterator fi = record.find("Facet");
122 3906 : DebDBParser::Record::const_iterator ti = record.find("Tag");
123 3906 : if (fi != record.end())
124 : {
125 : // Get the facet record
126 88 : FacetData& facet = obtainFacet(fi->second);
127 : //fprintf(stderr, "Read facet@%d %.*s\n", parser.lineNumber(), PFSTR(facet.name));
128 88 : assert(facet.name == fi->second);
129 :
130 : // Merge the data
131 416 : for (DebDBParser::Record::const_iterator i = record.begin();
132 : i != record.end(); i++)
133 328 : if (i->first != "Facet")
134 240 : facet[i->first] = i->second;
135 : }
136 1865 : else if (ti != record.end())
137 : {
138 : // Get the tag record
139 1865 : TagData& tag = obtainTag(ti->second);
140 : //fprintf(stderr, "Read tag@%d %.*s\n", parser.lineNumber(), PFSTR(tag.name));
141 : //assert(tag.name == ti->second);
142 :
143 : // Merge the data
144 5796 : for (DebDBParser::Record::const_iterator i = record.begin();
145 : i != record.end(); i++)
146 3931 : if (i->first != "Tag")
147 2066 : tag[i->first] = i->second;
148 : }
149 : else
150 : {
151 : fprintf(stderr, "%s:%d: Skipping record without Tag or Facet field\n",
152 0 : input.fileName().c_str(), input.lineNumber());
153 : }
154 7 : }
155 7 : }
156 :
157 0 : bool VocabularyMerger::hasTag(const std::string& fullname) const
158 : {
159 0 : size_t p = fullname.find("::");
160 0 : std::string facetName;
161 0 : std::string tagName;
162 0 : if (p == string::npos)
163 : {
164 0 : facetName = "legacy";
165 0 : tagName = fullname;
166 : } else {
167 0 : facetName = fullname.substr(0, p);
168 0 : tagName = fullname.substr(p + 2);
169 : }
170 :
171 0 : std::map<std::string, FacetData>::const_iterator i = facets.find(facetName);
172 0 : if (i == facets.end())
173 0 : return false;
174 0 : return i->second.tags.find(tagName) != i->second.tags.end();
175 : }
176 :
177 0 : int VocabularyMerger::tagID(const std::string& fullname) const
178 : {
179 0 : size_t p = fullname.find("::");
180 0 : std::string facetName;
181 0 : std::string tagName;
182 0 : if (p == string::npos)
183 : {
184 0 : facetName = "legacy";
185 0 : tagName = fullname;
186 : } else {
187 0 : facetName = fullname.substr(0, p);
188 0 : tagName = fullname.substr(p + 2);
189 : }
190 :
191 0 : std::map<std::string, FacetData>::const_iterator i = facets.find(facetName);
192 0 : if (i == facets.end())
193 0 : return -1;
194 0 : std::map<std::string, TagData>::const_iterator j = i->second.tags.find(tagName);
195 0 : if (j == i->second.tags.end())
196 0 : return -1;
197 0 : return j->second.id;
198 : }
199 :
200 1 : std::set<std::string> VocabularyMerger::tagNames() const
201 : {
202 1 : set<string> res;
203 30 : for (std::map<std::string, FacetData>::const_iterator f = facets.begin(); f != facets.end(); f++)
204 649 : for (std::map<std::string, TagData>::const_iterator t = f->second.tags.begin();
205 : t != f->second.tags.end(); t++)
206 620 : res.insert(f->first + "::" + t->first);
207 0 : return res;
208 : }
209 :
210 4 : void VocabularyMerger::write(const std::string& fname)
211 : {
212 4 : FILE* out = fopen(fname.c_str(), "wt");
213 4 : if (!out)
214 0 : throw wibble::exception::File(fname, "cept_debtags_vocabularymerger:reating file ");
215 4 : write(out);
216 4 : fclose(out);
217 4 : }
218 :
219 4 : void VocabularyMerger::write(FILE* out)
220 : {
221 4 : long start_ofs = ftell(out);
222 4 : int facetid = 0;
223 4 : int tagid = 0;
224 :
225 : //fprintf(stderr, "Write\n");
226 37 : for (std::map<std::string, FacetData>::iterator f = facets.begin(); f != facets.end(); f++)
227 : {
228 33 : f->second.id = facetid++;
229 : //fprintf(stderr, "Writing facet %.*s\n", PFSTR(f->first));
230 33 : f->second.ofs = ftell(out) - start_ofs;
231 33 : writeDebStyleField(out, "Facet", f->first);
232 115 : for (std::map<std::string, std::string>::const_iterator j = f->second.begin();
233 : j != f->second.end(); j++)
234 82 : writeDebStyleField(out, j->first, j->second);
235 33 : fputc('\n', out);
236 33 : f->second.len = ftell(out) - f->second.ofs;
237 :
238 658 : for (std::map<std::string, TagData>::iterator t = f->second.tags.begin();
239 : t != f->second.tags.end(); t++)
240 : {
241 625 : t->second.id = tagid++;
242 : //fprintf(stderr, "Writing tag %.*s\n", PFSTR(t->first));
243 625 : t->second.ofs = ftell(out) - start_ofs;
244 625 : writeDebStyleField(out, "Tag", f->first + "::" + t->first);
245 1317 : for (std::map<std::string, std::string>::const_iterator j = t->second.begin();
246 : j != t->second.end(); j++)
247 692 : writeDebStyleField(out, j->first, j->second);
248 625 : fputc('\n', out);
249 625 : t->second.len = ftell(out) - t->second.ofs;
250 : }
251 : }
252 :
253 4 : tagCount = tagid;
254 4 : }
255 :
256 :
257 4 : int VocabularyMerger::FacetIndexer::encodedSize() const
258 : {
259 : // First the main offset table
260 4 : int size = vm.facets.size() * sizeof(int);
261 :
262 37 : for (std::map<std::string, FacetData>::const_iterator f = vm.facets.begin(); f != vm.facets.end(); f++)
263 : {
264 : // offset of record in vocabulary
265 : // size of record in vocabulary
266 : // id of first tag
267 : // id of last tag
268 : // name (0-terminated)
269 33 : size += 4 * sizeof(int) + f->first.size() + 1;
270 :
271 : // Align to int boundaries
272 33 : if ((size % sizeof(int)) != 0)
273 25 : size = (size + sizeof(int)) / sizeof(int) * sizeof(int);
274 : }
275 :
276 4 : return tagcoll::diskindex::MMap::align(size);
277 : }
278 :
279 4 : void VocabularyMerger::FacetIndexer::encode(char* buf) const
280 : {
281 4 : int pos = vm.facets.size() * sizeof(int);
282 :
283 37 : for (std::map<std::string, FacetData>::const_iterator f = vm.facets.begin(); f != vm.facets.end(); f++)
284 : {
285 33 : ((int*)buf)[f->second.id] = pos;
286 :
287 : // offset of record in vocabulary
288 33 : *(int*)(buf+pos) = f->second.ofs;
289 33 : pos += sizeof(int);
290 :
291 : // size of record in vocabulary
292 33 : *(int*)(buf+pos) = f->second.len;
293 33 : pos += sizeof(int);
294 :
295 33 : if (f->second.tags.empty())
296 : {
297 : // id of first tag
298 1 : *(int*)(buf+pos) = -1;
299 1 : pos += sizeof(int);
300 :
301 : // id of last tag
302 1 : *(int*)(buf+pos) = -1;
303 1 : pos += sizeof(int);
304 : } else {
305 : // id of first tag
306 32 : *(int*)(buf+pos) = f->second.tags.begin()->second.id;
307 32 : pos += sizeof(int);
308 :
309 : // id of last tag
310 32 : *(int*)(buf+pos) = f->second.tags.rbegin()->second.id;
311 32 : pos += sizeof(int);
312 : }
313 :
314 : // name (0-terminated)
315 33 : memcpy(buf + pos, f->first.c_str(), f->first.size() + 1);
316 33 : pos += f->first.size() + 1;
317 :
318 : // Align to int boundaries
319 33 : if ((pos % sizeof(int)) != 0)
320 25 : pos = (pos + sizeof(int)) / sizeof(int) * sizeof(int);
321 : }
322 4 : }
323 :
324 4 : int VocabularyMerger::TagIndexer::encodedSize() const
325 : {
326 : // First the main offset table
327 4 : int size = vm.tagCount * sizeof(int);
328 :
329 37 : for (std::map<std::string, FacetData>::const_iterator f = vm.facets.begin(); f != vm.facets.end(); f++)
330 : {
331 658 : for (std::map<std::string, TagData>::const_iterator t = f->second.tags.begin();
332 : t != f->second.tags.end(); t++)
333 : {
334 : // offset of record in vocabulary
335 : // size of record in vocabulary
336 : // id of facet
337 : // name (0-terminated)
338 625 : size += 3 * sizeof(int) + f->first.size() + t->first.size() + 3;
339 :
340 : // Align to int boundaries
341 625 : if ((size % sizeof(int)) != 0)
342 479 : size = (size + sizeof(int)) / sizeof(int) * sizeof(int);
343 : }
344 : }
345 4 : return tagcoll::diskindex::MMap::align(size);
346 : }
347 :
348 4 : void VocabularyMerger::TagIndexer::encode(char* buf) const
349 : {
350 4 : int pos = vm.tagCount * sizeof(int);
351 :
352 37 : for (std::map<std::string, FacetData>::const_iterator f = vm.facets.begin(); f != vm.facets.end(); f++)
353 : {
354 658 : for (std::map<std::string, TagData>::const_iterator t = f->second.tags.begin();
355 : t != f->second.tags.end(); t++)
356 : {
357 625 : ((int*)buf)[t->second.id] = pos;
358 :
359 : // offset of record in vocabulary
360 625 : *(int*)(buf+pos) = t->second.ofs;
361 625 : pos += sizeof(int);
362 :
363 : // size of record in vocabulary
364 625 : *(int*)(buf+pos) = t->second.len;
365 625 : pos += sizeof(int);
366 :
367 : // id of facet
368 625 : *(int*)(buf+pos) = f->second.id;
369 625 : pos += sizeof(int);
370 :
371 : // name (0-terminated)
372 625 : string name = f->first + "::" + t->first;
373 625 : memcpy(buf + pos, name.c_str(), name.size() + 1);
374 625 : pos += name.size() + 1;
375 :
376 : // Align to int boundaries
377 625 : if ((pos % sizeof(int)) != 0)
378 479 : pos = (pos + sizeof(int)) / sizeof(int) * sizeof(int);
379 : }
380 : }
381 4 : }
382 :
383 : }
384 6 : }
385 :
386 : // vim:set ts=4 sw=4:
|