| 1822 | } |
| 1823 | |
| 1824 | int renameFile(std::string& newPath, const tm* tm, Exiv2::ExifData& exifData) { |
| 1825 | auto p = fs::path(newPath); |
| 1826 | std::string path = newPath; |
| 1827 | auto oldFsPath = fs::path(path); |
| 1828 | std::string format = Params::instance().format_; |
| 1829 | std::string filename = p.stem().string(); |
| 1830 | std::string basesuffix = ""; |
| 1831 | const size_t pos = filename.find('.'); |
| 1832 | if (pos != std::string::npos) |
| 1833 | basesuffix = filename.substr(pos); |
| 1834 | replace(format, ":basename:", p.stem().string()); |
| 1835 | replace(format, ":basesuffix:", basesuffix); |
| 1836 | replace(format, ":dirname:", p.parent_path().filename().string()); |
| 1837 | replace(format, ":parentname:", p.parent_path().parent_path().filename().string()); |
| 1838 | |
| 1839 | const size_t max = 1024; |
| 1840 | char basename[max] = {}; |
| 1841 | if (strftime(basename, max, format.c_str(), tm) == 0) { |
| 1842 | std::cerr << _("Filename format yields empty filename for the file") << " " << path << "\n"; |
| 1843 | return 1; |
| 1844 | } |
| 1845 | |
| 1846 | // get parent path with separator |
| 1847 | // for concatenation of new file name, concatenation operator of std::filesystem::path is not used: |
| 1848 | // On MSYS2 UCRT64 the path separator to be used in terminal is slash, but as concatenation operator |
| 1849 | // a back slash will be added. Rename works but with verbose a path with different operators will be shown. |
| 1850 | const size_t len = p.parent_path().string().length(); |
| 1851 | std::string parent_path_sep = ""; |
| 1852 | if (len > 0) |
| 1853 | parent_path_sep = newPath.substr(0, len + 1); |
| 1854 | |
| 1855 | newPath = parent_path_sep + std::string(basename) + p.extension().string(); |
| 1856 | |
| 1857 | // rename using exiv2 tags |
| 1858 | // is done after calling setting date/time: the value retrieved from tag might include something like %Y, which then |
| 1859 | // should not be replaced by year |
| 1860 | std::regex format_regex(":{1}?(Exif\\..*?):{1}?"); |
| 1861 | #if defined(_WIN32) |
| 1862 | std::string illegalChars = "\\/:*?\"<>|"; |
| 1863 | #else |
| 1864 | std::string illegalChars = "/:"; |
| 1865 | #endif |
| 1866 | std::regex_token_iterator<std::string::iterator> rend; |
| 1867 | std::regex_token_iterator<std::string::iterator> token(format.begin(), format.end(), format_regex); |
| 1868 | while (token != rend) { |
| 1869 | Exiv2::Internal::enforce<std::overflow_error>(token->str().length() >= 2, "token too short"); |
| 1870 | std::string tag = token->str().substr(1, token->str().length() - 2); |
| 1871 | const auto key = exifData.findKey(Exiv2::ExifKey(tag)); |
| 1872 | std::string val = ""; |
| 1873 | if (key != exifData.end()) { |
| 1874 | val = key->print(&exifData); |
| 1875 | if (val.length() == 0) { |
| 1876 | std::cerr << path << ": " << _("Warning: ") << tag << _(" is empty.") << std::endl; |
| 1877 | } else { |
| 1878 | // replace characters invalid in file name |
| 1879 | for (std::string::iterator it = val.begin(); it < val.end(); ++it) { |
| 1880 | bool found = illegalChars.find(*it) != std::string::npos; |
| 1881 | if (found) { |