OSDN Git Service

Update CWindowsManagerDialog - check some pointers for null and made … (#824) (2)
[winmerge-jp/winmerge-jp.git] / Src / MergeCmdLineInfo.cpp
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 /////////////////////////////////////////////////////////////////////////////
6 /** 
7  * @file  MergeCmdLineInfo.cpp
8  *
9  * @brief MergeCmdLineInfo class implementation.
10  *
11  */
12
13
14 #include "pch.h"
15 #include "MergeCmdLineInfo.h"
16 #include "Constants.h"
17 #include "Paths.h"
18 #include "OptionsDef.h"
19 #include "unicoder.h"
20
21 // MergeCmdLineInfo
22
23 /**
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.
29  */
30 const TCHAR *MergeCmdLineInfo::EatParam(const TCHAR *p, String &param, bool *flag /*= nullptr*/)
31 {
32         if (p != nullptr && *(p += _tcsspn(p, _T(" \t\r\n"))) == _T('\0'))
33                 p = nullptr;
34         const TCHAR *q = p;
35         if (q != nullptr)
36         {
37                 TCHAR c = *q;
38                 bool quoted = false;
39                 do
40                 {
41                         if (c == _T('"'))
42                                 quoted = !quoted;
43                         c = *++q;
44                 } while (c != _T('\0') && (quoted ||
45                         c != _T(' ') && c != _T('\t') && c != _T('\r') && c != _T('\n')));
46         }
47         if (q > p && flag)
48         {
49                 if (*p == _T('-') || *p == _T('/'))
50                 {
51                         *flag = true;
52                         ++p;
53                         for (const TCHAR *i = q; i >= p; --i)
54                                 if (*i == ':')
55                                 {
56                                         q = i;
57                                         break;
58                                 }
59                 }
60                 else
61                 {
62                         *flag = false;
63                         flag = nullptr;
64                 }
65         }
66         param.assign(p ? p : _T(""), q - p);
67         if (q > p && flag != nullptr)
68         {
69                 param = strutils::makelower(param);
70         }
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);
74         return q;
75 }
76
77 /**
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.
83  */
84 const TCHAR *MergeCmdLineInfo::SetOption(const TCHAR *q, const String& key, const TCHAR *value)
85 {
86         String s;
87         if (*q == _T(':'))
88         {
89                 q = EatParam(q, s);
90                 value = s.c_str() + 1;
91         }
92         m_Options.insert_or_assign(key, value);
93         return q;
94 }
95
96 const TCHAR *MergeCmdLineInfo::SetConfig(const TCHAR *q)
97 {
98         String s;
99         if (*q == ':')
100                 ++q;
101         q = EatParam(q, s);
102         size_t pos = s.find_first_of('=');
103         if (pos != String::npos)
104         {
105                 String key = s.substr(0, pos);
106                 String value = s.c_str() + pos + 1;
107                 m_Options.insert_or_assign(key, value);
108         }
109         return q;
110 }
111
112 /**
113  * @brief MergeCmdLineParser'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_bEscShutdown(false),
119         m_bExitIfNoDiff(Disabled),
120         m_bRecurse(false),
121         m_bNonInteractive(false),
122         m_nSingleInstance(),
123         m_bShowUsage(false),
124         m_bNoPrefs(false),
125         m_nCodepage(0),
126         m_bSelfCompare(false),
127         m_dwLeftFlags(FFILEOPEN_NONE),
128         m_dwMiddleFlags(FFILEOPEN_NONE),
129         m_dwRightFlags(FFILEOPEN_NONE)
130 {
131         String exeName;
132         q = EatParam(q, exeName);
133         ParseWinMergeCmdLine(q);
134 }
135
136 /**
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.
141  */
142 void MergeCmdLineInfo::AddPath(const String &path)
143 {
144         String param(path);
145
146         // Set flag indicating path is from command line
147         const size_t ord = m_Files.GetSize();
148         if (ord == 0)
149                 m_dwLeftFlags |= FFILEOPEN_CMDLINE;
150         else if (ord == 1)
151                 m_dwRightFlags |= FFILEOPEN_CMDLINE;
152         else if (ord == 2)
153                 m_dwMiddleFlags |= FFILEOPEN_CMDLINE;
154
155         if (!paths::IsURLorCLSID(path))
156         {
157                 // Convert paths given in Linux-style ('/' as separator) given from
158                 // Cygwin to Windows style ('\' as separator)
159                 strutils::replace(param, _T("/"), _T("\\"));
160
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);
166         }
167         else
168         {
169                 m_Files.SetPath(m_Files.GetSize(), param, false);
170         }
171 }
172
173 /**
174  * @brief Parse native WinMerge command line.
175  * @param [in] p Points into the command line.
176  */
177 void MergeCmdLineInfo::ParseWinMergeCmdLine(const TCHAR *q)
178 {
179         String param;
180         bool flag;
181
182         while ((q = EatParam(q, param, &flag)) != 0)
183         {
184                 if (!flag)
185                 {
186                         // Its not a flag so it is a path
187                         AddPath(param);
188                 }
189                 else if (param == _T("?"))
190                 {
191                         // -? to show common command line arguments.
192                         m_bShowUsage = true;
193                 }
194                 else if (param == _T("o"))
195                 {
196                         // -o "outputfilename"
197                         q = EatParam(q, m_sOutputpath);
198                 }
199                 else if (param == _T("or"))
200                 {
201                         // -or "reportfilename"
202                         q = EatParam(q, m_sReportFile);
203                 }
204                 else if (param == _T("dl"))
205                 {
206                         // -dl "desc" - description for left file
207                         q = EatParam(q, m_sLeftDesc);
208                 }
209                 else if (param == _T("dm"))
210                 {
211                         // -dr "desc" - description for middle file
212                         q = EatParam(q, m_sMiddleDesc);
213                 }
214                 else if (param == _T("dr"))
215                 {
216                         // -dr "desc" - description for right file
217                         q = EatParam(q, m_sRightDesc);
218                 }
219                 else if (param == _T("e"))
220                 {
221                         // -e to allow closing with single esc press
222                         m_bEscShutdown = true;
223                 }
224                 else if (param == _T("f"))
225                 {
226                         // -f "mask" - file filter mask ("*.h *.cpp")
227                         q = EatParam(q, m_sFileFilter);
228                 }
229                 else if (param == _T("m"))
230                 {
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;
250                         else
251                                 m_sErrorMessages.push_back(_T("Unknown compare method '") + param + _T("' specified"));
252                 }
253                 else if (param == _T("r"))
254                 {
255                         // -r to compare recursively
256                         m_bRecurse = true;
257                 }
258                 else if (param == _T("s-"))
259                 {
260                         // -s- to not allow only one instance
261                         m_nSingleInstance = 0;
262                 }
263                 else if (param == _T("sw"))
264                 {
265                         // -sw to allow only one instance and wait for the instance to terminate
266                         m_nSingleInstance = 2;
267                 }
268                 else if (param == _T("s"))
269                 {
270                         // -s to allow only one instance
271                         if (*q == ':')
272                         {
273                                 q = EatParam(q + 1, param);
274                                 m_nSingleInstance = _ttoi(param.c_str());
275                         }
276                                 
277                         else
278                                 m_nSingleInstance = 1;
279                 }
280                 else if (param == _T("noninteractive"))
281                 {
282                         // -noninteractive to suppress message boxes & close with result code
283                         m_bNonInteractive = true;
284                 }
285                 else if (param == _T("noprefs"))
286                 {
287                         // -noprefs means do not load or remember options (preferences)
288                         m_bNoPrefs = true;
289                 }
290                 else if (param == _T("self-compare"))
291                 {
292                         // -self-compare means compare a specified file with a copy of the file
293                         m_bSelfCompare = true;
294                 }
295                 else if (param == _T("minimize"))
296                 {
297                         // -minimize means minimize the main window.
298                         m_nCmdShow = MINIMIZE;
299                 }
300                 else if (param == _T("maximize"))
301                 {
302                         // -maximize means maximize the main window.
303                         m_nCmdShow = MAXIMIZE;
304                 }
305                 else if (param == _T("prediffer"))
306                 {
307                         // Get prediffer if specified (otherwise prediffer will be blank, which is default)
308                         q = EatParam(q, m_sPreDiffer);
309                 }
310                 else if (param == _T("unpacker"))
311                 {
312                         // Get unpacker if specified (otherwise unpacker will be blank, which is default)
313                         q = EatParam(q, m_sUnpacker);
314                 }
315                 else if (param == _T("wl"))
316                 {
317                         // -wl to open left path as read-only
318                         m_dwLeftFlags |= FFILEOPEN_READONLY;
319                 }
320                 else if (param == _T("wm"))
321                 {
322                         // -wm to open middle path as read-only
323                         m_dwMiddleFlags |= FFILEOPEN_READONLY;
324                 }
325                 else if (param == _T("wr"))
326                 {
327                         // -wr to open right path as read-only
328                         m_dwRightFlags |= FFILEOPEN_READONLY;
329                 }
330                 else if (param == _T("ul"))
331                 {
332                         // -ul to not add left path to MRU
333                         m_dwLeftFlags |= FFILEOPEN_NOMRU;
334                 }
335                 else if (param == _T("um"))
336                 {
337                         // -um to not add middle path to MRU
338                         m_dwMiddleFlags |= FFILEOPEN_NOMRU;
339                 }
340                 else if (param == _T("ur"))
341                 {
342                         // -ur to not add right path to MRU
343                         m_dwRightFlags |= FFILEOPEN_NOMRU;
344                 }
345                 else if (param == _T("u") || param == _T("ub"))
346                 {
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;
351                 }
352                 else if (param == _T("fl"))
353                 {
354                         // -fl to set focus to the left panbe
355                         m_dwLeftFlags |= FFILEOPEN_SETFOCUS;
356                 }
357                 else if (param == _T("fm"))
358                 {
359                         // -fm to set focus to the middle pane
360                         m_dwMiddleFlags |= FFILEOPEN_SETFOCUS;
361                 }
362                 else if (param == _T("fr"))
363                 {
364                         // -fr to set focus to the right pane
365                         m_dwRightFlags |= FFILEOPEN_SETFOCUS;
366                 }
367                 else if (param == _T("al"))
368                 {
369                         // -al to auto-merge at the left pane
370                         m_dwLeftFlags |= FFILEOPEN_AUTOMERGE;
371                 }
372                 else if (param == _T("am"))
373                 {
374                         // -am to auto-merge at the middle pane
375                         m_dwMiddleFlags |= FFILEOPEN_AUTOMERGE;
376                 }
377                 else if (param == _T("ar"))
378                 {
379                         // -ar to auto-merge at the right pane
380                         m_dwRightFlags |= FFILEOPEN_AUTOMERGE;
381                 }
382                 else if (param == _T("x"))
383                 {
384                         // -x to close application if files are identical.
385                         m_bExitIfNoDiff = Exit;
386                 }
387                 else if (param == _T("xq"))
388                 {
389                         // -xn to close application if files are identical without showing
390                         // any messages
391                         m_bExitIfNoDiff = ExitQuiet;
392                 }
393                 else if (param == _T("cp"))
394                 {
395                         String codepage;
396                         q = EatParam(q, codepage);
397                         m_nCodepage = atoi(ucr::toUTF8(codepage).c_str());
398                 }
399                 else if (param == _T("ignorews"))
400                 {
401                         q = SetOption(q, OPT_CMP_IGNORE_WHITESPACE);
402                 }
403                 else if (param == _T("ignoreblanklines"))
404                 {
405                         q = SetOption(q, OPT_CMP_IGNORE_BLANKLINES);
406                 }
407                 else if (param == _T("ignorecase"))
408                 {
409                         q = SetOption(q, OPT_CMP_IGNORE_CASE);
410                 }
411                 else if (param == _T("ignoreeol"))
412                 {
413                         q = SetOption(q, OPT_CMP_IGNORE_EOL);
414                 }
415                 else if (param == _T("ignorecodepage"))
416                 {
417                         q = SetOption(q, OPT_CMP_IGNORE_CODEPAGE);
418                 }
419                 else if (param == _T("ignorecomments"))
420                 {
421                         q = SetOption(q, OPT_CMP_FILTER_COMMENTLINES);
422                 }
423                 else if (param == _T("cfg") || param == _T("config"))
424                 {
425                         q = SetConfig(q);
426                 }
427                 else
428                 {
429                         m_sErrorMessages.push_back(_T("Unknown option '/") + param + _T("'"));
430                 }
431         }
432         // If "compare file dir" make it "compare file dir\file".
433         if (m_Files.GetSize() >= 2)
434         {
435                 paths::PATH_EXISTENCE p1 = paths::DoesPathExist(m_Files[0]);
436                 paths::PATH_EXISTENCE p2 = paths::DoesPathExist(m_Files[1]);
437
438                 if ((p1 == paths::IS_EXISTING_FILE) && (p2 == paths::IS_EXISTING_DIR))
439                 {
440                         m_Files[1] = paths::ConcatPath(m_Files[1], paths::FindFileName(m_Files[0]));
441                 }
442         }
443         if (m_bShowUsage)
444         {
445                 m_bNonInteractive = false;
446         }
447 }