1 
2 
3 #ifdef _WIN32
4 //#define UNICODE  // per le funzioni di conversione da/a UNC
5 #include <windows.h>
6 #include <lm.h>
7 
8 const char slash     = '\\';
9 const char auxslash  = '/';
10 const char wslash    = L'\\';
11 const char wauxslash = L'/';
12 #else
13 const char slash     = '/';
14 const char auxslash  = '\\';
15 const char wslash    = '/';
16 const char wauxslash = '\\';
17 #endif
18 
19 //=============================================================================
20 
21 #include "tfilepath.h"
22 #include "tconvert.h"
23 #include "tfiletype.h"
24 #include <cmath>
25 #include <cctype>
26 #include <sstream>
27 
28 // QT
29 #include <QObject>
30 
31 bool TFilePath::m_underscoreFormatAllowed = true;
32 
33 namespace {
34 
35 /*-- fromSeg位置 と
36  * toSeg位置は含まず、それらの間に挟まれている文字列が「数字4ケタ」ならtrueを返す
37  * --*/
isNumbers(std::wstring str,int fromSeg,int toSeg)38 bool isNumbers(std::wstring str, int fromSeg, int toSeg) {
39   /*
40     if (toSeg - fromSeg != 5) return false;
41     for (int pos = fromSeg + 1; pos < toSeg; pos++) {
42       if (str[pos] < '0' || str[pos] > '9') return false;
43     }
44   */
45   // Let's check if it follows the format ####A (i.e 00001 or 00001a)
46   int numDigits = 0, numLetters = 0;
47   for (int pos = fromSeg + 1; pos < toSeg; pos++) {
48     if ((str[pos] >= 'A' && str[pos] <= 'Z') ||
49         (str[pos] >= 'a' && str[pos] <= 'z')) {
50       // Not the right format if we ran into a letter without first finding a
51       // number
52       if (!numDigits) return false;
53 
54       // We'll keep track of the number of letters we find.
55       // NOTE: From here on out we should only see letters
56       numLetters++;
57     } else if (str[pos] >= '0' && str[pos] <= '9') {
58       // Not the right format if we ran into a number that followed a letter.
59       // This format is not something we expect currently
60       if (numLetters) return false;  // not right format
61 
62       // We'll keep track of the number of digits we find.
63       numDigits++;
64     } else  // Not the right format if we found something we didn't expect
65       return false;
66   }
67 
68   // Not the right format if we see too many letters.
69   // At the time of this logic, we only expect 1 letter.  Can expand to 2 or
70   // more later, if we want.
71   if (numLetters > 1) return false;
72 
73   return true;  // We're good!
74 }
75 
checkForSeqNum(QString type)76 bool checkForSeqNum(QString type) {
77   TFileType::Type typeInfo = TFileType::getInfoFromExtension(type);
78   if ((typeInfo & TFileType::IMAGE) && !(typeInfo & TFileType::LEVEL))
79     return true;
80   else
81     return false;
82 }
83 };  // namespace
84 
85 // TFrameId::operator string() const
expand(FrameFormat format) const86 std::string TFrameId::expand(FrameFormat format) const {
87   if (m_frame == EMPTY_FRAME)
88     return "";
89   else if (m_frame == NO_FRAME)
90     return "-";
91   std::ostringstream o_buff;
92   if (format == FOUR_ZEROS || format == UNDERSCORE_FOUR_ZEROS) {
93     o_buff.fill('0');
94     o_buff.width(4);
95     o_buff << m_frame;
96     o_buff.width(0);
97   } else if (format == CUSTOM_PAD || format == UNDERSCORE_CUSTOM_PAD) {
98     o_buff.fill('0');
99     o_buff.width(m_zeroPadding);
100     o_buff << m_frame;
101     o_buff.width(0);
102   } else {
103     o_buff << m_frame;
104   }
105   if (m_letter != '\0') o_buff << m_letter;
106   return o_buff.str();
107 }
108 
109 //-------------------------------------------------------------------
110 
operator ++()111 const TFrameId &TFrameId::operator++() {
112   ++m_frame;
113   m_letter = 0;
114   return *this;
115 }
116 
117 //-------------------------------------------------------------------
118 
operator --()119 const TFrameId &TFrameId::operator--() {
120   if (m_letter > 0)
121     m_letter = 0;
122   else
123     --m_frame;
124   return *this;
125 }
126 
127 //=============================================================================
128 
isSlash(char c)129 inline bool isSlash(char c) { return c == slash || c == auxslash; }
130 
131 //-----------------------------------------------------------------------------
132 
isSlash(wchar_t c)133 inline bool isSlash(wchar_t c) { return c == wslash || c == wauxslash; }
134 
135 //-----------------------------------------------------------------------------
136 
137 // cerca l'ultimo '/' o '\'. Se non c'e' ritorna -1
138 // di modo che la sottostringa che parte da getLastSlash() + 1 e'
139 // nome.frame.tipo
getLastSlash(const std::wstring & path)140 inline int getLastSlash(const std::wstring &path) {
141   int i;
142   for (i = path.length() - 1; i >= 0 && !isSlash(path[i]); i--) {
143   }
144   return i;
145 }
146 
147 //-----------------------------------------------------------------------------
148 
149 /*
150 void TFilePath::setPath(string path)
151 {
152 bool isUncName = false;
153   // elimino i '//', './' e '/' finali; raddrizzo gli slash 'storti'.
154   // se il path comincia con  "<alpha>:" aggiungo uno slash
155   int length =path.length();
156   int pos = 0;
157   if(path.length()>=2 && isalpha(path[0]) && path[1] == ':')
158     {
159      m_path.append(path,0,2);
160      pos=2;
161      if(path.length()==2 || !isSlash(path[pos])) m_path.append(1,slash);
162     }
163 #ifdef _WIN32
164   else  //se si tratta di un path in formato UNC e' del tipo "\\\\MachineName"
165         //RICONTROLLARE! SE SI HA IP ADDRESS FALLIVA!
166     if (path.length() >= 3 && path[0] == '\\' &&  path[1] == '\\' &&
167 (isalpha(path[2]) || isdigit(path[2])) )
168       {
169       isUncName = true;
170       m_path.append(path,0,3);
171       pos = 3;
172       }
173 #endif
174   for(;pos<length;pos++)
175     if(path[pos] == '.')
176     {
177       pos++;
178       if(pos>=length)
179       {
180         if(pos>1) m_path.append(1,'.');
181       }
182       else if(!isSlash(path[pos])) m_path.append(path,pos-1,2);
183       else {
184          while(pos+1<length && isSlash(path[pos+1]))
185            pos++;
186       }
187     }
188     else if(isSlash(path[pos]))
189     {
190       do pos++;
191       while(pos<length && isSlash(path[pos]));
192       pos--;
193       m_path.append(1,slash);
194     }
195     else
196     {
197       m_path.append(1,path[pos]);
198     }
199 
200     // rimuovo l'eventuale '/' finale (a meno che m_path == "/" o m_path ==
201 "<letter>:\"
202     // oppure sia UNC (Windows only) )
203     if(!(m_path.length()==1 && m_path[0] == slash ||
204          m_path.length()==3 && isalpha(m_path[0]) && m_path[1] == ':' &&
205 m_path[2] == slash)
206       && m_path.length()>1 && m_path[m_path.length()-1] == slash)
207       m_path.erase(m_path.length()-1, 1);
208 
209   if (isUncName && m_path.find_last_of('\\') == 1) // e' indicato solo il nome
210 della macchina...
211     m_path.append(1, slash);
212 }
213 */
214 
215 //-----------------------------------------------------------------------------
216 /*
217 void append(string &out, wchar_t c)
218 {
219   if(32 <= c && c<=127 && c!='&') out.append(1, (char)c);
220   else if(c=='&') out.append("&amp;");
221   else
222     {
223      ostringstream ss;
224      ss << "&#" <<  c << ";" << '\0';
225      out += ss.str();
226      ss.freeze(0);
227     }
228 }
229 */
230 //-----------------------------------------------------------------------------
231 
setPath(std::wstring path)232 void TFilePath::setPath(std::wstring path) {
233   bool isUncName = false;
234   // elimino i '//', './' e '/' finali; raddrizzo gli slash 'storti'.
235   // se il path comincia con  "<alpha>:" aggiungo uno slash
236   int length = path.length();
237   int pos    = 0;
238 
239   if (path.length() >= 2 && iswalpha(path[0]) && path[1] == L':') {
240     m_path.append(1, (wchar_t)path[0]);
241     m_path.append(1, L':');
242     // m_path.append(path,0,2);
243     pos = 2;
244     if (path.length() == 2 || !isSlash(path[pos])) m_path.append(1, wslash);
245   }
246   // se si tratta di un path in formato UNC e' del tipo "\\\\MachineName"
247   else if ((path.length() >= 3 && path[0] == L'\\' && path[1] == L'\\' &&
248                iswalnum(path[2])) ||
249            (path.length() >= 3 && path[0] == L'/' && path[1] == L'/' &&
250                iswalnum(path[2]))) {
251     isUncName = true;
252     m_path.append(2, L'\\');
253     m_path.append(1, path[2]);
254     pos = 3;
255   }
256   for (; pos < length; pos++)
257     if (path[pos] == L'.') {
258       pos++;
259       if (pos >= length) {
260         if (pos > 1) m_path.append(1, L'.');
261       } else if (!isSlash(path[pos])) {
262         m_path.append(1, L'.');
263         m_path.append(1, path[pos]);
264       } else {
265         while (pos + 1 < length && isSlash(path[pos + 1])) pos++;
266       }
267     } else if (isSlash(path[pos])) {
268       int firstSlashPos = pos;
269       do
270         pos++;
271       while (pos < length && isSlash(path[pos]));
272       if (firstSlashPos == 0 && pos == 4)  // Caso "\\\\MachineName"
273         m_path.append(2, wslash);
274       else
275         m_path.append(1, wslash);
276       pos--;
277     } else {
278       m_path.append(1, path[pos]);
279     }
280 
281   // rimuovo l'eventuale '/' finale (a meno che m_path == "/" o m_path ==
282   // "<letter>:\"
283   // oppure sia UNC (Windows only) )
284   if (!((m_path.length() == 1 && m_path[0] == wslash) ||
285         (m_path.length() == 3 && iswalpha(m_path[0]) && m_path[1] == L':' &&
286             m_path[2] == wslash)) &&
287       (m_path.length() > 1 && m_path[m_path.length() - 1] == wslash))
288     m_path.erase(m_path.length() - 1, 1);
289 
290   if (isUncName &&
291       !(m_path.find_last_of(L'\\') > 1 ||
292         m_path.find_last_of(L'/') >
293             1))  // e' indicato solo il nome della macchina...
294     m_path.append(1, wslash);
295 }
296 
297 //-----------------------------------------------------------------------------
298 
TFilePath(const char * path)299 TFilePath::TFilePath(const char *path) { setPath(::to_wstring(path)); }
300 
301 //-----------------------------------------------------------------------------
302 
TFilePath(const std::string & path)303 TFilePath::TFilePath(const std::string &path) { setPath(::to_wstring(path)); }
304 
305 //-----------------------------------------------------------------------------
306 
TFilePath(const std::wstring & path)307 TFilePath::TFilePath(const std::wstring &path) { setPath(path); }
308 
309 //-----------------------------------------------------------------------------
310 
TFilePath(const QString & path)311 TFilePath::TFilePath(const QString &path) { setPath(path.toStdWString()); }
312 
313 //-----------------------------------------------------------------------------
314 
operator ==(const TFilePath & fp) const315 bool TFilePath::operator==(const TFilePath &fp) const {
316 #ifdef _WIN32
317   return _wcsicmp(m_path.c_str(), fp.m_path.c_str()) == 0;
318 #else
319   return m_path == fp.m_path;
320 #endif
321 }
322 
323 //-----------------------------------------------------------------------------
324 
operator <(const TFilePath & fp) const325 bool TFilePath::operator<(const TFilePath &fp) const {
326   std::wstring iName = m_path;
327   std::wstring jName = fp.m_path;
328   int i1 = 0, j1 = 0;
329   int i2 = m_path.find(L"\\");
330   int j2 = fp.m_path.find(L"\\");
331   if (i2 == j2 && j2 == -1)
332 #ifdef _WIN32
333     return _wcsicmp(m_path.c_str(), fp.m_path.c_str()) < 0;
334 #else
335     return m_path < fp.m_path;
336 #endif
337   if (!i2) {
338     ++i1;
339     i2 = m_path.find(L"\\", i1);
340   }
341   if (!j2) {
342     ++j1;
343     j2 = fp.m_path.find(L"\\", j1);
344   }
345   while (i2 != -1 || j2 != -1) {
346     iName = (i2 != -1) ? m_path.substr(i1, i2 - i1) : m_path;
347     jName = (j2 != -1) ? fp.m_path.substr(j1, j2 - j1) : fp.m_path;
348 // se le due parti di path, conpresi tra slash sono uguali
349 // itero il processo di confronto altrimenti ritorno
350 #ifdef _WIN32
351     char differ;
352     differ = _wcsicmp(iName.c_str(), jName.c_str());
353     if (differ != 0) return differ < 0 ? true : false;
354 #else
355     if (TFilePath(iName) != TFilePath(jName))
356       return TFilePath(iName) < TFilePath(jName);
357 #endif
358     i1 = (i2 != -1) ? i2 + 1 : m_path.size();
359     j1 = (j2 != -1) ? j2 + 1 : fp.m_path.size();
360     i2 = m_path.find(L"\\", i1);
361     j2 = fp.m_path.find(L"\\", j1);
362   }
363 
364   iName = m_path.substr(i1, m_path.size() - i1);
365   jName = fp.m_path.substr(j1, fp.m_path.size() - j1);
366 #ifdef _WIN32
367   return _wcsicmp(iName.c_str(), jName.c_str()) < 0;
368 #else
369   return TFilePath(iName) < TFilePath(jName);
370 #endif
371 }
372 
373 #ifdef LEVO
operator <(const TFilePath & fp) const374 bool TFilePath::operator<(const TFilePath &fp) const {
375   /*
376 wstring a = m_path, b = fp.m_path;
377 for(;;)
378 {
379 wstring ka,kb;
380 int i;
381 i = a.find_first_of("/\\");
382 if(i==wstring::npos) {ka = a; a = L"";}
383 i = b.find_first_of("/\\");
384 if(i==wstring::npos) {ka = a; a = L"";}
385 
386 }
387 */
388   wstring a = toLower(m_path), b = toLower(fp.m_path);
389   return a < b;
390 }
391 #endif
392 
393 //-----------------------------------------------------------------------------
394 
operator +=(const TFilePath & fp)395 TFilePath &TFilePath::operator+=(const TFilePath &fp) {
396   assert(!fp.isAbsolute());
397   if (fp.isEmpty())
398     return *this;
399   else if (isEmpty()) {
400     *this = fp;
401     return *this;
402   } else if (m_path.length() != 1 || m_path[0] != slash) {
403     assert(!m_path.empty());
404     if (!isSlash(m_path[m_path.length() - 1])) m_path.append(1, wslash);
405     m_path += fp.m_path;
406     return *this;
407   } else {
408     *this = TFilePath(m_path + fp.m_path);
409     return *this;
410   }
411 }
412 //-----------------------------------------------------------------------------
operator +(const TFilePath & fp) const413 TFilePath TFilePath::operator+(const TFilePath &fp) const {
414   TFilePath ret(*this);
415   ret += fp;
416   return ret;
417 }
418 //-----------------------------------------------------------------------------
419 
operator +=(const std::wstring & s)420 TFilePath &TFilePath::operator+=(const std::wstring &s) {
421   if (s.empty()) return *this;
422   // if(m_path.length()!=1 || m_path[0] != slash)
423   //  m_path += slash;
424   if (m_path.length() > 0 && !isSlash(m_path[m_path.length() - 1]))
425     m_path.append(1, wslash);
426   m_path += s;
427   return *this;
428 }
429 
430 //-----------------------------------------------------------------------------
431 
getWideString() const432 const std::wstring TFilePath::getWideString() const {
433   return m_path;
434   /*
435 std::wstring s;
436 int n = m_path.size();
437 for(int i=0;i<n; i++)
438 {
439 char c = m_path[i];
440 if(c!='&') s.append(1, (unsigned short)c);
441 else
442  {
443   i++;
444   if(m_path[i] == '#')
445     {
446      unsigned short w = 0;
447      i++;
448      while(i<n)
449        {
450         c = m_path[i];
451         if('0'<=c && c<='9')
452           w = w*10 + c - '0';
453         else if('a' <=c && c<='f')
454           w = w*10 + c - 'a' + 10;
455         else if('A' <=c && c<='F')
456           w = w*10 + c - 'A' + 10;
457         else
458           break;
459         i++;
460        }
461      s.append(1, w);
462     }
463  }
464 }
465 return s;
466 */
467 }
468 /*
469 #else
470 const wstring TFilePath::getWideString() const
471 {
472    wstring a(L"dummy string");
473    return a;
474 }
475 #endif
476 */
477 
478 //-----------------------------------------------------------------------------
479 
getQString() const480 QString TFilePath::getQString() const {
481   return QString::fromStdWString(getWideString());
482 }
483 
484 //-----------------------------------------------------------------------------
485 
operator <<(std::ostream & out,const TFilePath & path)486 std::ostream &operator<<(std::ostream &out, const TFilePath &path) {
487   std::wstring w = path.getWideString();
488   return out << ::to_string(w) << " ";
489   //  string w = path.getString();
490   //  return out << w << " ";
491 }
492 
493 //-----------------------------------------------------------------------------
494 
495 /*
496 TFilePath TFilePath::operator+ (const TFilePath &fp) const
497 {
498 assert(!fp.isAbsolute());
499 if(fp.isEmpty()) return *this;
500 else if(isEmpty()) return fp;
501 else if(m_path.length()!=1 || m_path[0] != slash)
502   return TFilePath(m_path + slash + fp.m_path);
503 else
504   return TFilePath(m_path + fp.m_path);
505 }
506 */
507 //-----------------------------------------------------------------------------
508 
isAbsolute() const509 bool TFilePath::isAbsolute() const {
510   return ((m_path.length() >= 1 && m_path[0] == slash) ||
511           (m_path.length() >= 2 && iswalpha(m_path[0]) && m_path[1] == ':'));
512 }
513 
514 //-----------------------------------------------------------------------------
515 
isRoot() const516 bool TFilePath::isRoot() const {
517   return ((m_path.length() == 1 && m_path[0] == slash) ||
518           (m_path.length() == 3 && iswalpha(m_path[0]) && m_path[1] == ':' &&
519              m_path[2] == slash) ||
520           ((m_path.length() > 2 && m_path[0] == slash && m_path[1] == slash) &&
521              (std::string::npos == m_path.find(slash, 2) ||
522               m_path.find(slash, 2) == (m_path.size() - 1))));
523 }
524 
525 //-----------------------------------------------------------------------------
526 
527 // ritorna ""(niente tipo, niente punto), "." (file con tipo) o ".." (file con
528 // tipo e frame)
getDots() const529 std::string TFilePath::getDots() const {
530   QString type = QString::fromStdString(getType()).toLower();
531   if (isFfmpegType()) return ".";
532   int i            = getLastSlash(m_path);
533   std::wstring str = m_path.substr(i + 1);
534   // potrei anche avere a.b.c.d dove d e' l'estensione
535   i = str.rfind(L".");
536   if (i == (int)std::wstring::npos || str == L"..") return "";
537 
538   int j = str.substr(0, i).rfind(L".");
539   if (j == (int)std::wstring::npos && m_underscoreFormatAllowed)
540     j = str.substr(0, i).rfind(L"_");
541 
542   if (j != (int)std::wstring::npos)
543     return (j == i - 1 || (checkForSeqNum(type) && isNumbers(str, j, i))) ? ".."
544                                                                           : ".";
545   else
546     return ".";
547 }
548 
549 //-----------------------------------------------------------------------------
550 
getDottedType() const551 std::string TFilePath::getDottedType()
552     const  // ritorna l'estensione con PUNTO (se c'e')
553 {
554   int i            = getLastSlash(m_path);
555   std::wstring str = m_path.substr(i + 1);
556   i                = str.rfind(L".");
557   if (i == (int)std::wstring::npos) return "";
558 
559   return toLower(::to_string(str.substr(i)));
560 }
561 
562 //-----------------------------------------------------------------------------
563 
getUndottedType() const564 std::string TFilePath::getUndottedType()
565     const  // ritorna l'estensione senza PUNTO
566 {
567   size_t i         = getLastSlash(m_path);
568   std::wstring str = m_path.substr(i + 1);
569   i                = str.rfind(L".");
570   if (i == std::wstring::npos || i == str.length() - 1) return "";
571   return toLower(::to_string(str.substr(i + 1)));
572 }
573 
574 //-----------------------------------------------------------------------------
575 
getWideName() const576 std::wstring TFilePath::getWideName() const  // noDot! noSlash!
577 {
578   QString type     = QString::fromStdString(getType()).toLower();
579   int i            = getLastSlash(m_path);  // cerco l'ultimo slash
580   std::wstring str = m_path.substr(i + 1);
581   i                = str.rfind(L".");
582   if (i == (int)std::wstring::npos) return str;
583   int j = str.substr(0, i).rfind(L".");
584   if (j != (int)std::wstring::npos) {
585     if (checkForSeqNum(type) && isNumbers(str, j, i)) i = j;
586   } else if (m_underscoreFormatAllowed) {
587     j = str.substr(0, i).rfind(L"_");
588     if (j != (int)std::wstring::npos && checkForSeqNum(type) &&
589         isNumbers(str, j, i))
590       i = j;
591   }
592   return str.substr(0, i);
593 }
594 
595 //-----------------------------------------------------------------------------
596 
getName() const597 std::string TFilePath::getName() const  // noDot! noSlash!
598 {
599   return ::to_string(getWideName());
600 }
601 
602 //-----------------------------------------------------------------------------
603 // es. TFilePath("/pippo/pluto.0001.gif").getLevelName() == "pluto..gif"
getLevelName() const604 std::string TFilePath::getLevelName() const {
605   return ::to_string(getLevelNameW());
606 }
607 
608 //-----------------------------------------------------------------------------
609 // es. TFilePath("/pippo/pluto.0001.gif").getLevelName() == "pluto..gif"
610 
getLevelNameW() const611 std::wstring TFilePath::getLevelNameW() const {
612   int i            = getLastSlash(m_path);  // cerco l'ultimo slash
613   std::wstring str = m_path.substr(i + 1);  // str e' m_path senza directory
614   QString type     = QString::fromStdString(getType()).toLower();
615   if (isFfmpegType()) return str;
616   int j = str.rfind(L".");                       // str[j..] = ".type"
617   if (j == (int)std::wstring::npos) return str;  // no frame; no type
618   i = str.substr(0, j).rfind(L'.');
619   if (i == (int)std::wstring::npos && m_underscoreFormatAllowed)
620     i = str.substr(0, j).rfind(L'_');
621 
622   if (j == i || j - i == 1)  // prova.tif o prova..tif
623     return str;
624 
625   if (!checkForSeqNum(type) || !isNumbers(str, i, j) ||
626       i == (int)std::wstring::npos) return str;
627   // prova.0001.tif
628   return str.erase(i + 1, j - i - 1);
629 }
630 
631 //-----------------------------------------------------------------------------
632 
getParentDir() const633 TFilePath TFilePath::getParentDir() const  // noSlash!
634 {
635   int i = getLastSlash(m_path);  // cerco l'ultimo slash
636   if (i < 0) {
637     if (m_path.length() >= 2 && (('a' <= m_path[0] && m_path[0] <= 'z') ||
638                                  ('A' <= m_path[0] && m_path[0] <= 'Z')) &&
639         m_path[1] == ':')
640       return TFilePath(m_path.substr(0, 2));
641     else
642       return TFilePath("");
643   } else if (i == 0)
644     return TFilePath("/");
645   else
646     return TFilePath(m_path.substr(0, i));
647 }
648 
649 //-----------------------------------------------------------------------------
650 
isLevelName() const651 bool TFilePath::isLevelName() const {
652   QString type = QString::fromStdString(getType()).toLower();
653   if (isFfmpegType() || !checkForSeqNum(type)) return false;
654   try {
655     return getFrame() == TFrameId(TFrameId::EMPTY_FRAME);
656   }
657 
658   catch (...) {
659     return false;
660   }
661 }
662 
getFrame() const663 TFrameId TFilePath::getFrame() const {
664   int i            = getLastSlash(m_path);  // cerco l'ultimo slash
665   std::wstring str = m_path.substr(i + 1);  // str e' il path senza parentdir
666   QString type     = QString::fromStdString(getType()).toLower();
667   i                = str.rfind(L'.');
668   if (i == (int)std::wstring::npos || str == L"." || str == L"..")
669     return TFrameId(TFrameId::NO_FRAME);
670   int j;
671 
672   j = str.substr(0, i).rfind(L'.');
673   if (j == (int)std::wstring::npos && m_underscoreFormatAllowed)
674     j = str.substr(0, i).rfind(L'_');
675 
676   if (j == (int)std::wstring::npos) return TFrameId(TFrameId::NO_FRAME);
677   if (i == j + 1) return TFrameId(TFrameId::EMPTY_FRAME);
678 
679   /*-- 間が数字でない場合(ファイル名にまぎれた"_" や "."がある場合)を除外する
680    * --*/
681   if (!checkForSeqNum(type) || !isNumbers(str, j, i))
682     return TFrameId(TFrameId::NO_FRAME);
683 
684   int k, number = 0, digits = 0;
685   for (k = j + 1; k < i && iswdigit(str[k]); k++) {
686     digits++;
687     number = number * 10 + str[k] - L'0';
688   }
689   char letter                  = '\0';
690   if (iswalpha(str[k])) letter = str[k++] + ('a' - L'a');
691   /*
692     if (number == 0 || k < i)  // || letter!='\0')
693       throw TMalformedFrameException(
694           *this,
695           str + L": " + QObject::tr("Malformed frame name").toStdWString());
696   */
697   int padding = 0;
698 
699   if (str[j + 1] == '0') padding = digits;
700 
701   return TFrameId(number, letter, padding, str[j]);
702 }
703 
704 //-----------------------------------------------------------------------------
705 
isFfmpegType() const706 bool TFilePath::isFfmpegType() const {
707   QString type = QString::fromStdString(getType()).toLower();
708   if (type == "gif" || type == "mp4" || type == "webm")
709     return true;
710   else
711     return false;
712 }
713 
714 //-----------------------------------------------------------------------------
withType(const std::string & type) const715 TFilePath TFilePath::withType(const std::string &type) const {
716   assert(type.length() < 2 || type.substr(0, 2) != "..");
717   int i            = getLastSlash(m_path);  // cerco l'ultimo slash
718   std::wstring str = m_path.substr(i + 1);  // str e' il path senza parentdir
719   int j            = str.rfind(L'.');
720   if (j == (int)std::wstring::npos || str == L"..")
721   // il path originale non ha tipo
722   {
723     if (type == "")
724       return *this;
725     else if (type[0] == '.')
726       return TFilePath(m_path + ::to_wstring(type));
727     else
728       return TFilePath(m_path + ::to_wstring("." + type));
729   } else
730   // il path originale ha gia' il tipo
731   {
732     if (type == "")
733       return TFilePath(m_path.substr(0, i + j + 1));
734     else if (type[0] == '.')
735       return TFilePath(m_path.substr(0, i + j + 1) + ::to_wstring(type));
736     else
737       return TFilePath(m_path.substr(0, i + j + 2) + ::to_wstring(type));
738   }
739 }
740 
741 //-----------------------------------------------------------------------------
742 
withName(const std::string & name) const743 TFilePath TFilePath::withName(const std::string &name) const {
744   return withName(::to_wstring(name));
745 }
746 
747 //-----------------------------------------------------------------------------
748 
withName(const std::wstring & name) const749 TFilePath TFilePath::withName(const std::wstring &name) const {
750   int i            = getLastSlash(m_path);  // cerco l'ultimo slash
751   std::wstring str = m_path.substr(i + 1);  // str e' il path senza parentdir
752   QString type     = QString::fromStdString(getType()).toLower();
753   int j;
754   j = str.rfind(L'.');
755 
756   if (j == (int)std::wstring::npos) {
757     if (m_underscoreFormatAllowed) {
758       j = str.rfind(L'_');
759       if (j != (int)std::wstring::npos)
760         return TFilePath(m_path.substr(0, i + 1) + name + str.substr(j));
761     }
762     return TFilePath(m_path.substr(0, i + 1) + name);
763   }
764   int k;
765 
766   k = str.substr(0, j).rfind(L".");
767   if (k == (int)std::wstring::npos && m_underscoreFormatAllowed)
768     k = str.substr(0, j).rfind(L"_");
769 
770   if (k == (int)(std::wstring::npos))
771     k = j;
772   else if (k != j - 1 && (!checkForSeqNum(type) || !isNumbers(str, k, j)))
773     k = j;
774 
775   return TFilePath(m_path.substr(0, i + 1) + name + str.substr(k));
776 }
777 
778 //-----------------------------------------------------------------------------
779 
withParentDir(const TFilePath & dir) const780 TFilePath TFilePath::withParentDir(const TFilePath &dir) const {
781   int i = getLastSlash(m_path);  // cerco l'ultimo slash
782   return dir + TFilePath(m_path.substr(i + 1));
783 }
784 
785 //-----------------------------------------------------------------------------
786 
withFrame(const TFrameId & frame,TFrameId::FrameFormat format) const787 TFilePath TFilePath::withFrame(const TFrameId &frame,
788                                TFrameId::FrameFormat format) const {
789   const std::wstring dot = L".", dotDot = L"..";
790   int i            = getLastSlash(m_path);  // cerco l'ultimo slash
791   std::wstring str = m_path.substr(i + 1);  // str e' il path senza parentdir
792   QString type     = QString::fromStdString(getType()).toLower();
793   assert(str != dot && str != dotDot);
794   int j          = str.rfind(L'.');
795   const char *ch = ".";
796   // Override format input because it may be wrong.
797   if (!isFfmpegType() && checkForSeqNum(type))
798     format = frame.getCurrentFormat();
799   if (m_underscoreFormatAllowed && (format == TFrameId::UNDERSCORE_FOUR_ZEROS ||
800                                     format == TFrameId::UNDERSCORE_NO_PAD ||
801                                     format == TFrameId::UNDERSCORE_CUSTOM_PAD))
802     ch = "_";
803   if (j == (int)std::wstring::npos) {
804     if (frame.isEmptyFrame() || frame.isNoFrame())
805       return *this;
806     else
807       return TFilePath(m_path + ::to_wstring(ch + frame.expand(format)));
808   }
809 
810   int k = str.substr(0, j).rfind(L'.');
811 
812   bool hasValidFrameNum = false;
813   if (!isFfmpegType() && checkForSeqNum(type) && isNumbers(str, k, j))
814     hasValidFrameNum = true;
815   std::string frameString;
816   if (frame.isNoFrame())
817     frameString = "";
818   else if (!frame.isEmptyFrame() && getDots() != "." && !hasValidFrameNum) {
819     if (k != (int)std::wstring::npos) {
820       std::wstring wstr = str.substr(k, j - k);
821       std::string str2(wstr.begin(), wstr.end());
822       frameString = str2;
823     } else
824       frameString = "";
825   } else
826     frameString = ch + frame.expand(format);
827 
828   if (k != (int)std::wstring::npos)
829     return TFilePath(m_path.substr(0, k + i + 1) + ::to_wstring(frameString) +
830                      str.substr(j));
831   else if (m_underscoreFormatAllowed) {
832     k = str.substr(0, j).rfind(L'_');
833     if (k != (int)std::wstring::npos &&
834         (k == j - 1 ||
835          (checkForSeqNum(type) &&
836           isNumbers(str, k,
837                     j)))) /*-- "_." の並びか、"_[数字]."の並びのとき --*/
838       return TFilePath(m_path.substr(0, k + i + 1) +
839                        ((frame.isNoFrame())
840                             ? L""
841                             : ::to_wstring("_" + frame.expand(format))) +
842                        str.substr(j));
843   }
844   return TFilePath(m_path.substr(0, j + i + 1) + ::to_wstring(frameString) +
845                    str.substr(j));
846 }
847 
848 //-----------------------------------------------------------------------------
849 
isAncestorOf(const TFilePath & possibleDescendent) const850 bool TFilePath::isAncestorOf(const TFilePath &possibleDescendent) const {
851   size_t len = m_path.length();
852   if (len == 0) {
853     // il punto e' antenato di tutti i path non assoluti
854     return !possibleDescendent.isAbsolute();
855   }
856 
857   // un path e' antenato di se stesso
858   if (m_path == possibleDescendent.m_path) return true;
859 
860   int possibleDescendentLen = possibleDescendent.m_path.length();
861   // altrimenti l'antenato deve essere piu' corto
862   if ((int)len >= possibleDescendentLen) return false;
863   // e deve coincidere con la prima parte del discendente
864   if (toLower(m_path) != toLower(possibleDescendent.m_path.substr(0, len)))
865     return false;
866   // se l'antenato non finisce con slash ci deve essere uno slash nel
867   // discendente, subito dopo
868   if (m_path[len - 1] != wslash && possibleDescendent.m_path[len] != wslash)
869     return false;
870 
871   return true;
872 }
873 
874 //-----------------------------------------------------------------------------
875 
operator -(const TFilePath & fp) const876 TFilePath TFilePath::operator-(const TFilePath &fp) const {
877   if (toLower(m_path) == toLower(fp.m_path)) return TFilePath("");
878   if (!fp.isAncestorOf(*this)) return *this;
879   int len = fp.m_path.length();
880   if (len == 0 || fp.m_path[len - 1] != wslash) len++;
881 
882   return TFilePath(m_path.substr(len));
883 }
884 
885 //-----------------------------------------------------------------------------
886 
match(const TFilePath & fp) const887 bool TFilePath::match(const TFilePath &fp) const {
888   return getParentDir() == fp.getParentDir() && getName() == fp.getName() &&
889          getFrame() == fp.getFrame() && getType() == fp.getType();
890 }
891 
892 //-----------------------------------------------------------------------------
893 
split(std::wstring & head,TFilePath & tail) const894 void TFilePath::split(std::wstring &head, TFilePath &tail) const {
895   TFilePath ancestor = getParentDir();
896   if (ancestor == TFilePath()) {
897     head = getWideString();
898     tail = TFilePath();
899     return;
900   }
901   for (;;) {
902     if (ancestor.isRoot()) break;
903     TFilePath p = ancestor.getParentDir();
904     if (p == TFilePath()) break;
905     ancestor = p;
906   }
907   head = ancestor.getWideString();
908   tail = *this - ancestor;
909 }
910