Access type tables using QStandardPaths.
[kdbg.git] / kdbg / typetable.cpp
blobecd14617da73535cecc6a5ac3b07af6e893f9452
1 /*
2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include "typetable.h"
8 #include <QDir>
9 #include <QFileInfo>
10 #include <QStandardPaths>
11 #include <kconfig.h>
12 #include <kconfiggroup.h>
13 #include <list>
14 #include <algorithm>
15 #include <iterator>
16 #include "mydebug.h"
18 //! the TypeTables of all known libraries
19 static std::list<TypeTable> typeTables;
20 bool typeTablesInited = false;
23 //! an indentifier for wchar_t
24 TypeInfo TypeInfo::m_wchartType("");
25 //! the unknown type
26 TypeInfo TypeInfo::m_unknownType("");
29 void TypeTable::initTypeLibraries()
31 if (!typeTablesInited) {
32 TypeTable::loadTypeTables();
36 void TypeTable::loadTypeTables()
38 typeTablesInited = true;
40 std::map<QString,QString> files;
41 for (const QString& dir: QStandardPaths::locateAll(QStandardPaths::AppDataLocation, "types",
42 QStandardPaths::LocateDirectory))
44 for (const QString& file: QDir(dir).entryList(QStringList() << QStringLiteral("*.kdbgtt")))
46 files.emplace(file, dir);
47 // this did not insert the entry if the same file name occurred earlier
48 // this is exactly what we want: earlier files have higher priority
52 if (files.empty()) {
53 TRACE("no type tables found");
54 return;
57 for (const auto& file: files) {
58 typeTables.push_back(TypeTable());
59 typeTables.back().loadFromFile(file.second + '/' + file.first);
64 TypeTable::TypeTable()
68 TypeTable::~TypeTable()
73 static const char TypeTableGroup[] = "Type Table";
74 static const char LibDisplayName[] = "LibDisplayName";
75 static const char ShlibRE[] = "ShlibRE";
76 static const char EnableBuiltin[] = "EnableBuiltin";
77 static const char PrintQStringCmd[] = "PrintQStringCmd";
78 static const char TypesEntryFmt[] = "Types%d";
79 static const char DisplayEntry[] = "Display";
80 static const char AliasEntry[] = "Alias";
81 static const char TemplateEntry[] = "Template";
82 static const char ExprEntryFmt[] = "Expr%d";
83 static const char FunctionGuardEntryFmt[] = "FunctionGuard%d";
86 void TypeTable::loadFromFile(const QString& fileName)
88 TRACE("reading file " + fileName);
89 KConfig confFile(fileName, KConfig::SimpleConfig);
92 * Read library name and properties.
94 KConfigGroup cf = confFile.group(TypeTableGroup);
95 m_displayName = cf.readEntry(LibDisplayName);
96 if (m_displayName.isEmpty()) {
97 // use file name instead
98 QFileInfo fi(fileName);
99 m_displayName = fi.completeBaseName();
102 m_shlibNameRE = QRegExp(cf.readEntry(ShlibRE));
103 m_enabledBuiltins = cf.readEntry(EnableBuiltin, QStringList());
105 QString printQString = cf.readEntry(PrintQStringCmd);
106 m_printQStringDataCmd = printQString.toAscii();
109 * Get the types. We search for entries of kind Types1, Types2, etc.
110 * because a single entry Types could get rather long for large
111 * libraries.
113 QString typesEntry;
114 for (int i = 1; ; i++) {
115 // next bunch of types
116 KConfigGroup cf = confFile.group(TypeTableGroup);
117 typesEntry.sprintf(TypesEntryFmt, i);
118 if (!cf.hasKey(typesEntry))
119 break;
121 QStringList typeNames = cf.readEntry(typesEntry, QStringList());
123 // now read them
124 QString alias;
125 for (QStringList::iterator it = typeNames.begin(); it != typeNames.end(); ++it)
127 KConfigGroup cf = confFile.group(*it);
128 // check if this is an alias
129 alias = cf.readEntry(AliasEntry);
130 if (alias.isEmpty()) {
131 readType(cf, *it);
132 } else {
133 // look up the alias type and insert it
134 TypeInfoMap::iterator i = m_typeDict.find(alias);
135 if (i == m_typeDict.end()) {
136 TRACE(*it + ": alias " + alias + " not found");
137 } else {
138 m_aliasDict.insert(std::make_pair(*it, &i->second));
139 TRACE(*it + ": alias " + alias);
143 } // for all Types%d
146 void TypeTable::readType(const KConfigGroup& cf, const QString& type)
148 // the display string
149 QString expr = cf.readEntry(DisplayEntry);
151 TypeInfo info(expr);
152 if (info.m_numExprs == 0) {
153 TRACE("bogus type " + type + ": no %% in Display: " + expr);
154 return;
157 info.m_templatePattern = cf.readEntry(TemplateEntry);
159 // Expr1, Expr2, etc...
160 QString exprEntry;
161 QString funcGuardEntry;
162 for (int j = 0; j < info.m_numExprs; j++)
164 exprEntry.sprintf(ExprEntryFmt, j+1);
165 expr = cf.readEntry(exprEntry);
166 info.m_exprStrings[j] = expr;
168 funcGuardEntry.sprintf(FunctionGuardEntryFmt, j+1);
169 expr = cf.readEntry(funcGuardEntry);
170 info.m_guardStrings[j] = expr;
173 // add the new type
174 if (info.m_templatePattern.indexOf('<') < 0)
175 m_typeDict.insert(std::make_pair(type, info));
176 else
177 m_templates.insert(std::make_pair(type, info));
178 TRACE(type + QString().sprintf(": %d exprs", info.m_numExprs));
181 void TypeTable::copyTypes(TypeInfoRefMap& dict)
183 for (TypeInfoMap::iterator i = m_typeDict.begin(); i != m_typeDict.end(); ++i) {
184 dict.insert(std::make_pair(i->first, &i->second));
186 std::copy(m_aliasDict.begin(), m_aliasDict.end(),
187 std::inserter(dict, dict.begin()));
190 bool TypeTable::isEnabledBuiltin(const QString& feature) const
192 return m_enabledBuiltins.indexOf(feature) >= 0;
195 TypeInfo::TypeInfo(const QString& displayString)
197 // decompose the input into the parts
198 int i = 0;
199 int startIdx = 0;
200 int idx;
201 while (i < typeInfoMaxExpr &&
202 (idx = displayString.indexOf('%', startIdx)) >= 0)
204 m_displayString[i] = displayString.mid(startIdx, idx-startIdx);
205 startIdx = idx+1;
206 i++;
208 m_numExprs = i;
210 * Remaining string; note that there's one more display string than
211 * sub-expressions.
213 m_displayString[i] = displayString.right(displayString.length()-startIdx);
216 TypeInfo::~TypeInfo()
221 ProgramTypeTable::ProgramTypeTable() :
222 m_parseQt2QStrings(false),
223 m_QCharIsShort(false),
224 m_printQStringDataCmd(0)
228 ProgramTypeTable::~ProgramTypeTable()
232 void ProgramTypeTable::loadTypeTable(TypeTable* table)
234 table->copyTypes(m_types);
236 // add templates
237 const TypeTable::TypeInfoMap& t = table->templates();
238 std::transform(t.begin(), t.end(),
239 std::inserter(m_templates, m_templates.begin()),
240 std::ptr_fun(template2Info));
242 // check whether to enable builtin QString support
243 if (!m_parseQt2QStrings) {
244 m_parseQt2QStrings = table->isEnabledBuiltin("QString::Data");
246 if (!m_QCharIsShort) {
247 m_QCharIsShort = table->isEnabledBuiltin("QCharIsShort");
249 if (m_printQStringDataCmd.isEmpty()) {
250 m_printQStringDataCmd = table->printQStringDataCmd();
254 ProgramTypeTable::TemplateMap::value_type
255 ProgramTypeTable::template2Info(const TypeTable::TypeInfoMap::value_type& tt)
257 QStringList args = splitTemplateArgs(tt.second.m_templatePattern);
259 TemplateMap::value_type result(args.front(), TemplateInfo());
260 result.second.type = &tt.second;
261 args.pop_front();
262 result.second.templateArgs = args;
263 return result;
267 * Splits the name \a t into the template name and its arguments.
268 * The first entry of the returned list is the template name, the remaining
269 * entries are the arguments.
271 QStringList ProgramTypeTable::splitTemplateArgs(const QString& t)
273 QStringList result;
274 result.push_back(t);
276 int i = t.indexOf('<');
277 if (i < 0)
278 return result;
280 // split off the template name
281 result.front().truncate(i);
283 i++; // skip '<'
284 // look for the next comma or the closing '>', skipping nested '<>'
285 int nest = 0;
286 int start = i;
287 for (; i < t.length() && nest >= 0; i++)
289 if (t[i] == '<')
290 nest++;
291 else if (t[i] == '>')
292 nest--;
293 else if (nest == 0 && t[i] == ',') {
294 // found end of argument
295 QString arg = t.mid(start, i-start);
296 result.push_back(arg);
297 start = i+1; // skip ','
300 // accept the template only if the closing '>' is the last character
301 if (nest < 0 && i == t.length()) {
302 QString arg = t.mid(start, i-start-1);
303 result.push_back(arg);
304 } else {
305 result.clear();
306 result.push_back(t);
308 return result;
311 const TypeInfo* ProgramTypeTable::lookup(QString type)
314 * Registered aliases contain the complete template parameter list.
315 * Check for an alias first so that this case is out of the way.
317 if (const TypeInfo* result = m_aliasDict[type])
318 return result;
321 * Check for a normal type. Even if type is a template instance,
322 * it could have been registered as a normal type instead of a pattern.
324 if (const TypeInfo* result = m_types[type])
325 return result;
328 * The hard part: Look up a template.
330 QStringList parts = splitTemplateArgs(type);
331 if (parts.size() == 1)
332 return 0; // not a template
334 // We can have several patterns for the same template name.
335 std::pair<TemplateMap::const_iterator, TemplateMap::const_iterator> range =
336 m_templates.equal_range(parts.front());
337 // We pick the one that has the wildcards in the later parameters.
338 unsigned minPenalty = ~0U;
339 const TypeInfo* result = 0;
340 parts.pop_front();
342 for (TemplateMap::const_iterator i = range.first; i != range.second; ++i)
344 const QStringList& pat = i->second.templateArgs;
345 if (parts.size() < pat.size())
346 continue; // too few arguments
348 // a "*" in the last position of the pattern matches all arguments
349 // at the end of the template's arguments
350 if (parts.size() > pat.size() && pat.back() != "*")
351 continue; // too many arguments and no wildcard
353 QStringList::const_iterator t = parts.begin();
354 QStringList::const_iterator p = pat.begin();
355 unsigned accumPenalty = 0;
356 bool equal = true;
357 unsigned penalty = ~(~0U>>1); // 1 in the leading bit
358 while (equal && p != pat.end())
360 if (*p == "*")
361 accumPenalty |= penalty; // penalize wildcards
362 else
363 equal = *p == *t;
364 ++p, ++t, penalty >>= 1;
366 if (equal)
368 if (accumPenalty == 0)
369 return i->second.type;
370 if (accumPenalty < minPenalty) {
371 result = i->second.type;
372 minPenalty = accumPenalty;
376 return result;
379 void ProgramTypeTable::registerAlias(const QString& name, const TypeInfo* type)
381 ASSERT(lookup(name) == 0 || lookup(name) == type);
382 m_aliasDict.insert(std::make_pair(name, type));
385 void ProgramTypeTable::loadLibTypes(const QStringList& libs)
387 for (QStringList::const_iterator it = libs.begin(); it != libs.end(); ++it)
389 // look up the library
390 for (std::list<TypeTable>::iterator t = typeTables.begin(); t != typeTables.end(); ++t)
392 if (t->matchFileName(*it))
394 TRACE("adding types for " + *it);
395 loadTypeTable(&*t);