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 // ID line follows -- this is updated by SVN
30 // $Id: MergeCmdLineInfo.cpp 6940 2009-08-01 17:29:01Z kimmov $
31
32 #include "MergeCmdLineInfo.h"
33 #include <cstring>
34 #include <algorithm>
35 #include "Constants.h"
36 #include "Paths.h"
37 #include "OptionsDef.h"
38
39 // MergeCmdLineInfo
40
41 /**
42  * @brief Eat and digest a command line parameter.
43  * @param [in] p Points into the command line.
44  * @param [out] param Receives the digested command line parameter.
45  * @param [out] flag Tells whether param is the name of a flag.
46  * @return Points to the remaining portion of the command line.
47  */
48 const TCHAR *MergeCmdLineInfo::EatParam(const TCHAR *p, String &param, bool *flag)
49 {
50         if (p && *(p += _tcsspn(p, _T(" \t\r\n"))) == _T('\0'))
51                 p = 0;
52         const TCHAR *q = p;
53         if (q)
54         {
55                 TCHAR c = *q;
56                 bool quoted = false;
57                 do
58                 {
59                         if (c == _T('"'))
60                                 quoted = !quoted;
61                         c = *++q;
62                 } while (c != _T('\0') && (quoted ||
63                         c != _T(' ') && c != _T('\t') && c != _T('\r') && c != _T('\n')));
64         }
65         if (q > p && flag)
66         {
67                 if (*p == _T('-') || *p == _T('/'))
68                 {
69                         *flag = true;
70                         ++p;
71                         for (const TCHAR *i = q; i >= p; --i)
72                                 if (*i == ':')
73                                 {
74                                         q = i;
75                                         break;
76                                 }
77                 }
78                 else
79                 {
80                         *flag = false;
81                         flag = 0;
82                 }
83         }
84         param.assign(p ? p : _T(""), q - p);
85         if (q > p && flag)
86         {
87                 param = string_makelower(param);
88         }
89         // Strip any leading or trailing whitespace or quotes
90         param.erase(0, param.find_first_not_of(_T(" \t\r\n\"")));
91         param.erase(param.find_last_not_of(_T(" \t\r\n\"")) + 1);
92         return q;
93 }
94
95 /**
96  * @brief Set WinMerge option from command line.
97  * @param [in] p Points into the command line.
98  * @param [in] key Name of WinMerge option to set.
99  * @param [in] value Default value in case none is specified.
100  * @return Points to the remaining portion of the command line.
101  */
102 const TCHAR *MergeCmdLineInfo::SetOption(const TCHAR *q, const TCHAR *key, const TCHAR *value)
103 {
104         String s;
105         if (*q == _T(':'))
106         {
107                 q = EatParam(q, s);
108                 value = s.c_str() + 1;
109         }
110 //      GetOptionsMgr()->SaveOption(key, value);
111         return q;
112 }
113
114 /**
115  * @brief ClearCaseCmdLineParser's constructor.
116  * @param [in] q Points to the beginning of the command line.
117  */
118 MergeCmdLineInfo::MergeCmdLineInfo(const TCHAR *q):
119         m_nCmdShow(SHOWNORMAL),
120         m_bClearCaseTool(false),
121         m_bEscShutdown(false),
122         m_bExitIfNoDiff(Disabled),
123         m_bRecurse(false),
124         m_bNonInteractive(false),
125         m_bSingleInstance(false),
126         m_bShowUsage(false),
127         m_nCodepage(0),
128         m_dwLeftFlags(FFILEOPEN_NONE),
129         m_dwMiddleFlags(FFILEOPEN_NONE),
130         m_dwRightFlags(FFILEOPEN_NONE)
131 {
132         // Rational ClearCase has a weird way of executing external
133         // tools which replace the build-in ones. It also doesn't allow
134         // you to define which parameters to send to the executable.
135         // So, in order to run as an external tool, WinMerge should do:
136         // if argv[0] is "xcompare" then it "knows" that it was
137         // executed from ClearCase. In this case, it should read and
138         // parse ClearCase's command line parameters and not the
139         // "regular" parameters. More information can be found in
140         // C:\Program Files\Rational\ClearCase\lib\mgrs\mgr_info.h file.
141         String exeName;
142         q = EatParam(q, exeName);
143         if (exeName == _T("compare") || exeName == _T("xcompare"))
144         {
145                 ParseClearCaseCmdLine(q, _T("<No Base>"));
146         }
147         else if (exeName == _T("merge") || exeName == _T("xmerge"))
148         {
149                 ParseClearCaseCmdLine(q, _T(""));
150         }
151         else
152         {
153                 ParseWinMergeCmdLine(q);
154         }
155 }
156
157 /**
158  * @brief Parse a command line passed in from ClearCase.
159  * @param [in] p Points into the command line.
160  */
161 void MergeCmdLineInfo::ParseClearCaseCmdLine(const TCHAR *q, const TCHAR *basedesc)
162 {
163         String sBaseFile;  /**< Base file path. */
164         String sBaseDesc = basedesc;  /**< Base file description. */
165         String sOutFile;   /**< Out file path. */
166         m_bClearCaseTool = true;
167         String param;
168         bool flag;
169         while ((q = EatParam(q, param, &flag)) != 0)
170         {
171                 if (!flag)
172                 {
173                         // Not a flag
174                         param = paths_GetLongPath(param);
175                         m_Files.SetPath(m_Files.GetSize(), param);
176                         if (param == m_sLeftDesc)
177                                 m_dwLeftFlags &= ~FFILEOPEN_READONLY;
178                         if (param == m_sRightDesc)
179                                 m_dwRightFlags &= ~FFILEOPEN_READONLY;
180                 }
181                 else if (param == _T("base"))
182                 {
183                         // -base is followed by common ancestor file description.
184                         q = EatParam(q, sBaseFile);
185                 }
186                 else if (param == _T("out"))
187                 {
188                         // -out is followed by merge's output file name.
189                         q = EatParam(q, sOutFile);
190                 }
191                 else if (param == _T("fname"))
192                 {
193                         // -fname is followed by file description.
194                         if (sBaseDesc.empty())
195                                 q = EatParam(q, sBaseDesc);
196                         else if (m_sLeftDesc.empty())
197                         {
198                                 q = EatParam(q, m_sLeftDesc);
199                                 m_dwLeftFlags |= FFILEOPEN_READONLY;
200                         }
201                         else if (m_sRightDesc.empty())
202                         {
203                                 q = EatParam(q, m_sRightDesc);
204                                 m_dwRightFlags |= FFILEOPEN_READONLY;
205                         }
206                         else
207                                 q = EatParam(q, param); // ignore excess arguments
208                 }
209         }
210         if (!sOutFile.empty())
211         {
212                 String path = paths_GetLongPath(sOutFile);
213                 m_sOutputpath = path;
214         }
215 }
216
217 /**
218  * @brief Add path to list of paths.
219  * This method adds given string as a path to the list of paths. Path
220  * are converted if needed, shortcuts expanded etc.
221  * @param [in] path Path string to add.
222  */
223 void MergeCmdLineInfo::AddPath(const String &path)
224 {
225         String param(path);
226
227         // Set flag indicating path is from command line
228         const size_t ord = m_Files.GetSize();
229         if (ord == 0)
230                 m_dwLeftFlags |= FFILEOPEN_CMDLINE;
231         else if (ord == 1)
232                 m_dwRightFlags |= FFILEOPEN_CMDLINE;
233
234         if (!paths_IsURLorCLSID(path))
235         {
236                 // Convert paths given in Linux-style ('/' as separator) given from
237                 // Cygwin to Windows style ('\' as separator)
238                 string_replace(param, _T("/"), _T("\\"));
239
240                 // If shortcut, expand it first
241                 if (paths_IsShortcut(param))
242                         param = ExpandShortcut(param);
243                 param = paths_GetLongPath(param);
244                 m_Files.SetPath(m_Files.GetSize(), param);
245         }
246         else
247         {
248                 m_Files.SetPath(m_Files.GetSize(), param, false);
249         }
250 }
251
252 /**
253  * @brief Parse native WinMerge command line.
254  * @param [in] p Points into the command line.
255  */
256 void MergeCmdLineInfo::ParseWinMergeCmdLine(const TCHAR *q)
257 {
258         String param;
259         bool flag;
260
261         while ((q = EatParam(q, param, &flag)) != 0)
262         {
263                 if (!flag)
264                 {
265                         // Its not a flag so it is a path
266                         AddPath(param);
267                 }
268                 else if (param == _T("?"))
269                 {
270                         // -? to show common command line arguments.
271                         m_bShowUsage = true;
272                 }
273                 else if (param == _T("o"))
274                 {
275                         // -o "outputfilename"
276                         q = EatParam(q, m_sOutputpath);
277                 }
278                 else if (param == _T("dl"))
279                 {
280                         // -dl "desc" - description for left file
281                         q = EatParam(q, m_sLeftDesc);
282                 }
283                 else if (param == _T("dm"))
284                 {
285                         // -dr "desc" - description for middle file
286                         q = EatParam(q, m_sMiddleDesc);
287                 }
288                 else if (param == _T("dr"))
289                 {
290                         // -dr "desc" - description for right file
291                         q = EatParam(q, m_sRightDesc);
292                 }
293                 else if (param == _T("e"))
294                 {
295                         // -e to allow closing with single esc press
296                         m_bEscShutdown = true;
297                 }
298                 else if (param == _T("f"))
299                 {
300                         // -f "mask" - file filter mask ("*.h *.cpp")
301                         q = EatParam(q, m_sFileFilter);
302                 }
303                 else if (param == _T("r"))
304                 {
305                         // -r to compare recursively
306                         m_bRecurse = true;
307                 }
308                 else if (param == _T("s"))
309                 {
310                         // -s to allow only one instance
311                         m_bSingleInstance = true;
312                 }
313                 else if (param == _T("noninteractive"))
314                 {
315                         // -noninteractive to suppress message boxes & close with result code
316                         m_bNonInteractive = true;
317                 }
318                 else if (param == _T("noprefs"))
319                 {
320                         // -noprefs means do not load or remember options (preferences)
321                         // Turn off serializing to registry.
322 //                      GetOptionsMgr()->SetSerializing(false);
323                         // Load all default settings.
324 //                      theApp.ResetOptions();
325                 }
326                 else if (param == _T("minimize"))
327                 {
328                         // -minimize means minimize the main window.
329                         m_nCmdShow = MINIMIZE;
330                 }
331                 else if (param == _T("maximize"))
332                 {
333                         // -maximize means maximize the main window.
334                         m_nCmdShow = MAXIMIZE;
335                 }
336                 else if (param == _T("prediffer"))
337                 {
338                         // Get prediffer if specified (otherwise prediffer will be blank, which is default)
339                         q = EatParam(q, m_sPreDiffer);
340                 }
341                 else if (param == _T("wl"))
342                 {
343                         // -wl to open left path as read-only
344                         m_dwLeftFlags |= FFILEOPEN_READONLY;
345                 }
346                 else if (param == _T("wm"))
347                 {
348                         // -wm to open middle path as read-only
349                         m_dwMiddleFlags |= FFILEOPEN_READONLY;
350                 }
351                 else if (param == _T("wr"))
352                 {
353                         // -wr to open right path as read-only
354                         m_dwRightFlags |= FFILEOPEN_READONLY;
355                 }
356                 else if (param == _T("ul"))
357                 {
358                         // -ul to not add left path to MRU
359                         m_dwLeftFlags |= FFILEOPEN_NOMRU;
360                 }
361                 else if (param == _T("um"))
362                 {
363                         // -um to not add middle path to MRU
364                         m_dwMiddleFlags |= FFILEOPEN_NOMRU;
365                 }
366                 else if (param == _T("ur"))
367                 {
368                         // -ur to not add right path to MRU
369                         m_dwRightFlags |= FFILEOPEN_NOMRU;
370                 }
371                 else if (param == _T("u") || param == _T("ub"))
372                 {
373                         // -u or -ub (deprecated) to add neither right nor left path to MRU
374                         m_dwLeftFlags |= FFILEOPEN_NOMRU;
375                         m_dwMiddleFlags |= FFILEOPEN_NOMRU;
376                         m_dwRightFlags |= FFILEOPEN_NOMRU;
377                 }
378                 else if (param == _T("fl"))
379                 {
380                         // -fl to set focus to the left panbe
381                         m_dwLeftFlags |= FFILEOPEN_SETFOCUS;
382                 }
383                 else if (param == _T("fm"))
384                 {
385                         // -fm to set focus to the middle pane
386                         m_dwMiddleFlags |= FFILEOPEN_SETFOCUS;
387                 }
388                 else if (param == _T("fr"))
389                 {
390                         // -fr to set focus to the right pane
391                         m_dwRightFlags |= FFILEOPEN_SETFOCUS;
392                 }
393                 else if (param == _T("al"))
394                 {
395                         // -al to auto-merge at the left pane
396                         m_dwLeftFlags |= FFILEOPEN_AUTOMERGE;
397                 }
398                 else if (param == _T("am"))
399                 {
400                         // -am to auto-merge at the middle pane
401                         m_dwMiddleFlags |= FFILEOPEN_AUTOMERGE;
402                 }
403                 else if (param == _T("ar"))
404                 {
405                         // -ar to auto-merge at the right pane
406                         m_dwRightFlags |= FFILEOPEN_AUTOMERGE;
407                 }
408                 else if (param == _T("x"))
409                 {
410                         // -x to close application if files are identical.
411                         m_bExitIfNoDiff = Exit;
412                 }
413                 else if (param == _T("xq"))
414                 {
415                         // -xn to close application if files are identical without showing
416                         // any messages
417                         m_bExitIfNoDiff = ExitQuiet;
418                 }
419                 else if (param == _T("cp"))
420                 {
421                         String codepage;
422                         q = EatParam(q, codepage);
423                         try { m_nCodepage = string_stoi(codepage); } catch (...) { /* FIXME: */ }
424                 }
425                 else if (param == _T("ignorews"))
426                 {
427                         q = SetOption(q, OPT_CMP_IGNORE_WHITESPACE);
428                 }
429                 else if (param == _T("ignoreblanklines"))
430                 {
431                         q = SetOption(q, OPT_CMP_IGNORE_BLANKLINES);
432                 }
433                 else if (param == _T("ignorecase"))
434                 {
435                         q = SetOption(q, OPT_CMP_IGNORE_CASE);
436                 }
437                 else if (param == _T("ignoreeol"))
438                 {
439                         q = SetOption(q, OPT_CMP_IGNORE_EOL);
440                 }
441         }
442         // If "compare file dir" make it "compare file dir\file".
443         if (m_Files.GetSize() >= 2)
444         {
445                 PATH_EXISTENCE p1 = paths_DoesPathExist(m_Files[0]);
446                 PATH_EXISTENCE p2 = paths_DoesPathExist(m_Files[1]);
447
448                 if ((p1 == IS_EXISTING_FILE) && (p2 == IS_EXISTING_DIR))
449                 {
450                         m_Files[1] = paths_ConcatPath(m_Files[1], paths_FindFileName(m_Files[0]));
451                 }
452         }
453         if (m_bShowUsage)
454         {
455                 m_bNonInteractive = false;
456         }
457 }