1 //=============================================================================
2 //
3 // File : KviKvsReport.cpp
4 // Creation date : Thu 25 Sep 2003 05.12 CEST by Szymon Stefanek
5 //
6 // This file is part of the KVIrc IRC client distribution
7 // Copyright (C) 2003-2010 Szymon Stefanek (pragma at kvirc dot net)
8 //
9 // This program is FREE software. You can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the HOPE that it will be USEFUL,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 // See the GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, write to the Free Software Foundation,
21 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24
25 #include "KviKvsReport.h"
26
27 #include <utility>
28 #include "KviControlCodes.h"
29 #include "KviWindow.h"
30 #include "kvi_out.h"
31 #include "KviApplication.h"
32 #include "KviLocale.h"
33 #include "KviDebugWindow.h"
34 #include "KviOptions.h"
35
KviKvsReport(Type t,QString szContext,QString szMessage,QString szLocation,KviWindow * pWindow)36 KviKvsReport::KviKvsReport(Type t, QString szContext, QString szMessage, QString szLocation, KviWindow * pWindow)
37 : m_eType(t)
38 , m_szContext(std::move(szContext))
39 , m_szMessage(std::move(szMessage))
40 , m_szLocation(std::move(szLocation))
41 , m_pWindow(pWindow)
42 {
43 m_pCallStack = nullptr;
44 m_pCodeListing = nullptr;
45 }
46
~KviKvsReport()47 KviKvsReport::~KviKvsReport()
48 {
49 if(m_pCallStack)
50 delete m_pCallStack;
51 if(m_pCodeListing)
52 delete m_pCodeListing;
53 }
54
findLineAndCol(const QChar * pBegin,const QChar * pPoint,int & iLine,int & iCol)55 void KviKvsReport::findLineAndCol(const QChar * pBegin, const QChar * pPoint, int & iLine, int & iCol)
56 {
57 iLine = 1;
58
59 const QChar * pLineBegin = pBegin;
60
61 unsigned short us = pBegin->unicode();
62
63 while(us && (pBegin < pPoint))
64 {
65 if(us == '\n')
66 {
67 pBegin++;
68 pLineBegin = pBegin;
69 iLine++;
70 }
71 else
72 {
73 pBegin++;
74 }
75 us = pBegin->unicode();
76 }
77
78 iCol = (pBegin - pLineBegin) + 1;
79 }
80
findLineColAndListing(const QChar * pBegin,const QChar * pPoint,int & iLine,int & iCol,KviPointerList<QString> * pListing)81 void KviKvsReport::findLineColAndListing(const QChar * pBegin, const QChar * pPoint, int & iLine, int & iCol, KviPointerList<QString> * pListing)
82 {
83 iLine = 1;
84
85 const QChar * pBufferBegin = pBegin;
86 const QChar * pPrevLine = nullptr;
87 const QChar * pLineBegin = pBegin;
88
89 unsigned short us = pBegin->unicode();
90
91 while(us && (pBegin < pPoint))
92 {
93 if(us == '\n')
94 {
95 pPrevLine = pLineBegin;
96 pBegin++;
97 pLineBegin = pBegin;
98 iLine++;
99 }
100 else
101 {
102 pBegin++;
103 }
104 us = pBegin->unicode();
105 }
106
107 iCol = (pBegin - pLineBegin) + 1;
108
109 // previous line
110 if(pPrevLine)
111 {
112 // there would be yet another line before
113 if(pPrevLine > pBufferBegin)
114 {
115 QString * pListingStrZ = new QString(QString("%1 ...").arg(iLine - 2));
116 pListing->append(pListingStrZ);
117 }
118
119 QString * pListingStr = new QString(QString("%1 ").arg(iLine - 1));
120 *pListingStr += QString(pPrevLine, pLineBegin - pPrevLine);
121 pListingStr->replace("\n", "");
122 pListing->append(pListingStr);
123 }
124
125 // current line
126 pBegin = pLineBegin;
127
128 us = pBegin->unicode();
129 while(us && (us != '\n'))
130 {
131 pBegin++;
132 us = pBegin->unicode();
133 }
134 if(us)
135 pBegin++;
136
137 {
138 QString * pListingStr = new QString(QString("%1%2 ").arg(QChar(KviControlCodes::Bold)).arg(iLine));
139 *pListingStr += QString(pLineBegin, pBegin - pLineBegin);
140 pListingStr->replace("\n", "");
141 pListing->append(pListingStr);
142 }
143
144 if(us)
145 {
146 // next line
147 pLineBegin = pBegin;
148
149 us = pBegin->unicode();
150 while(us && (us != '\n'))
151 {
152 pBegin++;
153 us = pBegin->unicode();
154 }
155 if(us)
156 pBegin++;
157
158 {
159 QString * pListingStr = new QString(QString("%1 ").arg(iLine + 1));
160 *pListingStr += QString(pLineBegin, pBegin - pLineBegin);
161 pListingStr->replace("\n", "");
162 pListing->append(pListingStr);
163 }
164
165 // there would be yet another line
166 if(us)
167 {
168 QString * pListingStr = new QString(QString("%1 ...").arg(iLine + 2));
169 pListing->append(pListingStr);
170 }
171 }
172 }
173
174 //
175 // ERROR REPORTING
176 //
177
report(KviKvsReport * r,KviWindow * pOutput)178 void KviKvsReport::report(KviKvsReport * r, KviWindow * pOutput)
179 {
180 if(!pOutput)
181 return; // ?
182
183 if(!g_pApp->windowExists(pOutput))
184 {
185 if(KVI_OPTION_BOOL(KviOption_boolScriptErrorsToDebugWindow))
186 {
187 // rethrow to the debug window
188 report(r, KviDebugWindow::getInstance());
189 } // else window lost: unrecoverable
190 return;
191 }
192
193 // make sure that the output window still exists!
194
195 int out = 0;
196
197 switch(r->type())
198 {
199 case KviKvsReport::ParserWarning:
200 out = KVI_OUT_PARSERWARNING;
201 pOutput->output(out, __tr2qs_ctx("[KVS]%c Warning: %Q", "kvs"), KviControlCodes::Bold, &(r->message()));
202 break;
203 case KviKvsReport::ParserError:
204 out = KVI_OUT_PARSERERROR;
205 pOutput->output(out, __tr2qs_ctx("[KVS]%c Compilation error: %Q", "kvs"), KviControlCodes::Bold, &(r->message()));
206 break;
207 case KviKvsReport::RunTimeWarning:
208 out = KVI_OUT_PARSERWARNING;
209 pOutput->output(out, __tr2qs_ctx("[KVS]%c Warning: %Q", "kvs"), KviControlCodes::Bold, &(r->message()));
210 break;
211 case KviKvsReport::RunTimeError:
212 out = KVI_OUT_PARSERERROR;
213 pOutput->output(out, __tr2qs_ctx("[KVS]%c Runtime error: %Q", "kvs"), KviControlCodes::Bold, &(r->message()));
214 break;
215 }
216
217 if(r->location().isEmpty())
218 pOutput->output(out, __tr2qs_ctx("[KVS] In script context \"%Q\"", "kvs"), &(r->context()));
219 else
220 pOutput->output(out, __tr2qs_ctx("[KVS] In script context \"%Q\", %Q", "kvs"), &(r->context()), &(r->location()));
221
222 if(pOutput == KviDebugWindow::instance())
223 {
224 KviPointerList<QString> * l;
225 if((l = r->codeListing()))
226 {
227 pOutput->outputNoFmt(out, __tr2qs_ctx("[KVS] Code listing:", "kvs"));
228 for(QString * s = l->first(); s; s = l->next())
229 pOutput->output(out, "[KVS] %Q", s);
230 }
231
232 pOutput->output(out, __tr2qs_ctx("[KVS] Window:", "kvs"));
233 if(g_pApp->windowExists(r->window()))
234 pOutput->output(out, "[KVS] %Q [ID: %u]", &(r->window()->windowName()), r->window()->numericId());
235 else
236 pOutput->output(out, __tr2qs_ctx("[KVS] Destroyed window with pointer %x", "kvs"), r->window());
237
238 if((l = r->callStack()))
239 {
240 pOutput->outputNoFmt(out, __tr2qs_ctx("[KVS] Call stack:", "kvs"));
241 for(QString * s = l->first(); s; s = l->next())
242 pOutput->output(out, "[KVS] %Q", s);
243 }
244
245 pOutput->outputNoFmt(out, "[KVS]");
246 }
247 else
248 {
249 if(KVI_OPTION_BOOL(KviOption_boolScriptErrorsToDebugWindow))
250 {
251 // rethrow to the debug window
252 if(pOutput != KviDebugWindow::getInstance())
253 report(r, KviDebugWindow::getInstance());
254 }
255 }
256 }
257