OSDN Git Service

Reduce compilation time (2)
[winmerge-jp/winmerge-jp.git] / Src / FileTransform.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean 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  *  @file FileTransform.cpp
23  *
24  *  @brief Implementation of file transformations
25  */ 
26
27 #include "pch.h"
28 #include "FileTransform.h"
29 #include <vector>
30 #include <Poco/Exception.h>
31 #include "Plugins.h"
32 #include "multiformatText.h"
33 #include "UniMarkdownFile.h"
34 #include "Environment.h"
35 #include "TFile.h"
36
37 using Poco::Exception;
38
39 namespace FileTransform
40 {
41
42 PLUGIN_MODE g_UnpackerMode = PLUGIN_MANUAL;
43 PLUGIN_MODE g_PredifferMode = PLUGIN_MANUAL;
44
45
46
47
48
49 ////////////////////////////////////////////////////////////////////////////////
50 // transformations : packing unpacking
51
52 // known handler
53 bool Packing(String & filepath, PackingInfo handler)
54 {
55         // no handler : return true
56         if (handler.m_PluginName.empty())
57                 return true;
58
59         storageForPlugins bufferData;
60         bufferData.SetDataFileAnsi(filepath);
61
62         // control value
63         bool bHandled = false;
64
65         PluginInfo * plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"FILE_PACK_UNPACK", handler.m_PluginName);
66         if (plugin == nullptr)
67                 plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"FILE_FOLDER_PACK_UNPACK", handler.m_PluginName);
68         if (plugin == nullptr)
69                 plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"BUFFER_PACK_UNPACK", handler.m_PluginName);
70         LPDISPATCH piScript = plugin->m_lpDispatch;
71         if (handler.m_bWithFile)
72         {
73                 // use a temporary dest name
74                 String srcFileName = bufferData.GetDataFileAnsi(); // <-Call order is important
75                 String dstFileName = bufferData.GetDestFileName(); // <-Call order is important
76                 bHandled = plugin::InvokePackFile(srcFileName,
77                         dstFileName,
78                         bufferData.GetNChanged(),
79                         piScript, handler.m_subcode);
80                 if (bHandled)
81                         bufferData.ValidateNewFile();
82         }
83         else
84         {
85                 bHandled = plugin::InvokePackBuffer(*bufferData.GetDataBufferAnsi(),
86                         bufferData.GetNChanged(),
87                         piScript, handler.m_subcode);
88                 if (bHandled)
89                         bufferData.ValidateNewBuffer();
90         }
91
92         // if this packer does not work, that is an error
93         if (!bHandled)
94                 return false;
95
96         // if the buffer changed, write it before leaving
97         bool bSuccess = true;
98         if (bufferData.GetNChangedValid() > 0)
99         {
100                 bSuccess = bufferData.SaveAsFile(filepath);
101         }
102
103         return bSuccess;
104 }
105
106 // known handler
107 bool Unpacking(String & filepath, const PackingInfo * handler, int * handlerSubcode)
108 {
109         // no handler : return true
110         if (handler == nullptr || handler->m_PluginName.empty())
111                 return true;
112
113         storageForPlugins bufferData;
114         bufferData.SetDataFileAnsi(filepath);
115
116         // temporary subcode 
117         int subcode;
118
119         // control value
120         bool bHandled = false;
121
122         PluginInfo * plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"FILE_PACK_UNPACK", handler->m_PluginName);
123         if (plugin == nullptr)
124                 plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"FILE_FOLDER_PACK_UNPACK", handler->m_PluginName);
125         if (plugin == nullptr)
126                 plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"BUFFER_PACK_UNPACK", handler->m_PluginName);
127         if (plugin == nullptr)
128                 return false;
129
130         LPDISPATCH piScript = plugin->m_lpDispatch;
131         if (handler->m_bWithFile)
132         {
133                 // use a temporary dest name
134                 String srcFileName = bufferData.GetDataFileAnsi(); // <-Call order is important
135                 String dstFileName = bufferData.GetDestFileName(); // <-Call order is important
136                 bHandled = plugin::InvokeUnpackFile(srcFileName,
137                         dstFileName,
138                         bufferData.GetNChanged(),
139                         piScript, subcode);
140                 if (bHandled)
141                         bufferData.ValidateNewFile();
142         }
143         else
144         {
145                 bHandled = plugin::InvokeUnpackBuffer(*bufferData.GetDataBufferAnsi(),
146                         bufferData.GetNChanged(),
147                         piScript, subcode);
148                 if (bHandled)
149                         bufferData.ValidateNewBuffer();
150         }
151
152         // if this unpacker does not work, that is an error
153         if (!bHandled)
154                 return false;
155
156         // valid the subcode
157         *handlerSubcode = subcode;
158
159         // if the buffer changed, write it before leaving
160         bool bSuccess = true;
161         if (bufferData.GetNChangedValid() > 0)
162         {
163                 bSuccess = bufferData.SaveAsFile(filepath);
164         }
165
166         return bSuccess;
167 }
168
169
170 // scan plugins for the first handler
171 bool Unpacking(String & filepath, const String& filteredText, PackingInfo * handler, int * handlerSubcode)
172 {
173         // PLUGIN_BUILTIN_XML : read source file through custom UniFile
174         if (handler->m_PluginOrPredifferMode == PLUGIN_BUILTIN_XML)
175         {
176                 handler->m_pufile = new UniMarkdownFile;
177                 handler->m_textType = _T("xml");
178                 handler->m_bDisallowMixedEOL = true;
179                 handler->m_PluginName.erase(); // Make FileTransform_Packing() a NOP
180                 // Leave eToBeScanned alone so above lines will continue to execute on
181                 // subsequent calls to this function
182                 return true;
183         }
184
185         storageForPlugins bufferData;
186         bufferData.SetDataFileAnsi(filepath);
187
188         // control value
189         bool bHandled = false;
190
191         PluginInfo * plugin = CAllThreadsScripts::GetActiveSet()->GetAutomaticPluginByFilter(L"FILE_PACK_UNPACK", filteredText);
192         if (plugin == nullptr)
193                 plugin = CAllThreadsScripts::GetActiveSet()->GetAutomaticPluginByFilter(L"FILE_FOLDER_PACK_UNPACK", filteredText);
194         if (plugin != nullptr)
195         {
196                 handler->m_PluginName = plugin->m_name;
197                 handler->m_bWithFile = true;
198                 // use a temporary dest name
199                 String srcFileName = bufferData.GetDataFileAnsi(); // <-Call order is important
200                 String dstFileName = bufferData.GetDestFileName(); // <-Call order is important
201                 bHandled = plugin::InvokeUnpackFile(srcFileName,
202                         dstFileName,
203                         bufferData.GetNChanged(),
204                         plugin->m_lpDispatch, handler->m_subcode);
205                 if (bHandled)
206                         bufferData.ValidateNewFile();
207         }
208
209         // We can not assume that the file is text, so use a safearray and not a BSTR
210         // TODO : delete this event ?   Is anyone going to use this ?
211
212         if (!bHandled)
213         {
214                 plugin = CAllThreadsScripts::GetActiveSet()->GetAutomaticPluginByFilter(L"BUFFER_PACK_UNPACK", filteredText);
215                 if (plugin != nullptr)
216                 {
217                         handler->m_PluginName = plugin->m_name;
218                         handler->m_bWithFile = false;
219                         bHandled = plugin::InvokeUnpackBuffer(*bufferData.GetDataBufferAnsi(),
220                                 bufferData.GetNChanged(),
221                                 plugin->m_lpDispatch, handler->m_subcode);
222                         if (bHandled)
223                                 bufferData.ValidateNewBuffer();
224                 }
225         }
226
227         if (!bHandled)
228         {
229                 // we didn't find any unpacker, just hope it is normal Ansi/Unicode
230                 handler->m_PluginName = _T("");
231                 handler->m_subcode = 0;
232                 bHandled = true;
233         }
234
235         // the handler is now defined
236         handler->m_PluginOrPredifferMode = PLUGIN_MANUAL;
237
238         // assign the sucode
239         *handlerSubcode = handler->m_subcode;
240
241         // if the buffer changed, write it before leaving
242         bool bSuccess = true;
243         if (bufferData.GetNChangedValid() > 0)
244         {
245                 bSuccess = bufferData.SaveAsFile(filepath);
246         }
247
248         return bSuccess;
249 }
250
251 bool Unpacking(PackingInfo *handler, String& filepath, const String& filteredText)
252 {
253         if (handler->m_PluginOrPredifferMode != PLUGIN_MANUAL)
254                 return Unpacking(filepath, filteredText, handler, &handler->m_subcode);
255         else
256                 return Unpacking(filepath, handler, &handler->m_subcode);
257 }
258
259 ////////////////////////////////////////////////////////////////////////////////
260 // transformation prediffing
261     
262 // known handler
263 bool Prediffing(String & filepath, PrediffingInfo handler, bool bMayOverwrite)
264 {
265         // no handler : return true
266         if (handler.m_PluginName.empty())
267                 return true;
268
269         storageForPlugins bufferData;
270         // detect Ansi or Unicode file
271         bufferData.SetDataFileUnknown(filepath, bMayOverwrite);
272         // TODO : set the codepage
273         // bufferData.SetCodepage();
274
275         // control value
276         bool bHandled = false;
277
278         PluginInfo * plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"FILE_PREDIFF", handler.m_PluginName);
279         if (plugin == nullptr)
280         {
281                 plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"BUFFER_PREDIFF", handler.m_PluginName);
282                 if (plugin == nullptr)
283                         return false;
284         }
285         LPDISPATCH piScript = plugin->m_lpDispatch;
286         if (handler.m_bWithFile)
287         {
288                 // use a temporary dest name
289                 String srcFileName = bufferData.GetDataFileAnsi(); // <-Call order is important
290                 String dstFileName = bufferData.GetDestFileName(); // <-Call order is important
291                 bHandled = plugin::InvokePrediffFile(srcFileName,
292                         dstFileName,
293                         bufferData.GetNChanged(),
294                         piScript);
295                 if (bHandled)
296                         bufferData.ValidateNewFile();
297         }
298         else
299         {
300                 // probably it is for VB/VBscript so use a BSTR as argument
301                 bHandled = plugin::InvokePrediffBuffer(*bufferData.GetDataBufferUnicode(),
302                         bufferData.GetNChanged(),
303                         piScript);
304                 if (bHandled)
305                         bufferData.ValidateNewBuffer();
306         }
307
308         // if this unpacker does not work, that is an error
309         if (!bHandled)
310                 return false;
311
312         // if the buffer changed, write it before leaving
313         bool bSuccess = true;
314         if (bufferData.GetNChangedValid() > 0)
315         {
316                 // bufferData changes filepath here to temp filepath
317                 bSuccess = bufferData.SaveAsFile(filepath);
318         }
319
320         return bSuccess;
321 }
322
323
324 // scan plugins for the first handler
325 bool Prediffing(String & filepath, const String& filteredText, PrediffingInfo * handler, bool bMayOverwrite)
326 {
327         storageForPlugins bufferData;
328         // detect Ansi or Unicode file
329         bufferData.SetDataFileUnknown(filepath, bMayOverwrite);
330         // TODO : set the codepage
331         // bufferData.SetCodepage();
332
333         // control value
334         bool bHandled = false;
335
336         PluginInfo * plugin = CAllThreadsScripts::GetActiveSet()->GetAutomaticPluginByFilter(L"FILE_PREDIFF", filteredText);
337         if (plugin != nullptr)
338         {
339                 handler->m_PluginName = plugin->m_name;
340                 handler->m_bWithFile = true;
341                 // use a temporary dest name
342                 String srcFileName = bufferData.GetDataFileAnsi(); // <-Call order is important
343                 String dstFileName = bufferData.GetDestFileName(); // <-Call order is important
344                 bHandled = plugin::InvokePrediffFile(srcFileName,
345                         dstFileName,
346                         bufferData.GetNChanged(),
347                         plugin->m_lpDispatch);
348                 if (bHandled)
349                         bufferData.ValidateNewFile();
350         }
351
352         if (!bHandled)
353         {
354                 plugin = CAllThreadsScripts::GetActiveSet()->GetAutomaticPluginByFilter(L"BUFFER_PREDIFF", filteredText);
355                 if (plugin != nullptr)
356                 {
357                         handler->m_PluginName = plugin->m_name;
358                         handler->m_bWithFile = false;
359                         // probably it is for VB/VBscript so use a BSTR as argument
360                         bHandled = plugin::InvokePrediffBuffer(*bufferData.GetDataBufferUnicode(),
361                                 bufferData.GetNChanged(),
362                                 plugin->m_lpDispatch);
363                         if (bHandled)
364                                 bufferData.ValidateNewBuffer();
365                 }
366         }
367
368         if (!bHandled)
369         {
370                 // we didn't find any prediffer, that is OK anyway
371                 handler->m_PluginName = _T("");
372                 bHandled = true;
373         }
374
375         // the handler is now defined
376         handler->m_PluginOrPredifferMode = PLUGIN_MANUAL;
377
378         // if the buffer changed, write it before leaving
379         bool bSuccess = true;
380         if (bufferData.GetNChangedValid() > 0)
381         {
382                 bSuccess = bufferData.SaveAsFile(filepath);
383         }
384
385         return bSuccess;
386 }
387
388 bool Prediffing(PrediffingInfo * handler, String & filepath, const String& filteredText, bool bMayOverwrite)
389 {
390         if (handler->m_PluginOrPredifferMode != PLUGIN_MANUAL)
391                 return Prediffing(filepath, filteredText, handler, bMayOverwrite);
392         else
393                 return Prediffing(filepath, *handler, bMayOverwrite);
394 }
395
396
397 ////////////////////////////////////////////////////////////////////////////////
398
399 bool AnyCodepageToUTF8(int codepage, String & filepath, bool bMayOverwrite)
400 {
401         String tempDir = env::GetTemporaryPath();
402         if (tempDir.empty())
403                 return false;
404         String tempFilepath = env::GetTemporaryFileName(tempDir, _T("_W3"));
405         if (tempFilepath.empty())
406                 return false;
407         // TODO : is it better with the BOM or without (just change the last argument)
408         int nFileChanged = 0;
409         bool bSuccess = ::AnyCodepageToUTF8(codepage, filepath, tempFilepath, nFileChanged, false); 
410         if (bSuccess && nFileChanged!=0)
411         {
412                 // we do not overwrite so we delete the old file
413                 if (bMayOverwrite)
414                 {
415                         try
416                         {
417                                 TFile(filepath).remove();
418                         }
419                         catch (Exception& e)
420                         {
421                                 LogErrorStringUTF8(e.displayText());
422                         }
423                 }
424                 // and change the filepath if everything works
425                 filepath = tempFilepath;
426         }
427         else
428         {
429                 try
430                 {
431                         TFile(tempFilepath).remove();
432                 }
433                 catch (Exception& e)
434                 {
435                         LogErrorStringUTF8(e.displayText());
436                 }
437         }
438
439         return bSuccess;
440 }
441
442
443 ////////////////////////////////////////////////////////////////////////////////
444 // transformation : TextTransform_Interactive (editor scripts)
445
446 void GetFreeFunctionsInScripts(std::vector<String>& sNamesArray, const wchar_t *TransformationEvent)
447 {
448         // get an array with the available scripts
449         PluginArray * piScriptArray = 
450                 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(TransformationEvent);
451
452         // fill in these structures
453         int nFnc = 0;   
454         size_t iScript;
455         for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++)
456         {
457                 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
458                 if (plugin->m_disabled)
459                         continue;
460                 LPDISPATCH piScript = plugin->m_lpDispatch;
461                 std::vector<String> scriptNamesArray;
462                 std::vector<int> scriptIdsArray;
463                 int nScriptFnc = plugin::GetMethodsFromScript(piScript, scriptNamesArray, scriptIdsArray);
464                 sNamesArray.resize(nFnc+nScriptFnc);
465
466                 int iFnc;
467                 for (iFnc = 0 ; iFnc < nScriptFnc ; iFnc++)
468                         sNamesArray[nFnc+iFnc] = scriptNamesArray[iFnc];
469
470                 nFnc += nScriptFnc;
471         }
472 }
473
474 bool Interactive(String & text, const wchar_t *TransformationEvent, int iFncChosen)
475 {
476         if (iFncChosen < 0)
477                 return false;
478
479         // get an array with the available scripts
480         PluginArray * piScriptArray = 
481                 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(TransformationEvent);
482
483         size_t iScript;
484         for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++)
485         {
486                 if (iFncChosen < piScriptArray->at(iScript)->m_nFreeFunctions)
487                         // we have found the script file
488                         break;
489                 iFncChosen -= piScriptArray->at(iScript)->m_nFreeFunctions;
490         }
491
492         if (iScript >= piScriptArray->size())
493                 return false;
494
495         // iFncChosen is the index of the function in the script file
496         // we must convert it to the function ID
497         int fncID = plugin::GetMethodIDInScript(piScriptArray->at(iScript)->m_lpDispatch, iFncChosen);
498
499         // execute the transform operation
500         int nChanged = 0;
501         plugin::InvokeTransformText(text, nChanged, piScriptArray->at(iScript)->m_lpDispatch, fncID);
502
503         return (nChanged != 0);
504 }
505
506 }
507
508 ////////////////////////////////////////////////////////////////////////////////