10#include <CommonCrypto/CommonDigest.h>
20#include <mach-o/dyld.h>
43 const char FILE_SEPARATOR =
'/';
45 size_t separatorIndex = path.rfind(FILE_SEPARATOR);
46 string fileAndExtension;
47 if (separatorIndex != string::npos)
49 dir = path.substr(0, separatorIndex + 1);
50 if (separatorIndex < path.length())
51 fileAndExtension = path.substr(separatorIndex + 1, path.length());
55 fileAndExtension = path;
58 size_t dotIndex = fileAndExtension.rfind(
".");
59 if (dotIndex != string::npos)
61 file = fileAndExtension.substr(0, dotIndex);
62 extension = fileAndExtension.substr(dotIndex + 1, fileAndExtension.length());
66 file = fileAndExtension;
77 for (
int i = 0; i < path.length(); ++i)
79 while (i+1 < path.length() && path[i+1] ==
'/')
84 path.erase(path.length()-1);
97 return path1 == path2;
108 char *cwd = getcwd(NULL, 0);
111 VUserLog(
"Couldn't get current working directory: %s", strerror(errno));
115 string absolutePath = string(cwd) +
"/" + path;
129 if (directory.empty())
131 if (directory.at(directory.length()-1) !=
'/')
133 string suffix = (extension.empty() ?
"" : (
"." + extension));
134 string pathTemplate = directory + file +
"-XXXXXX" + suffix;
135 char *path = (
char *)calloc(pathTemplate.length() + 1,
sizeof(char));
136 strncpy(path, pathTemplate.c_str(), pathTemplate.length());
137 int fd = mkstemps(path, suffix.length());
139 string pathStr(path);
155 string pathTemplate =
getTmpDir() +
"/" + prefix +
".XXXXXX";
156 char *path = (
char *)calloc(pathTemplate.length() + 1,
sizeof(char));
157 strncpy(path, pathTemplate.c_str(), pathTemplate.length());
159 string pathStr(path);
190 char *cwd = getcwd(NULL, 0);
192 VUserLog(
"Error in getcwd(): %s", strerror(errno));
194 if (cwd && strstr(cwd,
"/Library/Containers/"))
206 char userTempDir[PATH_MAX];
207 if (confstr(_CS_DARWIN_USER_TEMP_DIR, userTempDir, PATH_MAX) > 0)
209 string userTempDirS(userTempDir);
226 throw VuoException(
"Couldn't create directory with empty path");
231 const char FILE_SEPARATOR =
'/';
232 size_t lastNonSeparator = path.find_last_not_of(FILE_SEPARATOR);
233 if (lastNonSeparator != string::npos)
234 path.resize(lastNonSeparator + 1);
236 string parentDir, file, ext;
242 int ret = mkdir(path.c_str(), 0777);
243 int mkdirErrno = errno;
245 throw VuoException((
string(
"Couldn't create directory \"" + path +
"\": ") + strerror(mkdirErrno)).c_str());
256 static string frameworkPath;
257 static dispatch_once_t once = 0;
258 dispatch_once(&once, ^{
261 const char *frameworkPathFragment =
"/Vuo.framework/Versions/";
262 uint32_t imageCount = _dyld_image_count();
263 for (uint32_t i = 0; i < imageCount; ++i)
265 const char *dylibPath = _dyld_get_image_name(i);
266 const char *found = strstr(dylibPath, frameworkPathFragment);
269 char *pathC = strndup(dylibPath, found - dylibPath);
270 string path = string(pathC) +
"/Vuo.framework";
274 frameworkPath = path;
281 char executablePath[PATH_MAX + 1];
282 uint32_t size =
sizeof(executablePath);
284 if (! _NSGetExecutablePath(executablePath, &size))
286 char cleanedExecutablePath[PATH_MAX + 1];
288 realpath(executablePath, cleanedExecutablePath);
289 string path = cleanedExecutablePath;
290 string dir, file, ext;
292 path = dir.substr(0, dir.length() - 1);
294 path = dir.substr(0, dir.length() - 1);
295 path +=
"/Frameworks/Vuo.framework";
299 frameworkPath = path;
307 return frameworkPath;
316 static string runnerFrameworkPath;
317 static dispatch_once_t once = 0;
318 dispatch_once(&once, ^{
323 runnerFrameworkPath = possibleFrameworkPath;
328 const char *frameworkPathFragment =
"/VuoRunner.framework/Versions/";
329 uint32_t imageCount = _dyld_image_count();
330 for (uint32_t i = 0; i < imageCount; ++i)
332 const char *dylibPath = _dyld_get_image_name(i);
333 const char *found = strstr(dylibPath, frameworkPathFragment);
336 char *pathC = strndup(dylibPath, found - dylibPath);
337 string path = string(pathC) +
"/VuoRunner.framework";
341 runnerFrameworkPath = path;
348 char executablePath[PATH_MAX + 1];
349 uint32_t size =
sizeof(executablePath);
351 if (! _NSGetExecutablePath(executablePath, &size))
353 char cleanedExecutablePath[PATH_MAX + 1];
355 realpath(executablePath, cleanedExecutablePath);
356 string path = cleanedExecutablePath;
357 string dir, file, ext;
359 path = dir.substr(0, dir.length() - 1);
361 path = dir.substr(0, dir.length() - 1);
362 path +=
"/Frameworks/VuoRunner.framework";
366 runnerFrameworkPath = path;
373 return runnerFrameworkPath;
386 string compositionDir, compositionFile, ext;
390 string parentDir, compositionDirName;
391 splitPath(compositionDir, parentDir, compositionDirName, ext);
394 string compositionBaseDir = (compositionDirName ==
"Modules" ? parentDir : compositionDir);
396 string compositionModulesDir = compositionBaseDir +
"/Modules";
399 return compositionModulesDir;
412 const char *home = getenv(
"HOME");
416 return string(home) +
"/Library/Application Support/Vuo/Modules";
424 return "/Library/Application Support/Vuo/Modules";
437 const char *home = getenv(
"HOME");
441 return string(home) +
"/Library/Caches/org.vuo/" + VUO_VERSION_AND_BUILD_STRING;
454 string dir, file, ext;
479 string bomUtf8 =
"\xEF\xBB\xBF";
480 string bomUtf16Be =
"\xFE\xFF";
481 string bomUtf16Le =
"\xFF\xFE";
482 string boms[] = { bomUtf8, bomUtf16Be, bomUtf16Le };
483 for (
int i = 0; i < 3; ++i)
486 for (
int j = 0; j < boms[i].length(); ++j)
487 if (boms[i][j] != s[j])
491 return boms[i].length();
505 while (getline(cin, line))
520 ifstream in(path.c_str(), ios::in | ios::binary);
522 throw VuoException(
string(
"Couldn't read file: ") + strerror(errno) +
" — " + path);
525 in.seekg(0, ios::end);
526 contents.resize(in.tellg());
527 in.seekg(0, ios::beg);
528 in.read(&contents[0], contents.size());
542 FILE *f = fopen(file.c_str(),
"wb");
544 throw VuoException(
string(
"Couldn't open file for writing: ") + strerror(errno) +
" — " + file);
546 size_t numBytesWritten = fwrite(data,
sizeof(
char), numBytes, f);
547 if (numBytesWritten != numBytes)
550 throw VuoException(
string(
"Couldn't write all data: ") + strerror(errno) +
" — " + file);
578 string dir, file, ext;
580 string tmpPath =
makeTmpFile(
"." + file, ext, dir);
598 return access(path.c_str(), 0) == 0;
613 int status = stat(path.c_str(), &st_buf);
614 return (! status && S_ISDIR(st_buf.st_mode));
623 int status = lstat(path.c_str(), &st_buf);
624 return (! status && S_ISLNK(st_buf.st_mode));
638 return access(path.c_str(), R_OK) == 0;
652 FILE *f = fopen(path.c_str(),
"rb");
656 fseek(f, 0, SEEK_END);
665 fseek(f, 0, SEEK_SET);
667 size_t ret = fread(&z, 1, 1, f);
677 FILE *f = fopen(path.c_str(),
"a");
686 int ret = remove(path.c_str());
688 VUserLog(
"Couldn't delete file: %s — %s", strerror(errno), path.c_str());
699 DIR *d = opendir(path.c_str());
702 VUserLog(
"Couldn't read directory: %s — %s", strerror(errno), path.c_str());
707 while( (de=readdir(d)) )
709 string fileName = de->d_name;
710 string filePath = path +
"/" + fileName;
712 if (fileName ==
"." || fileName ==
"..")
733 int ret = rename(fromPath.c_str(), toPath.c_str());
735 throw VuoException(
string(
"Couldn't move file: ") + strerror(errno) +
" — " + toPath);
761 int i = open(fromPath.c_str(), O_RDONLY);
763 throw VuoException(
string(
"Couldn't open copy source: ") + strerror(errno) +
" — " + fromPath);
767 int o = open(toPath.c_str(), O_WRONLY | O_CREAT, s.st_mode & 0777);
771 throw VuoException(
string(
"Couldn't open copy destination: ") + strerror(errno) +
" — " + toPath);
774 int ret = fcopyfile(i, o, NULL, COPYFILE_DATA | (preserveMetadata ? COPYFILE_STAT : 0));
775 char *e = strerror(errno);
780 throw VuoException(
string(
"Couldn't copy ") + fromPath +
" to " + toPath +
": " + e);
782 VDebugLog(
"%s -> %s", fromPath.c_str(), toPath.c_str());
799 char linkDestination[PATH_MAX + 1];
800 ssize_t len = readlink(fromPath.c_str(), linkDestination, PATH_MAX);
801 linkDestination[len] = 0;
802 if (symlink(linkDestination, toPath.c_str()) == -1)
803 throw VuoException(
string(
"Couldn't copy symlink \"") + fromPath +
"\" to \"" + toPath +
"\": " + strerror(errno));
805 VDebugLog(
"%s -> %s (symlink)", fromPath.c_str(), toPath.c_str());
816 for (
auto file : files)
818 string sourceFile = fromPath +
"/" + file->getRelativePath();
819 string targetFile = toPath +
"/" + file->getRelativePath();
832 int fd = open(path.c_str(), O_RDONLY);
834 throw VuoException(
"Error: Couldn't open \"" + path +
"\": " + strerror(errno));
838 if (fstat(fd, &stat) != 0)
839 throw VuoException(
"Error: Couldn't fstat \"" + path +
"\": " + strerror(errno));
843 void *data = mmap(
nullptr, stat.st_size, PROT_READ, MAP_PRIVATE | MAP_NOCACHE, fd, 0);
844 if (data == MAP_FAILED)
845 throw VuoException(
"Error: Couldn't mmap \"" + path +
"\": " + strerror(errno));
846 VuoDefer(^{ munmap(data, stat.st_size); });
848 unsigned char hash[CC_SHA256_DIGEST_LENGTH];
849 if (!CC_SHA256(data, stat.st_size, hash))
850 throw VuoException(
"Error: CC_SHA256 failed on file \"" + path +
"\"");
853 oss << setfill(
'0') << hex;
854 for (
int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i)
855 oss << setw(2) << (int)hash[i];
866 lstat(path.c_str(), &s);
867 return s.st_mtimespec.tv_sec;
876 lstat(path.c_str(), &s);
879 gettimeofday(&t, NULL);
881 return t.tv_sec - s.st_atimespec.tv_sec;
899 bool shouldSearchRecursively)
907 DIR *d = opendir(dirPath.c_str());
910 if (access(dirPath.c_str(), F_OK) != -1)
911 throw VuoException(
string(
"Couldn't read directory: ") + strerror(errno) +
" — " + dirPath);
916 while( (de=readdir(d)) )
918 string fileName = de->d_name;
919 string relativeFilePath = dirPath +
"/" + fileName;
921 if (fileName ==
"." || fileName ==
"..")
924 bool isArchive =
false;
925 for (set<string>::iterator archiveExtension = archiveExtensions.begin(); archiveExtension != archiveExtensions.end(); ++archiveExtension)
935 files.insert(fs.begin(), fs.end());
940 bool shouldSearchDir = shouldSearchRecursively
946 for (set<File *>::iterator i = filesInDir.begin(); i != filesInDir.end(); ++i)
949 f->dirPath = dirPath;
950 f->filePath = fileName +
"/" + f->filePath;
952 files.insert(filesInDir.begin(), filesInDir.end());
956 File *f =
new File(dirPath, fileName);
979 set<File *> matchingFiles;
981 for (set<File *>::iterator i = allFiles.begin(); i != allFiles.end(); ++i)
983 bool endsWithExtension =
false;
984 for (set<string>::iterator extension = extensions.begin(); extension != extensions.end(); ++extension)
986 endsWithExtension =
true;
989 matchingFiles.insert(*i);
994 return matchingFiles;
1014 mz_uint numFiles = mz_zip_reader_get_num_files(archive->
zipArchive);
1015 for (mz_uint i = 0; i < numFiles; ++i)
1017 mz_zip_archive_file_stat file_stat;
1018 bool success = mz_zip_reader_file_stat(archive->
zipArchive, i, &file_stat);
1021 VUserLog(
"Error: Couldn't read file '%u' in zip archive '%s'.", i, archive->
path.c_str());
1025 File *f =
new File(archive, file_stat.m_filename);
1047 set<File *> matchingFiles;
1049 for (set<File *>::iterator i = allFiles.begin(); i != allFiles.end(); ++i)
1053 string dir, file, ext;
1057 bool endsWithExtension =
false;
1058 for (set<string>::iterator extension = extensions.begin(); extension != extensions.end(); ++extension)
1060 endsWithExtension =
true;
1062 if (beginsWithDir && endsWithExtension)
1063 matchingFiles.insert(f);
1068 return matchingFiles;
1084 for (set<File *>::iterator i = archiveFiles.begin(); i != archiveFiles.end(); ++i)
1113 vector<string> processAndArgs{
1119 if (!entitlementsPath.empty())
1121 processAndArgs.push_back(
"--entitlements");
1122 processAndArgs.push_back(entitlementsPath);
1140 this->
zipArchive = (mz_zip_archive *)calloc(1,
sizeof(mz_zip_archive));
1142 bool success = mz_zip_reader_init_file(
zipArchive,
path.c_str(), 0);
1157 mz_zip_reader_end(zipArchive);
1178 this->fileDescriptor = -1;
1179 this->archive = NULL;
1191 this->fileDescriptor = -1;
1192 this->archive = archive;
1202 string newFilePath = basename() +
"." + extension;
1205 return File(archive, newFilePath);
1207 return File(dirPath, newFilePath);
1216 if (archive && --archive->referenceCount == 0)
1225 return (archive != NULL);
1235 return (archive ? archive->path :
"");
1254 throw VuoException(
"Can't return a simple absolute path for a file in an archive.");
1256 return dirPath +
"/" + filePath;
1267 throw VuoException(
"Can't return a simple absolute path for a file in an archive.");
1277 return filePath.substr(0, filePath.find_last_of(
'.'));
1285 return filePath.substr(filePath.find_last_of(
'.') + 1);
1294 return mz_zip_reader_locate_file(archive->zipArchive, filePath.c_str(),
nullptr, MZ_ZIP_FLAG_CASE_SENSITIVE) != -1;
1316 string fullPath = (dirPath.empty() ?
"" : (dirPath +
"/")) + filePath;
1318 FILE *pFile = fopen ( fullPath.c_str() ,
"rb" );
1320 throw VuoException(
string(
"Couldn't open file: ") + strerror(errno) +
" — " + fullPath);
1323 fseek (pFile , 0 , SEEK_END);
1324 size_t lSize = ftell (pFile);
1328 buffer = (
char*) malloc (
sizeof(
char)*lSize);
1331 numBytes = fread (buffer,1,lSize,pFile);
1333 if (numBytes != lSize)
1336 throw VuoException(
string(
"Couldn't read file: ") + strerror(errno) +
" — " + fullPath);
1341 buffer = (
char *)mz_zip_reader_extract_file_to_heap(archive->zipArchive, filePath.c_str(), &numBytes, 0);
1355 char *buffer = getContentsAsRawData(numBytes);
1356 string s(buffer, numBytes);
1372 if (fileDescriptor < 0)
1373 fileDescriptor = open((dirPath +
"/" + filePath).c_str(), O_RDONLY);
1374 int ret = flock(fileDescriptor, LOCK_SH | (nonBlocking ? LOCK_NB : 0));
1389 if (fileDescriptor < 0)
1390 fileDescriptor = open((dirPath +
"/" + filePath).c_str(), O_RDONLY);
1391 int ret = flock(fileDescriptor, LOCK_EX | (nonBlocking ? LOCK_NB : 0));
1401 flock(fileDescriptor, LOCK_UN);
1402 close(fileDescriptor);
1403 fileDescriptor = -1;
1426 string binaryDir, binaryFile, binaryExt;
1427 splitPath(processAndArgs[0], binaryDir, binaryFile, binaryExt);
1429 string errorPrefix =
"Couldn't execute " + binaryFile +
": ";
1433 int ret = pipe(outputPipe);
1435 throw VuoException(errorPrefix +
"couldn't open pipe: " + strerror(ret));
1436 int pipeRead = outputPipe[0];
1437 int pipeWrite = outputPipe[1];
1439 posix_spawn_file_actions_t fileActions;
1440 posix_spawn_file_actions_init(&fileActions);
1441 posix_spawn_file_actions_adddup2(&fileActions, pipeWrite, STDOUT_FILENO);
1442 posix_spawn_file_actions_adddup2(&fileActions, pipeWrite, STDERR_FILENO);
1443 posix_spawn_file_actions_addclose(&fileActions, pipeRead);
1447 char **processAndArgsZ = (
char **)malloc(
sizeof(
char *) * (processAndArgs.size() + 1));
1450 for (
auto arg : processAndArgs)
1452 processAndArgsZ[argCount++] = strdup(arg.c_str());
1455 commandLine +=
'"' + arg +
'"';
1457 processAndArgsZ[argCount] =
nullptr;
1462 char **environmentZ = (
char **)malloc(
sizeof(
char *) * (environment.size() + 1));
1463 int environmentVarCount = 0;
1464 for (
auto environmentVar : environment)
1466 environmentZ[environmentVarCount++] = strdup(environmentVar.c_str());
1467 commandLine = environmentVar +
" " + commandLine;
1469 environmentZ[environmentVarCount] =
nullptr;
1475 ret = posix_spawn(&pid, processAndArgs[0].c_str(), &fileActions, NULL, (
char *
const *)processAndArgsZ, (
char *
const *)environmentZ);
1477 posix_spawn_file_actions_destroy(&fileActions);
1478 for (
int i = 0; i < argCount; ++i)
1479 free(processAndArgsZ[i]);
1489 ret = waitpid(pid, &status, 0);
1490 if (ret == -1 && errno == EINTR)
1495 throw VuoException(errorPrefix +
"waitpid failed: " + strerror(errno));
1496 else if (status != 0)
1501 while (read(pipeRead, &buf, 255) > 0)
1521 return extension ==
"vuo";
1547 return extensions.find(extension) != extensions.end();
1558 e.insert(
"vuoshader");
1559 e.insert(
"vuoobjectfilter");
1560 return e.find(extension) != e.end();
1568 return string() +
"the cache of " + (generated ?
"generated" :
"installed") +
" modules at '" + moduleCachePath +
"'";
1576 string moduleCachePathCanonical = moduleCachePath;
1579 return moduleCachePathCanonical +
"/libVuoModuleCache-" + (generated ?
"generated" :
"installed") +
".txt";
1589 string moduleCachePathCanonical = moduleCachePath;
1592 string partialPath = moduleCachePathCanonical +
"/libVuoModuleCache-" + (generated ?
"generated" :
"installed");
1594 return partialPath +
".dylib";
1607 unsigned long &lastModified)
1625 string dir, file, ext;
1627 string fileName = file +
"." + ext;
1629 vector< pair<string, unsigned long> > existingRevisions;
1635 delete existingFile;
1638 if (existingRevisions.empty())
1644 auto latest = std::max_element(existingRevisions.begin(), existingRevisions.end(),
1645 [](pair<string, unsigned long> p1, pair<string, unsigned long> p2) { return p1.second < p2.second; });
1647 lastModified = latest->second;
1648 return latest->first;
1656 string moduleCachePath, file, ext;
1657 splitPath(dylibPath, moduleCachePath, file, ext);
1658 string dylibFileName = file +
"." + ext;
1674 string dir1, dir2, file1, file2, ext;
1675 splitPath(dylibPath1, dir1, file1, ext);
1676 splitPath(dylibPath2, dir2, file2, ext);
1683 if (parts1.size() == 3 && parts2.size() == 3 && parts1.back() != parts2.back())
1687 return parts1 == parts2;