summaryrefslogtreecommitdiffstats
path: root/src/corelib/global/qlogging.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/global/qlogging.cpp')
-rw-r--r--src/corelib/global/qlogging.cpp313
1 files changed, 312 insertions, 1 deletions
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp
index bc26c9b6b7..39b3205571 100644
--- a/src/corelib/global/qlogging.cpp
+++ b/src/corelib/global/qlogging.cpp
@@ -39,7 +39,13 @@
**
****************************************************************************/
-#include <qlogging.h>
+#include "qlogging.h"
+#include "qlist.h"
+#include "qbytearray.h"
+#include "qstring.h"
+#include "qvarlengtharray.h"
+
+#include <stdio.h>
QT_BEGIN_NAMESPACE
@@ -73,4 +79,309 @@ QT_BEGIN_NAMESPACE
\sa QMessageLogContext, qDebug(), qWarning(), qCritical(), qFatal()
*/
+/*!
+ \internal
+*/
+Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info)
+{
+ // Strip the function info down to the base function name
+ // note that this throws away the template definitions,
+ // the parameter types (overloads) and any const/volatile qualifiers.
+
+ if (info.isEmpty())
+ return info;
+
+ int pos;
+
+ // skip trailing [with XXX] for templates (gcc)
+ pos = info.size() - 1;
+ if (info.endsWith(']')) {
+ while (--pos) {
+ if (info.at(pos) == '[')
+ info.truncate(pos);
+ }
+ }
+
+ // operator names with '(', ')', '<', '>' in it
+ static const char operator_call[] = "operator()";
+ static const char operator_lessThan[] = "operator<";
+ static const char operator_greaterThan[] = "operator>";
+ static const char operator_lessThanEqual[] = "operator<=";
+ static const char operator_greaterThanEqual[] = "operator>=";
+
+ // canonize operator names
+ info.replace("operator ", "operator");
+
+ // remove argument list
+ forever {
+ int parencount = 0;
+ pos = info.lastIndexOf(')');
+ if (pos == -1) {
+ // Don't know how to parse this function name
+ return info;
+ }
+
+ // find the beginning of the argument list
+ --pos;
+ ++parencount;
+ while (pos && parencount) {
+ if (info.at(pos) == ')')
+ ++parencount;
+ else if (info.at(pos) == '(')
+ --parencount;
+ --pos;
+ }
+ if (parencount != 0)
+ return info;
+
+ info.truncate(++pos);
+
+ if (info.at(pos - 1) == ')') {
+ if (info.indexOf(operator_call) == pos - (int)strlen(operator_call))
+ break;
+
+ // this function returns a pointer to a function
+ // and we matched the arguments of the return type's parameter list
+ // try again
+ info.remove(0, info.indexOf('('));
+ info.chop(1);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ // find the beginning of the function name
+ int parencount = 0;
+ int templatecount = 0;
+ --pos;
+
+ // make sure special characters in operator names are kept
+ if (pos > -1) {
+ switch (info.at(pos)) {
+ case ')':
+ if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1)
+ pos -= 2;
+ break;
+ case '<':
+ if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1)
+ --pos;
+ break;
+ case '>':
+ if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1)
+ --pos;
+ break;
+ case '=': {
+ int operatorLength = (int)strlen(operator_lessThanEqual);
+ if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
+ pos -= 2;
+ else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
+ pos -= 2;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ while (pos > -1) {
+ if (parencount < 0 || templatecount < 0)
+ return info;
+
+ char c = info.at(pos);
+ if (c == ')')
+ ++parencount;
+ else if (c == '(')
+ --parencount;
+ else if (c == '>')
+ ++templatecount;
+ else if (c == '<')
+ --templatecount;
+ else if (c == ' ' && templatecount == 0 && parencount == 0)
+ break;
+
+ --pos;
+ }
+ info = info.mid(pos + 1);
+
+ // we have the full function name now.
+ // clean up the templates
+ while ((pos = info.lastIndexOf('>')) != -1) {
+ if (!info.contains('<'))
+ break;
+
+ // find the matching close
+ int end = pos;
+ templatecount = 1;
+ --pos;
+ while (pos && templatecount) {
+ register char c = info.at(pos);
+ if (c == '>')
+ ++templatecount;
+ else if (c == '<')
+ --templatecount;
+ --pos;
+ }
+ ++pos;
+ info.remove(pos, end - pos + 1);
+ }
+
+ return info;
+}
+
+// tokens as recognized in QT_MESSAGE_PATTERN
+static const char typeTokenC[] = "%{type}";
+static const char messageTokenC[] = "%{message}";
+static const char fileTokenC[] = "%{file}";
+static const char lineTokenC[] = "%{line}";
+static const char functionTokenC[] = "%{function}";
+static const char emptyTokenC[] = "";
+
+struct QMessagePattern {
+ QMessagePattern();
+ ~QMessagePattern();
+
+ // 0 terminated arrays of literal tokens / literal or placeholder tokens
+ const char **literals;
+ const char **tokens;
+};
+
+QMessagePattern::QMessagePattern()
+{
+ QString pattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
+ if (pattern.isEmpty()) {
+ pattern = QLatin1String("%{message}");
+ }
+
+ // scanner
+ QList<QString> lexemes;
+ QString lexeme;
+ bool inPlaceholder = false;
+ for (int i = 0; i < pattern.size(); ++i) {
+ const QChar c = pattern.at(i);
+ if ((c == QLatin1Char('%'))
+ && !inPlaceholder) {
+ if ((i + 1 < pattern.size())
+ && pattern.at(i + 1) == QLatin1Char('{')) {
+ // beginning of placeholder
+ if (!lexeme.isEmpty()) {
+ lexemes.append(lexeme);
+ lexeme.clear();
+ }
+ inPlaceholder = true;
+ }
+ }
+
+ lexeme.append(c);
+
+ if ((c == QLatin1Char('}') && inPlaceholder)) {
+ // end of placeholder
+ lexemes.append(lexeme);
+ lexeme.clear();
+ inPlaceholder = false;
+ }
+ }
+ if (!lexeme.isEmpty())
+ lexemes.append(lexeme);
+
+ // tokenizer
+ QVarLengthArray<const char*> literalsVar;
+ tokens = new const char*[lexemes.size() + 1];
+ tokens[lexemes.size()] = 0;
+
+ for (int i = 0; i < lexemes.size(); ++i) {
+ const QString lexeme = lexemes.at(i);
+ if (lexeme.startsWith(QLatin1String("%{"))
+ && lexeme.endsWith(QLatin1Char('}'))) {
+ // placeholder
+ if (lexeme == QLatin1String(typeTokenC)) {
+ tokens[i] = typeTokenC;
+ } else if (lexeme == QLatin1String(messageTokenC))
+ tokens[i] = messageTokenC;
+ else if (lexeme == QLatin1String(fileTokenC))
+ tokens[i] = fileTokenC;
+ else if (lexeme == QLatin1String(lineTokenC))
+ tokens[i] = lineTokenC;
+ else if (lexeme == QLatin1String(functionTokenC))
+ tokens[i] = functionTokenC;
+ else {
+ fprintf(stderr, "%s\n",
+ QString::fromLatin1("QT_MESSAGE_PATTERN: Unknown placeholder %1\n"
+ ).arg(lexeme).toLocal8Bit().constData());
+ fflush(stderr);
+ tokens[i] = emptyTokenC;
+ }
+ } else {
+ char *literal = new char[lexeme.size() + 1];
+ strncpy(literal, lexeme.toLocal8Bit().constData(), lexeme.size());
+ literal[lexeme.size()] = '\0';
+ literalsVar.append(literal);
+ tokens[i] = literal;
+ }
+ }
+ literals = new const char*[literalsVar.size() + 1];
+ literals[literalsVar.size()] = 0;
+ memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*));
+}
+
+QMessagePattern::~QMessagePattern()
+{
+ for (int i = 0; literals[i] != 0; ++i)
+ delete [] literals[i];
+ delete [] literals;
+ literals = 0;
+ delete [] tokens;
+ tokens = 0;
+}
+
+Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
+
+/*!
+ \internal
+*/
+Q_CORE_EXPORT QByteArray qMessageFormatString(QtMsgType type, const QMessageLogContext &context,
+ const char *str)
+{
+ QByteArray message;
+
+ QMessagePattern *pattern = qMessagePattern();
+ if (!pattern) {
+ // after destruction of static QMessagePattern instance
+ message.append(str);
+ message.append('\n');
+ return message;
+ }
+
+ // we do not convert file, function, line literals to local encoding due to overhead
+ for (int i = 0; pattern->tokens[i] != 0; ++i) {
+ const char *token = pattern->tokens[i];
+ if (token == messageTokenC) {
+ message.append(str);
+ } else if (token == typeTokenC) {
+ switch (type) {
+ case QtDebugMsg: message.append("debug"); break;
+ case QtWarningMsg: message.append("warning"); break;
+ case QtCriticalMsg:message.append("critical"); break;
+ case QtFatalMsg: message.append("fatal"); break;
+ }
+ } else if (token == fileTokenC) {
+ if (context.file)
+ message.append(context.file);
+ else
+ message.append("unknown");
+ } else if (token == lineTokenC) {
+ message.append(QString::number(context.line).toLatin1().constData());
+ } else if (token == functionTokenC) {
+ if (context.function)
+ message.append(qCleanupFuncinfo(context.function));
+ else
+ message.append("unknown");
+ } else {
+ message.append(token);
+ }
+ }
+ message.append('\n');
+ return message;
+}
+
QT_END_NAMESPACE