1 /***************************************************************************
2 rkrbackendprotocol - description
3 -------------------
4 begin : Thu Nov 04 2010
5 copyright : (C) 2010, 2011, 2013, 2017 by Thomas Friedrichsmeier
6 email : thomas.friedrichsmeier@kdemail.net
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "rkrbackendprotocol_shared.h"
19
20 #include "../debug.h"
21
RCommandProxy(const QString & command,int type)22 RCommandProxy::RCommandProxy (const QString &command, int type) {
23 RK_TRACE (RBACKEND);
24
25 RCommandProxy::command = command;
26 RCommandProxy::type = type;
27 id = -1;
28 status = 0;
29 }
30
~RCommandProxy()31 RCommandProxy::~RCommandProxy () {
32 RK_TRACE (RBACKEND);
33
34 RK_ASSERT ((type & RCommand::Internal) || (getDataType () == RData::NoData));
35 }
36
37
38 int RBackendRequest::_id = 0;
RBackendRequest(bool synchronous,RCallbackType type)39 RBackendRequest::RBackendRequest (bool synchronous, RCallbackType type) {
40 RK_TRACE (RBACKEND);
41
42 RBackendRequest::synchronous = synchronous;
43 RBackendRequest::type = type;
44 id = ++_id;
45 done = false;
46 command = 0;
47 output = 0;
48 }
49
~RBackendRequest()50 RBackendRequest::~RBackendRequest () {
51 RK_TRACE (RBACKEND);
52
53 delete command;
54 delete output;
55 };
56
mergeReply(RBackendRequest * reply)57 void RBackendRequest::mergeReply (RBackendRequest *reply) {
58 RK_TRACE (RBACKEND);
59
60 RK_ASSERT (reply->id == id);
61 command = reply->command;
62 params = reply->params;
63 output = reply->output;
64 reply->command = 0;
65 reply->output = 0;
66 }
67
duplicate()68 RBackendRequest* RBackendRequest::duplicate () {
69 RK_TRACE (RBACKEND);
70
71 RBackendRequest* ret = new RBackendRequest (synchronous, type);
72 --_id; // for pretty, consecutive numbering
73 ret->id = id;
74 ret->done = done;
75 ret->command = command;
76 ret->params = params;
77 ret->output = output;
78 // prevent double deletion issues
79 command = 0;
80 output = 0;
81 return ret;
82 }
83
84
85
86 #define MAX_BUF_LENGTH 16000
87 #define OUTPUT_STRING_RESERVE 1000
88
RKROutputBuffer()89 RKROutputBuffer::RKROutputBuffer () {
90 RK_TRACE (RBACKEND);
91
92 out_buf_len = 0;
93 }
94
~RKROutputBuffer()95 RKROutputBuffer::~RKROutputBuffer () {
96 RK_TRACE (RBACKEND);
97
98 if (!output_captures.isEmpty ()) RK_DEBUG (RBACKEND, DL_WARNING, "%d requests for recording output still active on interface shutdown", output_captures.size ());
99 }
100
pushOutputCapture(int capture_mode)101 void RKROutputBuffer::pushOutputCapture (int capture_mode) {
102 RK_TRACE (RBACKEND);
103
104 OutputCapture capture;
105 capture.mode = capture_mode;
106 output_captures.append (capture);
107 }
108
popOutputCapture(bool highlighted)109 QString RKROutputBuffer::popOutputCapture (bool highlighted) {
110 RK_TRACE (RBACKEND);
111
112 if (output_captures.isEmpty ()) {
113 RK_ASSERT (!output_captures.isEmpty ());
114 return QString ();
115 }
116 OutputCapture capture = output_captures.takeLast ();
117 if (capture.recorded.isEmpty ()) return QString ();
118
119 QString ret;
120 ROutput::ROutputType previous_type = ROutput::NoOutput;
121 for (int i = 0; i < capture.recorded.length (); ++i) {
122 const ROutput * output = capture.recorded[i];
123 if (output->output.isEmpty ()) continue;
124
125 if (output->type != ROutput::Error) { // NOTE: skip error output. It has already been written as a warning.
126 if (highlighted && (output->type != previous_type)) {
127 if (!ret.isEmpty ()) ret.append ("</pre>\n");
128
129 if (output->type == ROutput::Output) ret.append ("<pre class=\"output_normal\">");
130 else if (output->type == ROutput::Warning) ret.append ("<pre class=\"output_warning\">");
131 else {
132 RK_ASSERT (false);
133 ret.append ("<pre>");
134 }
135 }
136 if (highlighted) ret.append (output->output.toHtmlEscaped ());
137 else ret.append (output->output);
138
139 previous_type = output->type;
140 }
141 }
142 if (highlighted && !ret.isEmpty ()) ret.append ("</pre>\n");
143 return ret;
144 }
145
appendToOutputList(ROutputList * list,const QString & output,ROutput::ROutputType output_type)146 void appendToOutputList (ROutputList *list, const QString &output, ROutput::ROutputType output_type) {
147 // No trace
148 ROutput *current_output = 0;
149 if (!list->isEmpty ()) {
150 // Merge with previous output fragment, if of the same type
151 current_output = list->last ();
152 if (current_output->type != output_type) current_output = 0;
153 }
154 if (!current_output) {
155 current_output = new ROutput;
156 current_output->type = output_type;
157 current_output->output.reserve (OUTPUT_STRING_RESERVE);
158 list->append (current_output);
159 }
160 current_output->output.append (output);
161 }
162
handleOutput(const QString & output,int buf_length,ROutput::ROutputType output_type,bool allow_blocking)163 bool RKROutputBuffer::handleOutput (const QString &output, int buf_length, ROutput::ROutputType output_type, bool allow_blocking) {
164 if (!buf_length) return false;
165 RK_TRACE (RBACKEND);
166 RK_DEBUG (RBACKEND, DL_DEBUG, "Output type %d: %s", output_type, qPrintable (output));
167
168 // wait while the output buffer is exceeded to give downstream threads a chance to catch up
169 while ((out_buf_len > MAX_BUF_LENGTH) && allow_blocking) {
170 if (!doMSleep (10)) break;
171 }
172
173 QMutexLocker lock (&output_buffer_mutex);
174 bool previously_empty = (out_buf_len <= 0);
175
176 for (int i = output_captures.length () - 1; i >= 0; --i) {
177 OutputCapture &cap = output_captures[i];
178 if (output_type == ROutput::Output) {
179 if (cap.mode & RecordOutput) appendToOutputList (&(cap.recorded), output, output_type);
180 if (cap.mode & SuppressOutput) return previously_empty;
181 } else {
182 if (cap.mode & RecordMessages) appendToOutputList (&(cap.recorded), output, output_type);
183 if (cap.mode & SuppressMessages) return previously_empty;
184 }
185 }
186
187 appendToOutputList (&output_buffer, output, output_type);
188 out_buf_len += buf_length;
189
190 return previously_empty;
191 }
192
flushOutput(bool forcibly)193 ROutputList RKROutputBuffer::flushOutput (bool forcibly) {
194 ROutputList ret;
195
196 if (out_buf_len == 0) return ret; // if there is absolutely no output, just skip.
197 RK_TRACE (RBACKEND);
198
199 if (!forcibly) {
200 if (!output_buffer_mutex.tryLock ()) return ret;
201 } else {
202 output_buffer_mutex.lock ();
203 }
204
205 RK_ASSERT (!output_buffer.isEmpty ()); // see check for out_buf_len, above
206
207 ret = output_buffer;
208 output_buffer.clear ();
209 out_buf_len = 0;
210
211 output_buffer_mutex.unlock ();
212 return ret;
213 }
214
215
216
quote(const QString & string)217 QString RKRSharedFunctionality::quote (const QString &string) {
218 QString ret;
219 int s = string.size ();
220 ret.reserve (s + 2); // typical case: Only quotes added, no escapes needed.
221 ret.append ('\"');
222 for (int i = 0; i < s; ++i) {
223 const QChar c = string[i];
224 if ((c == '\\') || (c == '\"')) ret.append ('\\');
225 ret.append (c);
226 }
227 ret.append ('\"');
228
229 return ret;
230 }
231
232 #include <QFile>
233 /* These definitions don't really belong into this file, but, importantly, they need to be compiled for both frontend and backend. */
234 namespace RK_Debug {
235 int RK_Debug_Level = 0;
236 int RK_Debug_Flags = DEBUG_ALL;
237 int RK_Debug_CommandStep = 0;
238 QFile *debug_file = 0;
239
setupLogFile(const QString & basename)240 bool setupLogFile (const QString &basename) {
241 QStringList all_debug_files (basename);
242 all_debug_files << basename + ".0" << basename + ".1";
243 for (int i = all_debug_files.size () -1; i >= 0; --i) {
244 QFile oldfile (all_debug_files[i]);
245 if (oldfile.exists ()) {
246 if (i < all_debug_files.size () -1) {
247 oldfile.rename (all_debug_files[i+1]);
248 } else {
249 oldfile.remove ();
250 }
251 }
252 }
253 debug_file = new QFile (basename);
254 return (debug_file->open (QIODevice::WriteOnly | QIODevice::Truncate));
255 }
256 };
257