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         // Convert paths given in Linux-style ('/' as separator) given from
228         // Cygwin to Windows style ('\' as separator)
229         string_replace(param, _T("/"), _T("\\"));
230
231         // If shortcut, expand it first
232         if (paths_IsShortcut(param))
233                 param = ExpandShortcut(param);
234         param = paths_GetLongPath(param);
235
236         // Set flag indicating path is from command line
237         const size_t ord = m_Files.GetSize();
238         if (ord == 0)
239                 m_dwLeftFlags |= FFILEOPEN_CMDLINE;
240         else if (ord == 1)
241                 m_dwRightFlags |= FFILEOPEN_CMDLINE;
242
243         m_Files.SetPath(m_Files.GetSize(), param);
244 }
245
246 /**
247  * @brief Parse native WinMerge command line.
248  * @param [in] p Points into the command line.
249  */
250 void MergeCmdLineInfo::ParseWinMergeCmdLine(const TCHAR *q)
251 {
252         String param;
253         bool flag;
254
255         while ((q = EatParam(q, param, &flag)) != 0)
256         {
257                 if (!flag)
258                 {
259                         // Its not a flag so it is a path
260                         AddPath(param);
261                 }
262                 else if (param == _T("?"))
263                 {
264                         // -? to show common command line arguments.
265                         m_bShowUsage = true;
266                 }
267                 else if (param == _T("o"))
268                 {
269                         // -o "outputfilename"
270                         q = EatParam(q, m_sOutputpath);
271                 }
272                 else if (param == _T("dl"))
273                 {
274                         // -dl "desc" - description for left file
275                         q = EatParam(q, m_sLeftDesc);
276                 }
277                 else if (param == _T("dm"))
278                 {
279                         // -dr "desc" - description for middle file
280                         q = EatParam(q, m_sMiddleDesc);
281                 }
282                 else if (param == _T("dr"))
283                 {
284                         // -dr "desc" - description for right file
285                         q = EatParam(q, m_sRightDesc);
286                 }
287                 else if (param == _T("e"))
288                 {
289                         // -e to allow closing with single esc press
290                         m_bEscShutdown = true;
291                 }
292                 else if (param == _T("f"))
293                 {
294                         // -f "mask" - file filter mask ("*.h *.cpp")
295                         q = EatParam(q, m_sFileFilter);
296                 }
297                 else if (param == _T("r"))
298                 {
299                         // -r to compare recursively
300                         m_bRecurse = true;
301                 }
302                 else if (param == _T("s"))
303                 {
304                         // -s to allow only one instance
305                         m_bSingleInstance = true;
306                 }
307                 else if (param == _T("noninteractive"))
308                 {
309                         // -noninteractive to suppress message boxes & close with result code
310                         m_bNonInteractive = true;
311                 }
312                 else if (param == _T("noprefs"))
313                 {
314                         // -noprefs means do not load or remember options (preferences)
315                         // Turn off serializing to registry.
316 //                      GetOptionsMgr()->SetSerializing(false);
317                         // Load all default settings.
318 //                      theApp.ResetOptions();
319                 }
320                 else if (param == _T("minimize"))
321                 {
322                         // -minimize means minimize the main window.
323                         m_nCmdShow = MINIMIZE;
324                 }
325                 else if (param == _T("maximize"))
326                 {
327                         // -maximize means maximize the main window.
328                         m_nCmdShow = MAXIMIZE;
329                 }
330                 else if (param == _T("prediffer"))
331                 {
332                         // Get prediffer if specified (otherwise prediffer will be blank, which is default)
333                         q = EatParam(q, m_sPreDiffer);
334                 }
335                 else if (param == _T("wl"))
336                 {
337                         // -wl to open left path as read-only
338                         m_dwLeftFlags |= FFILEOPEN_READONLY;
339                 }
340                 else if (param == _T("wm"))
341                 {
342                         // -wm to open middle path as read-only
343                         m_dwMiddleFlags |= FFILEOPEN_READONLY;
344                 }
345                 else if (param == _T("wr"))
346                 {
347                         // -wr to open right path as read-only
348                         m_dwRightFlags |= FFILEOPEN_READONLY;
349                 }
350                 else if (param == _T("ul"))
351                 {
352                         // -ul to not add left path to MRU
353                         m_dwLeftFlags |= FFILEOPEN_NOMRU;
354                 }
355                 else if (param == _T("um"))
356                 {
357                         // -um to not add middle path to MRU
358                         m_dwMiddleFlags |= FFILEOPEN_NOMRU;
359                 }
360                 else if (param == _T("ur"))
361                 {
362                         // -ur to not add right path to MRU
363                         m_dwRightFlags |= FFILEOPEN_NOMRU;
364                 }
365                 else if (param == _T("u") || param == _T("ub"))
366                 {
367                         // -u or -ub (deprecated) to add neither right nor left path to MRU
368                         m_dwLeftFlags |= FFILEOPEN_NOMRU;
369                         m_dwMiddleFlags |= FFILEOPEN_NOMRU;
370                         m_dwRightFlags |= FFILEOPEN_NOMRU;
371                 }
372                 else if (param == _T("fl"))
373                 {
374                         // -fl to set focus to the left panbe
375                         m_dwLeftFlags |= FFILEOPEN_SETFOCUS;
376                 }
377                 else if (param == _T("fm"))
378                 {
379                         // -fm to set focus to the middle pane
380                         m_dwMiddleFlags |= FFILEOPEN_SETFOCUS;
381                 }
382                 else if (param == _T("fr"))
383                 {
384                         // -fr to set focus to the right pane
385                         m_dwRightFlags |= FFILEOPEN_SETFOCUS;
386                 }
387                 else if (param == _T("al"))
388                 {
389                         // -al to auto-merge at the left pane
390                         m_dwLeftFlags |= FFILEOPEN_AUTOMERGE;
391                 }
392                 else if (param == _T("am"))
393                 {
394                         // -am to auto-merge at the middle pane
395                         m_dwMiddleFlags |= FFILEOPEN_AUTOMERGE;
396                 }
397                 else if (param == _T("ar"))
398                 {
399                         // -ar to auto-merge at the right pane
400                         m_dwRightFlags |= FFILEOPEN_AUTOMERGE;
401                 }
402                 else if (param == _T("x"))
403                 {
404                         // -x to close application if files are identical.
405                         m_bExitIfNoDiff = Exit;
406                 }
407                 else if (param == _T("xq"))
408                 {
409                         // -xn to close application if files are identical without showing
410                         // any messages
411                         m_bExitIfNoDiff = ExitQuiet;
412                 }
413                 else if (param == _T("cp"))
414                 {
415                         String codepage;
416                         q = EatParam(q, codepage);
417                         try { m_nCodepage = string_stoi(codepage); } catch (...) { /* FIXME: */ }
418                 }
419                 else if (param == _T("ignorews"))
420                 {
421                         q = SetOption(q, OPT_CMP_IGNORE_WHITESPACE);
422                 }
423                 else if (param == _T("ignoreblanklines"))
424                 {
425                         q = SetOption(q, OPT_CMP_IGNORE_BLANKLINES);
426                 }
427                 else if (param == _T("ignorecase"))
428                 {
429                         q = SetOption(q, OPT_CMP_IGNORE_CASE);
430                 }
431                 else if (param == _T("ignoreeol"))
432                 {
433                         q = SetOption(q, OPT_CMP_IGNORE_EOL);
434                 }
435         }
436         // If "compare file dir" make it "compare file dir\file".
437         if (m_Files.GetSize() >= 2)
438         {
439                 PATH_EXISTENCE p1 = paths_DoesPathExist(m_Files[0]);
440                 PATH_EXISTENCE p2 = paths_DoesPathExist(m_Files[1]);
441
442                 if ((p1 == IS_EXISTING_FILE) && (p2 == IS_EXISTING_DIR))
443                 {
444                         m_Files[1] = paths_ConcatPath(m_Files[1], paths_FindFileName(m_Files[0]));
445                 }
446         }
447         if (m_bShowUsage)
448         {
449                 m_bNonInteractive = false;
450         }
451 }