Skip to content

Commit 1080f1a

Browse files
committed
os::path::join : added addl vector signature for joining >2 dir paths
1 parent 235bcf5 commit 1080f1a

File tree

2 files changed

+125
-72
lines changed

2 files changed

+125
-72
lines changed

pystring.cpp

Lines changed: 124 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,114 +1132,145 @@ namespace path
11321132
///
11331133
///
11341134

1135-
// Join two or more pathname components, inserting "\\" as needed.
1136-
std::string join_nt(const std::string & a, const std::string & b)
1135+
std::string join_nt(const std::vector< std::string > & paths)
11371136
{
1138-
std::string path = a;
1139-
1140-
bool b_nts = false;
1141-
if(path.empty())
1142-
{
1143-
b_nts = true;
1144-
}
1145-
else if(isabs_nt(b))
1137+
if(paths.empty()) return "";
1138+
if(paths.size() == 1) return paths[0];
1139+
1140+
std::string path = paths[0];
1141+
1142+
for(unsigned int i=1; i<paths.size(); ++i)
11461143
{
1147-
// This probably wipes out path so far. However, it's more
1148-
// complicated if path begins with a drive letter:
1149-
// 1. join('c:', '/a') == 'c:/a'
1150-
// 2. join('c:/', '/a') == 'c:/a'
1151-
// But
1152-
// 3. join('c:/a', '/b') == '/b'
1153-
// 4. join('c:', 'd:/') = 'd:/'
1154-
// 5. join('c:/', 'd:/') = 'd:/'
1155-
1156-
if( (pystring::slice(path, 1, 2) != ":") ||
1157-
(pystring::slice(b, 1, 2) == ":") )
1158-
{
1159-
// Path doesnt start with a drive letter
1160-
b_nts = true;
1161-
}
1162-
// Else path has a drive letter, and b doesn't but is absolute.
1163-
else if((path.size()>3) ||
1164-
((path.size()==3) && !pystring::endswith(path, "/") && !pystring::endswith(path, "\\")))
1144+
std::string b = paths[i];
1145+
1146+
bool b_nts = false;
1147+
if(path.empty())
11651148
{
11661149
b_nts = true;
11671150
}
1168-
}
1169-
1170-
if(b_nts)
1171-
{
1172-
path = b;
1173-
}
1174-
else
1175-
{
1176-
// Join, and ensure there's a separator.
1177-
// assert len(path) > 0
1178-
if( pystring::endswith(path, "/") || pystring::endswith(path, "\\"))
1151+
else if(isabs_nt(b))
11791152
{
1180-
if(pystring::startswith(b,"/") || pystring::startswith(b,"\\"))
1153+
// This probably wipes out path so far. However, it's more
1154+
// complicated if path begins with a drive letter:
1155+
// 1. join('c:', '/a') == 'c:/a'
1156+
// 2. join('c:/', '/a') == 'c:/a'
1157+
// But
1158+
// 3. join('c:/a', '/b') == '/b'
1159+
// 4. join('c:', 'd:/') = 'd:/'
1160+
// 5. join('c:/', 'd:/') = 'd:/'
1161+
1162+
if( (pystring::slice(path, 1, 2) != ":") ||
1163+
(pystring::slice(b, 1, 2) == ":") )
11811164
{
1182-
path += pystring::slice(b, 1);
1165+
// Path doesnt start with a drive letter
1166+
b_nts = true;
11831167
}
1184-
else
1168+
// Else path has a drive letter, and b doesn't but is absolute.
1169+
else if((path.size()>3) ||
1170+
((path.size()==3) && !pystring::endswith(path, "/") && !pystring::endswith(path, "\\")))
11851171
{
1186-
path += b;
1172+
b_nts = true;
11871173
}
11881174
}
1189-
else if(pystring::endswith(path, ":"))
1175+
1176+
if(b_nts)
11901177
{
1191-
path += b;
1178+
path = b;
11921179
}
1193-
else if(!b.empty())
1180+
else
11941181
{
1195-
if(pystring::startswith(b,"/") || pystring::startswith(b,"\\"))
1182+
// Join, and ensure there's a separator.
1183+
// assert len(path) > 0
1184+
if( pystring::endswith(path, "/") || pystring::endswith(path, "\\"))
1185+
{
1186+
if(pystring::startswith(b,"/") || pystring::startswith(b,"\\"))
1187+
{
1188+
path += pystring::slice(b, 1);
1189+
}
1190+
else
1191+
{
1192+
path += b;
1193+
}
1194+
}
1195+
else if(pystring::endswith(path, ":"))
11961196
{
11971197
path += b;
11981198
}
1199+
else if(!b.empty())
1200+
{
1201+
if(pystring::startswith(b,"/") || pystring::startswith(b,"\\"))
1202+
{
1203+
path += b;
1204+
}
1205+
else
1206+
{
1207+
path += "\\" + b;
1208+
}
1209+
}
11991210
else
12001211
{
1201-
path += "\\" + b;
1212+
// path is not empty and does not end with a backslash,
1213+
// but b is empty; since, e.g., split('a/') produces
1214+
// ('a', ''), it's best if join() adds a backslash in
1215+
// this case.
1216+
path += "\\";
12021217
}
12031218
}
1204-
else
1205-
{
1206-
// path is not empty and does not end with a backslash,
1207-
// but b is empty; since, e.g., split('a/') produces
1208-
// ('a', ''), it's best if join() adds a backslash in
1209-
// this case.
1210-
path += "\\";
1211-
}
12121219
}
1213-
1220+
12141221
return path;
12151222
}
1223+
1224+
// Join two or more pathname components, inserting "\\" as needed.
1225+
std::string join_nt(const std::string & a, const std::string & b)
1226+
{
1227+
std::vector< std::string > paths(2);
1228+
paths[0] = a;
1229+
paths[1] = b;
1230+
return join_nt(paths);
1231+
}
12161232

