1 /////////////////////////////////////////////////////////////////////////////
3 // WinMerge: An interactive diff/merge utility
4 // Copyright (C) 1997 Dean P. Grimm
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /////////////////////////////////////////////////////////////////////////////
23 * @file MergeCmdLineInfo.cpp
25 * @brief MergeCmdLineInfo class implementation.
30 #include "MergeCmdLineInfo.h"
33 #include "Constants.h"
35 #include "OptionsDef.h"
40 * @brief Eat and digest a command line parameter.
41 * @param [in] p Points into the command line.
42 * @param [out] param Receives the digested command line parameter.
43 * @param [out] flag Tells whether param is the name of a flag.
44 * @return Points to the remaining portion of the command line.
46 const TCHAR *MergeCmdLineInfo::EatParam(const TCHAR *p, String ¶m, bool *flag)
48 if (p && *(p += _tcsspn(p, _T(" \t\r\n"))) == _T('\0'))
60 } while (c != _T('\0') && (quoted ||
61 c != _T(' ') && c != _T('\t') && c != _T('\r') && c != _T('\n')));
65 if (*p == _T('-') || *p == _T('/'))
69 for (const TCHAR *i = q; i >= p; --i)
82 param.assign(p ? p : _T(""), q - p);
85 param = string_makelower(param);
87 // Strip any leading or trailing whitespace or quotes
88 param.erase(0, param.find_first_not_of(_T(" \t\r\n\"")));
89 param.erase(param.find_last_not_of(_T(" \t\r\n\"")) + 1);
94 * @brief Set WinMerge option from command line.
95 * @param [in] p Points into the command line.
96 * @param [in] key Name of WinMerge option to set.
97 * @param [in] value Default value in case none is specified.
98 * @return Points to the remaining portion of the command line.
100 const TCHAR *MergeCmdLineInfo::SetOption(const TCHAR *q, const TCHAR *key, const TCHAR *value)
106 // value = s.c_str() + 1;
108 // GetOptionsMgr()->SaveOption(key, value);
113 * @brief ClearCaseCmdLineParser'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_bClearCaseTool(false),
119 m_bEscShutdown(false),
120 m_bExitIfNoDiff(Disabled),
122 m_bNonInteractive(false),
123 m_bSingleInstance(false),
126 m_dwLeftFlags(FFILEOPEN_NONE),
127 m_dwMiddleFlags(FFILEOPEN_NONE),
128 m_dwRightFlags(FFILEOPEN_NONE)
130 // Rational ClearCase has a weird way of executing external
131 // tools which replace the build-in ones. It also doesn't allow
132 // you to define which parameters to send to the executable.
133 // So, in order to run as an external tool, WinMerge should do:
134 // if argv[0] is "xcompare" then it "knows" that it was
135 // executed from ClearCase. In this case, it should read and
136 // parse ClearCase's command line parameters and not the
137 // "regular" parameters. More information can be found in
138 // C:\Program Files\Rational\ClearCase\lib\mgrs\mgr_info.h file.
140 q = EatParam(q, exeName);
141 if (exeName == _T("compare") || exeName == _T("xcompare"))
143 ParseClearCaseCmdLine(q, _T("<No Base>"));
145 else if (exeName == _T("merge") || exeName == _T("xmerge"))
147 ParseClearCaseCmdLine(q, _T(""));
151 ParseWinMergeCmdLine(q);
156 * @brief Parse a command line passed in from ClearCase.
157 * @param [in] p Points into the command line.
159 void MergeCmdLineInfo::ParseClearCaseCmdLine(const TCHAR *q, const TCHAR *basedesc)
161 String sBaseFile; /**< Base file path. */
162 String sBaseDesc = basedesc; /**< Base file description. */
163 String sOutFile; /**< Out file path. */
164 m_bClearCaseTool = true;
167 while ((q = EatParam(q, param, &flag)) != 0)
172 param = paths_GetLongPath(param);
173 m_Files.SetPath(m_Files.GetSize(), param);
174 if (param == m_sLeftDesc)
175 m_dwLeftFlags &= ~FFILEOPEN_READONLY;
176 if (param == m_sRightDesc)
177 m_dwRightFlags &= ~FFILEOPEN_READONLY;
179 else if (param == _T("base"))
181 // -base is followed by common ancestor file description.
182 q = EatParam(q, sBaseFile);
184 else if (param == _T("out"))
186 // -out is followed by merge's output file name.
187 q = EatParam(q, sOutFile);
189 else if (param == _T("fname"))
191 // -fname is followed by file description.
192 if (sBaseDesc.empty())
193 q = EatParam(q, sBaseDesc);
194 else if (m_sLeftDesc.empty())
196 q = EatParam(q, m_sLeftDesc);
197 m_dwLeftFlags |= FFILEOPEN_READONLY;
199 else if (m_sRightDesc.empty())
201 q = EatParam(q, m_sRightDesc);
202 m_dwRightFlags |= FFILEOPEN_READONLY;
205 q = EatParam(q, param); // ignore excess arguments
208 if (!sOutFile.empty())
210 String path = paths_GetLongPath(sOutFile);
211 m_sOutputpath = path;
216 * @brief Add path to list of paths.
217 * This method adds given string as a path to the list of paths. Path
218 * are converted if needed, shortcuts expanded etc.
219 * @param [in] path Path string to add.
221 void MergeCmdLineInfo::AddPath(const String &path)
225 // Set flag indicating path is from command line
226 const size_t ord = m_Files.GetSize();
228 m_dwLeftFlags |= FFILEOPEN_CMDLINE;
230 m_dwRightFlags |= FFILEOPEN_CMDLINE;
232 if (!paths_IsURLorCLSID(path))
234 // Convert paths given in Linux-style ('/' as separator) given from
235 // Cygwin to Windows style ('\' as separator)
236 string_replace(param, _T("/"), _T("\\"));
238 // If shortcut, expand it first
239 if (paths_IsShortcut(param))
240 param = ExpandShortcut(param);
241 param = paths_GetLongPath(param);
242 m_Files.SetPath(m_Files.GetSize(), param);
246 m_Files.SetPath(m_Files.GetSize(), param, false);
251 * @brief Parse native WinMerge command line.
252 * @param [in] p Points into the command line.
254 void MergeCmdLineInfo::ParseWinMergeCmdLine(const TCHAR *q)
259 while ((q = EatParam(q, param, &flag)) != 0)
263 // Its not a flag so it is a path
266 else if (param == _T("?"))
268 // -? to show common command line arguments.
271 else if (param == _T("o"))
273 // -o "outputfilename"
274 q = EatParam(q, m_sOutputpath);
276 else if (param == _T("dl"))
278 // -dl "desc" - description for left file
279 q = EatParam(q, m_sLeftDesc);
281 else if (param == _T("dm"))
283 // -dr "desc" - description for middle file
284 q = EatParam(q, m_sMiddleDesc);
286 else if (param == _T("dr"))
288 // -dr "desc" - description for right file
289 q = EatParam(q, m_sRightDesc);
291 else if (param == _T("e"))
293 // -e to allow closing with single esc press
294 m_bEscShutdown = true;
296 else if (param == _T("f"))
298 // -f "mask" - file filter mask ("*.h *.cpp")
299 q = EatParam(q, m_sFileFilter);
301 else if (param == _T("r"))
303 // -r to compare recursively
306 else if (param == _T("s"))
308 // -s to allow only one instance
309 m_bSingleInstance = true;
311 else if (param == _T("noninteractive"))
313 // -noninteractive to suppress message boxes & close with result code
314 m_bNonInteractive = true;
316 else if (param == _T("noprefs"))
318 // -noprefs means do not load or remember options (preferences)
319 // Turn off serializing to registry.
320 // GetOptionsMgr()->SetSerializing(false);
321 // Load all default settings.
322 // theApp.ResetOptions();
324 else if (param == _T("minimize"))
326 // -minimize means minimize the main window.
327 m_nCmdShow = MINIMIZE;
329 else if (param == _T("maximize"))
331 // -maximize means maximize the main window.
332 m_nCmdShow = MAXIMIZE;
334 else if (param == _T("prediffer"))
336 // Get prediffer if specified (otherwise prediffer will be blank, which is default)
337 q = EatParam(q, m_sPreDiffer);
339 else if (param == _T("wl"))
341 // -wl to open left path as read-only
342 m_dwLeftFlags |= FFILEOPEN_READONLY;
344 else if (param == _T("wm"))
346 // -wm to open middle path as read-only
347 m_dwMiddleFlags |= FFILEOPEN_READONLY;
349 else if (param == _T("wr"))
351 // -wr to open right path as read-only
352 m_dwRightFlags |= FFILEOPEN_READONLY;
354 else if (param == _T("ul"))
356 // -ul to not add left path to MRU
357 m_dwLeftFlags |= FFILEOPEN_NOMRU;
359 else if (param == _T("um"))
361 // -um to not add middle path to MRU
362 m_dwMiddleFlags |= FFILEOPEN_NOMRU;
364 else if (param == _T("ur"))
366 // -ur to not add right path to MRU
367 m_dwRightFlags |= FFILEOPEN_NOMRU;
369 else if (param == _T("u") || param == _T("ub"))
371 // -u or -ub (deprecated) to add neither right nor left path to MRU
372 m_dwLeftFlags |= FFILEOPEN_NOMRU;
373 m_dwMiddleFlags |= FFILEOPEN_NOMRU;
374 m_dwRightFlags |= FFILEOPEN_NOMRU;
376 else if (param == _T("fl"))
378 // -fl to set focus to the left panbe
379 m_dwLeftFlags |= FFILEOPEN_SETFOCUS;
381 else if (param == _T("fm"))
383 // -fm to set focus to the middle pane
384 m_dwMiddleFlags |= FFILEOPEN_SETFOCUS;
386 else if (param == _T("fr"))
388 // -fr to set focus to the right pane
389 m_dwRightFlags |= FFILEOPEN_SETFOCUS;
391 else if (param == _T("al"))
393 // -al to auto-merge at the left pane
394 m_dwLeftFlags |= FFILEOPEN_AUTOMERGE;
396 else if (param == _T("am"))
398 // -am to auto-merge at the middle pane
399 m_dwMiddleFlags |= FFILEOPEN_AUTOMERGE;
401 else if (param == _T("ar"))
403 // -ar to auto-merge at the right pane
404 m_dwRightFlags |= FFILEOPEN_AUTOMERGE;
406 else if (param == _T("x"))
408 // -x to close application if files are identical.
409 m_bExitIfNoDiff = Exit;
411 else if (param == _T("xq"))
413 // -xn to close application if files are identical without showing
415 m_bExitIfNoDiff = ExitQuiet;
417 else if (param == _T("cp"))
420 q = EatParam(q, codepage);
421 try { m_nCodepage = string_stoi(codepage); } catch (...) { /* FIXME: */ }
423 else if (param == _T("ignorews"))
425 q = SetOption(q, OPT_CMP_IGNORE_WHITESPACE);
427 else if (param == _T("ignoreblanklines"))
429 q = SetOption(q, OPT_CMP_IGNORE_BLANKLINES);
431 else if (param == _T("ignorecase"))
433 q = SetOption(q, OPT_CMP_IGNORE_CASE);
435 else if (param == _T("ignoreeol"))
437 q = SetOption(q, OPT_CMP_IGNORE_EOL);
440 // If "compare file dir" make it "compare file dir\file".
441 if (m_Files.GetSize() >= 2)
443 PATH_EXISTENCE p1 = paths_DoesPathExist(m_Files[0]);
444 PATH_EXISTENCE p2 = paths_DoesPathExist(m_Files[1]);
446 if ((p1 == IS_EXISTING_FILE) && (p2 == IS_EXISTING_DIR))
448 m_Files[1] = paths_ConcatPath(m_Files[1], paths_FindFileName(m_Files[0]));
453 m_bNonInteractive = false;