Use QFileInfo to get the file's name instead of parsing the path manually.
[kdbg.git] / kdbg / typetable.cpp
blob502c97629b9dfb993aa9552d0c6a605b36db42bc
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 <qdir.h>
8 #include <qptrlist.h>
9 #include <kglobal.h>
10 #include <kstandarddirs.h>
11 #include <ksimpleconfig.h>
12 #include <algorithm>
13 #include <iterator>
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17 #include "typetable.h"
18 #include "mydebug.h"
20 //! the TypeTables of all known libraries
21 static QList<TypeTable> typeTables;
22 bool typeTablesInited = false;
25 //! an indentifier for wchar_t
26 TypeInfo TypeInfo::m_wchartType("");
27 //! the unknown type
28 TypeInfo TypeInfo::m_unknownType("");
31 void TypeTable::initTypeLibraries()
33 if (!typeTablesInited) {
34 TypeTable::loadTypeTables();
38 void TypeTable::loadTypeTables()
40 typeTablesInited = true;
42 const QStringList files = KGlobal::dirs()->findAllResources("types", "*.kdbgtt",
43 false, true);
45 if (files.isEmpty()) {
46 TRACE("no type tables found");
47 return;
50 QString fileName;
51 for (QValueListConstIterator<QString> p = files.begin(); p != files.end(); ++p) {
52 fileName = *p;
53 TypeTable* newTable = new TypeTable;
54 newTable->loadFromFile(fileName);
55 typeTables.append(newTable);
60 TypeTable::TypeTable() :
61 m_printQStringDataCmd(0)
63 m_typeDict.setAutoDelete(true);
64 // aliasDict keeps only pointers to items into typeDict
65 m_aliasDict.setAutoDelete(false);
68 TypeTable::~TypeTable()
70 delete[] m_printQStringDataCmd;
71 while (!m_templates.empty()) {
72 delete m_templates.begin()->second;
73 m_templates.erase(m_templates.begin());
78 static const char TypeTableGroup[] = "Type Table";
79 static const char LibDisplayName[] = "LibDisplayName";
80 static const char ShlibRE[] = "ShlibRE";
81 static const char EnableBuiltin[] = "EnableBuiltin";
82 static const char PrintQStringCmd[] = "PrintQStringCmd";
83 static const char TypesEntryFmt[] = "Types%d";
84 static const char DisplayEntry[] = "Display";
85 static const char AliasEntry[] = "Alias";
86 static const char TemplateEntry[] = "Template";
87 static const char ExprEntryFmt[] = "Expr%d";
88 static const char FunctionGuardEntryFmt[] = "FunctionGuard%d";
91 void TypeTable::loadFromFile(const QString& fileName)
93 TRACE("reading file " + fileName);
94 KSimpleConfig cf(fileName, true); /* read-only */
97 * Read library name and properties.
99 cf.setGroup(TypeTableGroup);
100 m_displayName = cf.readEntry(LibDisplayName);
101 if (m_displayName.isEmpty()) {
102 // use file name instead
103 QFileInfo fi(fileName);
104 m_displayName = fi.baseName(true);
107 m_shlibNameRE = QRegExp(cf.readEntry(ShlibRE));
108 m_enabledBuiltins = cf.readListEntry(EnableBuiltin);
110 QString printQString = cf.readEntry(PrintQStringCmd);
111 const char* ascii = printQString.ascii();
112 if (ascii == 0)
113 ascii = "";
114 m_printQStringDataCmd = new char[strlen(ascii)+1];
115 strcpy(m_printQStringDataCmd, ascii);
118 * Get the types. We search for entries of kind Types1, Types2, etc.
119 * because a single entry Types could get rather long for large
120 * libraries.
122 QString typesEntry;
123 for (int i = 1; ; i++) {
124 // next bunch of types
125 cf.setGroup(TypeTableGroup);
126 typesEntry.sprintf(TypesEntryFmt, i);
127 if (!cf.hasKey(typesEntry))
128 break;
130 QStringList typeNames = cf.readListEntry(typesEntry, ',');
132 // now read them
133 QString alias;
134 for (QStringList::iterator it = typeNames.begin(); it != typeNames.end(); ++it)
136 cf.setGroup(*it);
137 // check if this is an alias
138 alias = cf.readEntry(AliasEntry);
139 if (alias.isEmpty()) {
140 readType(cf, *it);
141 } else {
142 // look up the alias type and insert it
143 TypeInfo* info = m_typeDict[alias];
144 if (info == 0) {
145 TRACE(*it + ": alias " + alias + " not found");
146 } else {
147 m_aliasDict.insert(alias, info);
148 TRACE(*it + ": alias " + alias);
152 } // for all Types%d
155 void TypeTable::readType(KConfigBase& cf, const QString& type)
157 // the display string
158 QString expr = cf.readEntry(DisplayEntry);
160 TypeInfo* info = new TypeInfo(expr);
161 if (info->m_numExprs == 0) {
162 TRACE("bogus type " + type + ": no %% in Display: " + expr);
163 delete info;
164 return;
167 info->m_templatePattern = cf.readEntry(TemplateEntry);
169 // Expr1, Expr2, etc...
170 QString exprEntry;
171 QString funcGuardEntry;
172 for (int j = 0; j < info->m_numExprs; j++) {
173 exprEntry.sprintf(ExprEntryFmt, j+1);
174 expr = cf.readEntry(exprEntry);
175 info->m_exprStrings[j] = expr;
177 funcGuardEntry.sprintf(FunctionGuardEntryFmt, j+1);
178 expr = cf.readEntry(funcGuardEntry);
179 info->m_guardStrings[j] = expr;
182 // add the new type
183 if (info->m_templatePattern.find('<') < 0)
184 m_typeDict.insert(type, info);
185 else
186 m_templates[type] = info;
187 TRACE(type + QString().sprintf(": %d exprs", info->m_numExprs));
190 void TypeTable::copyTypes(QDict<TypeInfo>& dict)
192 for (QDictIterator<TypeInfo> it = m_typeDict; it != 0; ++it) {
193 dict.insert(it.currentKey(), it);
195 for (QDictIterator<TypeInfo> it = m_aliasDict; it != 0; ++it) {
196 dict.insert(it.currentKey(), it);
200 bool TypeTable::isEnabledBuiltin(const QString& feature) const
202 return m_enabledBuiltins.find(feature) != m_enabledBuiltins.end();
205 TypeInfo::TypeInfo(const QString& displayString)
207 // decompose the input into the parts
208 int i = 0;
209 int startIdx = 0;
210 int idx;
211 while (i < typeInfoMaxExpr &&
212 (idx = displayString.find('%', startIdx)) >= 0)
214 m_displayString[i] = displayString.mid(startIdx, idx-startIdx);
215 startIdx = idx+1;
216 i++;
218 m_numExprs = i;
220 * Remaining string; note that there's one more display string than
221 * sub-expressions.
223 m_displayString[i] = displayString.right(displayString.length()-startIdx);
226 TypeInfo::~TypeInfo()
231 ProgramTypeTable::ProgramTypeTable() :
232 m_parseQt2QStrings(false),
233 m_QCharIsShort(false),
234 m_printQStringDataCmd(0)
236 m_types.setAutoDelete(false); /* paranoia */
237 m_aliasDict.setAutoDelete(false); /* paranoia */
240 ProgramTypeTable::~ProgramTypeTable()
244 void ProgramTypeTable::loadTypeTable(TypeTable* table)
246 table->copyTypes(m_types);
248 // add templates
249 const TypeTable::TypeMap& t = table->templates();
250 std::transform(t.begin(), t.end(),
251 std::inserter(m_templates, m_templates.begin()),
252 std::ptr_fun(template2Info));
254 // check whether to enable builtin QString support
255 if (!m_parseQt2QStrings) {
256 m_parseQt2QStrings = table->isEnabledBuiltin("QString::Data");
258 if (!m_QCharIsShort) {
259 m_QCharIsShort = table->isEnabledBuiltin("QCharIsShort");
261 if (!m_printQStringDataCmd && *table->printQStringDataCmd()) {
262 m_printQStringDataCmd = table->printQStringDataCmd();
266 ProgramTypeTable::TemplateMap::value_type
267 ProgramTypeTable::template2Info(const TypeTable::TypeMap::value_type& tt)
269 QStringList args = splitTemplateArgs(tt.second->m_templatePattern);
271 TemplateMap::value_type result(args.front(), TemplateInfo());
272 result.second.type = tt.second;
273 args.pop_front();
274 result.second.templateArgs = args;
275 return result;
279 * Splits the name \a t into the template name and its arguments.
280 * The first entry of the returned list is the template name, the remaining
281 * entries are the arguments.
283 QStringList ProgramTypeTable::splitTemplateArgs(const QString& t)
285 QStringList result;
286 result.push_back(t);
288 int i = t.find('<');
289 if (i < 0)
290 return result;
292 // split off the template name
293 result.front().truncate(i);
295 i++; // skip '<'
296 // look for the next comma or the closing '>', skipping nested '<>'
297 int nest = 0;
298 int start = i;
299 for (; unsigned(i) < t.length() && nest >= 0; i++)
301 if (t[i] == '<')
302 nest++;
303 else if (t[i] == '>')
304 nest--;
305 else if (nest == 0 && t[i] == ',') {
306 // found end of argument
307 QString arg = t.mid(start, i-start);
308 result.push_back(arg);
309 start = i+1; // skip ','
312 // accept the template only if the closing '>' is the last character
313 if (nest < 0 && unsigned(i) == t.length()) {
314 QString arg = t.mid(start, i-start-1);
315 result.push_back(arg);
316 } else {
317 result.clear();
318 result.push_back(t);
320 return result;
323 TypeInfo* ProgramTypeTable::lookup(QString type)
326 * Registered aliases contain the complete template parameter list.
327 * Check for an alias first so that this case is out of the way.
329 if (TypeInfo* result = m_aliasDict[type])
330 return result;
333 * Check for a normal type. Even if type is a template instance,
334 * it could have been registered as a normal type instead of a pattern.
336 if (TypeInfo* result = m_types[type])
337 return result;
340 * The hard part: Look up a template.
342 QStringList parts = splitTemplateArgs(type);
343 if (parts.size() == 1)
344 return 0; // not a template
346 // We can have several patterns for the same template name.
347 std::pair<TemplateMap::const_iterator, TemplateMap::const_iterator> range =
348 m_templates.equal_range(parts.front());
349 // We pick the one that has the wildcards in the later parameters.
350 unsigned minPenalty = ~0U;
351 TypeInfo* result = 0;
352 parts.pop_front();
354 for (TemplateMap::const_iterator i = range.first; i != range.second; ++i)
356 const QStringList& pat = i->second.templateArgs;
357 if (parts.size() < pat.size())
358 continue; // too few arguments
360 // a "*" in the last position of the pattern matches all arguments
361 // at the end of the template's arguments
362 if (parts.size() > pat.size() && pat.back() != "*")
363 continue; // too many arguments and no wildcard
365 QStringList::const_iterator t = parts.begin();
366 QStringList::const_iterator p = pat.begin();
367 unsigned penalty = 0;
368 bool equal = true;
369 for (int j = 0; equal && p != pat.end(); ++p, ++t, ++j)
371 if (*p == "*")
372 penalty += 1U << j; // penalize wildcards
373 else
374 equal = *p == *t;
376 if (equal)
378 if (penalty == 0)
379 return i->second.type;
380 if (penalty < minPenalty)
381 result = i->second.type;
384 return result;
387 void ProgramTypeTable::registerAlias(const QString& name, TypeInfo* type)
389 ASSERT(lookup(name) == 0 || lookup(name) == type);
390 m_aliasDict.insert(name, type);
393 void ProgramTypeTable::loadLibTypes(const QStrList& libs)
395 QStrListIterator it = libs;
398 * We use a copy of the list of known libraries, from which we delete
399 * those libs that we already have added. This way we avoid to load a
400 * library twice.
402 QList<TypeTable> allTables = typeTables; /* shallow copy! */
403 allTables.setAutoDelete(false); /* important! */
405 for (; it && allTables.count() > 0; ++it)
407 // look up the library
408 repeatLookup:;
409 for (TypeTable* t = allTables.first(); t != 0; t = allTables.next())
411 if (t->matchFileName(it))
413 TRACE("adding types for " + QString(it));
414 loadTypeTable(t);
415 // remove the table
416 allTables.remove();
418 * continue the search (due to remove's unpredictable
419 * behavior of setting the current item we simply go
420 * through the whole list again)
422 goto repeatLookup;