Subversion Repositories spk

Rev

Rev 344 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
303 cycrow 1
#pragma once
2
 
3
#include "WString.h"
4
#include "WStringList.h"
5
#include "../File_IO.h"
6
#include "../DirIO.h"
7
 
345 cycrow 8
#include <bitset>
9
#include <cwctype>
10
#include <filesystem>
328 cycrow 11
 
319 cycrow 12
#ifdef _WIN32
13
#include <windows.h>
14
#include <direct.h>
15
#include <shlobj.h>
16
#else
345 cycrow 17
#include <dirent.h>
18
#include <sys/types.h>
19
#include <sys/param.h>
20
#include <sys/stat.h>
21
#include <unistd.h>
22
#include <cstdlib>
319 cycrow 23
#endif
24
 
303 cycrow 25
namespace Utils
26
{
345 cycrow 27
	namespace fs = std::filesystem;
28
 
303 cycrow 29
	class CommandLine
30
	{
31
	private:
32
		std::shared_ptr<CFileIO> _file;
319 cycrow 33
		Utils::WStringList		_options;
34
		CDirIO 					_myDoc;
35
		CDirIO 					_tempDir;
303 cycrow 36
		std::vector<Utils::WString> _args;
345 cycrow 37
		std::bitset<256>		_flags{};
319 cycrow 38
		CPackages				_p;
303 cycrow 39
 
40
	public:
345 cycrow 41
		explicit CommandLine(int argc, char* argv[], bool doFlags = false)
303 cycrow 42
		{
43
			_file = std::make_shared<CFileIO>(argv[0]);
44
 
345 cycrow 45
			for (int i = 1; i < argc; ++i)
303 cycrow 46
			{
47
				Utils::WString arg(argv[i]);
345 cycrow 48
				doCommandLine(std::move(arg), doFlags);
303 cycrow 49
			}
309 cycrow 50
 
344 cycrow 51
			finaliseCommandLine();
52
		}
345 cycrow 53
 
54
		explicit CommandLine(int argc, wchar_t* argv[], bool doFlags = false)
344 cycrow 55
		{
56
			_file = std::make_shared<CFileIO>(argv[0]);
319 cycrow 57
 
345 cycrow 58
			for (int i = 1; i < argc; ++i)
319 cycrow 59
			{
344 cycrow 60
				Utils::WString arg(argv[i]);
345 cycrow 61
				doCommandLine(std::move(arg), doFlags);
319 cycrow 62
			}
63
 
344 cycrow 64
			finaliseCommandLine();
303 cycrow 65
		}
66
 
345 cycrow 67
		const std::shared_ptr<CFileIO>& file() const noexcept
303 cycrow 68
		{
69
			return _file;
70
		}
71
 
345 cycrow 72
		const Utils::WString& cmdName() const noexcept
303 cycrow 73
		{
74
			return _file->filename();
75
		}
76
 
345 cycrow 77
		const Utils::WString& cmdDir() const noexcept
303 cycrow 78
		{
79
			return _file->dir();
80
		}
81
 
345 cycrow 82
		const Utils::WString& tempDir() const noexcept
319 cycrow 83
		{
84
			return _tempDir.dir();
85
		}
86
 
345 cycrow 87
		const Utils::WString& myDoc() const noexcept
319 cycrow 88
		{
89
			return _myDoc.dir();
90
		}
91
 
345 cycrow 92
		const Utils::WStringList& options() const noexcept
303 cycrow 93
		{
94
			return _options;
95
		}
96
 
345 cycrow 97
		const std::vector<Utils::WString>& args() const noexcept
303 cycrow 98
		{
99
			return _args;
100
		}
101
 
345 cycrow 102
		size_t argCount() const noexcept
303 cycrow 103
		{
104
			return _args.size();
105
		}
106
 
107
		Utils::WString arg(size_t i) const
108
		{
345 cycrow 109
			if (i >= _args.size())
303 cycrow 110
				return Utils::WString::Null();
111
			return _args[i];
112
		}
113
 
345 cycrow 114
		bool hasSwitch(const Utils::WString& s) const noexcept
303 cycrow 115
		{
116
			return _options.contains(s);
117
		}
345 cycrow 118
 
119
		bool hasFlag(unsigned char s) const noexcept
328 cycrow 120
		{
345 cycrow 121
			return _flags.test(s);
328 cycrow 122
		}
303 cycrow 123
 
345 cycrow 124
		Utils::WString switchData(const Utils::WString& s, const Utils::WString& defaultValue = Utils::WString::Null()) const
303 cycrow 125
		{
126
			if (_options.contains(s))
127
				return _options[s]->data;
345 cycrow 128
			return defaultValue;
303 cycrow 129
		}
319 cycrow 130
 
345 cycrow 131
		const CPackages& packages() const noexcept
319 cycrow 132
		{
133
			return _p;
134
		}
135
 
345 cycrow 136
		CPackages& packages() noexcept
328 cycrow 137
		{
138
			return _p;
139
		}
140
 
345 cycrow 141
		const CDirIO& dirIO() const noexcept
319 cycrow 142
		{
143
			return _file->dirIO();
144
		}
324 cycrow 145
 
146
		Utils::WString fullFilename(const Utils::WString& s) const
147
		{
345 cycrow 148
			// Keep simple cross-platform detection: recognize drive/protocols and path separators.
149
			if (s.contains(L":") || s.contains(L"/") || s.contains(L"\\"))
150
				return s;
151
			return _file->dirIO().file(s);
324 cycrow 152
		}
344 cycrow 153
 
154
	private:
345 cycrow 155
		void doCommandLine(Utils::WString arg, bool doFlags)
344 cycrow 156
		{
157
			if (arg.startsWith(L"--"))
158
			{
159
				arg = arg.substr(2);
160
 
161
				if (arg.contains(L":"))
162
				{
163
					Utils::WString a = arg.token(L":", 1);
164
					Utils::WString b = arg.tokens(L":", 2);
165
					_options.pushBack(a, b);
166
				}
167
				else
345 cycrow 168
				{
344 cycrow 169
					_options.pushBack(arg);
345 cycrow 170
				}
344 cycrow 171
			}
172
			else
173
			{
174
				if (doFlags)
175
				{
176
					if (arg.startsWith(L"-") && !arg.startsWith(L"--"))
177
					{
178
						Utils::WString flags = arg.substr(1);
345 cycrow 179
						for (size_t is = 0; is < flags.length(); ++is)
344 cycrow 180
						{
345 cycrow 181
							wchar_t ch = flags[static_cast<int>(is)];
182
							wchar_t upper = std::towupper(ch);
183
 
184
							if (upper >= L'A' && upper <= L'Z')
344 cycrow 185
							{
345 cycrow 186
								unsigned char idx = static_cast<unsigned char>(upper & 0xFF);
187
								_flags.set(idx);
344 cycrow 188
							}
189
						}
190
						return;
191
					}
192
				}
193
				_args.push_back(arg);
194
			}
195
		}
196
 
197
		void finaliseCommandLine()
198
		{
345 cycrow 199
			// Ensure command directory is a usable path; prefer std::filesystem current_path.
200
			const Utils::WString fileDir = _file->dir();
201
			bool looksLikePath = (!fileDir.empty() && (fileDir.contains(L"/") || fileDir.contains(L"\\") || fileDir.contains(L":")));
202
			if (_file->dir().empty() || !looksLikePath)
344 cycrow 203
			{
204
				Utils::WString d;
345 cycrow 205
				try
206
				{
207
					fs::path cwd = fs::current_path();
344 cycrow 208
#ifdef _WIN32
345 cycrow 209
					d = Utils::WString(cwd.wstring().c_str());
344 cycrow 210
#else
345 cycrow 211
					d = Utils::WString(cwd.string().c_str());
344 cycrow 212
#endif
345 cycrow 213
				}
214
				catch (...)
215
				{
216
					// fallback
217
#ifdef _WIN32
218
					d = Utils::WString(_getcwd(NULL, 0));
219
#else
220
					d = Utils::WString(getcwd(NULL, 0));
221
#endif
222
				}
223
 
344 cycrow 224
				if (d.empty())
225
					d = L"./";
226
				_file->setDir(d);
227
			}
228
 
345 cycrow 229
			// my documents
344 cycrow 230
			Utils::WString myDoc = L".";
345 cycrow 231
 
344 cycrow 232
#ifdef _WIN32
233
			TCHAR pszPath[MAX_PATH];
234
			if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, pszPath)))
