Vuo  2.0.0
VuoFileUtilitiesCocoa.mm
Go to the documentation of this file.
1 
10 #include "VuoFileUtilities.hh"
11 #include "VuoFileUtilitiesCocoa.hh"
12 #include "VuoException.hh"
13 
14 #ifndef NS_RETURNS_INNER_POINTER
15 #define NS_RETURNS_INNER_POINTER
16 #endif
17 #include <Cocoa/Cocoa.h>
18 
25 {
26  NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:filePath.c_str()]];
27  if (!url)
28  throw VuoException("Couldn't move file '" + filePath + "' to the trash: Couldn't create NSURL.");
29 
30  NSError *error = nil;
31  bool success = [[NSFileManager defaultManager] trashItemAtURL:url resultingItemURL:nil error:&error];
32  if (!success)
33  throw VuoException(string("Couldn't move file '" + filePath + "' to the trash: ") + [[error localizedDescription] UTF8String]);
34 }
35 
47 {
48  NSString *pathNS = [NSString stringWithUTF8String:path.c_str()];
49  if (!pathNS)
50  throw VuoException("Path \"" + path + "\" isn't a valid UTF-8 string.");
51 
52  NSURL *baseURL = [NSURL fileURLWithPath:pathNS isDirectory:YES];
53  if (!baseURL)
54  throw VuoException("Path \"" + path + "\" isn't a valid directory.");
55 
56  NSURL *temporaryDirectoryURL = nil;
57  while (true)
58  {
59  NSError *error = nil;
60  temporaryDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSItemReplacementDirectory
61  inDomain:NSUserDomainMask
62  appropriateForURL:baseURL
63  create:YES
64  error:&error];
65  if (error)
66  {
67  NSError *underlyingError = [[error userInfo] objectForKey:NSUnderlyingErrorKey];
68  if ([[underlyingError domain] isEqualToString:NSPOSIXErrorDomain]
69  && [underlyingError code] == ENOENT)
70  {
71  // File not found; go up a directory and try again.
72  baseURL = [baseURL URLByDeletingLastPathComponent];
73  }
74  else
75  throw VuoException((string("Couldn't get a temp folder: ") + [[error localizedDescription] UTF8String]).c_str());
76  }
77  else if (!temporaryDirectoryURL)
78  throw VuoException("Couldn't get a temp folder for path \"" + path + "\".");
79  else
80  break;
81  }
82 
83  return [[temporaryDirectoryURL path] UTF8String];
84 }
85 
94 {
95  NSString *pathNS = [NSString stringWithUTF8String:path.c_str()];
96  if (!pathNS)
97  throw VuoException("Path \"" + path + "\" isn't a valid UTF-8 string.");
98 
99  NSDictionary *fileAttributes;
100  while (true)
101  {
102  NSError *error = nil;
103  fileAttributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:pathNS error:&error];
104  if (error)
105  {
106  NSError *underlyingError = [[error userInfo] objectForKey:NSUnderlyingErrorKey];
107  if ([[underlyingError domain] isEqualToString:NSPOSIXErrorDomain]
108  && [underlyingError code] == ENOENT)
109  {
110  // File not found; go up a directory and try again.
111  pathNS = [pathNS stringByDeletingLastPathComponent];
112  }
113  else
114  throw VuoException((string("Couldn't get information about path: ") + [[error localizedDescription] UTF8String]).c_str());
115  }
116  else if (!fileAttributes)
117  throw VuoException("Couldn't get information about path \"" + path + "\".");
118  else
119  break;
120  }
121 
122  unsigned long long freeSpace = [fileAttributes[NSFileSystemFreeSize] longLongValue];
123  return freeSpace;
124 }
125 
131 void VuoFileUtilitiesCocoa_focusProcess(pid_t pid, bool force)
132 {
133  NSRunningApplication *compositionApp = [NSRunningApplication runningApplicationWithProcessIdentifier:pid];
134  [compositionApp activateWithOptions: NSApplicationActivateAllWindows
135  | (force ? NSApplicationActivateIgnoringOtherApps : 0)];
136 }
137 
142 {
143  return NSProcessInfo.processInfo.operatingSystemVersion.minorVersion;
144 }
145 
149 void VuoFileUtilitiesCocoa_setIcon(string filePath, string imagePath)
150 {
151  NSImage *image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:imagePath.c_str()]];
152  [[NSWorkspace sharedWorkspace] setIcon:image forFile:[NSString stringWithUTF8String:filePath.c_str()] options:0];
153  [image release];
154 }
155 
160 void VuoFileUtilitiesCocoa_setBundle(string filePath)
161 {
162  CFStringRef path = CFStringCreateWithCString(NULL, filePath.c_str(), kCFStringEncodingUTF8);
163  CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, true);
164  CFErrorRef error;
165  if (!CFURLSetResourcePropertyForKey(url, kCFURLIsPackageKey, kCFBooleanTrue, &error))
166  {
167  CFStringRef errorString = CFErrorCopyDescription(error);
168  CFIndex errorLength = CFStringGetLength(errorString);
169  char *errorCString = (char *)malloc(errorLength+1);
170  CFStringGetCString(errorString, errorCString, errorLength+1, kCFStringEncodingUTF8);
171  VUserLog("Warning: Couldn't set kCFURLIsPackageKey on this bundle: %s", errorCString);
172  free(errorCString);
173  CFRelease(errorString);
174  }
175  CFRelease(url);
176  CFRelease(path);
177 }
178 
182 static CFURLRef VuoFileUtilitiesCocoa_getCFURL(const string &path)
183 {
184  if (path.empty())
185  return nullptr;
186 
187  CFStringRef cfpath = CFStringCreateWithCString(nullptr, path.c_str(), kCFStringEncodingUTF8);
188  if (!cfpath)
189  throw VuoException("Path \"" + path + "\" isn't a valid UTF-8 string.");
190  VuoDefer(^{ CFRelease(cfpath); });
191 
192  CFURLRef url = CFURLCreateWithFileSystemPath(nullptr, cfpath, kCFURLPOSIXPathStyle, true);
193  if (!url)
194  throw VuoException("Path \"" + path + "\" isn't a valid POSIX path.");
195 
196  return url;
197 }
198 
202 static CFDataRef VuoFileUtilitiesCocoa_getMacAliasData(const string &path)
203 {
204  CFURLRef url = VuoFileUtilitiesCocoa_getCFURL(path);
205  if (!url)
206  return nullptr;
207  VuoDefer(^{ CFRelease(url); });
208 
209  return CFURLCreateBookmarkDataFromFile(nullptr, url, nullptr);
210 }
211 
217 bool VuoFileUtilitiesCocoa_isMacAlias(const string &path)
218 {
219  CFDataRef aliasData = VuoFileUtilitiesCocoa_getMacAliasData(path);
220  if (!aliasData)
221  return false;
222 
223  CFRelease(aliasData);
224  return true;
225 }
226 
230 static string VuoFileUtilitiesCocoa_CFStringToStdString(CFStringRef cfString)
231 {
232  const char *useUTF8StringPtr = NULL;
233  char *freeUTF8StringPtr = NULL;
234 
235  if ((useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8)) == NULL)
236  {
237  CFIndex stringLength = CFStringGetLength(cfString);
238  CFIndex maxBytes = 4 * stringLength + 1;
239  freeUTF8StringPtr = (char *)malloc(maxBytes);
240  CFStringGetCString(cfString, freeUTF8StringPtr, maxBytes, kCFStringEncodingUTF8);
241  useUTF8StringPtr = freeUTF8StringPtr;
242  }
243 
244  string stdString(useUTF8StringPtr);
245 
246  if (freeUTF8StringPtr != NULL)
247  free(freeUTF8StringPtr);
248 
249  return stdString;
250 }
251 
257 string VuoFileUtilitiesCocoa_resolveMacAlias(const string &path)
258 {
259  CFDataRef aliasData = VuoFileUtilitiesCocoa_getMacAliasData(path);
260  if (!aliasData)
261  throw VuoException("Path \"" + path + "\" isn't a macOS Alias.");
262  VuoDefer(^{ CFRelease(aliasData); });
263 
264  CFURLRef url = VuoFileUtilitiesCocoa_getCFURL(path);
265  VuoDefer(^{ CFRelease(url); });
266 
267  Boolean isStale = false;
268  CFErrorRef error = nullptr;
269  CFURLRef resolvedURL = CFURLCreateByResolvingBookmarkData(nullptr, aliasData, kCFBookmarkResolutionWithoutUIMask, url, nullptr, &isStale, &error);
270  if (!resolvedURL)
271  {
272  CFStringRef desc = CFErrorCopyDescription(error);
273  VuoDefer(^{ CFRelease(desc); });
274  throw VuoException("Path \"" + path + "\" is a macOS Alias, but it couldn't be resolved: \"" + VuoFileUtilitiesCocoa_CFStringToStdString(desc) + "\" (isStale=" + std::to_string(isStale) + ")");
275  }
276  VuoDefer(^{ CFRelease(resolvedURL); });
277 
278  CFStringRef resolvedPath = CFURLCopyFileSystemPath(resolvedURL, kCFURLPOSIXPathStyle);
279  if (!resolvedPath)
280  throw VuoException("Path \"" + path + "\" is a macOS Alias, but it resolved to something that isn't a POSIX path (\"" + VuoLog_copyCFDescription(resolvedURL) + "\").");
281  VuoDefer(^{ CFRelease(resolvedPath); });
282 
283  return VuoFileUtilitiesCocoa_CFStringToStdString(resolvedPath);
284 }