1 // SciTE - Scintilla based Text Editor
2 /** @file JobQueue.cxx
3 ** Define job queue
4 **/
5 // SciTE & Scintilla copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // Copyright 2007 by Neil Hodgson <neilh@scintilla.org>, from April White <april_white@sympatico.ca>
7 // The License.txt file describes the conditions under which this software may be distributed.
8
9 #include <cstddef>
10 #include <cstdlib>
11 #include <cstring>
12 #include <cstdio>
13 #include <cstdarg>
14 #include <ctime>
15
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <set>
20 #include <algorithm>
21 #include <memory>
22 #include <chrono>
23 #include <atomic>
24 #include <mutex>
25
26 #include <sys/stat.h>
27
28 #include "GUI.h"
29
30 #include "StringHelpers.h"
31 #include "FilePath.h"
32 #include "PropSetFile.h"
33 #include "SciTE.h"
34 #include "JobQueue.h"
35
SubsystemFromChar(char c)36 JobSubsystem SubsystemFromChar(char c) noexcept {
37 if (c == '1')
38 return jobGUI;
39 else if (c == '2')
40 return jobShell;
41 else if (c == '3')
42 return jobExtension;
43 else if (c == '4')
44 return jobHelp;
45 else if (c == '5')
46 return jobOtherHelp;
47 else if (c == '7')
48 return jobImmediate;
49 return jobCLI;
50 }
51
JobMode(PropSetFile & props,int item,const char * fileNameExt)52 JobMode::JobMode(PropSetFile &props, int item, const char *fileNameExt) : jobType(jobCLI), saveBefore(0), isFilter(false), flags(0) {
53 bool quiet = false;
54 int repSel = 0;
55 bool groupUndo = false;
56
57 const std::string itemSuffix = StdStringFromInteger(item) + ".";
58 std::string propName = std::string("command.mode.") + itemSuffix;
59 std::string modeVal(props.GetNewExpandString(propName.c_str(), fileNameExt));
60
61 modeVal.erase(std::remove(modeVal.begin(), modeVal.end(), ' '), modeVal.end());
62 std::vector<std::string> modes = StringSplit(modeVal, ',');
63 for (const std::string &mode : modes) {
64
65 std::vector<std::string> optValue = StringSplit(mode, ':');
66
67 if (optValue.size() == 0) {
68 continue;
69 }
70
71 const std::string opt = optValue[0];
72 const std::string value = (optValue.size() > 1) ? optValue[1] : std::string();
73
74 if (opt == "subsystem" && !value.empty()) {
75 if (value[0] == '0' || value == "console")
76 jobType = jobCLI;
77 else if (value[0] == '1' || value == "windows")
78 jobType = jobGUI;
79 else if (value[0] == '2' || value == "shellexec")
80 jobType = jobShell;
81 else if (value[0] == '3' || value == "lua" || value == "director")
82 jobType = jobExtension;
83 else if (value[0] == '4' || value == "htmlhelp")
84 jobType = jobHelp;
85 else if (value[0] == '5' || value == "winhelp")
86 jobType = jobOtherHelp;
87 else if (value[0] == '7' || value == "immediate")
88 jobType = jobImmediate;
89 }
90
91 if (opt == "quiet") {
92 if (value.empty() || value[0] == '1' || value == "yes")
93 quiet = true;
94 else if (value[0] == '0' || value == "no")
95 quiet = false;
96 }
97
98 if (opt == "savebefore") {
99 if (value.empty() || value[0] == '1' || value == "yes")
100 saveBefore = 1;
101 else if (value[0] == '0' || value == "no")
102 saveBefore = 2;
103 else if (value == "prompt")
104 saveBefore = 0;
105 }
106
107 if (opt == "filter") {
108 if (value.empty() || value[0] == '1' || value == "yes")
109 isFilter = true;
110 else if (value[0] == '0' || value == "no")
111 isFilter = false;
112 }
113
114 if (opt == "replaceselection") {
115 if (value.empty() || value[0] == '1' || value == "yes")
116 repSel = 1;
117 else if (value[0] == '0' || value == "no")
118 repSel = 0;
119 else if (value == "auto")
120 repSel = 2;
121 }
122
123 if (opt == "groupundo") {
124 if (value.empty() || value[0] == '1' || value == "yes")
125 groupUndo = true;
126 else if (value[0] == '0' || value == "no")
127 groupUndo = false;
128 }
129 }
130
131 // The mode flags also have classic properties with similar effect.
132 // If the classic property is specified, it overrides the mode.
133 // To see if the property is absent (as opposed to merely evaluating
134 // to nothing after variable expansion), use GetWild for the
135 // existence check. However, for the value check, use GetNewExpandString.
136
137 propName = "command.save.before.";
138 propName += itemSuffix;
139 if (props.GetWild(propName.c_str(), fileNameExt).length())
140 saveBefore = atoi(props.GetNewExpandString(propName.c_str(), fileNameExt).c_str());
141
142 propName = "command.is.filter.";
143 propName += itemSuffix;
144 if (props.GetWild(propName.c_str(), fileNameExt).length())
145 isFilter = (props.GetNewExpandString(propName.c_str(), fileNameExt) == "1");
146
147 propName = "command.subsystem.";
148 propName += itemSuffix;
149 if (props.GetWild(propName.c_str(), fileNameExt).length()) {
150 std::string subsystemVal = props.GetNewExpandString(propName.c_str(), fileNameExt);
151 jobType = SubsystemFromChar(subsystemVal[0]);
152 }
153
154 propName = "command.input.";
155 propName += itemSuffix;
156 if (props.GetWild(propName.c_str(), fileNameExt).length()) {
157 input = props.GetNewExpandString(propName.c_str(), fileNameExt);
158 flags |= jobHasInput;
159 }
160
161 propName = "command.quiet.";
162 propName += itemSuffix;
163 if (props.GetWild(propName.c_str(), fileNameExt).length())
164 quiet = props.GetNewExpandString(propName.c_str(), fileNameExt) == "1";
165 if (quiet)
166 flags |= jobQuiet;
167
168 propName = "command.replace.selection.";
169 propName += itemSuffix;
170 if (props.GetWild(propName.c_str(), fileNameExt).length())
171 repSel = atoi(props.GetNewExpandString(propName.c_str(), fileNameExt).c_str());
172
173 if (repSel == 1)
174 flags |= jobRepSelYes;
175 else if (repSel == 2)
176 flags |= jobRepSelAuto;
177
178 if (groupUndo)
179 flags |= jobGroupUndo;
180 }
181
Job()182 Job::Job() noexcept : jobType(jobCLI), flags(0) {
183 Clear();
184 }
185
Job(const std::string & command_,const FilePath & directory_,JobSubsystem jobType_,const std::string & input_,int flags_)186 Job::Job(const std::string &command_, const FilePath &directory_, JobSubsystem jobType_, const std::string &input_, int flags_)
187 : command(command_), directory(directory_), jobType(jobType_), input(input_), flags(flags_) {
188 }
189
Clear()190 void Job::Clear() noexcept {
191 command.clear();
192 directory.Init();
193 jobType = jobCLI;
194 input.clear();
195 flags = 0;
196 }
197
198
JobQueue()199 JobQueue::JobQueue() : jobQueue(commandMax) {
200 clearBeforeExecute = false;
201 isBuilding = false;
202 isBuilt = false;
203 executing = false;
204 commandCurrent = 0;
205 jobUsesOutputPane = false;
206 cancelFlag = false;
207 timeCommands = false;
208 }
209
~JobQueue()210 JobQueue::~JobQueue() {
211 }
212
TimeCommands() const213 bool JobQueue::TimeCommands() const noexcept {
214 return timeCommands;
215 }
216
ClearBeforeExecute() const217 bool JobQueue::ClearBeforeExecute() const noexcept {
218 return clearBeforeExecute;
219 }
220
ShowOutputPane() const221 bool JobQueue::ShowOutputPane() const noexcept {
222 return jobUsesOutputPane;
223 }
224
IsExecuting() const225 bool JobQueue::IsExecuting() const noexcept {
226 return executing;
227 }
228
SetExecuting(bool state)229 void JobQueue::SetExecuting(bool state) noexcept {
230 executing = state;
231 }
232
HasCommandToRun() const233 bool JobQueue::HasCommandToRun() const noexcept {
234 return commandCurrent > 0;
235 }
236
SetCancelFlag(bool value)237 bool JobQueue::SetCancelFlag(bool value) {
238 std::lock_guard<std::mutex> guard(mutex);
239 const bool cancelFlagPrevious = cancelFlag;
240 cancelFlag = value;
241 return cancelFlagPrevious;
242 }
243
Cancelled()244 bool JobQueue::Cancelled() noexcept {
245 return cancelFlag;
246 }
247
ClearJobs()248 void JobQueue::ClearJobs() noexcept {
249 for (Job &ic : jobQueue) {
250 ic.Clear();
251 }
252 commandCurrent = 0;
253 }
254
AddCommand(const std::string & command,const FilePath & directory,JobSubsystem jobType,const std::string & input,int flags)255 void JobQueue::AddCommand(const std::string &command, const FilePath &directory, JobSubsystem jobType, const std::string &input, int flags) {
256 if ((commandCurrent < commandMax) && (command.length())) {
257 if (commandCurrent == 0)
258 jobUsesOutputPane = false;
259 jobQueue[commandCurrent] = Job(command, directory, jobType, input, flags);
260 commandCurrent++;
261 if (jobType == jobCLI && !(flags & jobQuiet))
262 jobUsesOutputPane = true;
263 // For jobExtension, the Trace() method shows output pane on demand.
264 }
265 }
266