235
			{
236
				myDoc = pszPath;
237
			}
345 cycrow 238
#else
239
			// on POSIX try HOME, else leave '.',
240
			const char* home = std::getenv("HOME");
241
			if (home && *home)
242
			{
243
				myDoc = Utils::WString(home);
244
			}
344 cycrow 245
#endif
246
			_myDoc.setDir(myDoc);
247
 
345 cycrow 248
			// temp dir via filesystem (fallbacks included)
249
			Utils::WString tmp = L".";
250
			try
251
			{
252
				fs::path t = fs::temp_directory_path();
344 cycrow 253
#ifdef _WIN32
345 cycrow 254
				tmp = Utils::WString(t.wstring().c_str());
255
#else
256
				tmp = Utils::WString(t.string().c_str());
344 cycrow 257
#endif
345 cycrow 258
			}
259
			catch (...)
260
			{
261
#ifdef _WIN32
262
				TCHAR szPath[MAX_PATH + 1];
263
				DWORD result = GetTempPath(MAX_PATH + 1, szPath);
264
				if (result > 0)
265
					tmp = Utils::WString(szPath);
266
#else
267
				// leave as "."
268
#endif
269
			}
270
			_tempDir.setDir(tmp);
344 cycrow 271
 
272
			_p.startup(L".", _tempDir.dir(), _myDoc.dir());
273
		}
303 cycrow 274
	};
275
}