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