/MySVN.m

https://gitlab.com/nilesh1883/svnx · Objective C · 791 lines · 571 code · 172 blank · 48 comment · 38 complexity · 01578ed4b3652e91ea5d0ab171ea34e7 MD5 · raw file

  1. //
  2. // MySVN.m
  3. //
  4. #import "MySVN.h"
  5. #import "MyApp.h"
  6. #import "CommonUtils.h"
  7. #import "Tasks.h"
  8. #import <unistd.h>
  9. extern UInt32 gSvnVersion;
  10. //----------------------------------------------------------------------------------------
  11. static id
  12. makeTaskInfo (NSString* name, NSString* commmandPath, NSArray* arguments)
  13. {
  14. return [NSMutableDictionary dictionaryWithObjectsAndKeys:
  15. name, @"name",
  16. [NSString stringWithFormat: @"%@ %@",
  17. commmandPath, [arguments componentsJoinedByString: @" "]], @"command",
  18. nil];
  19. }
  20. //----------------------------------------------------------------------------------------
  21. static id
  22. makeSvnInfo (NSString* name, NSArray* arguments)
  23. {
  24. return makeTaskInfo(name, SvnCmdPath(), arguments);
  25. }
  26. //----------------------------------------------------------------------------------------
  27. static NSString*
  28. joinedOptions (NSArray* options1, NSArray* options2)
  29. {
  30. return [NSString stringWithFormat: @"%@ %@", [options1 componentsJoinedByString: @" "],
  31. [options2 componentsJoinedByString: @" "]];
  32. }
  33. //----------------------------------------------------------------------------------------
  34. static NSString*
  35. concatOptions (NSInvocation* generalOptions, NSArray* options)
  36. {
  37. return joinedOptions([MySvn optionsFromSvnOptionsInvocation: generalOptions], options);
  38. }
  39. //----------------------------------------------------------------------------------------
  40. static void
  41. addGeneralOptions (NSMutableArray* arguments, NSInvocation* generalOptions)
  42. {
  43. [arguments addObjectsFromArray: [MySvn optionsFromSvnOptionsInvocation: generalOptions]];
  44. }
  45. //----------------------------------------------------------------------------------------
  46. NSString*
  47. SvnPath ()
  48. {
  49. return GetPreference(@"svnBinariesFolder");
  50. }
  51. //----------------------------------------------------------------------------------------
  52. NSString*
  53. SvnCmdPath ()
  54. {
  55. return [SvnPath() stringByAppendingPathComponent: @"svn"];
  56. }
  57. //----------------------------------------------------------------------------------------
  58. NSString*
  59. ShellScriptPath (NSString* script)
  60. {
  61. return [[NSBundle mainBundle] pathForResource: script ofType: @"sh"];
  62. }
  63. //----------------------------------------------------------------------------------------
  64. NSString*
  65. GetDiffAppName ()
  66. {
  67. // 0: FileMerge, 1: TextWrangler, 2: CodeWarrior, 3: BBEdit,
  68. // 4: Araxis Merge, 5: DiffMerge, 6: Changes, 7: Guiffy, 8: KDiff3
  69. static NSString* const diffAppNames[] = {
  70. @"filemerge", @"textwrangler", @"codewarrior", @"bbedit",
  71. @"araxis", @"diffmerge", @"changes", @"guiffy", @"kdiff3"
  72. };
  73. int diffAppIndex = GetPreferenceInt(@"defaultDiffApplication");
  74. if (diffAppIndex < 0 || diffAppIndex >= sizeof(diffAppNames) / sizeof(diffAppNames[0]))
  75. diffAppIndex = 0;
  76. return diffAppNames[diffAppIndex];
  77. }
  78. //----------------------------------------------------------------------------------------
  79. #pragma mark -
  80. @implementation MySvn
  81. // New-style svn calls (>=0.8)
  82. + (NSMutableDictionary*) diffItems: (NSArray*) itemsPaths
  83. generalOptions: (NSInvocation*) generalOptions
  84. options: (NSArray*) options
  85. callback: (NSInvocation*) callback
  86. callbackInfo: (id) callbackInfo
  87. taskInfo: (id) taskInfo
  88. {
  89. NSMutableArray* arguments = [NSMutableArray arrayWithObjects:
  90. @"diff", @"--diff-cmd", ShellScriptPath(@"svndiff"),
  91. @"--extensions", GetDiffAppName(),
  92. nil];
  93. addGeneralOptions(arguments, generalOptions);
  94. [arguments addObjectsFromArray: options];
  95. [arguments addObjectsFromArray: itemsPaths];
  96. NSString* taskLaunchPath = SvnCmdPath();
  97. id additionalTaskInfo = makeTaskInfo(@"svn diff", taskLaunchPath, arguments);
  98. // <svnCmdPath> diff --diff-cmd <svndiff.sh> --extensions <diffAppName> ...
  99. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  100. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  101. }
  102. //----------------------------------------------------------------------------------------
  103. + (NSMutableDictionary*) runScript: (NSString*) scriptName
  104. options: (NSArray*) options
  105. args: (NSArray*) args
  106. name: (NSString*) name
  107. callback: (NSInvocation*) callback
  108. callbackInfo: (id) callbackInfo
  109. taskInfo: (id) taskInfo
  110. dataOnly: (BOOL) dataOnly
  111. {
  112. NSString* taskLaunchPath = ShellScriptPath(scriptName);
  113. NSMutableArray* arguments = [NSMutableArray arrayWithArray: options];
  114. [arguments addObjectsFromArray: args];
  115. id additionalTaskInfo = makeTaskInfo(name, taskLaunchPath, arguments);
  116. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  117. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: dataOnly];
  118. }
  119. //----------------------------------------------------------------------------------------
  120. + (NSMutableDictionary*) genericCommand: (NSString*) command
  121. arguments: (NSArray*) args
  122. generalOptions: (NSInvocation*) generalOptions
  123. options: (NSArray*) options
  124. callback: (NSInvocation*) callback
  125. callbackInfo: (id) callbackInfo
  126. taskInfo: (id) taskInfo
  127. {
  128. NSString *taskLaunchPath = SvnCmdPath();
  129. NSMutableArray *arguments = [NSMutableArray arrayWithObject: command];
  130. [arguments addObjectsFromArray: args];
  131. // dprintf("cmd='%@' gSvnVersion=%d", command, gSvnVersion);
  132. if (gSvnVersion >= 1006000 && [command isEqualToString: @"cleanup"])
  133. [arguments addObject: @"--non-interactive"];
  134. else if (![command isEqualToString: @"revert"] && ![command isEqualToString: @"add"] &&
  135. ![command isEqualToString: @"move"] && ![command isEqualToString: @"resolved"])
  136. addGeneralOptions(arguments, generalOptions);
  137. [arguments addObjectsFromArray: options];
  138. id additionalTaskInfo = makeTaskInfo([@"svn " stringByAppendingString: command], taskLaunchPath, arguments);
  139. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  140. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  141. }
  142. + (NSMutableDictionary*) moveMultiple: (NSArray*) files
  143. destination: (NSString*) destinationPath
  144. generalOptions: (NSInvocation*) generalOptions
  145. options: (NSArray*) options
  146. callback: (NSInvocation*) callback
  147. callbackInfo: (id) callbackInfo
  148. taskInfo: (id) taskInfo
  149. {
  150. #pragma unused(generalOptions)
  151. NSString *taskLaunchPath = ShellScriptPath(@"svnmove");
  152. NSMutableArray *arguments = [NSMutableArray arrayWithObject: SvnCmdPath()];
  153. [arguments addObject: [options componentsJoinedByString: @" "]]; // see svnmove.sh
  154. [arguments addObject: destinationPath]; // see svnmove.sh
  155. [arguments addObjectsFromArray: files];
  156. id additionalTaskInfo = makeTaskInfo(@"svn move multiple", taskLaunchPath, arguments);
  157. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  158. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  159. }
  160. + (NSMutableDictionary*) copyMultiple: (NSArray*) files
  161. destination: (NSString*) destinationPath
  162. generalOptions:(NSInvocation*) generalOptions
  163. options: (NSArray*) options
  164. callback: (NSInvocation*) callback
  165. callbackInfo: (id) callbackInfo
  166. taskInfo: (id) taskInfo
  167. {
  168. #pragma unused(generalOptions)
  169. NSString *taskLaunchPath = ShellScriptPath(@"svncopy");
  170. NSMutableArray *arguments = [NSMutableArray arrayWithObject: SvnCmdPath()];
  171. [arguments addObject: [options componentsJoinedByString: @" "]]; // see svncopy.sh
  172. [arguments addObject: destinationPath]; // see svncopy.sh
  173. [arguments addObjectsFromArray: files];
  174. id additionalTaskInfo = makeTaskInfo(@"svn copy multiple", taskLaunchPath, arguments);
  175. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  176. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  177. }
  178. + (NSMutableDictionary*) log: (NSString*) path
  179. generalOptions: (NSInvocation*) generalOptions
  180. options: (NSArray*) options
  181. callback: (NSInvocation*) callback
  182. callbackInfo: (id) callbackInfo
  183. taskInfo: (id) taskInfo
  184. {
  185. NSString *taskLaunchPath = SvnCmdPath();
  186. NSMutableArray *arguments = [NSMutableArray array];
  187. [arguments addObject: @"log"];
  188. addGeneralOptions(arguments, generalOptions);
  189. [arguments addObjectsFromArray: options];
  190. [arguments addObject: path];
  191. id additionalTaskInfo = makeTaskInfo(@"svn log", taskLaunchPath, arguments);
  192. // [additionalTaskInfo setObject: kNSTrue forKey: @"outputToData"];
  193. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  194. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: YES];
  195. }
  196. + (NSMutableDictionary*) list: (NSString*) path
  197. generalOptions: (NSInvocation*) generalOptions
  198. options: (NSArray*) options
  199. callback: (NSInvocation*) callback
  200. callbackInfo: (id) callbackInfo
  201. taskInfo: (id) taskInfo
  202. {
  203. NSString *taskLaunchPath = SvnCmdPath();
  204. NSMutableArray *arguments = [NSMutableArray array];
  205. [arguments addObject: @"list"];
  206. addGeneralOptions(arguments, generalOptions);
  207. [arguments addObjectsFromArray: options];
  208. [arguments addObject: path];
  209. id additionalTaskInfo = makeTaskInfo(@"svn list", taskLaunchPath, arguments);
  210. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  211. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: YES];
  212. }
  213. + (NSMutableDictionary*) statusAtWorkingCopyPath: (NSString*) path
  214. generalOptions: (NSInvocation*) generalOptions
  215. options: (NSArray*) options
  216. callback: (NSInvocation*) callback
  217. callbackInfo: (id) callbackInfo
  218. taskInfo: (id) taskInfo
  219. {
  220. NSString *taskLaunchPath = SvnCmdPath();
  221. NSMutableArray *arguments = [NSMutableArray array];
  222. [arguments addObject: @"status"];
  223. addGeneralOptions(arguments, generalOptions);
  224. [arguments addObjectsFromArray: options];
  225. [arguments addObject: path];
  226. id additionalTaskInfo = makeTaskInfo(@"svn status", taskLaunchPath, arguments);
  227. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  228. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: YES];
  229. }
  230. + (NSMutableDictionary*) updateAtWorkingCopyPath: (NSString*) path
  231. generalOptions: (NSInvocation*) generalOptions
  232. options: (NSArray*) options
  233. callback: (NSInvocation*) callback
  234. callbackInfo: (id) callbackInfo
  235. taskInfo: (id) taskInfo
  236. {
  237. NSString *taskLaunchPath = SvnCmdPath();
  238. NSMutableArray *arguments = [NSMutableArray array];
  239. [arguments addObject: @"update"];
  240. addGeneralOptions(arguments, generalOptions);
  241. [arguments addObjectsFromArray: options];
  242. [arguments addObject: path];
  243. id additionalTaskInfo = makeTaskInfo(@"svn update", taskLaunchPath, arguments);
  244. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  245. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  246. }
  247. + (NSMutableDictionary*) checkout: (NSString*) file
  248. destination: (NSString*) destinationPath
  249. generalOptions: (NSInvocation*) generalOptions
  250. options: (NSArray*) options
  251. callback: (NSInvocation*) callback
  252. callbackInfo: (id) callbackInfo
  253. taskInfo: (id) taskInfo
  254. {
  255. NSString *taskLaunchPath = SvnCmdPath();
  256. NSMutableArray *arguments = [NSMutableArray array];
  257. [arguments addObject: @"checkout"];
  258. addGeneralOptions(arguments, generalOptions);
  259. [arguments addObjectsFromArray: options];
  260. [arguments addObject: file];
  261. [arguments addObject: destinationPath];
  262. id additionalTaskInfo = makeTaskInfo(@"svn checkout", taskLaunchPath, arguments);
  263. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  264. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  265. }
  266. + (NSMutableDictionary*) extractItems: (NSArray*) items
  267. generalOptions: (NSInvocation*) generalOptions
  268. options: (NSArray*) options
  269. callback: (NSInvocation*) callback
  270. callbackInfo: (id) callbackInfo
  271. taskInfo: (id) taskInfo
  272. {
  273. NSString *taskLaunchPath = ShellScriptPath(@"svnextract");
  274. NSMutableArray *arguments = [NSMutableArray array];
  275. [arguments addObject: SvnCmdPath()];
  276. [arguments addObject: concatOptions(generalOptions, options)];
  277. [arguments addObjectsFromArray: items];
  278. id additionalTaskInfo = makeTaskInfo(@"extract", taskLaunchPath, arguments);
  279. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  280. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  281. }
  282. + (NSMutableDictionary*) import: (NSString*) file
  283. destination: (NSString*) destinationPath
  284. generalOptions: (NSInvocation*) generalOptions
  285. options: (NSArray*) options
  286. callback: (NSInvocation*) callback
  287. callbackInfo: (id) callbackInfo
  288. taskInfo: (id) taskInfo
  289. {
  290. NSString *taskLaunchPath = SvnCmdPath();
  291. NSMutableArray *arguments = [NSMutableArray array];
  292. [arguments addObject: @"import"];
  293. addGeneralOptions(arguments, generalOptions);
  294. [arguments addObjectsFromArray: options];
  295. [arguments addObject: file];
  296. [arguments addObject: destinationPath];
  297. id additionalTaskInfo = makeTaskInfo(@"svn import", taskLaunchPath, arguments);
  298. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  299. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  300. }
  301. + (NSMutableDictionary*) copy: (NSString*) file
  302. destination: (NSString*) destinationPath
  303. generalOptions: (NSInvocation*) generalOptions
  304. options: (NSArray*) options
  305. callback: (NSInvocation*) callback
  306. callbackInfo: (id) callbackInfo
  307. taskInfo: (id) taskInfo
  308. {
  309. NSString *taskLaunchPath = SvnCmdPath();
  310. NSMutableArray *arguments = [NSMutableArray array];
  311. [arguments addObject: @"copy"];
  312. addGeneralOptions(arguments, generalOptions);
  313. [arguments addObjectsFromArray: options];
  314. [arguments addObject: file];
  315. [arguments addObject: destinationPath];
  316. id additionalTaskInfo = makeTaskInfo(@"svn copy", taskLaunchPath, arguments);
  317. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  318. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  319. }
  320. + (NSMutableDictionary*) move: (NSString*) file
  321. destination: (NSString*) destinationPath
  322. generalOptions: (NSInvocation*) generalOptions
  323. options: (NSArray*) options
  324. callback: (NSInvocation*) callback
  325. callbackInfo: (id) callbackInfo
  326. taskInfo: (id) taskInfo
  327. {
  328. NSString *taskLaunchPath = SvnCmdPath();
  329. NSMutableArray *arguments = [NSMutableArray array];
  330. [arguments addObject: @"move"];
  331. addGeneralOptions(arguments, generalOptions);
  332. [arguments addObjectsFromArray: options];
  333. [arguments addObject: file];
  334. [arguments addObject: destinationPath];
  335. id additionalTaskInfo = makeTaskInfo(@"svn move", taskLaunchPath, arguments);
  336. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  337. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  338. }
  339. + (NSMutableDictionary*) mkdir: (NSArray*) files
  340. generalOptions: (NSInvocation*) generalOptions
  341. options: (NSArray*) options
  342. callback: (NSInvocation*) callback
  343. callbackInfo: (id) callbackInfo
  344. taskInfo: (id) taskInfo
  345. {
  346. NSString *taskLaunchPath = SvnCmdPath();
  347. NSMutableArray *arguments = [NSMutableArray array];
  348. [arguments addObject: @"mkdir"];
  349. addGeneralOptions(arguments, generalOptions);
  350. [arguments addObjectsFromArray: options];
  351. [arguments addObjectsFromArray: files];
  352. id additionalTaskInfo = makeTaskInfo(@"svn mkdir", taskLaunchPath, arguments);
  353. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  354. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  355. }
  356. + (NSMutableDictionary*) delete: (NSArray*) files
  357. generalOptions: (NSInvocation*) generalOptions
  358. options: (NSArray*) options
  359. callback: (NSInvocation*) callback
  360. callbackInfo: (id) callbackInfo
  361. taskInfo: (id) taskInfo
  362. {
  363. NSString *taskLaunchPath = SvnCmdPath();
  364. NSMutableArray *arguments = [NSMutableArray array];
  365. [arguments addObject: @"delete"];
  366. addGeneralOptions(arguments, generalOptions);
  367. [arguments addObjectsFromArray: options];
  368. [arguments addObjectsFromArray: files];
  369. id additionalTaskInfo = makeTaskInfo(@"svn delete", taskLaunchPath, arguments);
  370. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  371. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  372. }
  373. //----------------------------------------------------------------------------------------
  374. + (NSMutableDictionary*) blame: (NSArray*) files
  375. revision: (NSString*) revision
  376. generalOptions: (NSInvocation*) generalOptions
  377. options: (NSArray*) options
  378. callback: (NSInvocation*) callback
  379. callbackInfo: (id) callbackInfo
  380. taskInfo: (id) taskInfo
  381. {
  382. NSMutableArray* arguments = [NSMutableArray arrayWithObjects:
  383. SvnCmdPath(), GetDiffAppName(), revision, concatOptions(generalOptions, options), nil];
  384. [arguments addObjectsFromArray: files];
  385. NSString* taskLaunchPath = ShellScriptPath(@"svnblame");
  386. id additionalTaskInfo = makeTaskInfo(@"svn blame", taskLaunchPath, arguments);
  387. // svnblame.sh <svn-tool> <diff-app> <revision> <options> <url...>
  388. return [MySvn launchTask: taskLaunchPath arguments: arguments callback: callback callbackInfo: callbackInfo
  389. taskInfo: taskInfo additionalTaskInfo: additionalTaskInfo outputToData: NO];
  390. }
  391. //----------------------------------------------------------------------------------------
  392. #pragma mark -
  393. #pragma mark Helpers
  394. //----------------------------------------------------------------------------------------
  395. static NSDictionary*
  396. ensureDict (NSDictionary* dictOrNil)
  397. {
  398. return dictOrNil ? dictOrNil : [NSDictionary dictionary];
  399. }
  400. + (NSArray*) optionsFromSvnOptionsInvocation: (NSInvocation*) invocation
  401. {
  402. NSMutableDictionary *dic;
  403. [invocation invoke];
  404. [invocation getReturnValue: &dic];
  405. NSMutableArray *arr = [NSMutableArray array];
  406. NSString *username = [dic objectForKey: @"user"];
  407. NSString *password = [dic objectForKey: @"pass"];
  408. if ([username length])
  409. {
  410. [arr addObject: @"--username"];
  411. [arr addObject: username];
  412. if ([password length])
  413. {
  414. [arr addObject: @"--password"];
  415. [arr addObject: password];
  416. }
  417. }
  418. [arr addObject: @"--non-interactive"];
  419. return arr;
  420. }
  421. //----------------------------------------------------------------------------------------
  422. + (NSMutableDictionary*) launchTask: (NSString*) taskLaunchPath
  423. arguments: (NSArray*) arguments
  424. callback: (NSInvocation*) callback
  425. callbackInfo: (id) callbackInfo
  426. taskInfo: (id) taskInfo
  427. additionalTaskInfo: (id) additionalTaskInfo
  428. outputToData: (BOOL) outputToData
  429. {
  430. /* dprintf_("launchTask: `%@ %@`\n [%@ %s] callback=%@",
  431. taskLaunchPath, [arguments componentsJoinedByString: @" "],
  432. [callback target], sel_getName([callback selector]), callback);*/
  433. [callback retainArguments];
  434. NSTask* task = [[NSTask alloc] init];
  435. NSPipe* pipe = [[NSPipe alloc] init];
  436. NSPipe* errorPipe = [[NSPipe alloc] init];
  437. [task setEnvironment: [Task createEnvironment: TRUE]];
  438. [task setLaunchPath: taskLaunchPath];
  439. [task setArguments: arguments];
  440. [task setStandardOutput: pipe];
  441. [task setStandardError: errorPipe];
  442. NSFileHandle* handle = [pipe fileHandleForReading];
  443. NSFileHandle* errorHandle = [errorPipe fileHandleForReading];
  444. // this will be done by Tasks
  445. // [task launch];
  446. NSMutableDictionary *taskObj = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  447. @"pending", @"status",
  448. task, @"task",
  449. handle, @"handle",
  450. errorHandle, @"errorHandle",
  451. // [NSNumber numberWithInt: [task processIdentifier]], @"pid",
  452. callback, @"callback",
  453. ensureDict(callbackInfo), @"callbackInfo",
  454. ensureDict(taskInfo), @"taskInfo",
  455. ensureDict(additionalTaskInfo), @"additionalTaskInfo",
  456. NSBool(outputToData), @"outputToData",
  457. nil];
  458. [errorPipe release];
  459. [pipe release];
  460. [task release];
  461. [Tasks addTask: taskObj];
  462. return taskObj;
  463. }
  464. //----------------------------------------------------------------------------------------
  465. #pragma mark -
  466. //----------------------------------------------------------------------------------------
  467. + (void) killTask: (NSDictionary*) taskObj
  468. force: (BOOL) force
  469. {
  470. NSTask* const task = [taskObj objectForKey: @"task"];
  471. // dprintf("task=%@ force=%d taskObj=%@", task, force, [taskObj objectForKey: @"pid"]);
  472. // dprintf("task=%@ force=%d", task, force);
  473. AssertClass(task, NSTask);
  474. if ([task isRunning])
  475. {
  476. if (force) // Use kill -9 to kill.
  477. {
  478. pid_t pid = [task processIdentifier];
  479. if (kill(pid, SIGKILL))
  480. dprintf("kill(%d) => errno=%d", pid, errno);
  481. }
  482. else
  483. {
  484. [task terminate];
  485. }
  486. }
  487. }
  488. //----------------------------------------------------------------------------------------
  489. enum {
  490. kCacheMaxSize = 400,
  491. kCacheMinAge = 3600 * 8, // seconds
  492. kCacheMaxDeletableFileSize = 32 * 1024, // bytes
  493. kCacheDeleteCount = 8
  494. };
  495. //----------------------------------------------------------------------------------------
  496. // Try to delete up to kCacheDeleteCount entries & their files.
  497. // Called if adding an entry to the cache with grow it past kCacheMaxSize.
  498. // Only delete entries older than kCacheMinAge that have no file or whose size is <= kCacheMaxDeletableFileSize.
  499. // Deletes the least recently accessed entries first.
  500. static void
  501. removeOldEntries (NSMutableDictionary* cacheDict, NSString* cacheDir,
  502. NSFileManager* fileMgr, CFAbsoluteTime now)
  503. {
  504. struct Desc
  505. {
  506. CFAbsoluteTime time;
  507. id key;
  508. id path;
  509. };
  510. typedef struct Desc Desc;
  511. int count = 0, i;
  512. Desc descs[kCacheDeleteCount + 1];
  513. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  514. CFAbsoluteTime time0 = now - kCacheMinAge;
  515. id key, fattrs;
  516. NSEnumerator* enumerator = [cacheDict keyEnumerator];
  517. while ((key = [enumerator nextObject]))
  518. {
  519. NSDictionary* cacheEntry = [cacheDict objectForKey: key];
  520. CFAbsoluteTime time = [[cacheEntry objectForKey: @"time"] doubleValue];
  521. if (time < time0)
  522. {
  523. NSString* path = [cacheDir stringByAppendingPathComponent:
  524. [cacheEntry objectForKey: @"name"]];
  525. if ((fattrs = [fileMgr fileAttributesAtPath: path traverseLink: NO]) == nil ||
  526. [fattrs fileSize] <= kCacheMaxDeletableFileSize)
  527. {
  528. const Desc d = { time, key, fattrs ? path : nil };
  529. descs[count] = d;
  530. for (i = count - 1; i >= 0; --i)
  531. {
  532. if (time >= descs[i].time)
  533. break;
  534. descs[i + 1] = descs[i];
  535. descs[i] = d;
  536. }
  537. if (count < kCacheDeleteCount)
  538. ++count;
  539. else
  540. time0 = descs[kCacheDeleteCount - 1].time;
  541. }
  542. }
  543. }
  544. // NSLog(@"removeOldEntries: count=%d", count);
  545. for (i = 0; i < count; ++i)
  546. {
  547. Desc* d = &descs[i];
  548. if (d->path)
  549. if (![fileMgr removeFileAtPath: d->path handler: nil])
  550. continue;
  551. [cacheDict removeObjectForKey: d->key];
  552. // NSLog(@"\n %f '%@'", d->time, d->key);
  553. }
  554. [pool release];
  555. }
  556. //----------------------------------------------------------------------------------------
  557. + (NSString*) cachePathForKey: (NSString*) key
  558. {
  559. static NSMutableDictionary* cacheDict; // this dictionary contains the cache
  560. static NSString* cacheDir; // the path to the cache directory
  561. static unsigned int nextIndex = 0;
  562. NSFileManager* const fileMgr = [NSFileManager defaultManager];
  563. NSUserDefaults* const prefs = [NSUserDefaults standardUserDefaults];
  564. if (cacheDict == nil)
  565. {
  566. id obj = [prefs dictionaryForKey: @"cacheFilesDict"];
  567. cacheDict = obj ? [obj mutableCopyWithZone: nil] : [[NSMutableDictionary alloc] init];
  568. if ((obj = [prefs objectForKey: @"cacheNextIndex"]))
  569. nextIndex = [obj unsignedIntValue];
  570. }
  571. // Find or create the cache directory
  572. if (cacheDir == nil)
  573. {
  574. NSArray* libraryDirectories = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, TRUE);
  575. NSString* cachesFolder = [libraryDirectories objectAtIndex: 0];
  576. cacheDir = [cachesFolder stringByAppendingPathComponent: [[NSBundle mainBundle] bundleIdentifier]];
  577. [cacheDir retain];
  578. BOOL isDir;
  579. if ([fileMgr fileExistsAtPath: cacheDir isDirectory: &isDir])
  580. {
  581. if (!isDir)
  582. return nil;
  583. }
  584. else // create directory
  585. {
  586. if (![fileMgr createDirectoryAtPath: cacheDir attributes: nil])
  587. return nil;
  588. }
  589. }
  590. NSString* name = nil;
  591. NSDictionary* cacheEntry = [cacheDict objectForKey: key];
  592. if (cacheEntry)
  593. {
  594. name = [cacheEntry objectForKey: @"name"];
  595. }
  596. const CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
  597. if (name == nil)
  598. {
  599. const unichar kCacheFormat = 'A'; // cache format 'A'
  600. do {
  601. name = [NSString stringWithFormat: @"%C%09u.cache", kCacheFormat, nextIndex++];
  602. }
  603. while ([fileMgr fileExistsAtPath: [cacheDir stringByAppendingPathComponent: name]]);
  604. }
  605. if (cacheEntry == nil && [cacheDict count] >= kCacheMaxSize)
  606. removeOldEntries(cacheDict, cacheDir, fileMgr, now);
  607. cacheEntry = [NSDictionary dictionaryWithObjectsAndKeys: name, @"name",
  608. [NSNumber numberWithDouble: now], @"time",
  609. nil];
  610. [cacheDict setValue: cacheEntry forKey: key];
  611. [prefs setObject: cacheDict forKey: @"cacheFilesDict"];
  612. [prefs setObject: [NSNumber numberWithUnsignedInt: nextIndex] forKey: @"cacheNextIndex"];
  613. [prefs synchronize];
  614. NSString* resultString = [cacheDir stringByAppendingPathComponent: name];
  615. // NSLog(@"cacheDict=%@", cacheDict);
  616. // NSLog(@"cachePathForKey('%@') => '%@'", key, name);
  617. return resultString;
  618. }
  619. @end