1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
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.
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.
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.
20 /////////////////////////////////////////////////////////////////////////////
22 * @file FileTransform.cpp
24 * @brief Implementation of file transformations
28 #include "FileTransform.h"
30 #include <Poco/Exception.h>
32 #include "multiformatText.h"
33 #include "UniMarkdownFile.h"
34 #include "Environment.h"
37 using Poco::Exception;
39 namespace FileTransform
42 PLUGIN_MODE g_UnpackerMode = PLUGIN_MANUAL;
43 PLUGIN_MODE g_PredifferMode = PLUGIN_MANUAL;
49 ////////////////////////////////////////////////////////////////////////////////
50 // transformations : packing unpacking
53 bool Packing(String & filepath, PackingInfo handler)
55 // no handler : return true
56 if (handler.m_PluginName.empty())
59 storageForPlugins bufferData;
60 bufferData.SetDataFileAnsi(filepath);
63 bool bHandled = false;
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)
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,
78 bufferData.GetNChanged(),
79 piScript, handler.m_subcode);
81 bufferData.ValidateNewFile();
85 bHandled = plugin::InvokePackBuffer(*bufferData.GetDataBufferAnsi(),
86 bufferData.GetNChanged(),
87 piScript, handler.m_subcode);
89 bufferData.ValidateNewBuffer();
92 // if this packer does not work, that is an error
96 // if the buffer changed, write it before leaving
98 if (bufferData.GetNChangedValid() > 0)
100 bSuccess = bufferData.SaveAsFile(filepath);
107 bool Unpacking(String & filepath, const PackingInfo * handler, int * handlerSubcode)
109 // no handler : return true
110 if (handler == nullptr || handler->m_PluginName.empty())
113 storageForPlugins bufferData;
114 bufferData.SetDataFileAnsi(filepath);
120 bool bHandled = false;
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)
130 LPDISPATCH piScript = plugin->m_lpDispatch;
131 if (handler->m_bWithFile)
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,
138 bufferData.GetNChanged(),
141 bufferData.ValidateNewFile();
145 bHandled = plugin::InvokeUnpackBuffer(*bufferData.GetDataBufferAnsi(),
146 bufferData.GetNChanged(),
149 bufferData.ValidateNewBuffer();
152 // if this unpacker does not work, that is an error
157 *handlerSubcode = subcode;
159 // if the buffer changed, write it before leaving
160 bool bSuccess = true;
161 if (bufferData.GetNChangedValid() > 0)
163 bSuccess = bufferData.SaveAsFile(filepath);
170 // scan plugins for the first handler
171 bool Unpacking(String & filepath, const String& filteredText, PackingInfo * handler, int * handlerSubcode)
173 // PLUGIN_BUILTIN_XML : read source file through custom UniFile
174 if (handler->m_PluginOrPredifferMode == PLUGIN_BUILTIN_XML)
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
185 storageForPlugins bufferData;
186 bufferData.SetDataFileAnsi(filepath);
189 bool bHandled = false;
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)
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,
203 bufferData.GetNChanged(),
204 plugin->m_lpDispatch, handler->m_subcode);
206 bufferData.ValidateNewFile();
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 ?
214 plugin = CAllThreadsScripts::GetActiveSet()->GetAutomaticPluginByFilter(L"BUFFER_PACK_UNPACK", filteredText);
215 if (plugin != nullptr)
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);
223 bufferData.ValidateNewBuffer();
229 // we didn't find any unpacker, just hope it is normal Ansi/Unicode
230 handler->m_PluginName = _T("");
231 handler->m_subcode = 0;
235 // the handler is now defined
236 handler->m_PluginOrPredifferMode = PLUGIN_MANUAL;
239 *handlerSubcode = handler->m_subcode;
241 // if the buffer changed, write it before leaving
242 bool bSuccess = true;
243 if (bufferData.GetNChangedValid() > 0)
245 bSuccess = bufferData.SaveAsFile(filepath);
251 bool Unpacking(PackingInfo *handler, String& filepath, const String& filteredText)
253 if (handler->m_PluginOrPredifferMode != PLUGIN_MANUAL)
254 return Unpacking(filepath, filteredText, handler, &handler->m_subcode);
256 return Unpacking(filepath, handler, &handler->m_subcode);
259 ////////////////////////////////////////////////////////////////////////////////
260 // transformation prediffing
263 bool Prediffing(String & filepath, PrediffingInfo handler, bool bMayOverwrite)
265 // no handler : return true
266 if (handler.m_PluginName.empty())
269 storageForPlugins bufferData;
270 // detect Ansi or Unicode file
271 bufferData.SetDataFileUnknown(filepath, bMayOverwrite);
272 // TODO : set the codepage
273 // bufferData.SetCodepage();
276 bool bHandled = false;
278 PluginInfo * plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"FILE_PREDIFF", handler.m_PluginName);
279 if (plugin == nullptr)
281 plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(L"BUFFER_PREDIFF", handler.m_PluginName);
282 if (plugin == nullptr)
285 LPDISPATCH piScript = plugin->m_lpDispatch;
286 if (handler.m_bWithFile)
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,
293 bufferData.GetNChanged(),
296 bufferData.ValidateNewFile();
300 // probably it is for VB/VBscript so use a BSTR as argument
301 bHandled = plugin::InvokePrediffBuffer(*bufferData.GetDataBufferUnicode(),
302 bufferData.GetNChanged(),
305 bufferData.ValidateNewBuffer();
308 // if this unpacker does not work, that is an error
312 // if the buffer changed, write it before leaving
313 bool bSuccess = true;
314 if (bufferData.GetNChangedValid() > 0)
316 // bufferData changes filepath here to temp filepath
317 bSuccess = bufferData.SaveAsFile(filepath);
324 // scan plugins for the first handler
325 bool Prediffing(String & filepath, const String& filteredText, PrediffingInfo * handler, bool bMayOverwrite)
327 storageForPlugins bufferData;
328 // detect Ansi or Unicode file
329 bufferData.SetDataFileUnknown(filepath, bMayOverwrite);
330 // TODO : set the codepage
331 // bufferData.SetCodepage();
334 bool bHandled = false;
336 PluginInfo * plugin = CAllThreadsScripts::GetActiveSet()->GetAutomaticPluginByFilter(L"FILE_PREDIFF", filteredText);
337 if (plugin != nullptr)
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,
346 bufferData.GetNChanged(),
347 plugin->m_lpDispatch);
349 bufferData.ValidateNewFile();
354 plugin = CAllThreadsScripts::GetActiveSet()->GetAutomaticPluginByFilter(L"BUFFER_PREDIFF", filteredText);
355 if (plugin != nullptr)
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);
364 bufferData.ValidateNewBuffer();
370 // we didn't find any prediffer, that is OK anyway
371 handler->m_PluginName = _T("");
375 // the handler is now defined
376 handler->m_PluginOrPredifferMode = PLUGIN_MANUAL;
378 // if the buffer changed, write it before leaving
379 bool bSuccess = true;
380 if (bufferData.GetNChangedValid() > 0)
382 bSuccess = bufferData.SaveAsFile(filepath);
388 bool Prediffing(PrediffingInfo * handler, String & filepath, const String& filteredText, bool bMayOverwrite)
390 if (handler->m_PluginOrPredifferMode != PLUGIN_MANUAL)
391 return Prediffing(filepath, filteredText, handler, bMayOverwrite);
393 return Prediffing(filepath, *handler, bMayOverwrite);
397 ////////////////////////////////////////////////////////////////////////////////
399 bool AnyCodepageToUTF8(int codepage, String & filepath, bool bMayOverwrite)
401 String tempDir = env::GetTemporaryPath();
404 String tempFilepath = env::GetTemporaryFileName(tempDir, _T("_W3"));
405 if (tempFilepath.empty())
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)
412 // we do not overwrite so we delete the old file
417 TFile(filepath).remove();
421 LogErrorStringUTF8(e.displayText());
424 // and change the filepath if everything works
425 filepath = tempFilepath;
431 TFile(tempFilepath).remove();
435 LogErrorStringUTF8(e.displayText());
443 ////////////////////////////////////////////////////////////////////////////////
444 // transformation : TextTransform_Interactive (editor scripts)
446 void GetFreeFunctionsInScripts(std::vector<String>& sNamesArray, const wchar_t *TransformationEvent)
448 // get an array with the available scripts
449 PluginArray * piScriptArray =
450 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(TransformationEvent);
452 // fill in these structures
455 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++)
457 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
458 if (plugin->m_disabled)
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);
467 for (iFnc = 0 ; iFnc < nScriptFnc ; iFnc++)
468 sNamesArray[nFnc+iFnc] = scriptNamesArray[iFnc];
474 bool Interactive(String & text, const wchar_t *TransformationEvent, int iFncChosen)
479 // get an array with the available scripts
480 PluginArray * piScriptArray =
481 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(TransformationEvent);
484 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++)
486 if (iFncChosen < piScriptArray->at(iScript)->m_nFreeFunctions)
487 // we have found the script file
489 iFncChosen -= piScriptArray->at(iScript)->m_nFreeFunctions;
492 if (iScript >= piScriptArray->size())
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);
499 // execute the transform operation
501 plugin::InvokeTransformText(text, nChanged, piScriptArray->at(iScript)->m_lpDispatch, fncID);
503 return (nChanged != 0);
508 ////////////////////////////////////////////////////////////////////////////////