Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pto_move.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
12 /* This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This software is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public
23  * License along with this software. If not, see
24  * <http://www.gnu.org/licenses/>.
25  *
26  */
27 
28 #include "hugin_config.h"
29 #include <iostream>
30 #include <string>
31 #include <fstream>
32 #include <filesystem>
33 #include <getopt.h>
34 #include <panodata/Panorama.h>
35 #include <hugin_utils/stl_utils.h>
36 
37 std::string IncludeTrailingDelimiter(std::string path)
38 {
39  std::string s=path;
40 #ifdef _WIN32
41  if(s.compare(s.length()-1,1,"\\")!=0 && s.compare(s.length()-1,1,"/")!=0)
42  {
43  s.append("\\");
44  };
45 #else
46  if(s.compare(s.length()-1,1,"/")!=0)
47  {
48  s.append("/");
49  };
50 #endif
51  return s;
52 };
53 
54 // rebase a filename from relative to srcPath to relative to destPath and return absolute new dest path
55 bool RebaseFilename(std::filesystem::path srcFile, std::filesystem::path& destFile, std::string srcPath, std::string destPath)
56 {
57  std::filesystem::path input=std::filesystem::absolute(srcFile);
58  std::string fullInputPath=input.string();
59  std::string srcPathWithTrailingDelimiter=IncludeTrailingDelimiter(srcPath);
60  if(fullInputPath.compare(0, srcPathWithTrailingDelimiter.length(), srcPathWithTrailingDelimiter)!=0)
61  {
62  return false;
63  };
64  fullInputPath.replace(0, srcPathWithTrailingDelimiter.length(), IncludeTrailingDelimiter(destPath));
65  destFile=std::filesystem::path(fullInputPath);
66  return true;
67 };
68 
69 bool checkDestinationDirectory(std::string dir, std::filesystem::path& pathTo)
70 {
71  pathTo=std::filesystem::path(dir);
72  try
73  {
74  // check if a destination directory is given
75  if(pathTo.extension().string().length()>0)
76  {
77  std::cerr << "ERROR: Your destination is a file. Copy/Move several files to " << std::endl
78  << "a single file is not allowed." << std::endl
79  << "Canceled operation." << std::endl;
80  return false;
81  };
82  // create destination directory if not exists
83  if(!std::filesystem::exists(pathTo))
84  {
85  if(!std::filesystem::create_directories(pathTo))
86  {
87  std::cerr << "ERROR: Could not create destination directory: " << pathTo.string() << std::endl
88  << "Maybe you have not sufficient rights to create this directory." << std::endl;
89  return false;
90  };
91  };
92  }
93  catch (const std::filesystem::filesystem_error& ex)
94  {
95  std::cout << ex.what() << std::endl;
96  return false;
97  }
98  pathTo=std::filesystem::absolute(pathTo);
99  return true;
100 };
101 
102 typedef std::set<std::filesystem::path> pathVec;
103 
104 bool PTOCopyMove(bool movingFile, std::filesystem::path src, std::filesystem::path dest, bool overwriteAllFiles)
105 {
106  std::filesystem::path destFile(hugin_utils::GetAbsoluteFilename(dest.string()));
107  std::cout << (movingFile ? "Moving project file " : "Copying project file ") << src.filename() << std::endl
108  << " from " << src.parent_path().string() << std::endl
109  << " to " << destFile.parent_path().string() << std::endl;
110  // open project file
111  HuginBase::Panorama pano;
112  const std::string input=src.string();
113  const std::string inputPathPrefix = hugin_utils::getPathPrefix(input);
114  const std::string outputPathPrefix = hugin_utils::getPathPrefix(destFile.string());
115  if (!pano.ReadPTOFile(input, inputPathPrefix))
116  {
117  return false;
118  };
119  pathVec imagesFrom;
120  std::map<std::filesystem::path,std::filesystem::path> imagesTo;
121  // check if all images exists
122  for(size_t i=0; i<pano.getNrOfImages(); i++)
123  {
124  std::filesystem::path p(pano.getImage(i).getFilename());
125  if(!std::filesystem::exists(p) || !std::filesystem::is_regular_file(p))
126  {
127  std::cerr << "ERROR: image " << p.string() << " not found on disc." << std::endl
128  << "Skipping project " << input << std::endl;
129  return false;
130  };
131  p=std::filesystem::absolute(p);
132  auto result = imagesFrom.insert(p);
133  if (!result.second)
134  {
135  // file is already in list,
136  // update also this filename in pto file
137  pano.setImageFilename(i, imagesTo[p].string());
138  continue;
139  };
140  // now build now image filename
141  std::filesystem::path newFilename;
142  if(RebaseFilename(p, newFilename, inputPathPrefix, outputPathPrefix))
143  {
144  pano.setImageFilename(i, newFilename.string());
145  imagesTo[p] = newFilename;
146  };
147  };
148  if(!imagesFrom.empty())
149  {
150  if(imagesFrom.size()==imagesTo.size())
151  {
152  std::filesystem::path targetDir(destFile);
153  targetDir.remove_filename();
154  if(!checkDestinationDirectory(targetDir.string(), targetDir))
155  {
156  return false;
157  };
158  if(std::filesystem::exists(destFile) && !overwriteAllFiles)
159  {
160  std::cout << "Project file " << destFile << " does already exists." << std::endl
161  << " Overwrite this file? [Y|N] ";
162  std::string userAnswer;
163  while(userAnswer.length()==0)
164  {
165  std::cin >> userAnswer;
166  };
167  userAnswer=hugin_utils::toupper(userAnswer);
168  if(userAnswer!="YES" && userAnswer!="Y")
169  {
170  std::cout << std::endl << "Moving/Copying of project file " << input << " canceled." << std::endl << std::endl;
171  return false;
172  }
173  };
174  //copy/moving images
175  for(const auto& imgFrom: imagesFrom)
176  {
177  // check if target directory already exists
178  targetDir = imgFrom;
179  targetDir.remove_filename();
180  if(!checkDestinationDirectory(targetDir.string(), targetDir))
181  {
182  return false;
183  };
184  //check if target image file already exists
185  if(std::filesystem::exists(imagesTo[imgFrom]) && !overwriteAllFiles)
186  {
187  std::cout << "Images file " << imagesTo[imgFrom] << " does already exists." << std::endl
188  << " Overwrite this file? [Y|N] ";
189  std::string userAnswer;
190  while(userAnswer.length()==0)
191  {
192  std::cin >> userAnswer;
193  };
194  userAnswer=hugin_utils::toupper(userAnswer);
195  if(userAnswer!="YES" && userAnswer!="Y")
196  {
197  std::cout << std::endl << "Moving/Copying of project file " << input << " canceled." << std::endl << std::endl;
198  return false;
199  }
200  };
201  if(movingFile)
202  {
203  try
204  {
205  std::filesystem::rename(imgFrom, imagesTo[imgFrom]);
206  }
207  catch (const std::filesystem::filesystem_error& ex)
208  {
209  std::cout << ex.what() << std::endl;
210  return false;
211  }
212  }
213  else
214  {
215  try
216  {
217  std::filesystem::copy_file(imgFrom, imagesTo[imgFrom], std::filesystem::copy_options::overwrite_existing);
218  }
219  catch (const std::filesystem::filesystem_error& ex)
220  {
221  std::cout << ex.what() << std::endl;
222  return false;
223  }
224  };
225  }; // for loop for all images
226  // now create pano file in new destination
227  // write output
228  if (!pano.WritePTOFile(destFile.string(), outputPathPrefix))
229  {
230  return false;
231  };
232  if(movingFile)
233  {
234  try
235  {
236  std::filesystem::remove(src);
237  }
238  catch (const std::filesystem::filesystem_error& ex)
239  {
240  std::cout << "Could not remove original file: " << input << std::endl;
241  std::cout << ex.what() << std::endl;
242  return false;
243  }
244  };
245  }
246  else
247  {
248  // the images in the project file are not all relative to the same base path
249  std::cout << "WARNING: Images location in project file are not consistent. " << std::endl
250  << "So don't move/copy project file " << src.string() << std::endl;
251  return false;
252  };
253  }
254  else
255  {
256  // now create pano file in new destination, project contains images in paths
257  // not relative to base directory
258  // so create only the new project file without copying/moving image files
259  if (!pano.WritePTOFile(destFile.string(), outputPathPrefix))
260  {
261  return false;
262  };
263  if(movingFile)
264  {
265  try
266  {
267  std::filesystem::remove(src);
268  }
269  catch (const std::filesystem::filesystem_error& ex)
270  {
271  std::cout << "Could not remove original file: " << input << std::endl;
272  std::cout << ex.what() << std::endl;
273  return false;
274  }
275  };
276  };
277  return true;
278 };
279 
280 template <class iteratorType>
281 bool iterateFileSystem(std::string src, pathVec& projectFiles)
282 {
283  try
284  {
285  for(iteratorType it(src); it != iteratorType(); it++)
286  {
287  std::string ext=hugin_utils::toupper(it->path().extension().string());
288  if(ext==".PTO")
289  {
290  projectFiles.insert(*it);
291  };
292  }
293  }
294  catch(std::filesystem::filesystem_error& e)
295  {
296  std::cout << e.what() << std::endl;
297  return false;
298  }
299  return true;
300 };
301 
302 void SearchPTOFilesInDirectory(pathVec& projectFiles, std::string src, bool recursive)
303 {
304  if(recursive)
305  {
306  iterateFileSystem<std::filesystem::recursive_directory_iterator>(src, projectFiles);
307  }
308  else
309  {
310  iterateFileSystem<std::filesystem::directory_iterator>(src, projectFiles);
311  };
312 };
313 
314 static void usage(const char* name)
315 {
316  std::cout << name << ": move a project file with all images in it" << std::endl
317  << name << " version " << hugin_utils::GetHuginVersion() << std::endl
318  << std::endl
319  << "Usage: pto_move [options] path1/source.pto path2/dest.pto" << std::endl
320  << " Rename project file path1/source.pto to " << std::endl
321  << " path2/dest.pto. All images contained in the project will" << std::endl
322  << " be moved accordingly." << std::endl << std::endl
323  << " pto_move [options] sourceFolder destFolder" << std::endl
324  << " Moves all project files in the source folder to " << std::endl
325  << " the destination folder including all images." << std::endl
326  << std::endl
327  << "Options: " << std::endl
328  << " --copy Copy project files and images instead of moving" << std::endl
329  << " --recursive Only effective in use case 2. Go recursive in the" << std::endl
330  << " the source folder and move all project files with images" << std::endl
331  << " to destination folder by maintaining the folder structure" << std::endl
332  << " relative to source folder." << std::endl
333  << " --overwrite Overwrite all existing files. Otherwise you will be asked" << std::endl
334  << " for each existing file." << std::endl
335  << std::endl
336  << std::endl;
337 };
338 
339 int main(int argc, char* argv[])
340 {
341  // parse arguments
342  const char* optstring = "croh";
343 
344  static struct option longOptions[] =
345  {
346  {"copy", no_argument, NULL, 'c' },
347  {"recursive", no_argument, NULL, 'r' },
348  {"overwrite", no_argument, NULL, 'o' },
349  {"help", no_argument, NULL, 'h' },
350  0
351  };
352 
353  bool movingFiles=true; //movingFiles: false->copy, true->move
354  bool recursive=false;
355  bool forceOverwrite=false;
356  int c;
357  while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
358  {
359  switch (c)
360  {
361  case 'h':
362  usage(hugin_utils::stripPath(argv[0]).c_str());
363  return 0;
364  case 'c':
365  movingFiles=false;
366  break;
367  case 'r':
368  recursive=true;
369  break;
370  case 'o':
371  forceOverwrite=true;
372  break;
373  case ':':
374  case '?':
375  // missing argument or invalid switch
376  return 1;
377  break;
378  default:
379  // this should not happen
380  abort();
381  }
382  }
383 
384  if(argc-optind<2)
385  {
386  std::cerr << hugin_utils::stripPath(argv[0]) << ": You need to give at least a source and a destination project file or directory." << std::endl;
387  return 1;
388  };
389 
390  try
391  {
392  std::filesystem::path p(argv[optind]);
393  if(std::filesystem::exists(p))
394  {
395  p=std::filesystem::absolute(p);
396  if(std::filesystem::is_directory(p))
397  {
398  // first parameter is a directory
399  std::filesystem::path pathTo;
400  if(!checkDestinationDirectory(std::string(argv[argc-1]), pathTo))
401  {
402  return 1;
403  };
404  // search all pto files in directory
405  pathVec projectFiles;
406  std::cout << "Searching project files in " << p << std::endl;
407  SearchPTOFilesInDirectory(projectFiles, p.string(), recursive);
408  if(projectFiles.empty())
409  {
410  std::cout << "No project files found in given directory " << p.string() << std::endl;
411  return 0;
412  };
413  std::cout << "Found " << projectFiles.size() << " project files." << std::endl << std::endl;
414  for(pathVec::const_iterator it=projectFiles.cbegin(); it!=projectFiles.cend(); ++it)
415  {
416  std::filesystem::path newPath;
417  if(RebaseFilename(*it, newPath, p.string(), pathTo.string()))
418  {
419  PTOCopyMove(movingFiles, *it, newPath, forceOverwrite);
420  };
421  };
422  }
423  else
424  {
425  if(argc-optind>2)
426  {
427  // several files given
428  // check if destination is a directory and create it if necessary
429  std::filesystem::path pathTo;
430  if(!checkDestinationDirectory(std::string(argv[argc-1]), pathTo))
431  {
432  return 1;
433  };
434  while(optind<argc-1)
435  {
436  p=std::filesystem::path(argv[optind]);
437  std::string ext=hugin_utils::toupper(p.extension().string());
438  // work only on pto files
439  if(ext==".PTO")
440  {
441  if(std::filesystem::exists(p) && std::filesystem::is_regular_file(p))
442  {
443  p=std::filesystem::absolute(p);
444  std::filesystem::path newPath = pathTo / p.filename();
445  PTOCopyMove(movingFiles, p, newPath, forceOverwrite);
446  }
447  else
448  {
449  std::cout << "WARNING: File " << p << " does not exists" << std::endl
450  << "Skipping this file." << std::endl;
451  };
452  };
453  optind++;
454  };
455  }
456  else
457  {
458  // exactly 2 files given
459  std::filesystem::path pathTo(argv[argc-1]);
460  if(pathTo.extension().string().length()>0)
461  {
462  // user has given filename with extension
463  // so simply copy/move file
464  pathTo=std::filesystem::absolute(pathTo);
465  PTOCopyMove(movingFiles, p, pathTo, forceOverwrite);
466  }
467  else
468  {
469  // target is a directory
470  if(!checkDestinationDirectory(std::string(argv[argc-1]), pathTo))
471  {
472  return 1;
473  };
474  pathTo=pathTo / p.filename();
475  if(p==pathTo)
476  {
477  std::cerr << "ERROR: Target and destination file are the same." << std::endl
478  << "Skipping file processing." << std::endl;
479  return 1;
480  };
481  PTOCopyMove(movingFiles, p, pathTo, forceOverwrite);
482  };
483  };
484  };
485  };
486  }
487  catch (const std::filesystem::filesystem_error& ex)
488  {
489  std::cout << ex.what() << std::endl;
490  }
491  return 0;
492 }
void SearchPTOFilesInDirectory(pathVec &projectFiles, std::string src, bool recursive)
Definition: pto_move.cpp:302
bool checkDestinationDirectory(std::string dir, std::filesystem::path &pathTo)
Definition: pto_move.cpp:69
std::vector< std::filesystem::path > pathVec
std::string toupper(const std::string &s)
Definition: stl_utils.h:59
Model for a panorama.
Definition: Panorama.h:152
bool PTOCopyMove(bool movingFile, std::filesystem::path src, std::filesystem::path dest, bool overwriteAllFiles)
Definition: pto_move.cpp:104
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition: utils.cpp:184
std::string GetAbsoluteFilename(const std::string &filename)
returns the full absolute filename
Definition: utils.cpp:368
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
bool ReadPTOFile(const std::string &filename, const std::string &prefix="")
read pto file from the given filename into Panorama object it does some checks on the file and issues...
Definition: Panorama.cpp:2023
std::string IncludeTrailingDelimiter(std::string path)
Definition: pto_move.cpp:37
void setImageFilename(unsigned int img, const std::string &fname)
set a new image filename
Definition: Panorama.cpp:373
bool iterateFileSystem(std::string src, pathVec &projectFiles)
static void usage()
Definition: Main.cpp:32
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:907
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
Definition: Panorama.cpp:2059
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
bool RebaseFilename(std::filesystem::path srcFile, std::filesystem::path &destFile, std::string srcPath, std::string destPath)
Definition: pto_move.cpp:55
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition: utils.cpp:160
int main(int argc, char *argv[])
Definition: Main.cpp:167