1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: An interactive diff/merge utility
3 // Copyright (C) 1997 Dean P. Grimm
4 // SPDX-License-Identifier: GPL-2.0-or-later
5 /////////////////////////////////////////////////////////////////////////////
7 * @file MergeCmdLineInfo.cpp
9 * @brief MergeCmdLineInfo class implementation.
15 #include "MergeCmdLineInfo.h"
16 #include "Constants.h"
18 #include "OptionsDef.h"
24 * @brief Eat and digest a command line parameter.
25 * @param [in] p Points into the command line.
26 * @param [out] param Receives the digested command line parameter.
27 * @param [out] flag Tells whether param is the name of a flag.
28 * @return Points to the remaining portion of the command line.
30 const TCHAR *MergeCmdLineInfo::EatParam(const TCHAR *p, String ¶m, bool *flag /*= nullptr*/)
32 if (p != nullptr && *(p += _tcsspn(p, _T(" \t\r\n"))) == _T('\0'))
44 } while (c != _T('\0') && (quoted ||
45 c != _T(' ') && c != _T('\t') && c != _T('\r') && c != _T('\n')));
49 if (*p == _T('-') || *p == _T('/'))
53 for (const TCHAR *i = q; i >= p; --i)
66 param.assign(p ? p : _T(""), q - p);
67 if (q > p && flag != nullptr)
69 param = strutils::makelower(param);
71 // Strip any leading or trailing whitespace or quotes
72 param.erase(0, param.find_first_not_of(_T(" \t\r\n\"")));
73 param.erase(param.find_last_not_of(_T(" \t\r\n\"")) + 1);
78 * @brief Set WinMerge option from command line.
79 * @param [in] p Points into the command line.
80 * @param [in] key Name of WinMerge option to set.
81 * @param [in] value Default value in case none is specified.
82 * @return Points to the remaining portion of the command line.
84 const TCHAR *MergeCmdLineInfo::SetOption(const TCHAR *q, const String& key, const TCHAR *value)
90 value = s.c_str() + 1;
92 m_Options.insert_or_assign(key, value);
96 const TCHAR *MergeCmdLineInfo::SetConfig(const TCHAR *q)
102 size_t pos = s.find_first_of('=');
103 if (pos != String::npos)
105 String key = s.substr(0, pos);
106 String value = s.c_str() + pos + 1;
107 m_Options.insert_or_assign(key, value);
113 * @brief MergeCmdLineParser's constructor.
114 * @param [in] q Points to the beginning of the command line.
116 MergeCmdLineInfo::MergeCmdLineInfo(const TCHAR *q):
117 m_nCmdShow(SHOWNORMAL),
118 m_bEscShutdown(false),
119 m_bExitIfNoDiff(Disabled),
121 m_bNonInteractive(false),
126 m_bSelfCompare(false),
127 m_dwLeftFlags(FFILEOPEN_NONE),
128 m_dwMiddleFlags(FFILEOPEN_NONE),
129 m_dwRightFlags(FFILEOPEN_NONE)
132 q = EatParam(q, exeName);
133 ParseWinMergeCmdLine(q);
137 * @brief Add path to list of paths.
138 * This method adds given string as a path to the list of paths. Path
139 * are converted if needed, shortcuts expanded etc.
140 * @param [in] path Path string to add.
142 void MergeCmdLineInfo::AddPath(const String &path)
146 // Set flag indicating path is from command line
147 const size_t ord = m_Files.GetSize();
149 m_dwLeftFlags |= FFILEOPEN_CMDLINE;
151 m_dwRightFlags |= FFILEOPEN_CMDLINE;
153 m_dwMiddleFlags |= FFILEOPEN_CMDLINE;
155 if (!paths::IsURLorCLSID(path))
157 // Convert paths given in Linux-style ('/' as separator) given from
158 // Cygwin to Windows style ('\' as separator)
159 strutils::replace(param, _T("/"), _T("\\"));
161 // If shortcut, expand it first
162 if (paths::IsShortcut(param))
163 param = paths::ExpandShortcut(param);
164 param = paths::GetLongPath(param);
165 m_Files.SetPath(m_Files.GetSize(), param);
169 m_Files.SetPath(m_Files.GetSize(), param, false);
174 * @brief Parse native WinMerge command line.
175 * @param [in] p Points into the command line.
177 void MergeCmdLineInfo::ParseWinMergeCmdLine(const TCHAR *q)
182 while ((q = EatParam(q, param, &flag)) != 0)
186 // Its not a flag so it is a path
189 else if (param == _T("?"))
191 // -? to show common command line arguments.
194 else if (param == _T("o"))
196 // -o "outputfilename"
197 q = EatParam(q, m_sOutputpath);
199 else if (param == _T("or"))
201 // -or "reportfilename"
202 q = EatParam(q, m_sReportFile);
204 else if (param == _T("dl"))
206 // -dl "desc" - description for left file
207 q = EatParam(q, m_sLeftDesc);
209 else if (param == _T("dm"))
211 // -dr "desc" - description for middle file
212 q = EatParam(q, m_sMiddleDesc);
214 else if (param == _T("dr"))
216 // -dr "desc" - description for right file
217 q = EatParam(q, m_sRightDesc);
219 else if (param == _T("e"))
221 // -e to allow closing with single esc press
222 m_bEscShutdown = true;
224 else if (param == _T("f"))
226 // -f "mask" - file filter mask ("*.h *.cpp")
227 q = EatParam(q, m_sFileFilter);
229 else if (param == _T("m"))
231 // -m "method" - compare method
232 q = EatParam(q, param);
233 param = strutils::makelower(param);
234 strutils::replace(param, _T("and"), _T(""));
235 strutils::replace(param, _T("contents"), _T(""));
236 strutils::replace(param, _T("modified"), _T(""));
237 strutils::replace(param, _T(" "), _T(""));
238 if (param == _T("full"))
239 m_nCompMethod = CompareMethodType::CONTENT;
240 else if (param == _T("quick"))
241 m_nCompMethod = CompareMethodType::QUICK_CONTENT;
242 else if (param == _T("binary"))
243 m_nCompMethod = CompareMethodType::BINARY_CONTENT;
244 else if (param == _T("date"))
245 m_nCompMethod = CompareMethodType::DATE;
246 else if (param == _T("sizedate") || param == _T("datesize"))
247 m_nCompMethod = CompareMethodType::DATE_SIZE;
248 else if (param == _T("size"))
249 m_nCompMethod = CompareMethodType::SIZE;
251 m_sErrorMessages.push_back(_T("Unknown compare method '") + param + _T("' specified"));
253 else if (param == _T("r"))
255 // -r to compare recursively
258 else if (param == _T("s-"))
260 // -s- to not allow only one instance
261 m_nSingleInstance = 0;
263 else if (param == _T("sw"))
265 // -sw to allow only one instance and wait for the instance to terminate
266 m_nSingleInstance = 2;
268 else if (param == _T("s"))
270 // -s to allow only one instance
273 q = EatParam(q + 1, param);
274 m_nSingleInstance = _ttoi(param.c_str());
278 m_nSingleInstance = 1;
280 else if (param == _T("noninteractive"))
282 // -noninteractive to suppress message boxes & close with result code
283 m_bNonInteractive = true;
285 else if (param == _T("noprefs"))
287 // -noprefs means do not load or remember options (preferences)
290 else if (param == _T("self-compare"))
292 // -self-compare means compare a specified file with a copy of the file
293 m_bSelfCompare = true;
295 else if (param == _T("minimize"))
297 // -minimize means minimize the main window.
298 m_nCmdShow = MINIMIZE;
300 else if (param == _T("maximize"))
302 // -maximize means maximize the main window.
303 m_nCmdShow = MAXIMIZE;
305 else if (param == _T("prediffer"))
307 // Get prediffer if specified (otherwise prediffer will be blank, which is default)
308 q = EatParam(q, m_sPreDiffer);
310 else if (param == _T("unpacker"))
312 // Get unpacker if specified (otherwise unpacker will be blank, which is default)
313 q = EatParam(q, m_sUnpacker);
315 else if (param == _T("wl"))
317 // -wl to open left path as read-only
318 m_dwLeftFlags |= FFILEOPEN_READONLY;
320 else if (param == _T("wm"))
322 // -wm to open middle path as read-only
323 m_dwMiddleFlags |= FFILEOPEN_READONLY;
325 else if (param == _T("wr"))
327 // -wr to open right path as read-only
328 m_dwRightFlags |= FFILEOPEN_READONLY;
330 else if (param == _T("ul"))
332 // -ul to not add left path to MRU
333 m_dwLeftFlags |= FFILEOPEN_NOMRU;
335 else if (param == _T("um"))
337 // -um to not add middle path to MRU
338 m_dwMiddleFlags |= FFILEOPEN_NOMRU;
340 else if (param == _T("ur"))
342 // -ur to not add right path to MRU
343 m_dwRightFlags |= FFILEOPEN_NOMRU;
345 else if (param == _T("u") || param == _T("ub"))
347 // -u or -ub (deprecated) to add neither right nor left path to MRU
348 m_dwLeftFlags |= FFILEOPEN_NOMRU;
349 m_dwMiddleFlags |= FFILEOPEN_NOMRU;
350 m_dwRightFlags |= FFILEOPEN_NOMRU;
352 else if (param == _T("fl"))
354 // -fl to set focus to the left panbe
355 m_dwLeftFlags |= FFILEOPEN_SETFOCUS;
357 else if (param == _T("fm"))
359 // -fm to set focus to the middle pane
360 m_dwMiddleFlags |= FFILEOPEN_SETFOCUS;
362 else if (param == _T("fr"))
364 // -fr to set focus to the right pane
365 m_dwRightFlags |= FFILEOPEN_SETFOCUS;
367 else if (param == _T("al"))
369 // -al to auto-merge at the left pane
370 m_dwLeftFlags |= FFILEOPEN_AUTOMERGE;
372 else if (param == _T("am"))
374 // -am to auto-merge at the middle pane
375 m_dwMiddleFlags |= FFILEOPEN_AUTOMERGE;
377 else if (param == _T("ar"))
379 // -ar to auto-merge at the right pane
380 m_dwRightFlags |= FFILEOPEN_AUTOMERGE;
382 else if (param == _T("x"))
384 // -x to close application if files are identical.
385 m_bExitIfNoDiff = Exit;
387 else if (param == _T("xq"))
389 // -xn to close application if files are identical without showing
391 m_bExitIfNoDiff = ExitQuiet;
393 else if (param == _T("cp"))
396 q = EatParam(q, codepage);
397 m_nCodepage = atoi(ucr::toUTF8(codepage).c_str());
399 else if (param == _T("ignorews"))
401 q = SetOption(q, OPT_CMP_IGNORE_WHITESPACE);
403 else if (param == _T("ignoreblanklines"))
405 q = SetOption(q, OPT_CMP_IGNORE_BLANKLINES);
407 else if (param == _T("ignorecase"))
409 q = SetOption(q, OPT_CMP_IGNORE_CASE);
411 else if (param == _T("ignoreeol"))
413 q = SetOption(q, OPT_CMP_IGNORE_EOL);
415 else if (param == _T("ignorecodepage"))
417 q = SetOption(q, OPT_CMP_IGNORE_CODEPAGE);
419 else if (param == _T("ignorecomments"))
421 q = SetOption(q, OPT_CMP_FILTER_COMMENTLINES);
423 else if (param == _T("cfg") || param == _T("config"))
429 m_sErrorMessages.push_back(_T("Unknown option '/") + param + _T("'"));
432 // If "compare file dir" make it "compare file dir\file".
433 if (m_Files.GetSize() >= 2)
435 paths::PATH_EXISTENCE p1 = paths::DoesPathExist(m_Files[0]);
436 paths::PATH_EXISTENCE p2 = paths::DoesPathExist(m_Files[1]);
438 if ((p1 == paths::IS_EXISTING_FILE) && (p2 == paths::IS_EXISTING_DIR))
440 m_Files[1] = paths::ConcatPath(m_Files[1], paths::FindFileName(m_Files[0]));
445 m_bNonInteractive = false;