OSDN Git Service

Merge with stable
[winmerge-jp/winmerge-jp.git] / Src / MergeCmdLineInfo.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //
3 //    WinMerge: An interactive diff/merge utility
4 //    Copyright (C) 1997 Dean P. Grimm
5 //
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.
10 //
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.
15 //
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.
19 //
20 /////////////////////////////////////////////////////////////////////////////
21
22 /** 
23  * @file  MergeCmdLineInfo.cpp
24  *
25  * @brief MergeCmdLineInfo class implementation.
26  *
27  */
28
29
30 #include "MergeCmdLineInfo.h"
31 #include <cstring>
32 #include <algorithm>
33 #include "Constants.h"
34 #include "Paths.h"
35 #include "OptionsDef.h"
36
37 // MergeCmdLineInfo
38
39 /**
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.
45  */
46 const TCHAR *MergeCmdLineInfo::EatParam(const TCHAR *p, String &param, bool *flag)
47 {
48         if (p && *(p += _tcsspn(p, _T(" \t\r\n"))) == _T('\0'))
49                 p = 0;
50         const TCHAR *q = p;
51         if (q)
52         {
53                 TCHAR c = *q;
54                 bool quoted = false;
55                 do
56                 {
57                         if (c == _T('"'))
58                                 quoted = !quoted;
59                         c = *++q;
60                 } while (c != _T('\0') && (quoted ||
61                         c != _T(' ') && c != _T('\t') && c != _T('\r') && c != _T('\n')));
62         }
63         if (q > p && flag)
64         {
65                 if (*p == _T('-') || *p == _T('/'))
66                 {
67                         *flag = true;
68                         ++p;
69                         for (const TCHAR *i = q; i >= p; --i)
70                                 if (*i == ':')
71                                 {
72                                         q = i;
73                                         break;
74                                 }
75                 }
76                 else
77                 {
78                         *flag = false;
79                         flag = 0;
80                 }
81         }
82         param.assign(p ? p : _T(""), q - p);
83         if (q > p && flag)
84         {
85                 param = string_makelower(param);
86         }
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);
90         return q;
91 }
92
93 /**
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.
99  */
100 const TCHAR *MergeCmdLineInfo::SetOption(const TCHAR *q, const TCHAR *key, const TCHAR *value)
101 {
102         String s;
103         if (*q == _T(':'))
104         {
105                 q = EatParam(q, s);
106 //              value = s.c_str() + 1;
107         }
108 //      GetOptionsMgr()->SaveOption(key, value);
109         return q;
110 }
111
112 /**
113  * @brief ClearCaseCmdLineParser's constructor.
114  * @param [in] q Points to the beginning of the command line.
115  */
116 MergeCmdLineInfo::MergeCmdLineInfo(const TCHAR *q):
117         m_nCmdShow(SHOWNORMAL),
118         m_bClearCaseTool(false),
119         m_bEscShutdown(false),
120         m_bExitIfNoDiff(Disabled),
121         m_bRecurse(false),
122         m_bNonInteractive(false),
123         m_bSingleInstance(false),
124         m_bShowUsage(false),
125         m_nCodepage(0),
126         m_dwLeftFlags(FFILEOPEN_NONE),
127         m_dwMiddleFlags(FFILEOPEN_NONE),
128         m_dwRightFlags(FFILEOPEN_NONE)
129 {
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.
139         String exeName;
140         q = EatParam(q, exeName);
141         if (exeName == _T("compare") || exeName == _T("xcompare"))
142         {
143                 ParseClearCaseCmdLine(q, _T("<No Base>"));
144         }
145         else if (exeName == _T("merge") || exeName == _T("xmerge"))
146         {
147                 ParseClearCaseCmdLine(q, _T(""));
148         }
149         else
150         {
151                 ParseWinMergeCmdLine(q);
152         }
153 }
154
155 /**
156  * @brief Parse a command line passed in from ClearCase.
157  * @param [in] p Points into the command line.
158  */
159 void MergeCmdLineInfo::ParseClearCaseCmdLine(const TCHAR *q, const TCHAR *basedesc)
160 {
161         String sBaseFile;  /**< Base file path. */
162         String sBaseDesc = basedesc;  /**< Base file description. */
163         String sOutFile;   /**< Out file path. */
164         m_bClearCaseTool = true;
165         String param;
166         bool flag;
167         while ((q = EatParam(q, param, &flag)) != 0)
168         {
169                 if (!flag)
170                 {
171                         // Not a flag
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;
178                 }
179                 else if (param == _T("base"))
180                 {
181                         // -base is followed by common ancestor file description.
182                         q = EatParam(q, sBaseFile);
183                 }
184                 else if (param == _T("out"))
185                 {
186                         // -out is followed by merge's output file name.
187                         q = EatParam(q, sOutFile);
188                 }
189                 else if (param == _T("fname"))
190                 {
191                         // -fname is followed by file description.
192                         if (sBaseDesc.empty())
193                                 q = EatParam(q, sBaseDesc);
194                         else if (m_sLeftDesc.empty())
195                         {
196                                 q = EatParam(q, m_sLeftDesc);
197                                 m_dwLeftFlags |= FFILEOPEN_READONLY;
198                         }
199                         else if (m_sRightDesc.empty())
200                         {
201                                 q = EatParam(q, m_sRightDesc);
202                                 m_dwRightFlags |= FFILEOPEN_READONLY;
203                         }
204                         else
205                                 q = EatParam(q, param); // ignore excess arguments
206                 }
207         }
208         if (!sOutFile.empty())
209         {
210                 String path = paths_GetLongPath(sOutFile);
211                 m_sOutputpath = path;
212         }
213 }
214
215 /**
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.
220  */
221 void MergeCmdLineInfo::AddPath(const String &path)
222 {
223         String param(path);
224
225         // Set flag indicating path is from command line
226         const size_t ord = m_Files.GetSize();
227         if (ord == 0)
228                 m_dwLeftFlags |= FFILEOPEN_CMDLINE;
229         else if (ord == 1)
230                 m_dwRightFlags |= FFILEOPEN_CMDLINE;
231
232         if (!paths_IsURLorCLSID(path))
233         {
234                 // Convert paths given in Linux-style ('/' as separator) given from
235                 // Cygwin to Windows style ('\' as separator)
236                 string_replace(param, _T("/"), _T("\\"));
237
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);
243         }
244         else
245         {
246                 m_Files.SetPath(m_Files.GetSize(), param, false);
247         }
248 }
249
250 /**
251  * @brief Parse native WinMerge command line.
252  * @param [in] p Points into the command line.
253  */
254 void MergeCmdLineInfo::ParseWinMergeCmdLine(const TCHAR *q)
255 {
256         String param;
257         bool flag;
258
259         while ((q = EatParam(q, param, &flag)) != 0)
260         {
261                 if (!flag)
262                 {
263                         // Its not a flag so it is a path
264                         AddPath(param);
265                 }
266                 else if (param == _T("?"))
267                 {
268                         // -? to show common command line arguments.
269                         m_bShowUsage = true;
270                 }
271                 else if (param == _T("o"))
272                 {
273                         // -o "outputfilename"
274                         q = EatParam(q, m_sOutputpath);
275                 }
276                 else if (param == _T("dl"))
277                 {
278                         // -dl "desc" - description for left file
279                         q = EatParam(q, m_sLeftDesc);
280                 }
281                 else if (param == _T("dm"))
282                 {
283                         // -dr "desc" - description for middle file
284                         q = EatParam(q, m_sMiddleDesc);
285                 }
286                 else if (param == _T("dr"))
287                 {
288                         // -dr "desc" - description for right file
289                         q = EatParam(q, m_sRightDesc);
290                 }
291                 else if (param == _T("e"))
292                 {
293                         // -e to allow closing with single esc press
294                         m_bEscShutdown = true;
295                 }
296                 else if (param == _T("f"))
297                 {
298                         // -f "mask" - file filter mask ("*.h *.cpp")
299                         q = EatParam(q, m_sFileFilter);
300                 }
301                 else if (param == _T("r"))
302                 {
303                         // -r to compare recursively
304                         m_bRecurse = true;
305                 }
306                 else if (param == _T("s"))
307                 {
308                         // -s to allow only one instance
309                         m_bSingleInstance = true;
310                 }
311                 else if (param == _T("noninteractive"))
312                 {
313                         // -noninteractive to suppress message boxes & close with result code
314                         m_bNonInteractive = true;
315                 }
316                 else if (param == _T("noprefs"))
317                 {
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();
323                 }
324                 else if (param == _T("minimize"))
325                 {
326                         // -minimize means minimize the main window.
327                         m_nCmdShow = MINIMIZE;
328                 }
329                 else if (param == _T("maximize"))
330                 {
331                         // -maximize means maximize the main window.
332                         m_nCmdShow = MAXIMIZE;
333                 }
334                 else if (param == _T("prediffer"))
335                 {
336                         // Get prediffer if specified (otherwise prediffer will be blank, which is default)
337                         q = EatParam(q, m_sPreDiffer);
338                 }
339                 else if (param == _T("wl"))
340                 {
341                         // -wl to open left path as read-only
342                         m_dwLeftFlags |= FFILEOPEN_READONLY;
343                 }
344                 else if (param == _T("wm"))
345                 {
346                         // -wm to open middle path as read-only
347                         m_dwMiddleFlags |= FFILEOPEN_READONLY;
348                 }
349                 else if (param == _T("wr"))
350                 {
351                         // -wr to open right path as read-only
352                         m_dwRightFlags |= FFILEOPEN_READONLY;
353                 }
354                 else if (param == _T("ul"))
355                 {
356                         // -ul to not add left path to MRU
357                         m_dwLeftFlags |= FFILEOPEN_NOMRU;
358                 }
359                 else if (param == _T("um"))
360                 {
361                         // -um to not add middle path to MRU
362                         m_dwMiddleFlags |= FFILEOPEN_NOMRU;
363                 }
364                 else if (param == _T("ur"))
365                 {
366                         // -ur to not add right path to MRU
367                         m_dwRightFlags |= FFILEOPEN_NOMRU;
368                 }
369                 else if (param == _T("u") || param == _T("ub"))
370                 {
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;
375                 }
376                 else if (param == _T("fl"))
377                 {
378                         // -fl to set focus to the left panbe
379                         m_dwLeftFlags |= FFILEOPEN_SETFOCUS;
380                 }
381                 else if (param == _T("fm"))
382                 {
383                         // -fm to set focus to the middle pane
384                         m_dwMiddleFlags |= FFILEOPEN_SETFOCUS;
385                 }
386                 else if (param == _T("fr"))
387                 {
388                         // -fr to set focus to the right pane
389                         m_dwRightFlags |= FFILEOPEN_SETFOCUS;
390                 }
391                 else if (param == _T("al"))
392                 {
393                         // -al to auto-merge at the left pane
394                         m_dwLeftFlags |= FFILEOPEN_AUTOMERGE;
395                 }
396                 else if (param == _T("am"))
397                 {
398                         // -am to auto-merge at the middle pane
399                         m_dwMiddleFlags |= FFILEOPEN_AUTOMERGE;
400                 }
401                 else if (param == _T("ar"))
402                 {
403                         // -ar to auto-merge at the right pane
404                         m_dwRightFlags |= FFILEOPEN_AUTOMERGE;
405                 }
406                 else if (param == _T("x"))
407                 {
408                         // -x to close application if files are identical.
409                         m_bExitIfNoDiff = Exit;
410                 }
411                 else if (param == _T("xq"))
412                 {
413                         // -xn to close application if files are identical without showing
414                         // any messages
415                         m_bExitIfNoDiff = ExitQuiet;
416                 }
417                 else if (param == _T("cp"))
418                 {
419                         String codepage;
420                         q = EatParam(q, codepage);
421                         try { m_nCodepage = string_stoi(codepage); } catch (...) { /* FIXME: */ }
422                 }
423                 else if (param == _T("ignorews"))
424                 {
425                         q = SetOption(q, OPT_CMP_IGNORE_WHITESPACE);
426                 }
427                 else if (param == _T("ignoreblanklines"))
428                 {
429                         q = SetOption(q, OPT_CMP_IGNORE_BLANKLINES);
430                 }
431                 else if (param == _T("ignorecase"))
432                 {
433                         q = SetOption(q, OPT_CMP_IGNORE_CASE);
434                 }
435                 else if (param == _T("ignoreeol"))
436                 {
437                         q = SetOption(q, OPT_CMP_IGNORE_EOL);
438                 }
439         }
440         // If "compare file dir" make it "compare file dir\file".
441         if (m_Files.GetSize() >= 2)
442         {
443                 PATH_EXISTENCE p1 = paths_DoesPathExist(m_Files[0]);
444                 PATH_EXISTENCE p2 = paths_DoesPathExist(m_Files[1]);
445
446                 if ((p1 == IS_EXISTING_FILE) && (p2 == IS_EXISTING_DIR))
447                 {
448                         m_Files[1] = paths_ConcatPath(m_Files[1], paths_FindFileName(m_Files[0]));
449                 }
450         }
451         if (m_bShowUsage)
452         {
453                 m_bNonInteractive = false;
454         }
455 }