2020#define NOHELP
2121#pragma warning(pop)
2222
23+ #if __cplusplus < 201703L
24+ #error Requires C++17 (and /Zc:__cplusplus with MSVC)
25+ #endif
26+
2327#include < algorithm>
2428#include < cassert>
2529#include < cstddef>
2933#include < cstring>
3034#include < cwchar>
3135#include < cwctype>
36+ #include < filesystem>
3237#include < fstream>
3338#include < iterator>
3439#include < list>
@@ -357,33 +362,37 @@ namespace
357362 }
358363else
359364 {
365+ std::filesystem::pathpath (fname +1 );
366+ auto & npath = path.make_preferred ();
360367if (wcspbrk (fname,L" ?*" ) !=nullptr )
361368 {
362369 std::list<SConversion> removeFiles;
363- SearchForFiles (&fname[ 1 ] , removeFiles,false );
370+ SearchForFiles (npath. c_str () , removeFiles,false );
364371
365- for (auto it : removeFiles)
372+ for (auto & it : removeFiles)
366373 {
367374_wcslwr_s (it.szSrc );
368375 excludes.insert (it.szSrc );
369376 }
370377 }
371378else
372379 {
373- std::wstring name =(fname + 1 );
380+ std::wstring name =npath. c_str ( );
374381std::transform (name.begin (), name.end (), name.begin (), towlower);
375382 excludes.insert (name);
376383 }
377384 }
378385 }
379386else if (wcspbrk (fname,L" ?*" ) !=nullptr )
380387 {
381- SearchForFiles (fname, flist,false );
388+ std::filesystem::pathpath (fname);
389+ SearchForFiles (path.make_preferred ().c_str (), flist,false );
382390 }
383391else
384392 {
385393 SConversion conv = {};
386- wcscpy_s (conv.szSrc , MAX_PATH, fname);
394+ std::filesystem::pathpath (fname);
395+ wcscpy_s (conv.szSrc , path.make_preferred ().c_str ());
387396 flist.push_back (conv);
388397 }
389398
@@ -438,7 +447,7 @@ namespace
438447wprintf (L" \n " );
439448 }
440449
441- void PrintLogo ()
450+ void PrintLogo (bool versionOnly )
442451 {
443452wchar_t version[32 ] = {};
444453
@@ -466,20 +475,27 @@ namespace
466475swprintf_s (version,L" %03d (library)" , UVATLAS_VERSION);
467476 }
468477
469- wprintf (L" Microsoft (R) UVAtlas Command-line Tool Version %ls\n " , version);
470- wprintf (L" Copyright (C) Microsoft Corp.\n " );
471- #ifdef _DEBUG
472- wprintf (L" *** Debug build ***\n " );
473- #endif
474- wprintf (L" \n " );
478+ if (versionOnly)
479+ {
480+ wprintf (L" uvatlastool version %ls\n " , version);
481+ }
482+ else
483+ {
484+ wprintf (L" Microsoft (R) UVAtlas Command-line Tool Version %ls\n " , version);
485+ wprintf (L" Copyright (C) Microsoft Corp.\n " );
486+ #ifdef _DEBUG
487+ wprintf (L" *** Debug build ***\n " );
488+ #endif
489+ wprintf (L" \n " );
490+ }
475491 }
476492
477493void PrintUsage ()
478494 {
479- PrintLogo ();
495+ PrintLogo (false );
480496
481497static const wchar_t *const s_usage =
482- L" Usage: uvatlas <options> <files>\n "
498+ L" Usage: uvatlas <options>[--] <files>\n "
483499L" \n "
484500L" Input file type must be Wavefront Object (.obj)\n "
485501L" \n "
@@ -527,7 +543,9 @@ namespace
527543L" -fn <normal-format> format to use for writing normals/tangents/normals\n "
528544L" -fuv <uv-format> format to use for texture coordinates\n "
529545L" -fc <color-format> format to use for writing colors\n "
530- L" -uv2 place UVs into a second texture coordinate channel\n " ;
546+ L" -uv2 place UVs into a second texture coordinate channel\n "
547+ L" \n "
548+ L" '-- ' is needed if any input filepath starts with the '-' or '/' character\n " ;
531549
532550wprintf (L" %ls" , s_usage);
533551
@@ -635,12 +653,38 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
635653// Process command line
636654uint64_t dwOptions =0 ;
637655 std::list<SConversion> conversion;
656+ bool allowOpts =true ;
638657
639658for (int iArg =1 ; iArg < argc; iArg++)
640659 {
641660 PWSTR pArg = argv[iArg];
642661
643- if ((' -' == pArg[0 ]) || (' /' == pArg[0 ]))
662+ if (allowOpts
663+ && (' -' == pArg[0 ]) && (' -' == pArg[1 ]))
664+ {
665+ if (pArg[2 ] ==0 )
666+ {
667+ // "-- " is the POSIX standard for "end of options" marking to escape the '-' and '/' characters at the start of filepaths.
668+ allowOpts =false ;
669+ }
670+ else if (!_wcsicmp (pArg,L" --version" ))
671+ {
672+ PrintLogo (true );
673+ return 0 ;
674+ }
675+ else if (!_wcsicmp (pArg,L" --help" ))
676+ {
677+ PrintUsage ();
678+ return 0 ;
679+ }
680+ else
681+ {
682+ wprintf (L" Unknown option: %ls\n " , pArg);
683+ return 1 ;
684+ }
685+ }
686+ else if (allowOpts
687+ && ((' -' == pArg[0 ]) || (' /' == pArg[0 ])))
644688 {
645689 pArg++;
646690 PWSTR pValue;
@@ -789,8 +833,11 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
789833wprintf (L" Cannot use both if and iv at the same time\n " );
790834return 1 ;
791835 }
792-
793- wcscpy_s (szTexFile, MAX_PATH, pValue);
836+ else
837+ {
838+ std::filesystem::pathpath (pValue);
839+ wcscpy_s (szTexFile, path.make_preferred ().c_str ());
840+ }
794841break ;
795842
796843case OPT_IMT_VERTEX:
@@ -820,7 +867,10 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
820867break ;
821868
822869case OPT_OUTPUTFILE:
823- wcscpy_s (szOutputFile, MAX_PATH, pValue);
870+ {
871+ std::filesystem::pathpath (pValue);
872+ wcscpy_s (szOutputFile, path.make_preferred ().c_str ());
873+ }
824874break ;
825875
826876case OPT_TOPOLOGICAL_ADJ:
@@ -934,7 +984,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
934984
935985case OPT_FILELIST:
936986 {
937- std::wifstreaminFile (pValue);
987+ std::filesystem::pathpath (pValue);
988+ std::wifstreaminFile (path.make_preferred ().c_str ());
938989if (!inFile)
939990 {
940991wprintf (L" Error opening -flist file %ls\n " , pValue);
@@ -951,7 +1002,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
9511002else if (wcspbrk (pArg,L" ?*" ) !=nullptr )
9521003 {
9531004const size_t count = conversion.size ();
954- SearchForFiles (pArg, conversion, (dwOptions & (uint64_t (1 ) << OPT_RECURSIVE)) !=0 );
1005+ std::filesystem::pathpath (pArg);
1006+ SearchForFiles (path.make_preferred ().c_str (), conversion, (dwOptions& (1 << OPT_RECURSIVE)) !=0 );
9551007if (conversion.size () <= count)
9561008 {
9571009wprintf (L" No matching files found for %ls\n " , pArg);
@@ -961,8 +1013,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
9611013else
9621014 {
9631015 SConversion conv = {};
964- wcscpy_s (conv. szSrc , MAX_PATH, pArg);
965-
1016+ std::filesystem::path path ( pArg);
1017+ wcscpy_s (conv. szSrc , path. make_preferred (). c_str ());
9661018 conversion.push_back (conv);
9671019 }
9681020 }
@@ -980,7 +1032,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
9801032 }
9811033
9821034if (~dwOptions & (uint64_t (1 ) << OPT_NOLOGO))
983- PrintLogo ();
1035+ PrintLogo (false );
9841036
9851037// Process files
9861038for (auto pConv = conversion.begin (); pConv != conversion.end (); ++pConv)