12171233
// Join pathnames.
12181234
// If any component is an absolute path, all previous path components
12191235
// will be discarded.
12201236
// Ignore the previous parts if a part is absolute.
12211237
// Insert a '/' unless the first part is empty or already ends in '/'.
12221238

1223-
std::string join_posix(const std::string & a, const std::string & b)
1239+
std::string join_posix(const std::vector< std::string > & paths)
12241240
{
1225-
std::string path = a;
1226-
1227-
if(pystring::startswith(b, "/"))
1228-
{
1229-
path = b;
1230-
}
1231-
else if(path.empty() || pystring::endswith(path, "/"))
1232-
{
1233-
path += b;
1234-
}
1235-
else
1241+
if(paths.empty()) return "";
1242+
if(paths.size() == 1) return paths[0];
1243+
1244+
std::string path = paths[0];
1245+
1246+
for(unsigned int i=1; i<paths.size(); ++i)
12361247
{
1237-
path += "/" + b;
1248+
std::string b = paths[i];
1249+
if(pystring::startswith(b, "/"))
1250+
{
1251+
path = b;
1252+
}
1253+
else if(path.empty() || pystring::endswith(path, "/"))
1254+
{
1255+
path += b;
1256+
}
1257+
else
1258+
{
1259+
path += "/" + b;
1260+
}
12381261
}
1239-
1262+
12401263
return path;
12411264
}
12421265

1266+
std::string join_posix(const std::string & a, const std::string & b)
1267+
{
1268+
std::vector< std::string > paths(2);
1269+
paths[0] = a;
1270+
paths[1] = b;
1271+
return join_posix(paths);
1272+
}
1273+
12431274
std::string join(const std::string & path1, const std::string & path2)
12441275
{
12451276
#ifdef WINDOWS
@@ -1250,6 +1281,15 @@ namespace path
12501281
}
12511282

12521283

1284+
std::string join(const std::vector< std::string > & paths)
1285+
{
1286+
#ifdef WINDOWS
1287+
return join_nt(paths);
1288+
#else
1289+
return join_posix(paths);
1290+
#endif
1291+
}
1292+
12531293
//////////////////////////////////////////////////////////////////////////////////////////////
12541294
///
12551295
///
@@ -1593,6 +1633,19 @@ PYSTRING_ADD_TEST(pystring_os_path, join)
15931633
PYSTRING_CHECK_EQUAL(join_posix("../a","/b"), "/b" );
15941634
PYSTRING_CHECK_EQUAL(join_posix("../a","b"), "../a/b" );
15951635

1636+
std::vector< std::string > paths;
1637+
PYSTRING_CHECK_EQUAL(join_posix(paths), "" );
1638+
paths.push_back("/a");
1639+
PYSTRING_CHECK_EQUAL(join_posix(paths), "/a" );
1640+
paths.push_back("b");
1641+
PYSTRING_CHECK_EQUAL(join_posix(paths), "/a/b" );
1642+
paths.push_back("c/");
1643+
PYSTRING_CHECK_EQUAL(join_posix(paths), "/a/b/c/" );
1644+
paths.push_back("d");
1645+
PYSTRING_CHECK_EQUAL(join_posix(paths), "/a/b/c/d" );
1646+
paths.push_back("/e");
1647+
PYSTRING_CHECK_EQUAL(join_posix(paths), "/e" );
1648+
15961649
PYSTRING_CHECK_EQUAL(join_nt("c:","/a"), "c:/a" );
15971650
PYSTRING_CHECK_EQUAL(join_nt("c:/","/a"), "c:/a" );
15981651
PYSTRING_CHECK_EQUAL(join_nt("c:/a","/b"), "/b" );

pystring.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ namespace path
333333
/// current directory on drive C: (c:foo), not c:\foo.
334334

335335
std::string join(const std::string & path1, const std::string & path2);
336-
// std::string join(const std::vector< std::string > & paths);
336+
std::string join(const std::vector< std::string > & paths);
337337

338338
//////////////////////////////////////////////////////////////////////////////////////////////
339339
/// @brief Normalize a pathname. This collapses redundant separators and up-level references

0 commit comments

Comments
 (0)