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