initial import
[vuplus_webkit] / Source / WebKit / mac / Misc / WebNSFileManagerExtras.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import <WebKit/WebNSFileManagerExtras.h>
30
31 #import "WebKitNSStringExtras.h"
32 #import "WebNSURLExtras.h"
33 #import <JavaScriptCore/Assertions.h>
34 #import <WebKitSystemInterface.h>
35 #import <sys/stat.h>
36
37 @implementation NSFileManager (WebNSFileManagerExtras)
38
39 - (NSString *)_webkit_carbonPathForPath:(NSString *)posixPath
40 {
41     OSStatus error;
42     FSRef ref, rootRef, parentRef;
43     FSCatalogInfo info;
44     NSMutableArray *carbonPathPieces;
45     HFSUniStr255 nameString;
46
47     // Make an FSRef.
48     error = FSPathMakeRef((const UInt8 *)[posixPath fileSystemRepresentation], &ref, NULL);
49     if (error != noErr) {
50         return nil;
51     }
52
53     // Get volume refNum.
54     error = FSGetCatalogInfo(&ref, kFSCatInfoVolume, &info, NULL, NULL, NULL);
55     if (error != noErr) {
56         return nil;
57     }
58
59     // Get root directory FSRef.
60     error = FSGetVolumeInfo(info.volume, 0, NULL, kFSVolInfoNone, NULL, NULL, &rootRef);
61     if (error != noErr) {
62         return nil;
63     }
64
65     // Get the pieces of the path.
66     carbonPathPieces = [NSMutableArray array];
67     for (;;) {
68         error = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, &nameString, NULL, &parentRef);
69         if (error != noErr) {
70             return nil;
71         }
72         [carbonPathPieces insertObject:[NSString stringWithCharacters:nameString.unicode length:nameString.length] atIndex:0];
73         if (FSCompareFSRefs(&ref, &rootRef) == noErr) {
74             break;
75         }
76         ref = parentRef;
77     }
78
79     // Volume names need trailing : character.
80     if ([carbonPathPieces count] == 1) {
81         [carbonPathPieces addObject:@""];
82     }
83
84     return [carbonPathPieces componentsJoinedByString:@":"];
85 }
86
87 typedef struct MetaDataInfo
88 {
89     CFStringRef URLString;
90     CFStringRef referrer;
91     CFStringRef path;
92 } MetaDataInfo;
93
94 static void *setMetaData(void* context)
95 {
96     MetaDataInfo *info = (MetaDataInfo *)context;
97     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
98     WKSetMetadataURL((NSString *)info->URLString, (NSString *)info->referrer, (NSString *)info->path);
99
100     if (info->URLString)
101         CFRelease(info->URLString);
102     if (info->referrer)
103         CFRelease(info->referrer);
104     if (info->path)
105         CFRelease(info->path);
106
107     free(info);
108     [pool drain];
109
110     return 0;
111 }
112
113 - (void)_webkit_setMetadataURL:(NSString *)URLString referrer:(NSString *)referrer atPath:(NSString *)path
114 {
115     ASSERT(URLString);
116     ASSERT(path);
117
118     NSURL *URL = [NSURL _web_URLWithUserTypedString:URLString];
119     if (URL)
120         URLString = [[URL _web_URLByRemovingUserInfo] _web_userVisibleString];
121  
122     // Spawn a background thread for WKSetMetadataURL because this function will not return until mds has
123     // journaled the data we're're trying to set. Depending on what other I/O is going on, it can take some
124     // time. 
125     pthread_t tid;
126     pthread_attr_t attr;
127     pthread_attr_init(&attr);
128     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
129
130     MetaDataInfo *info = static_cast<MetaDataInfo *>(malloc(sizeof(MetaDataInfo)));
131     
132     info->URLString = URLString ? CFStringCreateCopy(0, (CFStringRef)URLString) : 0;
133     info->referrer = referrer ? CFStringCreateCopy(0, (CFStringRef)referrer) : 0;
134     info->path = path ? CFStringCreateCopy(0, (CFStringRef)path) : 0;
135
136     pthread_create(&tid, &attr, setMetaData, info);
137     pthread_attr_destroy(&attr);
138 }
139
140 - (NSString *)_webkit_startupVolumeName
141 {
142     NSString *path = [self _webkit_carbonPathForPath:@"/"];
143     return [path substringToIndex:[path length]-1];
144 }
145
146 // -[NSFileManager fileExistsAtPath:] returns NO if there is a broken symlink at the path.
147 // So we use this function instead, which returns YES if there is anything there, including
148 // a broken symlink.
149 static BOOL fileExists(NSString *path)
150 {
151     struct stat statBuffer;
152     return !lstat([path fileSystemRepresentation], &statBuffer);
153 }
154
155 - (NSString *)_webkit_pathWithUniqueFilenameForPath:(NSString *)path
156 {
157     // "Fix" the filename of the path.
158     NSString *filename = [[path lastPathComponent] _webkit_filenameByFixingIllegalCharacters];
159     path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename];
160
161     if (fileExists(path)) {
162         // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename.
163         NSString *extensions = nil;
164         NSString *pathWithoutExtensions;
165         NSString *lastPathComponent = [path lastPathComponent];
166         NSRange periodRange = [lastPathComponent rangeOfString:@"."];
167         
168         if (periodRange.location == NSNotFound) {
169             pathWithoutExtensions = path;
170         } else {
171             extensions = [lastPathComponent substringFromIndex:periodRange.location + 1];
172             lastPathComponent = [lastPathComponent substringToIndex:periodRange.location];
173             pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent];
174         }
175
176         for (unsigned i = 1; ; i++) {
177             NSString *pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i];
178             path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber;
179             if (!fileExists(path))
180                 break;
181         }
182     }
183
184     return path;
185 }
186
187 @end
188