From bostonvaulter at gmail.com Fri Jan 1 06:38:15 2010 From: bostonvaulter at gmail.com (Jason Axelson) Date: Fri, 1 Jan 2010 01:38:15 -1000 Subject: [Unison-hackers] Error compiling unison (scanf) Message-ID: <21810b631001010338gd51ee9bq1af195cb8d802631@mail.gmail.com> Hi, I get the following error after trying to compile unison 2.32 or 2.27 from svn. However, I haven't found any problems when using trunk and it appears to build a usable program. Fatal error: exception Scanf.Scan_failure("scanf: bad input at char number 4: looking for ':', found '$'") Does this point to a problem with my setup? Here's a link to the full output from make: http://gist.github.com/267092 Thanks, Jason From eric.kow at gmail.com Sat Jan 2 14:06:49 2010 From: eric.kow at gmail.com (Eric Y. Kow) Date: Sat, 2 Jan 2010 19:06:49 +0000 Subject: [Unison-hackers] patch: make arrows for suggested changes darker Message-ID: <20100102190649.GC28046@dewdrop.local> Here's a trivial patch to darken the grey arrows Unison displays on MacOS X. This makes me more confident as Unison user because I don't have to squint so hard to determine what direction it thinks files should go :-) Thanks, -- Eric Kow PGP Key ID: 08AC04F9 -------------- next part -------------- Index: src/uimacnew/ReconItem.m =================================================================== --- src/uimacnew/ReconItem.m (revision 390) +++ src/uimacnew/ReconItem.m (working copy) @@ -824,8 +824,7 @@ { if (!_parentImages) { _parentImages = [[NSMutableDictionary alloc] init]; - _veryLightGreyColor = [[NSColor colorWithCalibratedRed:0.9 green:0.9 blue:0.9 alpha:1.0] retain]; - // [NSColor lightGrayColor] + _veryLightGreyColor = [[NSColor colorWithCalibratedRed:0.7 green:0.7 blue:0.7 alpha:1.0] retain]; } NSImage *baseImage = [super direction]; NSImage *parentImage = [_parentImages objectForKey:baseImage]; -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 195 bytes Desc: not available Url : http://lists.seas.upenn.edu/pipermail/unison-hackers/attachments/20100102/e50af55d/attachment.sig From Jerome.Vouillon at pps.jussieu.fr Mon Jan 4 07:47:11 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Mon, 4 Jan 2010 13:47:11 +0100 Subject: [Unison-hackers] windows 2003 crash .. unexplained In-Reply-To: <4B3CE339.3090400@gmail.com> References: <4B3CE339.3090400@gmail.com> Message-ID: <20100104124711.GA32073@pps.jussieu.fr> On Thu, Dec 31, 2009 at 11:45:29AM -0600, beginthreadex wrote: > Working in my lab with unison I have about 90GB of files creating a > count of about 20,000 files. It takes a long time to do the initial sync > but then seems to work. Our only test of this resulted in a complete > shutdown of the Windows 2003 server with no explanations. No crash dump, > no event log items, nothing. Has anyone else experienced something > similar to this? I'm trying to be cautious about running the test again. This kind of crash is usually due to either a corrupted file system or some hardware problem. So, make sure your disks are not dying and check your filesystems. -- Jerome From Jerome.Vouillon at pps.jussieu.fr Mon Jan 4 07:53:19 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Mon, 4 Jan 2010 13:53:19 +0100 Subject: [Unison-hackers] windows nmake? In-Reply-To: <4B3D5615.6050701@gmail.com> References: <4B3D5615.6050701@gmail.com> Message-ID: <20100104125319.GB32073@pps.jussieu.fr> On Thu, Dec 31, 2009 at 07:55:33PM -0600, beginthreadex wrote: > I have ocaml installed along with masm and vc++. Is there a simple > "nmake" that someone has or maybe a bat file so I can do a compile? > I've been at this for hours today and I'm just trying to get my first > compile done. I think I have all the tools setup outside cygwin. I'd > love to be able to compile without cygwin even if it was a really ugly > bat file. Maybe it would be simpler to use GNU make? http://gnuwin32.sourceforge.net/packages/make.htm -- Jerome From Jerome.Vouillon at pps.jussieu.fr Mon Jan 4 07:59:59 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Mon, 4 Jan 2010 13:59:59 +0100 Subject: [Unison-hackers] Error compiling unison (scanf) In-Reply-To: <21810b631001010338gd51ee9bq1af195cb8d802631@mail.gmail.com> References: <21810b631001010338gd51ee9bq1af195cb8d802631@mail.gmail.com> Message-ID: <20100104125959.GC32073@pps.jussieu.fr> Hi, On Fri, Jan 01, 2010 at 01:38:15AM -1000, Jason Axelson wrote: > I get the following error after trying to compile unison 2.32 or 2.27 > from svn. However, I haven't found any problems when using trunk and > it appears to build a usable program. > > Fatal error: exception Scanf.Scan_failure("scanf: bad input at char > number 4: looking for ':', found '$'") > > Does this point to a problem with my setup? I believe this is more a problem with svn not doing keyword substitutions. Edit file mkProjectInfo.ml and change the definition of variable "revisionString" to something like: let revisionString = "$Rev: 388$";; (It does not really matter which number you choose.) -- Jerome From Jerome.Vouillon at pps.jussieu.fr Mon Jan 4 08:36:26 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Mon, 4 Jan 2010 14:36:26 +0100 Subject: [Unison-hackers] command line to profile In-Reply-To: <4B3CE0BA.90105@gmail.com> References: <4B3CE0BA.90105@gmail.com> Message-ID: <20100104133626.GF32073@pps.jussieu.fr> On Thu, Dec 31, 2009 at 11:34:50AM -0600, beginthreadex wrote: > Is there a way I can put my profile name on the command line to my > exact file and not have it read from my user profile location? I would > like to use unison on my USB drive, executed via a bat. I would need to > have the profile and the archive/database all in the same directory. > How can this be done? Portability would be awesome for unison! The profile files must be in the unison directory (together with the archives). But you can set the unison directory using the UNISON environment variable. -- Jerome From bostonvaulter at gmail.com Mon Jan 4 15:57:06 2010 From: bostonvaulter at gmail.com (Jason Axelson) Date: Mon, 4 Jan 2010 10:57:06 -1000 Subject: [Unison-hackers] Error compiling unison (scanf) In-Reply-To: <20100104125959.GC32073@pps.jussieu.fr> References: <21810b631001010338gd51ee9bq1af195cb8d802631@mail.gmail.com> <20100104125959.GC32073@pps.jussieu.fr> Message-ID: <21810b631001041257v3ac35d4au50e15ce1b1afbf6f@mail.gmail.com> On Mon, Jan 4, 2010 at 2:59 AM, Jerome Vouillon wrote: > Hi, > > On Fri, Jan 01, 2010 at 01:38:15AM -1000, Jason Axelson wrote: >> Fatal error: exception Scanf.Scan_failure("scanf: bad input at char >> number 4: looking for ':', found '$'") >> >> Does this point to a problem with my setup? > > I believe this is more a problem with svn not doing keyword > substitutions. ?Edit file mkProjectInfo.ml and change the definition > of variable "revisionString" to something like: > > ? let revisionString = "$Rev: 388$";; Thanks Jerome, that fixed it. Although I don't fully understand how this sort of error popped up. Is SVN supposed to change mkProjectInfo.ml whenever I do a checkout? If so, it could also be a problem with git-svn (since I'm using that rather than SVN directly). Thanks Again, Jason From vouillon at seas.upenn.edu Tue Jan 5 04:33:18 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Tue, 5 Jan 2010 04:33:18 -0500 Subject: [Unison-hackers] [unison-svn] r391 - in trunk/src: . system uimacnew uimacnew09 Message-ID: <201001050933.o059XJKE029453@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-05 04:33:18 -0500 (Tue, 05 Jan 2010) New Revision: 391 Modified: trunk/src/Makefile.OCaml trunk/src/RECENTNEWS trunk/src/copy.ml trunk/src/mkProjectInfo.ml trunk/src/pixmaps.ml trunk/src/props.ml trunk/src/remote.ml trunk/src/system/system_win.ml trunk/src/system/system_win_stubs.c trunk/src/uigtk2.ml trunk/src/uimacnew/ReconItem.m trunk/src/uimacnew09/ReconItem.m Log: * GTK UI: - revert to the previous action pixmaps - made the progress bar slightly larger * MacOS GUI: - use darker grey arrows (patch contributed by Eric Y. Kow) * Make STATIC=true kind of work again (though it is not possible to make completely statically linked binaries with the glibc library) * Windows: added support for long UNC paths * Do not keep many files simultaneously opened anymore when the rsync algorithm is in use * IPV6: properly deal with Unix errors, so that Unison correctly fall backs to IPV4 if the kernel does not support IPV6 Modified: trunk/src/Makefile.OCaml =================================================================== --- trunk/src/Makefile.OCaml 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/Makefile.OCaml 2010-01-05 09:33:18 UTC (rev 391) @@ -292,7 +292,7 @@ ### Static build setup ifeq ($(STATIC), true) - STATICLIBS+=-cclib -static + CFLAGS+=-cclib -static endif #################################################################### @@ -409,7 +409,7 @@ $(NAME)$(EXEC_EXT): $(CAMLOBJS) $(COBJS) @echo Linking $@ - $(CAMLC) -verbose $(CAMLFLAGS) -o $@ $(CFLAGS) $(CAMLLIBS) $(CLIBS) $^ + $(CAMLC) -verbose $(CAMLFLAGS) -o $@ $(CFLAGS) $(CAMLLIBS) $^ $(CLIBS) # Unfortunately -output-obj does not put .o files into the output, only .cmx # files, so we have to use $(LD) to take care of COBJS. Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/RECENTNEWS 2010-01-05 09:33:18 UTC (rev 391) @@ -1,5 +1,21 @@ CHANGES FROM VERSION 2.38.0 +* GTK UI: + - revert to the previous action pixmaps + - made the progress bar slightly larger +* MacOS GUI: + - use darker grey arrows (patch contributed by Eric Y. Kow) +* Make STATIC=true kind of work again (though it is not possible to + make completely statically linked binaries with the glibc library) +* Windows: added support for long UNC paths +* Do not keep many files simultaneously opened anymore when the rsync + algorithm is in use +* IPV6: properly deal with Unix errors, so that Unison correctly fall + backs to IPV4 if the kernel does not support IPV6 + +------------------------------- +CHANGES FROM VERSION 2.38.0 + * Incorporated new version of OSX GUI with numerous improvements and bugfixes by Markus Gross. (Not compiling yet on my machine, though.) Modified: trunk/src/copy.ml =================================================================== --- trunk/src/copy.ml 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/copy.ml 2010-01-05 09:33:18 UTC (rev 391) @@ -408,6 +408,16 @@ | Some fd -> fd +(* Lazy opening of the reference file (for rsync algorithm) *) +let referenceFd fspath path kind infd = + match !infd with + None -> + let fd = openFileIn fspath path kind in + infd := Some fd; + fd + | Some fd -> + fd + let rsyncReg = Lwt_util.make_region (40 * 1024) let rsyncThrottle useRsync srcFileSize destFileSize f = if not useRsync then f () else @@ -417,8 +427,9 @@ let transferFileContents connFrom fspathFrom pathFrom fspathTo pathTo realPathTo update fileKind srcFileSize id = - (* We delay the opening of the file so that there are not too many - temporary files remaining after a crash *) + (* We delay the opening of the files so that there are not too many + temporary files remaining after a crash, and that they are not + too many files simultaneously opened. *) let outfd = ref None in let infd = ref None in let showProgress count = @@ -446,24 +457,25 @@ Util.convertUnixErrorsToTransient "preprocessing file" (fun () -> - let ifd = openFileIn fspathTo realPathTo fileKind in + let ifd = referenceFd fspathTo realPathTo fileKind infd in let (bi, blockSize) = protect (fun () -> Transfer.Rsync.rsyncPreprocess ifd srcFileSize destFileSize) (fun () -> close_in_noerr ifd) in - infd := Some ifd; + close_all infd outfd; (Some bi, (* Rsync decompressor *) fun ti -> + let ifd = referenceFd fspathTo realPathTo fileKind infd in let fd = destinationFd fspathTo pathTo fileKind srcFileSize outfd id in let eof = Transfer.Rsync.rsyncDecompress blockSize ifd fd showProgress ti in - if eof then begin close_out fd; outfd := None end)) + if eof then close_all infd outfd)) else (None, (* Simple generic decompressor *) @@ -471,7 +483,7 @@ let fd = destinationFd fspathTo pathTo fileKind srcFileSize outfd id in let eof = Transfer.receive fd showProgress ti in - if eof then begin close_out fd; outfd := None end) + if eof then close_all infd outfd) in let file_id = Remote.newMsgId () in Lwt.catch @@ -483,6 +495,7 @@ decompressor := Remote.MsgIdMap.remove file_id !decompressor; (* For GC *) close_all infd outfd; + (* JV: FIX: the file descriptors are already closed... *) Lwt.return ()) (fun e -> decompressor := Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/mkProjectInfo.ml 2010-01-05 09:33:18 UTC (rev 391) @@ -89,3 +89,4 @@ + Modified: trunk/src/pixmaps.ml =================================================================== --- trunk/src/pixmaps.ml 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/pixmaps.ml 2010-01-05 09:33:18 UTC (rev 391) @@ -18,147 +18,140 @@ let copyAB color = [| (* width height num_colors chars_per_pixel *) -" 28 14 3 1"; +" 28 14 2 1"; (* colors *) ". c None"; "# c #" ^ color; -" c #ffffff"; (* pixels *) "............................"; "............................"; -"..................... ......"; -".................... # ....."; -"................... ### ..."; -". #### ."; -" ########################## "; -" ########################## "; -". #### ."; -"................... ### ..."; -".................... # ....."; -"..................... ......"; "............................"; +"......................#....."; +".....................###...."; +"......................####.."; +"..##########################"; +"..##########################"; +"......................####.."; +".....................###...."; +"......................#....."; +"............................"; +"............................"; "............................" |] let copyBA color = [| (* width height num_colors chars_per_pixel *) -" 28 14 3 1"; +" 28 14 2 1"; (* colors *) ". c None"; "# c #" ^ color; -" c #ffffff"; (* pixels *) "............................"; "............................"; -"...... ....................."; -"..... # ...................."; -"... ### ..................."; -". #### ."; -" ########################## "; -" ########################## "; -". #### ."; -"... ### ..................."; -"..... # ...................."; -"...... ....................."; "............................"; +".....#......................"; +"....###....................."; +"..####......................"; +"##########################.."; +"##########################.."; +"..####......................"; +"....###....................."; +".....#......................"; +"............................"; +"............................"; "............................" |] let mergeLogo color = [| (* width height num_colors chars_per_pixel *) -" 28 14 3 1"; +" 28 14 2 1"; (* colors *) ". c None"; "# c #" ^ color; -" c #ffffff"; (* pixels *) "............................"; -"......... ...... ........."; -"........ ## .... ## ........"; -"........ ### .. ### ........"; -"........ #### #### ........"; -"........ ## #### ## ........"; -"........ ## ## ## ........"; -"........ ## . . ## ........"; -"........ ## .... ## ........"; -"........ ## .... ## ........"; -"........ ## .... ## ........"; -"........ ## .... ## ........"; -"......... ...... ........."; +"............................"; +".........##......##........."; +".........###....###........."; +".........####..####........."; +".........##.####.##........."; +".........##..##..##........."; +".........##......##........."; +".........##......##........."; +".........##......##........."; +".........##......##........."; +".........##......##........."; +"............................"; "............................" |] let ignore color = [| (* width height num_colors chars_per_pixel *) -" 20 14 3 1"; +" 20 14 2 1"; (* colors *) " c None"; "* c #" ^ color; -". c #ffffff"; (* pixels *) -" ..... "; -" .*****. "; -" .**...**. "; -" .**. .**. "; -" .. .**. "; -" .**. "; -" .**. "; -" .**. "; -" .**. "; -" .. "; -" .. "; -" .**. "; -" .**. "; -" .. " +" "; +" ***** "; +" ** ** "; +" ** ** "; +" ** "; +" ** "; +" ** "; +" ** "; +" ** "; +" "; +" "; +" ** "; +" ** "; +" " |] let success = [| (* width height num_colors chars_per_pixel *) -" 20 14 3 1"; +" 20 14 2 1"; (* colors *) " c None"; "* c #00dd00"; -". c #ffffff"; (* pixels *) +" "; " "; -" ... "; -" ..***. "; -" .******. "; -" .*****.*. "; -" ... .****.. . "; -" .***. .***. "; -" .***.**.. "; -" .******. "; -" ..***. "; -" .**. "; -" .**. "; -" .*. "; -" . " +" *** "; +" ****** "; +" ***** * "; +" **** "; +" *** *** "; +" *** ** "; +" ****** "; +" *** "; +" ** "; +" ** "; +" * "; +" " |] let failure = [| (* width height num_colors chars_per_pixel *) -" 20 15 3 1"; +" 20 14 2 1"; (* colors *) " c None"; "* c #ff0000"; -". c #ffffff"; (* pixels *) -" . .. "; -" .*. .**. "; -" .***. .***. "; -" .**. .***. "; -" .**..**. "; -" .*****. "; -" .****. "; -" .***. "; -" .*****. "; -" .**.**. "; -" .**. .**. "; -" .**. .***. "; -" .***. .**. "; -" .***. .. "; -" ... " +" * * "; +" *** ** "; +" *** *** "; +" ** ** "; +" ** ** "; +" ***** "; +" **** "; +" *** "; +" ***** "; +" ** ** "; +" ** ** "; +" ** *** "; +" *** ** "; +" *** " |] Modified: trunk/src/props.ml =================================================================== --- trunk/src/props.ml 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/props.ml 2010-01-05 09:33:18 UTC (rev 391) @@ -489,7 +489,7 @@ let toString t = Util.time2string (extract t) let syncedPartsToString t = match t with - Synced _ -> toString t + Synced _ -> Format.sprintf "%s (%f)" (toString t) (extract t) | NotSynced _ -> "" (* FIX: Probably there should be a check here that prevents us from ever *) Modified: trunk/src/remote.ml =================================================================== --- trunk/src/remote.ml 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/remote.ml 2010-01-05 09:33:18 UTC (rev 391) @@ -901,7 +901,7 @@ Lwt_util.run_in_region streamReg 1 (fun () -> sender (fun v -> client conn id v))) (fun v -> ping conn id >>= fun () -> Lwt.return v) - (fun e -> ping conn id >>= fun () -> Lwt.fail e) + (fun e -> ping conn id >>= fun () -> Lwt.fail e) end let commandAvailable = @@ -991,29 +991,88 @@ let targetHostEntry = Unix.gethostbyname host in targetHostEntry.Unix.h_addr_list.(0) +let rec findFirst f l = + match l with + [] -> None + | x :: r -> match f x with + None -> findFirst f r + | Some _ as v -> v + +let buildSocket host port kind = + let attemptCreation ai = + try + let socket = + Unix.socket ai.Unix.ai_family ai.Unix.ai_socktype ai.Unix.ai_protocol + in + try + begin match kind with + `Connect -> + (* Connect to the remote host *) + Unix.connect socket ai.Unix.ai_addr + | `Bind -> + (* Allow reuse of local addresses for bind *) + Unix.setsockopt socket Unix.SO_REUSEADDR true; + (* Bind the socket to portnum on the local host *) + Unix.bind socket ai.Unix.ai_addr; + (* Start listening, allow up to 1 pending request *) + Unix.listen socket 1 + end; + Some socket + with Unix.Unix_error _ as e -> + Unix.close socket; + raise e + with Unix.Unix_error (error, _, _) -> + begin match error with + Unix.EAFNOSUPPORT | Unix.EPROTONOSUPPORT | Unix.EINVAL -> + () + | _ -> + let msg = + match kind with + `Connect -> + Printf.sprintf "Can't connect to server (%s:%s): %s" + host port (Unix.error_message error) + | `Bind -> + Printf.sprintf + "Can't bind socket to port %s at address [%s]: %s\n" + port + (match ai.Unix.ai_addr with + Unix.ADDR_INET (addr, _) -> Unix.string_of_inet_addr addr + | _ -> assert false) + (Unix.error_message error) + in + Util.warn msg + end; + None + in + let options = + match kind with + `Connect -> [ Unix.AI_SOCKTYPE Unix.SOCK_STREAM ] + | `Bind -> [ Unix.AI_SOCKTYPE Unix.SOCK_STREAM ; Unix.AI_PASSIVE ] + in + match + findFirst attemptCreation (Unix.getaddrinfo host port options) + with + Some socket -> + socket + | None -> + let msg = + match kind with + `Connect -> + Printf.sprintf + "Can't find the IP address of the server (%s:%s)" host port + | `Bind -> + if host = "" then + Printf.sprintf "Can't bind socket to port %s" port + else + Printf.sprintf "Can't bind socket to port %s on host %s" + port host + in + raise (Util.Fatal msg) + let buildSocketConnection host port = Util.convertUnixErrorsToFatal "canonizeRoot" (fun () -> - let rec loop = function - [] -> - raise (Util.Fatal - (Printf.sprintf - "Can't find the IP address of the server (%s:%s)" host - port)) - | ai::r -> - (* create a socket to talk to the remote host *) - let socket = Unix.socket ai.Unix.ai_family ai.Unix.ai_socktype ai.Unix.ai_protocol in - begin try - Unix.connect socket ai.Unix.ai_addr; - initConnection socket socket - with - Unix.Unix_error (error, _, reason) -> - (if error != Unix.EAFNOSUPPORT then - Util.warn - (Printf.sprintf - "Can't connect to server (%s:%s): %s" host port reason); - loop r) - end - in loop (Unix.getaddrinfo host port [ Unix.AI_SOCKTYPE Unix.SOCK_STREAM ])) + let socket = buildSocket host port `Connect in + initConnection socket socket) let buildShellConnection shell host userOpt portOpt rootName termInteract = let remoteCmd = @@ -1359,38 +1418,7 @@ let host = match hostOpt with Some host -> host | None -> "" in - let rec loop = function - [] -> raise (Util.Fatal - (if host = "" then - Printf.sprintf "Can't bind socket to port %s" port - else - Printf.sprintf "Can't bind socket to port %s on host %s" port host)) - | ai::r -> - (* Open a socket to listen for queries *) - let socket = Unix.socket ai.Unix.ai_family ai.Unix.ai_socktype - ai.Unix.ai_protocol in - begin try - (* Allow reuse of local addresses for bind *) - Unix.setsockopt socket Unix.SO_REUSEADDR true; - (* Bind the socket to portnum on the local host *) - Unix.bind socket ai.Unix.ai_addr; - (* Start listening, allow up to 1 pending request *) - Unix.listen socket 1; - socket - with - Unix.Unix_error (error, _, reason) -> - (if error != Unix.EAFNOSUPPORT then - Util.msg - "Can't bind socket to port %s at address [%s]: %s\n" - port - (match ai.Unix.ai_addr with - Unix.ADDR_INET (addr, _) -> Unix.string_of_inet_addr addr - | _ -> assert false) - (Unix.error_message error); - loop r) - end in - let listening = loop (Unix.getaddrinfo host port [ Unix.AI_SOCKTYPE - Unix.SOCK_STREAM ; Unix.AI_PASSIVE ]) in + let listening = buildSocket host port `Bind in Util.msg "server started\n"; while (* Accept a connection *) Modified: trunk/src/system/system_win.ml =================================================================== --- trunk/src/system/system_win.ml 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/system/system_win.ml 2010-01-05 09:33:18 UTC (rev 391) @@ -41,11 +41,12 @@ done; f let winRootRx = Rx.rx "[a-zA-Z]:[/\\].*" -let winUncRx = Rx.rx "//[^/]+/[^/]+/.*" -(* FIX: we could also handle UNC paths *) +let winUncRx = Rx.rx "[/\\][/\\][^/\\]+[/\\][^/\\]+[/\\].*" let extendedPath f = if Rx.match_string winRootRx f then fixPath ("\\\\?\\" ^ f) + else if Rx.match_string winUncRx f then + fixPath ("\\\\?\\UNC" ^ String.sub f 1 (String.length f - 1)) else f Modified: trunk/src/system/system_win_stubs.c =================================================================== --- trunk/src/system/system_win_stubs.c 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/system/system_win_stubs.c 2010-01-05 09:33:18 UTC (rev 391) @@ -281,6 +281,10 @@ v = caml_alloc (12, 0); Store_field (v, 0, Val_int (info.dwVolumeSerialNumber)); + + // Apparently, we cannot trust the inode number to be stable when + // nFileIndexHigh is 0. + if (info.nFileIndexHigh == 0) info.nFileIndexLow = 0; /* The ocaml code truncates inode numbers to 31 bits. We hash the low and high parts in order to lose as little information as possible. */ Modified: trunk/src/uigtk2.ml =================================================================== --- trunk/src/uigtk2.ml 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/uigtk2.ml 2010-01-05 09:33:18 UTC (rev 391) @@ -108,10 +108,13 @@ Gdk.Cursor.create_from_pixmap (bitmap :> Gdk.pixmap) ~mask:bitmap ~fg:color ~bg:color ~x:2 ~y:2) -let make_busy w = Gdk.Window.set_cursor w#misc#window (Lazy.force leftPtrWatch) +let make_busy w = + if Util.osType <> `Win32 then + Gdk.Window.set_cursor w#misc#window (Lazy.force leftPtrWatch) let make_interactive w = - (* HACK: setting the cursor to NULL restore the default cursor *) - Gdk.Window.set_cursor w#misc#window (Obj.magic Gpointer.boxed_null) + if Util.osType <> `Win32 then + (* HACK: setting the cursor to NULL restore the default cursor *) + Gdk.Window.set_cursor w#misc#window (Obj.magic Gpointer.boxed_null) (********************************************************************* UI state variables @@ -1467,8 +1470,8 @@ let fastcheck = isLocal >> not >> (fun b -> b || Util.osType = `Win32) in (* Unicode mode can be problematic when the source machine is under Windows and the remote machine is not, as Unison may have already - been used using the legacy Latin 1 encoding. Cygwin (stable) - also does not handle Unicode at the moment. *) + been used using the legacy Latin 1 encoding. Cygwin also did not + handle Unicode before version 1.7. *) let vb = GPack.vbox ~spacing:6 ~packing:(options#pack ~expand:false) () in let askUnicode = isLocal >> not >> fun b -> (b || Util.isCygwin) && Util.osType = `Win32 in @@ -1505,11 +1508,18 @@ let unicode = React.lift2 (||) (askUnicode >> not) (GtkReact.toggle_button unicodeButton) in + let p = + assistant#append_page + ~title:"Specific Options" ~complete:true + ~page_type:`CONTENT + options#as_widget + in ignore - (assistant#append_page - ~title:"Specific Options" ~complete:true - ~page_type:`CONTENT - options#as_widget); + (assistant#connect#prepare (fun () -> + if assistant#current_page = p && + not (Util.osType <> `Win32 || React.state askUnicode) + then + assistant#set_current_page (p + 1))); let conclusion = GMisc.label @@ -2954,7 +2964,7 @@ let progressBar = GRange.progress_bar ~packing:(statusHBox#pack ~expand:false) () in - progressBar#misc#set_size_chars ~height:1 ~width:25 (); + progressBar#misc#set_size_chars ~height:1 ~width:28 (); progressBar#set_pulse_step 0.02; let progressBarPulse = ref false in Modified: trunk/src/uimacnew/ReconItem.m =================================================================== --- trunk/src/uimacnew/ReconItem.m 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/uimacnew/ReconItem.m 2010-01-05 09:33:18 UTC (rev 391) @@ -824,8 +824,7 @@ { if (!_parentImages) { _parentImages = [[NSMutableDictionary alloc] init]; - _veryLightGreyColor = [[NSColor colorWithCalibratedRed:0.9 green:0.9 blue:0.9 alpha:1.0] retain]; - // [NSColor lightGrayColor] + _veryLightGreyColor = [[NSColor colorWithCalibratedRed:0.7 green:0.7 blue:0.7 alpha:1.0] retain]; } NSImage *baseImage = [super direction]; NSImage *parentImage = [_parentImages objectForKey:baseImage]; Modified: trunk/src/uimacnew09/ReconItem.m =================================================================== --- trunk/src/uimacnew09/ReconItem.m 2009-12-04 01:30:20 UTC (rev 390) +++ trunk/src/uimacnew09/ReconItem.m 2010-01-05 09:33:18 UTC (rev 391) @@ -824,8 +824,7 @@ { if (!_parentImages) { _parentImages = [[NSMutableDictionary alloc] init]; - _veryLightGreyColor = [[NSColor colorWithCalibratedRed:0.9 green:0.9 blue:0.9 alpha:1.0] retain]; - // [NSColor lightGrayColor] + _veryLightGreyColor = [[NSColor colorWithCalibratedRed:0.7 green:0.7 blue:0.7 alpha:1.0] retain]; } NSImage *baseImage = [super direction]; NSImage *parentImage = [_parentImages objectForKey:baseImage]; From Jerome.Vouillon at pps.jussieu.fr Tue Jan 5 04:51:10 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Tue, 5 Jan 2010 10:51:10 +0100 Subject: [Unison-hackers] Error compiling unison (scanf) In-Reply-To: <21810b631001041257v3ac35d4au50e15ce1b1afbf6f@mail.gmail.com> References: <21810b631001010338gd51ee9bq1af195cb8d802631@mail.gmail.com> <20100104125959.GC32073@pps.jussieu.fr> <21810b631001041257v3ac35d4au50e15ce1b1afbf6f@mail.gmail.com> Message-ID: <20100105095110.GA6987@pps.jussieu.fr> On Mon, Jan 04, 2010 at 10:57:06AM -1000, Jason Axelson wrote: > > I believe this is more a problem with svn not doing keyword > > substitutions. ?Edit file mkProjectInfo.ml and change the definition > > of variable "revisionString" to something like: > > > > ? let revisionString = "$Rev: 388$";; > > Thanks Jerome, that fixed it. Although I don't fully understand how > this sort of error popped up. Is SVN supposed to change > mkProjectInfo.ml whenever I do a checkout? If so, it could also be a > problem with git-svn (since I'm using that rather than SVN directly). Right, git-svn probably does not perform SVN keyword substitution. -- Jerome From newton at mit.edu Tue Jan 5 09:54:32 2010 From: newton at mit.edu (Ryan Newton) Date: Tue, 5 Jan 2010 09:54:32 -0500 Subject: [Unison-hackers] Proposed mode for unison: "Salvage" Message-ID: The biggest complaint I hear from friends and family about unison is the ease of duplicating files. This happens most often when running unison without saved archives (e.g. because things get moved around, mixed up, moved to new machines, etc). A typical scenario that is difficult to handle with unison is that you come across an old copy of folder X that *might* contain something that you forgot to extract or move into your current, primary copy. But of course you don't know what's there and checking is manually hard. Further, performing a simple unison is the WRONG answer, because the organization may have changed substantially, making it very hard to tell if supposedly new files in the old copy are really new or have just been moved (duplication danger). In this case it would be very useful to run unison in mode where: (1) only copies from old->new are considered. The goal is not two identical archives, but to retrieve things from the old copy. (2) only files which do not exist ANYWHERE in the new archive are considered, the new archive is just a flat set of files for the purpose of this check. Secondary questions include where to put the new files (presumably the same path as in the old archive) and what to do with conflicts/collisions resulting from, for example, modified files (presumably they're treated in the normal unison way). The interface could perhaps be a "-salvage X" flag, where X is one of the roots (just like -force). Best, -Ryan P.S. I actually wrote a separate tool at some point (in ocaml) that could accomplish the above which I could provide to the curious. Its goal was to replace all the files in a folder X, that exist in a folder Y, with symlinks into folder Y. One could run this on the old archive and then use "find" to see all the files that were not turned to symlinks. However, I think leveraging unison for this purpose would be much more desirable. From bcpierce at cis.upenn.edu Tue Jan 5 10:34:04 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Tue, 5 Jan 2010 10:34:04 -0500 Subject: [Unison-hackers] [unison-svn] r389 - in trunk: doc src src/lwt src/uimacnew src/uimacnew/English.lproj/MainMenu.nib src/uimacnew/uimacnew.xcodeproj In-Reply-To: <20091228172538.GC29657@pps.jussieu.fr> References: <200911291356.nATDu91x026729@yaws.seas.upenn.edu> <20091228172538.GC29657@pps.jussieu.fr> Message-ID: <657586A3-95A6-49E0-95FD-C0ECB1C8B377@cis.upenn.edu> >> Modified: trunk/src/props.ml >> =================================================================== >> --- trunk/src/props.ml 2009-08-18 13:14:35 UTC (rev 388) >> +++ trunk/src/props.ml 2009-11-29 13:56:09 UTC (rev 389) >> @@ -215,7 +215,7 @@ >> >> let check fspath path stats (fp, mask) = >> let fp' = stats.Unix.LargeFile.st_perm in >> - if fp land mask <> fp' land mask then >> + if (not (Prefs.read dontChmod)) && (fp land mask <> fp' land >> mask) then >> raise >> (Util.Transient >> (Format.sprintf >> > > This looks wrong. If Unison does not fail here when the permissions > do not agree, it will report a spurious permission update next time it > is run, as the archive contents will not be in agreement with the > filesystem contents. > > I guess the correct fix is either the dontchmod preference should > imply perms=0, or it should be ignored when the perms preference is > not 0. Thanks for noticing this, Jerome. I'll shortly commit a fix implementing your first suggestion. (I've chosen to raise an exception if dontchmod is set and perms<> 0, so users aren't confused by perms settings being ignored silently.) - B From bcpierce at seas.upenn.edu Tue Jan 5 10:42:43 2010 From: bcpierce at seas.upenn.edu (bcpierce@seas.upenn.edu) Date: Tue, 5 Jan 2010 10:42:43 -0500 Subject: [Unison-hackers] [unison-svn] r392 - in trunk/src: . uimacnew09/English.lproj uimacnew09/uimacnew.xcodeproj Message-ID: <201001051542.o05FghVq002733@yaws.seas.upenn.edu> Author: bcpierce Date: 2010-01-05 10:42:43 -0500 (Tue, 05 Jan 2010) New Revision: 392 Modified: trunk/src/RECENTNEWS trunk/src/mkProjectInfo.ml trunk/src/props.ml trunk/src/props.mli trunk/src/uicommon.ml trunk/src/uicommon.mli trunk/src/uimacbridge.ml trunk/src/uimacbridgenew.ml trunk/src/uimacnew09/English.lproj/MainMenu.xib trunk/src/uimacnew09/uimacnew.xcodeproj/project.pbxproj Log: * Roll back a previous "fix" for a permission-setting issue and introduce a better one (as suggested by Jerome) Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/RECENTNEWS 2010-01-05 15:42:43 UTC (rev 392) @@ -1,5 +1,11 @@ CHANGES FROM VERSION 2.38.0 +* Roll back a previous "fix" for a permission-setting issue and + introduce a better one (as suggested by Jerome) + +------------------------------- +CHANGES FROM VERSION 2.38.0 + * GTK UI: - revert to the previous action pixmaps - made the progress bar slightly larger Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/mkProjectInfo.ml 2010-01-05 15:42:43 UTC (rev 392) @@ -90,3 +90,4 @@ + Modified: trunk/src/props.ml =================================================================== --- trunk/src/props.ml 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/props.ml 2010-01-05 15:42:43 UTC (rev 392) @@ -45,6 +45,7 @@ val dirDefault : t val extract : t -> int val check : Fspath.t -> Path.local -> Unix.LargeFile.stats -> t -> unit + val validatePrefs : unit -> unit end = struct (* We introduce a type, Perm.t, that holds a file's permissions along with *) @@ -188,11 +189,15 @@ Prefs.createBool "dontchmod" false "!When set, never use the chmod system call" - ("By default, Unison uses the 'chmod' system call to set the permission bits" + ( "By default, Unison uses the 'chmod' system call to set the permission bits" ^ " of files after it has copied them. But in some circumstances (and under " ^ " some operating systems), the chmod call always fails. Setting this " ^ " preference completely prevents Unison from ever calling chmod.") +let validatePrefs () = + if Prefs.read dontChmod && (Prefs.read permMask <> 0) then raise (Util.Fatal + "If the 'dontchmod' preference is set, the 'perms' preference should be 0") + let set fspath path kind (fp, mask) = (* BCP: removed "|| kind <> `Update" on 10/2005, but reinserted it on 11/2008. I'd removed it to make Dale Worley happy -- he wanted a way to make sure that @@ -215,7 +220,7 @@ let check fspath path stats (fp, mask) = let fp' = stats.Unix.LargeFile.st_perm in - if (not (Prefs.read dontChmod)) && (fp land mask <> fp' land mask) then + if fp land mask <> fp' land mask then raise (Util.Transient (Format.sprintf @@ -766,6 +771,8 @@ let syncModtimes = Time.sync +let validatePrefs = Perm.validatePrefs + (* ------------------------------------------------------------------------- *) (* Directory change stamps *) (* ------------------------------------------------------------------------- *) Modified: trunk/src/props.mli =================================================================== --- trunk/src/props.mli 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/props.mli 2010-01-05 15:42:43 UTC (rev 392) @@ -37,3 +37,6 @@ val changedDirStamp : dirChangedStamp val setDirChangeFlag : t -> dirChangedStamp -> int -> t * bool val dirMarkedUnchanged : t -> dirChangedStamp -> int -> bool + +val validatePrefs: unit -> unit + Modified: trunk/src/uicommon.ml =================================================================== --- trunk/src/uicommon.ml 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/uicommon.ml 2010-01-05 15:42:43 UTC (rev 392) @@ -446,11 +446,10 @@ (* During startup the client determines the case sensitivity of each root. If any root is case insensitive, all roots must know this -- it's - propagated in a pref. *) -(* FIX: this does more than check case sensitivity, it also detects - HFS (needed for resource forks) and Windows (needed for permissions)... - needs a new name *) -let checkCaseSensitivity () = + propagated in a pref. Also, detects HFS (needed for resource forks) and + Windows (needed for permissions) and does some sanity checking. *) +let validateAndFixupPrefs () = + Props.validatePrefs(); Globals.allRootsMap (fun r -> architecture r ()) >>= (fun archs -> let someHostIsRunningWindows = Safelist.exists (fun (isWin, _, _) -> isWin) archs in @@ -606,7 +605,7 @@ Recon.checkThatPreferredRootIsValid(); Lwt_unix.run - (checkCaseSensitivity () >>= + (validateAndFixupPrefs () >>= Globals.propagatePrefs); (* Initializes some backups stuff according to the preferences just loaded from the profile. Modified: trunk/src/uicommon.mli =================================================================== --- trunk/src/uicommon.mli 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/uicommon.mli 2010-01-05 15:42:43 UTC (rev 392) @@ -106,7 +106,7 @@ termInteract:(string -> string -> string) option -> unit -val checkCaseSensitivity : unit -> unit Lwt.t +val validateAndFixupPrefs : unit -> unit Lwt.t (* Exit codes *) val perfectExit: int (* when everything's okay *) Modified: trunk/src/uimacbridge.ml =================================================================== --- trunk/src/uimacbridge.ml 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/uimacbridge.ml 2010-01-05 15:42:43 UTC (rev 392) @@ -210,7 +210,7 @@ Recon.checkThatPreferredRootIsValid(); Lwt_unix.run - (Uicommon.checkCaseSensitivity () >>= + (Uicommon.validateAndFixupPrefs () >>= Globals.propagatePrefs); (* Initializes some backups stuff according to the preferences just loaded from the profile. Modified: trunk/src/uimacbridgenew.ml =================================================================== --- trunk/src/uimacbridgenew.ml 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/uimacbridgenew.ml 2010-01-05 15:42:43 UTC (rev 392) @@ -280,7 +280,7 @@ Recon.checkThatPreferredRootIsValid(); Lwt_unix.run - (Uicommon.checkCaseSensitivity () >>= + (Uicommon.validateAndFixupPrefs () >>= Globals.propagatePrefs); (* Initializes some backups stuff according to the preferences just loaded from the profile. Modified: trunk/src/uimacnew09/English.lproj/MainMenu.xib =================================================================== --- trunk/src/uimacnew09/English.lproj/MainMenu.xib 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/uimacnew09/English.lproj/MainMenu.xib 2010-01-05 15:42:43 UTC (rev 392) @@ -1,25 +1,45 @@ - + 1050 10C540 740 1038.25 458.00 - - 740 - 1.2.2 - - + + YES + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.brandonwalkin.BWToolkit + + + YES + 740 + 1.2.2 + + + + YES + - - - + + + YES com.apple.InterfaceBuilder.CocoaPlugin com.brandonwalkin.BWToolkit - - - + + + YES + + YES + + + YES + + + + YES NSApplication @@ -46,7 +66,8 @@ 256 - + + YES 296 @@ -62,7 +83,8 @@ 1044 - + + YES 24 @@ -94,11 +116,11 @@ 3 2 - + 1 - + {480, 360} {{0, 0}, {1440, 878}} @@ -108,7 +130,8 @@ MainMenu - + + YES Unison @@ -126,7 +149,8 @@ submenuAction: Unison - + + YES About Unison @@ -223,7 +247,7 @@ - + _NSAppleMenu @@ -240,7 +264,8 @@ Edit - + + YES Cut @@ -286,7 +311,7 @@ - + @@ -300,7 +325,8 @@ submenuAction: Actions - + + YES Propagate Left to Right @@ -410,7 +436,7 @@ - + @@ -424,7 +450,8 @@ submenuAction: Ignore - + + YES Ignore Path @@ -452,7 +479,7 @@ - + @@ -466,7 +493,8 @@ submenuAction: Help - + + YES Unison Online Help @@ -476,16 +504,17 @@ - + - + _NSMainMenu 256 - + + YES 266 @@ -544,11 +573,13 @@ 274 - + + YES 2304 - + + YES 274 @@ -568,7 +599,8 @@ {{307, 0}, {16, 17}} - + + YES profiles 303.47698974609375 @@ -609,7 +641,7 @@ YES - + 3 2 @@ -632,7 +664,7 @@ YES 0 - + {{1, 17}, {306, 190}} @@ -667,9 +699,10 @@ 2304 - + + YES - + {{1, 0}, {306, 17}} @@ -678,7 +711,7 @@ 4 - + {{20, 20}, {323, 208}} @@ -690,7 +723,7 @@ QSAAAEEgAABBmAAAQZgAAA - + {363, 281} NSView NSResponder @@ -698,19 +731,23 @@ 274 - + + YES 274 - + + YES 274 - + + YES 2304 - + + YES 256 @@ -730,7 +767,8 @@ {{-26, 0}, {16, 17}} - + + YES path 426 @@ -887,7 +925,7 @@ compare: - + 3 2 @@ -902,7 +940,7 @@ YES 0 - + {{0, 17}, {730, 410}} @@ -932,9 +970,10 @@ 2304 - + + YES - + {730, 17} @@ -943,7 +982,7 @@ 4 - + {730, 427} @@ -958,7 +997,8 @@ 274 - + + YES 274 @@ -975,7 +1015,7 @@ - + {{0, 437}, {730, 85}} @@ -1004,34 +1044,50 @@ 0.30000001192092896 0.0 - + {{0, 24}, {730, 522}} NO - - + + YES + + + YES + + + + YES + + + YES + + YES - + + YES - - + + + YES - + YES - + + YES - - + + + YES - + 0 NO @@ -1061,7 +1117,7 @@ - + {730, 546} NSView @@ -1082,15 +1138,18 @@ 256 - + + YES 258 - + + YES 256 - + + YES 256 @@ -1134,11 +1193,11 @@ - + {{2, 2}, {493, 51}} - + {{20, 129}, {497, 71}} {0, 0} @@ -1162,11 +1221,13 @@ 258 - + + YES 256 - + + YES 256 @@ -1175,7 +1236,8 @@ YES 2 1 - + + YES -2080244224 0 @@ -1209,7 +1271,7 @@ 200 25 - + {70, 18} {4, 2} 1143472128 @@ -1330,11 +1392,11 @@ - + {{2, 2}, {493, 86}} - + {{20, 16}, {497, 106}} {0, 0} @@ -1388,7 +1450,7 @@ - + {534, 250} NSView @@ -1396,29 +1458,29 @@ NSResponder - + 256 - + + YES 1325 {{419, 263}, {32, 32}} - 20490 100 274 - + + YES 301 {{304, 254}, {263, 19}} - YES 68288064 @@ -1434,10 +1496,9 @@ - + {871, 577} - 1 MC42NzU3Njg1MjI3IDAuNzIxOTQ4MTMwNiAwLjc2NTMwNjEyMjQAA @@ -1464,10 +1525,8 @@ 0.30000001192092896 0.0 - + {871, 577} - - NSView @@ -1488,7 +1547,8 @@ 256 - + + YES 256 @@ -1576,7 +1636,7 @@ - + {227, 128} {{0, 0}, {1440, 878}} @@ -1598,7 +1658,8 @@ 256 - + + YES 256 @@ -1643,14 +1704,18 @@ 256 - - Apple PDF pasteboard type - Apple PICT pasteboard type - Apple PNG pasteboard type - NSFilenamesPboardType - NeXT Encapsulated PostScript v1.2 pasteboard type - NeXT TIFF v4.0 pasteboard type - + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + {{20, 182}, {224, 64}} YES @@ -1704,7 +1769,7 @@ - + {262, 266} {{0, 0}, {1440, 878}} @@ -1728,15 +1793,18 @@ 256 - + + YES 319 - + + YES 2304 - + + YES 2322 @@ -1750,9 +1818,10 @@ - + + YES - + 6 @@ -1765,28 +1834,46 @@ 0 - - - 6 - System - selectedTextBackgroundColor - + + YES + + YES + NSBackgroundColor + NSColor - - 6 - System - selectedTextColor - + + YES + + 6 + System + selectedTextBackgroundColor + + + + 6 + System + selectedTextColor + + - + - - - 1 - MCAwIDEAA + + YES + + YES + NSColor + NSUnderline - - + + YES + + 1 + MCAwIDEAA + + + + 6 @@ -1794,7 +1881,7 @@ {114, 0} - + {505, 342} @@ -1826,7 +1913,7 @@ 1 0.94565218687057495 - + {505, 342} @@ -1835,7 +1922,7 @@ - + {505, 342} {{0, 0}, {1440, 878}} @@ -1857,7 +1944,8 @@ 256 - + + YES 256 @@ -1969,7 +2057,7 @@ - + {400, 229} {{0, 0}, {1440, 878}} @@ -1994,43 +2082,56 @@ NO 1 1 - - - - 0D5950D1-D4A8-44C6-9DBC-251CFEF852E2 + + YES + + YES + 0D5950D1-D4A8-44C6-9DBC-251CFEF852E2 + BWToolbarShowFontsItem + NSToolbarFlexibleSpaceItem + NSToolbarSeparatorItem + NSToolbarSpaceItem + + + YES + + + 0D5950D1-D4A8-44C6-9DBC-251CFEF852E2 + + General + General + + + + NSImage + NSPreferencesGeneral + + + toggleActiveView: + {0, 0} + {0, 0} + YES + YES + -1 + YES + 0 - General - General - - - - NSImage - NSPreferencesGeneral - - - toggleActiveView: - {0, 0} - {0, 0} - YES - YES - -1 - YES - 0 - - - BWToolbarShowFontsItem - Fonts - Fonts - Show Font Panel - - - 12582912 - - - - - - TU0AKgAAAwyAACBQOCQWDQeEQmFQuGQ2HQ+IRGJROHAOLCJ/Rl3v+OO+KR+QSGRQYOSVtO+UIt6ytNyO + + BWToolbarShowFontsItem + Fonts + Fonts + Show Font Panel + + + 12582912 + + YES + + YES + + + + TU0AKgAAAwyAACBQOCQWDQeEQmFQuGQ2HQ+IRGJROHAOLCJ/Rl3v+OO+KR+QSGRQYOSVtO+UIt6ytNyO XS+YQYHzM7BubIZyzk9PGeIqYz+gRIBUMIiSjNoEUkIuymL1zU8j0GpVOEg6rHYPVlDRYBgB3V9nOOxD aqWWpAG0BEQ2ttAu3BGhgIAPi6ABs3cCWa9TEG32azcC4EAWgAgB9YcAN3FDZ941nXvIRGuCINZVkvPM IsM5utxYAY19gBwaMjvnTL3I6mGzMHpcE68hujZCYRbV+UkEAB+bsAU9zHp6cGfariQTJ5sMtp28s0vf @@ -2056,14 +2157,15 @@ AAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAGN1cnYAAAAAAAAAAQHN AABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAFhZWiAAAAAAAAB5vQAAQVIAAAS5WFlaIAAA AAAAAFb4AACsLwAAHQNYWVogAAAAAAAAJiIAABJ/AACxcA + - - - - - - TU0AKgAAAaiAACBQOCQWDQeEQmFQuGQ2HQ+IRGJROKRWLReMRmLFWBquNR+QQYiQNdwMAyGURkGStdhK + + YES + + + + TU0AKgAAAaiAACBQOCQWDQeEQmFQuGQ2HQ+IRGJROKRWLReMRmLFWBquNR+QQYiQNdwMAyGURkGStdhK XERxTEJQN3ymbRAiCqdLsAz0ANWgEWBryb0WEgWkMwRUsIgSnCKgNUzwNNUarQQzAKtDMHV0ABWwGZt2 M8QNE1e0N2BkWtAIqiG4Ihy3NePi7UK0TYzQMZwOpwIqiDBKp3YVmvLEDS8za1QK8ABvXwOZNmPvLAB0 5mT4uNHeD2eD2AKv8B6UAOjUYqBM3ORIIz0Asx/7PVACawcIblugfeCLMunH0TWxA704CHd+cnWQuug4 @@ -2083,369 +2185,386 @@ AAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAAAAAAAAAAAABjdXJ2AAAAAAAA AAEBzQAAY3VydgAAAAAAAAABAc0AAGN1cnYAAAAAAAAAAQHNAABYWVogAAAAAAAAeb0AAEFSAAAEuVhZ WiAAAAAAAABW+AAArC8AAB0DWFlaIAAAAAAAACYiAAASfwAAsXA + - - - - 3 - MCAwAA + + + 3 + MCAwAA + + + orderFrontFontPanel: + {0, 0} + {0, 0} + YES + YES + -1 + YES + 0 - - orderFrontFontPanel: - {0, 0} - {0, 0} - YES - YES - -1 - YES - 0 - - - NSToolbarFlexibleSpaceItem - - Flexible Space - - - - - - {1, 5} - {20000, 32} - YES - YES - -1 - YES - 0 - - YES - YES - - - 1048576 - 2147483647 - - + + NSToolbarFlexibleSpaceItem + + Flexible Space + + + + + + {1, 5} + {20000, 32} + YES + YES + -1 + YES + 0 + + YES + YES + + + 1048576 + 2147483647 + + + - - - NSToolbarSeparatorItem - - Separator - - - - - - {12, 5} - {12, 1000} - YES - YES - -1 - YES - 0 - - YES - YES - - - 1048576 - 2147483647 - - + + NSToolbarSeparatorItem + + Separator + + + + + + {12, 5} + {12, 1000} + YES + YES + -1 + YES + 0 + + YES + YES + + + 1048576 + 2147483647 + + + - - - NSToolbarSpaceItem - - Space - - - - - - {32, 5} - {32, 32} - YES - YES - -1 - YES - 0 - - YES - YES - - - 1048576 - 2147483647 - - + + NSToolbarSpaceItem + + Space + + + + + + {32, 5} + {32, 32} + YES + YES + -1 + YES + 0 + + YES + YES + + + 1048576 + 2147483647 + + + - - + + + YES - - + + + YES - - + + + YES + - - - - 256 - - - - 268 - {{18, 45}, {174, 18}} - - YES - - -2080244224 - 0 - Open profile on startup: - - - 1211912703 - 2 - - NSImage - NSSwitch + + YES + + YES + 0D5950D1-D4A8-44C6-9DBC-251CFEF852E2 + BWToolbarShowFontsItem + + + YES + + + 256 + + YES + + + 268 + {{18, 45}, {174, 18}} + + YES + + -2080244224 + 0 + Open profile on startup: + + + 1211912703 + 2 + + NSImage + NSSwitch + + + + + 200 + 25 - - - - 200 - 25 - - - - 268 - {{18, 18}, {410, 18}} - - YES - - 67239424 - 0 - Delete log-file (~/unison.log) automatically on complete sync - - - 1211912703 - 2 - - - - - 200 - 25 + + + 268 + {{18, 18}, {410, 18}} + + YES + + 67239424 + 0 + Delete log-file (~/unison.log) automatically on complete sync + + + 1211912703 + 2 + + + + + 200 + 25 + - - - - 268 - {{198, 40}, {231, 26}} - - YES - - 72482368 - 272630784 - - - - YES - - - 5 - YES - YES - - - - 274 - {15, 0} - - - YES - - - - 12 - 10 - 1000 - - 75628032 - 0 - - - - 3 - MC4zMzMzMzI5ODU2AA + + + 268 + {{198, 40}, {231, 26}} + + YES + + 72482368 + 272630784 + + + + YES + + + 5 + YES + YES + + + + 274 + {15, 0} + + + YES + + YES + + + 12 + 10 + 1000 + + 75628032 + 0 + + + + 3 + MC4zMzMzMzI5ODU2AA + + - + + 338820672 + 1024 + + + YES + + + + 3 + YES + - - 338820672 - 1024 - - - YES - - - - 3 - YES - - - 3 - 2 - - - 19 - tableViewAction: - -767524864 - - - - 1 - 15 - 0 - YES - 0 + 3 + 2 + + + 19 + tableViewAction: + -767524864 + + + + 1 + 15 + 0 + YES + 0 + - - {446, 84} - - - - 256 - - - - 268 - {{125, 50}, {213, 17}} - - - YES - - 68288064 - 272630784 - xxxx - - - - + {446, 84} + + + + 256 + + YES + + + 268 + {{125, 50}, {213, 17}} + + + YES + + 68288064 + 272630784 + xxxx + + + + + - - - - 268 - {{343, 45}, {59, 25}} - - - YES - - -2080244224 - 134217728 - choose - - - -2038152961 - 163 - - - 400 - 75 + + + 268 + {{343, 45}, {59, 25}} + + + YES + + -2080244224 + 134217728 + choose + + + -2038152961 + 163 + + + 400 + 75 + - - - - 268 - {{125, 20}, {213, 17}} - - - YES - - 68288064 - 272630784 - xxxx - - - - + + + 268 + {{125, 20}, {213, 17}} + + + YES + + 68288064 + 272630784 + xxxx + + + + + - - - - 268 - {{343, 15}, {59, 25}} - - - YES - - -2080244224 - 134217728 - choose - - - -2038152961 - 163 - - - 400 - 75 + + + 268 + {{343, 15}, {59, 25}} + + + YES + + -2080244224 + 134217728 + choose + + + -2038152961 + 163 + + + 400 + 75 + - - - - 268 - {{17, 20}, {106, 17}} - - - YES - - 68288064 - 71304192 - Diff font: - - - - + + + 268 + {{17, 20}, {106, 17}} + + + YES + + 68288064 + 71304192 + Diff font: + + + + + - - - - 268 - {{17, 50}, {106, 17}} - - - YES - - 68288064 - 71304192 - Details font: - - - - + + + 268 + {{17, 50}, {106, 17}} + + + YES + + 68288064 + 71304192 + Details font: + + + + + - - {422, 87} + {422, 87} + - + BAtzdHJlYW10eXBlZIHoA4QBQISEhAxOU0RpY3Rpb25hcnkAhIQITlNPYmplY3QAhYQBaQKShISECE5T U3RyaW5nAZSEASskMEQ1OTUwRDEtRDRBOC00NEM2LTlEQkMtMjUxQ0ZFRjg1MkUyhpKEhIQHTlNWYWx1 @@ -2458,7 +2577,13 @@ YES YES - + + YES + + + YES + + {1.79769e+308, 1.79769e+308} @@ -2468,9 +2593,10 @@ YES - + - + + YES terminate: @@ -3231,12 +3357,13 @@ 684 - + - + + YES 0 - + @@ -3261,53 +3388,59 @@ 21 - + + YES - + Window 2 - + + YES - + 515 - + + YES - + 29 - + + YES - + MainMenu 56 - + + YES - + 57 - + + YES @@ -3318,7 +3451,7 @@ - + @@ -3374,17 +3507,19 @@ 103 - + + YES - + 106 - + + YES - + @@ -3395,21 +3530,23 @@ 163 - + + YES - + 169 - + + YES - + @@ -3440,19 +3577,21 @@ 253 - + + YES - + 254 - + + YES - + @@ -3473,15 +3612,17 @@ 261 - + + YES - + 262 - + + YES @@ -3494,7 +3635,7 @@ - + @@ -3560,65 +3701,72 @@ 197 - + + YES - + chooseProfileView 199 - + + YES - + 201 - + + YES - + 203 - + + YES - + 205 - + + YES - + 202 - + + YES - + 198 - + + YES - + updatesView @@ -3637,80 +3785,89 @@ 234 - + + YES - + PasswordWindow 235 - + + YES - + 236 - + + YES - + 237 - + + YES - + 238 - + + YES - + 239 - + + YES - + 307 - + + YES - + PreferencesView 321 - + + YES - + 323 - + + YES @@ -3718,23 +3875,25 @@ - + 329 - + + YES - + 330 - + + YES - + @@ -3746,62 +3905,69 @@ 402 - + + YES - + AboutWindow 401 - + + YES - + 406 - + + YES - + 407 - + + YES - + 409 - + + YES - + 410 - + + YES - + 411 - + + YES - + @@ -3813,28 +3979,31 @@ 475 - + + YES - + DiffWindow 476 - + + YES - + 477 - + + YES - + @@ -3845,62 +4014,69 @@ 488 - + + YES - + CltoolWindow 489 - + + YES - + 491 - + + YES - + 493 - + + YES - + 494 - + + YES - + 495 - + + YES - + 496 - + + YES - + @@ -4031,9 +4207,10 @@ 362 - + + YES - + @@ -4044,9 +4221,10 @@ 363 - + + YES - + @@ -4057,11 +4235,12 @@ 364 - + + YES - + @@ -4082,9 +4261,10 @@ 367 - + + YES - + @@ -4095,9 +4275,10 @@ 368 - + + YES - + @@ -4108,9 +4289,10 @@ 369 - + + YES - + @@ -4121,9 +4303,10 @@ 370 - + + YES - + @@ -4134,9 +4317,10 @@ 371 - + + YES - + @@ -4147,9 +4331,10 @@ 372 - + + YES - + @@ -4160,26 +4345,29 @@ 586 - + + YES - + 589 - + + YES - + 607 - + + YES - + @@ -4195,32 +4383,35 @@ 590 - + + YES - + 594 - + + YES - + 599 - + + YES - + @@ -4231,9 +4422,10 @@ 598 - + + YES - + @@ -4244,9 +4436,10 @@ 597 - + + YES - + @@ -4257,9 +4450,10 @@ 596 - + + YES - + @@ -4270,9 +4464,10 @@ 595 - + + YES - + @@ -4298,9 +4493,10 @@ 583 - + + YES - + @@ -4311,32 +4507,35 @@ 618 - + + YES - + PreferencesWindow 619 - + + YES - + 423 - + + YES - + ConnectingView @@ -4348,13 +4547,14 @@ 620 - + + YES - + @@ -4395,22 +4595,24 @@ 678 - + + YES - + 636 - + + YES - + @@ -4421,9 +4623,10 @@ 637 - + + YES - + @@ -4434,9 +4637,10 @@ 640 - + + YES - + @@ -4447,9 +4651,10 @@ 641 - + + YES - + @@ -4460,9 +4665,10 @@ 644 - + + YES - + @@ -4473,9 +4679,10 @@ 646 - + + YES - + @@ -4486,9 +4693,10 @@ 629 - + + YES - + @@ -4499,9 +4707,10 @@ 630 - + + YES - + @@ -4512,9 +4721,10 @@ 631 - + + YES - + @@ -4525,17 +4735,19 @@ 681 - + + YES - + 682 - + + YES - + @@ -4543,374 +4755,752 @@ - + - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{582, 1091}, {202, 23}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{407, 1011}, {179, 103}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{345, 795}, {363, 281}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{357, 361}, {730, 546}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - ProfileTableView - com.apple.InterfaceBuilder.CocoaPlugin - - - {{717, 719}, {480, 360}} - com.apple.InterfaceBuilder.CocoaPlugin - {{717, 719}, {480, 360}} - - - {213, 107} - - {{345, 994}, {227, 128}} - com.apple.InterfaceBuilder.CocoaPlugin - {{345, 994}, {227, 128}} - - - {213, 107} - com.apple.InterfaceBuilder.CocoaPlugin - - NSSecureTextField - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{520, 1051}, {191, 63}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{451, 881}, {323, 233}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{326, 1114}, {317, 20}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{345, 803}, {534, 250}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{345, 879}, {262, 266}} - com.apple.InterfaceBuilder.CocoaPlugin - {{345, 879}, {262, 266}} - - - {213, 107} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{443, 288}, {871, 577}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - - {{345, 792}, {505, 342}} - com.apple.InterfaceBuilder.CocoaPlugin - {{345, 792}, {505, 342}} - - - {213, 107} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{345, 916}, {400, 229}} - com.apple.InterfaceBuilder.CocoaPlugin - {{345, 916}, {400, 229}} - - - {213, 107} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{338, 941}, {266, 173}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - ReconTableView - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - {{446, 645}, {446, 84}} - com.apple.InterfaceBuilder.CocoaPlugin - {{446, 645}, {446, 84}} - - com.apple.InterfaceBuilder.CocoaPlugin - {{466, 530}, {616, 0}} - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - com.brandonwalkin.BWToolkit - - + + YES + + YES + -3.IBPluginDependency + -3.ImportedFromIB2 + 103.IBPluginDependency + 103.ImportedFromIB2 + 106.IBEditorWindowLastContentRect + 106.IBPluginDependency + 106.ImportedFromIB2 + 111.IBPluginDependency + 111.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 157.IBPluginDependency + 157.ImportedFromIB2 + 160.IBPluginDependency + 160.ImportedFromIB2 + 163.IBPluginDependency + 163.ImportedFromIB2 + 169.IBEditorWindowLastContentRect + 169.IBPluginDependency + 169.ImportedFromIB2 + 171.IBPluginDependency + 171.ImportedFromIB2 + 172.IBPluginDependency + 172.ImportedFromIB2 + 196.IBPluginDependency + 196.ImportedFromIB2 + 197.IBEditorWindowLastContentRect + 197.IBPluginDependency + 197.ImportedFromIB2 + 198.IBEditorWindowLastContentRect + 198.IBPluginDependency + 198.ImportedFromIB2 + 199.IBPluginDependency + 199.ImportedFromIB2 + 2.IBPluginDependency + 2.ImportedFromIB2 + 201.IBPluginDependency + 201.ImportedFromIB2 + 202.IBPluginDependency + 202.ImportedFromIB2 + 203.IBPluginDependency + 203.ImportedFromIB2 + 205.CustomClassName + 205.IBPluginDependency + 205.ImportedFromIB2 + 209.ImportedFromIB2 + 21.IBEditorWindowLastContentRect + 21.IBPluginDependency + 21.IBWindowTemplateEditedContentRect + 21.ImportedFromIB2 + 21.windowTemplate.hasMinSize + 21.windowTemplate.minSize + 217.ImportedFromIB2 + 234.IBEditorWindowLastContentRect + 234.IBPluginDependency + 234.IBWindowTemplateEditedContentRect + 234.ImportedFromIB2 + 234.windowTemplate.hasMinSize + 234.windowTemplate.minSize + 235.IBPluginDependency + 235.ImportedFromIB2 + 236.CustomClassName + 236.IBPluginDependency + 236.ImportedFromIB2 + 237.IBPluginDependency + 237.ImportedFromIB2 + 238.IBPluginDependency + 238.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 253.IBPluginDependency + 253.ImportedFromIB2 + 254.IBEditorWindowLastContentRect + 254.IBPluginDependency + 254.ImportedFromIB2 + 255.IBPluginDependency + 255.ImportedFromIB2 + 256.IBPluginDependency + 256.ImportedFromIB2 + 257.IBPluginDependency + 257.ImportedFromIB2 + 261.IBPluginDependency + 261.ImportedFromIB2 + 262.IBEditorWindowLastContentRect + 262.IBPluginDependency + 262.ImportedFromIB2 + 263.IBPluginDependency + 263.ImportedFromIB2 + 266.IBPluginDependency + 266.ImportedFromIB2 + 267.IBPluginDependency + 267.ImportedFromIB2 + 268.IBPluginDependency + 268.ImportedFromIB2 + 269.IBPluginDependency + 269.ImportedFromIB2 + 281.IBPluginDependency + 281.ImportedFromIB2 + 284.IBPluginDependency + 284.ImportedFromIB2 + 29.IBEditorWindowLastContentRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 307.IBEditorWindowLastContentRect + 307.IBPluginDependency + 307.ImportedFromIB2 + 321.IBPluginDependency + 321.ImportedFromIB2 + 323.IBPluginDependency + 323.ImportedFromIB2 + 329.IBPluginDependency + 329.ImportedFromIB2 + 330.IBPluginDependency + 330.ImportedFromIB2 + 331.ImportedFromIB2 + 362.IBPluginDependency + 362.ImportedFromIB2 + 363.IBPluginDependency + 363.ImportedFromIB2 + 364.IBPluginDependency + 364.ImportedFromIB2 + 365.IBPluginDependency + 365.ImportedFromIB2 + 366.IBPluginDependency + 366.ImportedFromIB2 + 367.IBPluginDependency + 367.ImportedFromIB2 + 368.IBPluginDependency + 368.ImportedFromIB2 + 369.IBPluginDependency + 369.ImportedFromIB2 + 370.IBPluginDependency + 370.ImportedFromIB2 + 371.IBPluginDependency + 371.ImportedFromIB2 + 372.IBPluginDependency + 372.ImportedFromIB2 + 401.IBPluginDependency + 401.ImportedFromIB2 + 402.IBEditorWindowLastContentRect + 402.IBPluginDependency + 402.IBWindowTemplateEditedContentRect + 402.ImportedFromIB2 + 402.windowTemplate.hasMinSize + 402.windowTemplate.minSize + 406.IBPluginDependency + 406.ImportedFromIB2 + 407.IBPluginDependency + 407.ImportedFromIB2 + 409.IBPluginDependency + 409.ImportedFromIB2 + 410.IBPluginDependency + 410.ImportedFromIB2 + 411.IBPluginDependency + 411.ImportedFromIB2 + 414.IBPluginDependency + 414.ImportedFromIB2 + 417.IBPluginDependency + 417.ImportedFromIB2 + 419.IBPluginDependency + 419.ImportedFromIB2 + 420.IBPluginDependency + 420.ImportedFromIB2 + 423.IBEditorWindowLastContentRect + 423.IBPluginDependency + 423.ImportedFromIB2 + 428.IBPluginDependency + 428.ImportedFromIB2 + 430.IBPluginDependency + 430.ImportedFromIB2 + 437.ImportedFromIB2 + 475.IBEditorWindowLastContentRect + 475.IBPluginDependency + 475.IBWindowTemplateEditedContentRect + 475.ImportedFromIB2 + 475.windowTemplate.hasMinSize + 475.windowTemplate.minSize + 476.IBPluginDependency + 476.ImportedFromIB2 + 477.IBPluginDependency + 477.ImportedFromIB2 + 478.IBPluginDependency + 478.ImportedFromIB2 + 481.IBPluginDependency + 481.ImportedFromIB2 + 484.IBPluginDependency + 484.ImportedFromIB2 + 486.IBPluginDependency + 486.ImportedFromIB2 + 488.IBEditorWindowLastContentRect + 488.IBPluginDependency + 488.IBWindowTemplateEditedContentRect + 488.ImportedFromIB2 + 488.windowTemplate.hasMinSize + 488.windowTemplate.minSize + 489.IBPluginDependency + 489.ImportedFromIB2 + 491.IBPluginDependency + 491.ImportedFromIB2 + 493.IBPluginDependency + 493.ImportedFromIB2 + 494.IBPluginDependency + 494.ImportedFromIB2 + 495.IBPluginDependency + 495.ImportedFromIB2 + 496.IBPluginDependency + 496.ImportedFromIB2 + 515.IBPluginDependency + 515.ImportedFromIB2 + 527.IBPluginDependency + 528.IBPluginDependency + 529.IBPluginDependency + 531.IBPluginDependency + 532.IBPluginDependency + 533.IBPluginDependency + 534.IBPluginDependency + 535.IBPluginDependency + 536.IBPluginDependency + 537.IBPluginDependency + 538.IBPluginDependency + 539.IBPluginDependency + 540.IBPluginDependency + 541.IBPluginDependency + 542.IBPluginDependency + 543.IBPluginDependency + 544.IBPluginDependency + 545.IBPluginDependency + 546.IBPluginDependency + 547.IBPluginDependency + 548.IBPluginDependency + 549.IBPluginDependency + 551.IBPluginDependency + 552.IBPluginDependency + 553.IBPluginDependency + 554.IBPluginDependency + 555.IBPluginDependency + 556.IBPluginDependency + 557.IBPluginDependency + 557.IBShouldRemoveOnLegacySave + 56.IBPluginDependency + 56.ImportedFromIB2 + 561.IBPluginDependency + 561.IBShouldRemoveOnLegacySave + 562.IBPluginDependency + 562.IBShouldRemoveOnLegacySave + 563.IBPluginDependency + 563.IBShouldRemoveOnLegacySave + 569.IBPluginDependency + 569.IBShouldRemoveOnLegacySave + 57.IBEditorWindowLastContentRect + 57.IBPluginDependency + 57.ImportedFromIB2 + 570.IBPluginDependency + 570.IBShouldRemoveOnLegacySave + 576.IBPluginDependency + 583.IBPluginDependency + 584.IBPluginDependency + 586.IBPluginDependency + 589.IBPluginDependency + 590.IBPluginDependency + 590.ImportedFromIB2 + 591.IBPluginDependency + 591.IBShouldRemoveOnLegacySave + 592.IBPluginDependency + 592.IBShouldRemoveOnLegacySave + 593.IBPluginDependency + 593.IBShouldRemoveOnLegacySave + 594.CustomClassName + 594.IBPluginDependency + 594.ImportedFromIB2 + 595.IBPluginDependency + 595.ImportedFromIB2 + 596.IBPluginDependency + 596.ImportedFromIB2 + 597.IBPluginDependency + 597.ImportedFromIB2 + 598.IBPluginDependency + 598.ImportedFromIB2 + 599.IBPluginDependency + 599.ImportedFromIB2 + 600.IBPluginDependency + 600.ImportedFromIB2 + 601.IBPluginDependency + 601.ImportedFromIB2 + 602.IBPluginDependency + 602.IBShouldRemoveOnLegacySave + 603.IBPluginDependency + 603.IBShouldRemoveOnLegacySave + 604.IBPluginDependency + 604.IBShouldRemoveOnLegacySave + 607.IBPluginDependency + 608.IBPluginDependency + 618.IBEditorWindowLastContentRect + 618.IBPluginDependency + 618.IBWindowTemplateEditedContentRect + 618.NSWindowTemplate.visibleAtLaunch + 619.IBPluginDependency + 620.IBEditorWindowLastContentRect + 620.IBPluginDependency + 622.IBPluginDependency + 624.IBPluginDependency + 626.IBPluginDependency + 627.IBPluginDependency + 628.IBPluginDependency + 629.IBPluginDependency + 630.IBPluginDependency + 631.IBPluginDependency + 632.IBPluginDependency + 633.IBPluginDependency + 634.IBPluginDependency + 636.IBPluginDependency + 637.IBPluginDependency + 638.IBPluginDependency + 639.IBPluginDependency + 640.IBPluginDependency + 641.IBPluginDependency + 642.IBPluginDependency + 643.IBPluginDependency + 644.IBPluginDependency + 645.IBPluginDependency + 646.IBPluginDependency + 647.IBPluginDependency + 653.IBPluginDependency + 678.IBPluginDependency + 681.IBPluginDependency + 682.IBPluginDependency + 683.IBPluginDependency + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{582, 1091}, {202, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{407, 1011}, {179, 103}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{345, 795}, {363, 281}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{357, 361}, {730, 546}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + ProfileTableView + com.apple.InterfaceBuilder.CocoaPlugin + + + {{717, 719}, {480, 360}} + com.apple.InterfaceBuilder.CocoaPlugin + {{717, 719}, {480, 360}} + + + {213, 107} + + {{345, 994}, {227, 128}} + com.apple.InterfaceBuilder.CocoaPlugin + {{345, 994}, {227, 128}} + + + {213, 107} + com.apple.InterfaceBuilder.CocoaPlugin + + NSSecureTextField + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{520, 1051}, {191, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{451, 881}, {323, 233}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{326, 1114}, {317, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{345, 803}, {534, 250}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{345, 879}, {262, 266}} + com.apple.InterfaceBuilder.CocoaPlugin + {{345, 879}, {262, 266}} + + + {213, 107} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{443, 288}, {871, 577}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + + {{345, 792}, {505, 342}} + com.apple.InterfaceBuilder.CocoaPlugin + {{345, 792}, {505, 342}} + + + {213, 107} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{345, 916}, {400, 229}} + com.apple.InterfaceBuilder.CocoaPlugin + {{345, 916}, {400, 229}} + + + {213, 107} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{338, 941}, {266, 173}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + ReconTableView + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + {{446, 645}, {446, 84}} + com.apple.InterfaceBuilder.CocoaPlugin + {{446, 645}, {446, 84}} + + com.apple.InterfaceBuilder.CocoaPlugin + {{466, 530}, {616, 0}} + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + com.brandonwalkin.BWToolkit + + + + YES + + + YES + + - + + YES + + + YES + + 684 - + + YES FirstResponder NSObject - - id - id - id - id - id - id - id - id - id - id - id - id - + + YES + + YES + copyLR: + copyRL: + forceNewer: + forceOlder: + ignoreExt: + ignoreName: + ignorePath: + leaveAlone: + merge: + revert: + selectConflicts: + showDiff: + + + YES + id + id + id + id + id + id + id + id + id + id + id + id + + IBUserSource @@ -4919,63 +5509,130 @@ MyController NSObject - - id - id - id - id - id - id - id - id - id - id - id - id - NSWindow - id - id - id - id - id - id - - - NSView - NSWindow - NSButton - NSButton - NSButton - NSView - NSButton - NSWindow - NSProgressIndicator - NSTextField - NSTextField - NSTextField - NSTextView - NSWindow - id - NSWindow - NotificationController - NSButton - NSTextField - NSTextField - NSWindow - PreferencesController - NSView - NSWindow - NSComboBox - ProfileController - NSProgressIndicator - NSSplitView - NSTextField - NSSegmentedControl - ReconTableView - NSTextField - NSView - NSTextField - + + YES + + YES + cancelProfileButton: + checkOpenProfileChanged: + chooseFont: + cltoolNoButton: + cltoolYesButton: + createButton: + endPasswordWindow: + installCommandLineTool: + onlineHelp: + openButton: + raiseAboutWindow: + raiseCltoolWindow: + raiseWindow: + rescan: + restartButton: + saveProfileButton: + showPreferences: + syncButton: + tableModeChanged: + + + YES + id + id + id + id + id + id + id + id + id + id + id + id + NSWindow + id + id + id + id + id + id + + + + YES + + YES + ConnectingView + aboutWindow + checkOpenProfile + chooseDetailsFont + chooseDiffFont + chooseProfileView + cltoolPref + cltoolWindow + connectingAnimation + detailsFontLabel + detailsTextView + diffFontLabel + diffView + diffWindow + fontChangeTarget + mainWindow + notificationController + passwordCancelButton + passwordPrompt + passwordText + passwordWindow + preferencesController + preferencesView + preferencesWindow + profileBox + profileController + progressBar + splitView + statusText + tableModeSelector + tableView + updatesText + updatesView + versionText + + + YES + NSView + NSWindow + NSButton + NSButton + NSButton + NSView + NSButton + NSWindow + NSProgressIndicator + NSTextField + NSTextField + NSTextField + NSTextView + NSWindow + id + NSWindow + NotificationController + NSButton + NSTextField + NSTextField + NSWindow + PreferencesController + NSView + NSWindow + NSComboBox + ProfileController + NSProgressIndicator + NSSplitView + NSTextField + NSSegmentedControl + ReconTableView + NSTextField + NSView + NSTextField + + IBProjectSource MyController.h @@ -5031,20 +5688,44 @@ PreferencesController NSObject - - id - id - id - - - NSTextField - NSButtonCell - NSTextField - NSButtonCell - NSTextField - NSTextField - NSTextField - + + YES + + YES + anyEnter: + localClick: + remoteClick: + + + YES + id + id + id + + + + YES + + YES + firstRootText + localButtonCell + profileNameText + remoteButtonCell + secondRootHost + secondRootText + secondRootUser + + + YES + NSTextField + NSButtonCell + NSTextField + NSButtonCell + NSTextField + NSTextField + NSTextField + + IBProjectSource PreferencesController.h @@ -5101,20 +5782,39 @@ ReconTableView NSOutlineView - - id - id - id - id - id - id - id - id - id - id - id - id - + + YES + + YES + copyLR: + copyRL: + forceNewer: + forceOlder: + ignoreExt: + ignoreName: + ignorePath: + leaveAlone: + merge: + revert: + selectConflicts: + showDiff: + + + YES + id + id + id + id + id + id + id + id + id + id + id + id + + @@ -5125,8 +5825,9 @@ - - + + + YES BWGradientBox NSView @@ -5206,7 +5907,7 @@ BWToolkitFramework.framework/Headers/NSWindow+BWAdditions.h - + 0 @@ -5217,6 +5918,10 @@ com.apple.InterfaceBuilder.CocoaPlugin.macosx + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + YES ../uimacnew.xcodeproj 3 Modified: trunk/src/uimacnew09/uimacnew.xcodeproj/project.pbxproj =================================================================== --- trunk/src/uimacnew09/uimacnew.xcodeproj/project.pbxproj 2010-01-05 09:33:18 UTC (rev 391) +++ trunk/src/uimacnew09/uimacnew.xcodeproj/project.pbxproj 2010-01-05 15:42:43 UTC (rev 392) @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 42; + objectVersion = 45; objects = { /* Begin PBXAggregateTarget section */ @@ -108,6 +108,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BB6E50CF10CAA57600E23F8A /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 7; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -371,6 +380,7 @@ 69C625E90664EC3300B3C46A /* Sources */, 69C625F10664EC3300B3C46A /* Frameworks */, 2A3C3F3709922AA600E404E9 /* CopyFiles */, + BB6E50CF10CAA57600E23F8A /* CopyFiles */, ); buildRules = ( ); @@ -389,7 +399,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; buildConfigurationList = 2A3C3F2C0992245300E404E9 /* Build configuration list for PBXProject "uimacnew" */; - compatibilityVersion = "Xcode 2.4"; + compatibilityVersion = "Xcode 3.1"; hasScannedForEncodings = 1; mainGroup = 29B97314FDCFA39411CA2CEA /* uimac */; projectDirPath = ""; @@ -686,7 +696,7 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = "Frameworks/**"; LIBRARY_SEARCH_PATHS = ""; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.5; USER_HEADER_SEARCH_PATHS = $OCAMLLIBDIR; }; name = Development; @@ -697,7 +707,7 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = "Frameworks/**"; LIBRARY_SEARCH_PATHS = ""; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.5; USER_HEADER_SEARCH_PATHS = $OCAMLLIBDIR; }; name = Deployment; @@ -708,7 +718,7 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = "Frameworks/**"; LIBRARY_SEARCH_PATHS = ""; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.5; USER_HEADER_SEARCH_PATHS = $OCAMLLIBDIR; }; name = Default; From bcpierce at cis.upenn.edu Tue Jan 5 11:49:21 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Tue, 5 Jan 2010 11:49:21 -0500 Subject: [Unison-hackers] Proposed mode for unison: "Salvage" In-Reply-To: References: Message-ID: <7211CE58-2B30-4151-AE17-BB7AD0C702A6@cis.upenn.edu> This would take some hacking - in particular, I'm not sure I see how the user interface should work (especially the graphical one). But I agree that unison would be a decent base for building such a tool. - Benjamin On Jan 5, 2010, at 9:54 AM, Ryan Newton wrote: > The biggest complaint I hear from friends and family about unison is > the ease of duplicating files. This happens most often when running > unison without saved archives (e.g. because things get moved around, > mixed up, moved to new machines, etc). > > A typical scenario that is difficult to handle with unison is that you > come across an old copy of folder X that *might* contain something > that you forgot to extract or move into your current, primary copy. > But of course you don't know what's there and checking is manually > hard. Further, performing a simple unison is the WRONG answer, > because the organization may have changed substantially, making it > very hard to tell if supposedly new files in the old copy are really > new or have just been moved (duplication danger). > > In this case it would be very useful to run unison in mode where: > > (1) only copies from old->new are considered. The goal is not two > identical archives, but to retrieve things from the old copy. > (2) only files which do not exist ANYWHERE in the new archive are > considered, the new archive is just a flat set of files for the > purpose of this check. > > Secondary questions include where to put the new files (presumably the > same path as in the old archive) and what to do with > conflicts/collisions resulting from, for example, modified files > (presumably they're treated in the normal unison way). > > The interface could perhaps be a "-salvage X" flag, where X is one of > the roots (just like -force). > > Best, > -Ryan > > P.S. I actually wrote a separate tool at some point (in ocaml) that > could accomplish the above which I could provide to the curious. Its > goal was to replace all the files in a folder X, that exist in a > folder Y, with symlinks into folder Y. One could run this on the old > archive and then use "find" to see all the files that were not turned > to symlinks. However, I think leveraging unison for this purpose > would be much more desirable. > _______________________________________________ > Unison-hackers mailing list > Unison-hackers at lists.seas.upenn.edu > http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers From Jerome.Vouillon at pps.jussieu.fr Tue Jan 5 12:12:47 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Tue, 5 Jan 2010 18:12:47 +0100 Subject: [Unison-hackers] patch: make arrows for suggested changes darker In-Reply-To: <20100102190649.GC28046@dewdrop.local> References: <20100102190649.GC28046@dewdrop.local> Message-ID: <20100105171247.GA13397@pps.jussieu.fr> On Sat, Jan 02, 2010 at 07:06:49PM +0000, Eric Y. Kow wrote: > Here's a trivial patch to darken the grey arrows Unison displays on > MacOS X. Thanks. I have committed this change to the subversion repository. -- Jerome From newton at mit.edu Tue Jan 5 12:08:59 2010 From: newton at mit.edu (Ryan Newton) Date: Tue, 5 Jan 2010 12:08:59 -0500 Subject: [Unison-hackers] Proposed mode for unison: "Salvage" In-Reply-To: <7211CE58-2B30-4151-AE17-BB7AD0C702A6@cis.upenn.edu> References: <7211CE58-2B30-4151-AE17-BB7AD0C702A6@cis.upenn.edu> Message-ID: > This would take some hacking - in particular, I'm not sure I see how > the user interface should work (especially the graphical one). Perhaps there was something I've missed... but I was thinking that no significant UI change is needed. Rather, the effect is mainly on the scan phase (apologies if I've forgotten the correct unison terminology -- update detection phase?). The effect of (1) and (2) on the user-visible interface should be only to apply a filter over the usual "<-" entries that are presented. That is, the changes presented would be a subset of the changes presented if running without -salvage (namely, files duplicated in other locations are suppressed, and changes from new->old are suppressed). I was hoping this could be implemented by leaving the whole "backend" the same and literally filtering the changset. If anyone else feels they need this functionality let me know. I could hack on it but would love to partner with someone who knows the code better. -Ryan On Tue, Jan 5, 2010 at 11:49 AM, Benjamin Pierce wrote: > This would take some hacking - in particular, I'm not sure I see how > the user interface should work (especially the graphical one). ?But I > agree that unison would be a decent base for building such a tool. > > ? ? ? ?- Benjamin > > On Jan 5, 2010, at 9:54 AM, Ryan Newton wrote: > >> The biggest complaint I hear from friends and family about unison is >> the ease of duplicating files. ?This happens most often when running >> unison without saved archives (e.g. because things get moved around, >> mixed up, moved to new machines, etc). >> >> A typical scenario that is difficult to handle with unison is that you >> come across an old copy of folder X that *might* contain something >> that you forgot to extract or move into your current, primary copy. >> But of course you don't know what's there and checking is manually >> hard. ?Further, performing a simple unison is the WRONG answer, >> because the organization may have changed substantially, making it >> very hard to tell if supposedly new files in the old copy are really >> new or have just been moved (duplication danger). >> >> In this case it would be very useful to run unison in mode where: >> >> ?(1) only copies from old->new are considered. ?The goal is not two >> identical archives, but to retrieve things from the old copy. >> ?(2) only files which do not exist ANYWHERE in the new archive are >> considered, the new archive is just a flat set of files for the >> purpose of this check. >> >> Secondary questions include where to put the new files (presumably the >> same path as in the old archive) and what to do with >> conflicts/collisions resulting from, for example, modified files >> (presumably they're treated in the normal unison way). >> >> The interface could perhaps be a "-salvage X" flag, where X is one of >> the roots (just like -force). >> >> Best, >> -Ryan >> >> P.S. I actually wrote a separate tool at some point (in ocaml) that >> could accomplish the above which I could provide to the curious. ?Its >> goal was to replace all the files in a folder X, that exist in a >> folder Y, with symlinks into folder Y. ?One could run this on the old >> archive and then use "find" to see all the files that were not turned >> to symlinks. ?However, I think leveraging unison for this purpose >> would be much more desirable. >> _______________________________________________ >> Unison-hackers mailing list >> Unison-hackers at lists.seas.upenn.edu >> http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers > _______________________________________________ > Unison-hackers mailing list > Unison-hackers at lists.seas.upenn.edu > http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers > From bcpierce at cis.upenn.edu Tue Jan 5 15:45:05 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Tue, 5 Jan 2010 15:45:05 -0500 Subject: [Unison-hackers] Proposed mode for unison: "Salvage" In-Reply-To: References: <7211CE58-2B30-4151-AE17-BB7AD0C702A6@cis.upenn.edu> Message-ID: <3E0FC745-7E2E-44D9-9F73-C383B459852F@cis.upenn.edu> Ah, that might not be so hard, then. Maybe just a matter of extending the function Recon.overrideReconcilerChoices, using the information from the Xferhint module to see which files are duplicates. One slight complexity I can see now is that you'd need to check this locally, on the appropriate host, not just on the client. But anyway, this should get you started. - B On Jan 5, 2010, at 12:08 PM, Ryan Newton wrote: >> This would take some hacking - in particular, I'm not sure I see how >> the user interface should work (especially the graphical one). > > Perhaps there was something I've missed... but I was thinking that no > significant UI change is needed. Rather, the effect is mainly on the > scan phase (apologies if I've forgotten the correct unison terminology > -- update detection phase?). > > The effect of (1) and (2) on the user-visible interface should be only > to apply a filter over the usual "<-" entries that are presented. > That is, the changes presented would be a subset of the changes > presented if running without -salvage (namely, files duplicated in > other locations are suppressed, and changes from new->old are > suppressed). > > I was hoping this could be implemented by leaving the whole "backend" > the same and literally filtering the changset. > > If anyone else feels they need this functionality let me know. I > could hack on it > but would love to partner with someone who knows the code better. > > -Ryan > > On Tue, Jan 5, 2010 at 11:49 AM, Benjamin Pierce > wrote: >> This would take some hacking - in particular, I'm not sure I see how >> the user interface should work (especially the graphical one). But I >> agree that unison would be a decent base for building such a tool. >> >> - Benjamin >> >> On Jan 5, 2010, at 9:54 AM, Ryan Newton wrote: >> >>> The biggest complaint I hear from friends and family about unison is >>> the ease of duplicating files. This happens most often when running >>> unison without saved archives (e.g. because things get moved around, >>> mixed up, moved to new machines, etc). >>> >>> A typical scenario that is difficult to handle with unison is that >>> you >>> come across an old copy of folder X that *might* contain something >>> that you forgot to extract or move into your current, primary copy. >>> But of course you don't know what's there and checking is manually >>> hard. Further, performing a simple unison is the WRONG answer, >>> because the organization may have changed substantially, making it >>> very hard to tell if supposedly new files in the old copy are really >>> new or have just been moved (duplication danger). >>> >>> In this case it would be very useful to run unison in mode where: >>> >>> (1) only copies from old->new are considered. The goal is not two >>> identical archives, but to retrieve things from the old copy. >>> (2) only files which do not exist ANYWHERE in the new archive are >>> considered, the new archive is just a flat set of files for the >>> purpose of this check. >>> >>> Secondary questions include where to put the new files (presumably >>> the >>> same path as in the old archive) and what to do with >>> conflicts/collisions resulting from, for example, modified files >>> (presumably they're treated in the normal unison way). >>> >>> The interface could perhaps be a "-salvage X" flag, where X is one >>> of >>> the roots (just like -force). >>> >>> Best, >>> -Ryan >>> >>> P.S. I actually wrote a separate tool at some point (in ocaml) that >>> could accomplish the above which I could provide to the curious. >>> Its >>> goal was to replace all the files in a folder X, that exist in a >>> folder Y, with symlinks into folder Y. One could run this on the >>> old >>> archive and then use "find" to see all the files that were not >>> turned >>> to symlinks. However, I think leveraging unison for this purpose >>> would be much more desirable. >>> _______________________________________________ >>> Unison-hackers mailing list >>> Unison-hackers at lists.seas.upenn.edu >>> http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers >> _______________________________________________ >> Unison-hackers mailing list >> Unison-hackers at lists.seas.upenn.edu >> http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers >> > _______________________________________________ > Unison-hackers mailing list > Unison-hackers at lists.seas.upenn.edu > http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers > From vouillon at seas.upenn.edu Wed Jan 6 16:20:15 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Wed, 6 Jan 2010 16:20:15 -0500 Subject: [Unison-hackers] [unison-svn] r393 - trunk/src Message-ID: <201001062120.o06LKGDe010829@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-06 16:20:12 -0500 (Wed, 06 Jan 2010) New Revision: 393 Added: trunk/src/fpcache.ml trunk/src/fpcache.mli Modified: trunk/src/.depend trunk/src/Makefile.OCaml trunk/src/RECENTNEWS trunk/src/copy.ml trunk/src/mkProjectInfo.ml trunk/src/path.ml trunk/src/path.mli trunk/src/update.ml trunk/src/update.mli Log: * Implemented an on-disk file fingerprint cache to speed-up update detection after a crash: this way, Unison does not have do recompute all the file fingerprints from scratch. Modified: trunk/src/.depend =================================================================== --- trunk/src/.depend 2010-01-05 15:42:43 UTC (rev 392) +++ trunk/src/.depend 2010-01-06 21:20:12 UTC (rev 393) @@ -13,6 +13,7 @@ lwt/lwt.cmi common.cmi fileutil.cmi: fingerprint.cmi: uutil.cmi path.cmi fspath.cmi +fpcache.cmi: fs.cmi: system/system_intf.cmo fspath.cmi fspath.cmi: system.cmi path.cmi name.cmi globals.cmi: ubase/prefs.cmi pred.cmi path.cmi lwt/lwt.cmi common.cmi @@ -35,10 +36,10 @@ transfer.cmi: uutil.cmi lwt/lwt.cmi bytearray.cmi transport.cmi: uutil.cmi lwt/lwt.cmi common.cmi tree.cmi: +ui.cmi: uicommon.cmi: uutil.cmi ubase/prefs.cmi path.cmi lwt/lwt.cmi common.cmi +uigtk.cmi: uicommon.cmi uigtk2.cmi: uicommon.cmi -uigtk.cmi: uicommon.cmi -ui.cmi: uitext.cmi: uicommon.cmi unicode.cmi: update.cmi: uutil.cmi tree.cmi props.cmi path.cmi osx.cmi os.cmi name.cmi \ @@ -93,6 +94,12 @@ fileutil.cmx: fileutil.cmi fingerprint.cmo: uutil.cmi ubase/util.cmi fspath.cmi fs.cmi fingerprint.cmi fingerprint.cmx: uutil.cmx ubase/util.cmx fspath.cmx fs.cmx fingerprint.cmi +fpcache.cmo: uutil.cmi ubase/util.cmi ubase/trace.cmi system.cmi \ + ubase/safelist.cmi props.cmi path.cmi osx.cmi os.cmi fileinfo.cmi \ + fpcache.cmi +fpcache.cmx: uutil.cmx ubase/util.cmx ubase/trace.cmx system.cmx \ + ubase/safelist.cmx props.cmx path.cmx osx.cmx os.cmx fileinfo.cmx \ + fpcache.cmi fs.cmo: fspath.cmi fs.cmi fs.cmx: fspath.cmx fs.cmi fspath.cmo: uutil.cmi ubase/util.cmi system.cmi ubase/rx.cmi path.cmi \ @@ -105,10 +112,10 @@ globals.cmx: ubase/util.cmx ubase/trace.cmx ubase/safelist.cmx remote.cmx \ ubase/prefs.cmx pred.cmx path.cmx os.cmx name.cmx lwt/lwt_util.cmx \ lwt/lwt_unix.cmx lwt/lwt.cmx common.cmx clroot.cmx globals.cmi +linkgtk.cmo: uigtk.cmi main.cmo +linkgtk.cmx: uigtk.cmx main.cmx linkgtk2.cmo: uigtk2.cmi main.cmo linkgtk2.cmx: uigtk2.cmx main.cmx -linkgtk.cmo: uigtk.cmi main.cmo -linkgtk.cmx: uigtk.cmx main.cmx linktext.cmo: uitext.cmi main.cmo linktext.cmx: uitext.cmx main.cmx lock.cmo: ubase/util.cmi system.cmi lock.cmi @@ -213,6 +220,16 @@ recon.cmx props.cmx ubase/prefs.cmx path.cmx osx.cmx os.cmx name.cmx \ lwt/lwt_unix.cmx lwt/lwt.cmx globals.cmx fspath.cmx files.cmx \ fileinfo.cmx common.cmx clroot.cmx case.cmx uicommon.cmi +uigtk.cmo: uutil.cmi ubase/util.cmi update.cmi uitext.cmi uicommon.cmi \ + transport.cmi ubase/trace.cmi system.cmi strings.cmi sortri.cmi \ + ubase/safelist.cmi remote.cmi recon.cmi ubase/prefs.cmi pixmaps.cmo \ + path.cmi os.cmi lwt/lwt_util.cmi lwt/lwt_unix.cmi lwt/lwt.cmi globals.cmi \ + files.cmi common.cmi clroot.cmi uigtk.cmi +uigtk.cmx: uutil.cmx ubase/util.cmx update.cmx uitext.cmx uicommon.cmx \ + transport.cmx ubase/trace.cmx system.cmx strings.cmx sortri.cmx \ + ubase/safelist.cmx remote.cmx recon.cmx ubase/prefs.cmx pixmaps.cmx \ + path.cmx os.cmx lwt/lwt_util.cmx lwt/lwt_unix.cmx lwt/lwt.cmx globals.cmx \ + files.cmx common.cmx clroot.cmx uigtk.cmi uigtk2.cmo: uutil.cmi ubase/util.cmi update.cmi unicode.cmi uitext.cmi \ uicommon.cmi transport.cmi ubase/trace.cmi system.cmi strings.cmi \ sortri.cmi ubase/safelist.cmi remote.cmi recon.cmi ubase/prefs.cmi \ @@ -223,16 +240,6 @@ sortri.cmx ubase/safelist.cmx remote.cmx recon.cmx ubase/prefs.cmx \ pixmaps.cmx path.cmx os.cmx lwt/lwt_util.cmx lwt/lwt_unix.cmx lwt/lwt.cmx \ globals.cmx files.cmx common.cmx clroot.cmx case.cmx uigtk2.cmi -uigtk.cmo: uutil.cmi ubase/util.cmi update.cmi uitext.cmi uicommon.cmi \ - transport.cmi ubase/trace.cmi system.cmi strings.cmi sortri.cmi \ - ubase/safelist.cmi remote.cmi recon.cmi ubase/prefs.cmi pixmaps.cmo \ - path.cmi os.cmi lwt/lwt_util.cmi lwt/lwt_unix.cmi lwt/lwt.cmi globals.cmi \ - files.cmi common.cmi clroot.cmi uigtk.cmi -uigtk.cmx: uutil.cmx ubase/util.cmx update.cmx uitext.cmx uicommon.cmx \ - transport.cmx ubase/trace.cmx system.cmx strings.cmx sortri.cmx \ - ubase/safelist.cmx remote.cmx recon.cmx ubase/prefs.cmx pixmaps.cmx \ - path.cmx os.cmx lwt/lwt_util.cmx lwt/lwt_unix.cmx lwt/lwt.cmx globals.cmx \ - files.cmx common.cmx clroot.cmx uigtk.cmi uimacbridge.cmo: xferhint.cmi uutil.cmi ubase/util.cmi update.cmi \ uicommon.cmi transport.cmi ubase/trace.cmi terminal.cmi system.cmi \ stasher.cmi ubase/safelist.cmi remote.cmi recon.cmi ubase/prefs.cmi \ @@ -271,12 +278,12 @@ system.cmi ubase/safelist.cmi remote.cmi props.cmi ubase/proplist.cmi \ ubase/prefs.cmi pred.cmi path.cmi osx.cmi os.cmi name.cmi ubase/myMap.cmi \ lwt/lwt_unix.cmi lwt/lwt.cmi lock.cmi globals.cmi fspath.cmi fs.cmi \ - fingerprint.cmi fileinfo.cmi common.cmi case.cmi update.cmi + fpcache.cmi fingerprint.cmi fileinfo.cmi common.cmi case.cmi update.cmi update.cmx: xferhint.cmx uutil.cmx ubase/util.cmx tree.cmx ubase/trace.cmx \ system.cmx ubase/safelist.cmx remote.cmx props.cmx ubase/proplist.cmx \ ubase/prefs.cmx pred.cmx path.cmx osx.cmx os.cmx name.cmx ubase/myMap.cmx \ lwt/lwt_unix.cmx lwt/lwt.cmx lock.cmx globals.cmx fspath.cmx fs.cmx \ - fingerprint.cmx fileinfo.cmx common.cmx case.cmx update.cmi + fpcache.cmx fingerprint.cmx fileinfo.cmx common.cmx case.cmx update.cmi uutil.cmo: ubase/util.cmi ubase/trace.cmi ubase/projectInfo.cmo uutil.cmi uutil.cmx: ubase/util.cmx ubase/trace.cmx ubase/projectInfo.cmx uutil.cmi xferhint.cmo: ubase/util.cmi ubase/trace.cmi ubase/prefs.cmi path.cmi os.cmi \ Modified: trunk/src/Makefile.OCaml =================================================================== --- trunk/src/Makefile.OCaml 2010-01-05 15:42:43 UTC (rev 392) +++ trunk/src/Makefile.OCaml 2010-01-06 21:20:12 UTC (rev 393) @@ -214,7 +214,7 @@ props.cmo fileinfo.cmo os.cmo lock.cmo clroot.cmo common.cmo \ tree.cmo checksum.cmo terminal.cmo \ transfer.cmo xferhint.cmo remote.cmo globals.cmo \ - update.cmo copy.cmo stasher.cmo \ + fpcache.cmo update.cmo copy.cmo stasher.cmo \ files.cmo sortri.cmo recon.cmo transport.cmo \ strings.cmo uicommon.cmo uitext.cmo test.cmo Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-05 15:42:43 UTC (rev 392) +++ trunk/src/RECENTNEWS 2010-01-06 21:20:12 UTC (rev 393) @@ -1,3 +1,10 @@ +CHANGES FROM VERSION 2.38.5 + +* Implemented an on-disk file fingerprint cache to speed-up update + detection after a crash: this way, Unison does not have do recompute + all the file fingerprints from scratch. + +------------------------------- CHANGES FROM VERSION 2.38.0 * Roll back a previous "fix" for a permission-setting issue and Modified: trunk/src/copy.ml =================================================================== --- trunk/src/copy.ml 2010-01-05 15:42:43 UTC (rev 392) +++ trunk/src/copy.ml 2010-01-06 21:20:12 UTC (rev 393) @@ -55,7 +55,7 @@ let dataClearlyUnchanged = not clearlyModified && Props.same_time info.Fileinfo.desc archDesc - && not (Update.excelFile pathFrom) + && not (Fpcache.excelFile pathFrom) && match archStamp with Some (Fileinfo.InodeStamp inode) -> info.Fileinfo.inode = inode | Some (Fileinfo.CtimeStamp ctime) -> true Added: trunk/src/fpcache.ml =================================================================== --- trunk/src/fpcache.ml (rev 0) +++ trunk/src/fpcache.ml 2010-01-06 21:20:12 UTC (rev 393) @@ -0,0 +1,251 @@ +(* Unison file synchronizer: src/fpcache.ml *) +(* Copyright 1999-2010, Benjamin C. Pierce + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*) + +let debug = Trace.debug "fpcache" + +(* In-memory cache *) + +module PathTbl = + Hashtbl.Make + (struct + type t = string + let equal (s1 : string) (s2 : string) = s1 = s2 + let hash = Hashtbl.hash + end) + +let tbl = PathTbl.create 101 + +(* Information for writing to the on-disk cache *) + +type entry = int * string * (Fileinfo.t * Os.fullfingerprint) + +type state = + { oc : out_channel; + mutable count : int; + mutable size : Uutil.Filesize.t; + mutable last : string; + mutable queue : entry list } + +let state = ref None + +(****) + +(* Path compression and decompression (use delta from previous path for + compression) *) + +let decompress st i path = + let l = String.length path in + let s = String.create (l + i) in + String.blit !st 0 s 0 i; + String.blit path 0 s i l; + st := s; + s + +let compress state path = + let s = state.last in + let p = Path.toString path in + let l = String.length s in + let i = ref 0 in + while !i < l && p.[!i] = s.[!i] do incr i done; + state.last <- p; + (!i, String.sub p !i (String.length p - !i)) + +(*****) + +(* Read and write a chunk of file fingerprints from the cache *) + +let read st ic = + (* I/O errors are dealt with at a higher level *) + let fp1 = Digest.input ic in + let fp2 = Digest.input ic in + let headerSize = Marshal.header_size in + let header = String.create headerSize in + really_input ic header 0 headerSize; + if fp1 <> Digest.string header then begin + debug (fun () -> Util.msg "bad header checksum\n"); + raise End_of_file + end; + let dataSize = Marshal.data_size header 0 in + let s = String.create (headerSize + dataSize) in + String.blit header 0 s 0 headerSize; + really_input ic s headerSize dataSize; + if fp2 <> Digest.string s then begin + debug (fun () -> Util.msg "bad chunk checksum\n"); + raise End_of_file + end; + let q : entry list = Marshal.from_string s 0 in + debug (fun () -> Util.msg "read chunk of %d files\n" (List.length q)); + List.iter (fun (l, p, i) -> PathTbl.add tbl (decompress st l p) i) q + +let closeOut st = + state := None; + try + close_out st.oc + with Sys_error error -> + debug (fun () -> Util.msg "error in closing cache file: %s\n" error) + +let write state = + let q = Safelist.rev state.queue in + let s = Marshal.to_string q [Marshal.No_sharing] in + let fp1 = Digest.substring s 0 Marshal.header_size in + let fp2 = Digest.string s in + begin try + Digest.output state.oc fp1; Digest.output state.oc fp2; + output_string state.oc s; flush state.oc + with Sys_error error -> + debug (fun () -> Util.msg "error in writing to cache file: %s\n" error); + closeOut state + end; + state.count <- 0; + state.size <- Uutil.Filesize.zero; + state.queue <- [] + +(****) + +(* Start and finish dealing with the cache *) + +let finish () = + PathTbl.clear tbl; + match !state with + Some st -> if st.queue <> [] then write st; + closeOut st + | None -> () + +let magic = "Unison fingerprint cache format 1" + +let init fastCheck fspath = + finish (); + if fastCheck then begin + begin try + let ic = System.open_in_bin fspath in + begin try + let header = input_line ic in + if header <> magic then raise (Sys_error "wrong header"); + let st = ref "" in + while true do read st ic done + with + Sys_error error -> + debug (fun () -> Util.msg "error in loading cache file %s: %s\n" + (System.fspathToDebugString fspath) error) + | End_of_file -> + () + end; + begin try + close_in ic + with Sys_error error -> + debug (fun () -> Util.msg "error in closing cache file %s: %s\n" + (System.fspathToDebugString fspath) error) + end; + with Sys_error error -> + debug (fun () -> Util.msg "could not open cache file %s: %s\n" + (System.fspathToDebugString fspath) error) + end; + begin try + debug (fun () -> Util.msg "opening cache file %s for output\n" + (System.fspathToDebugString fspath)); + let oc = + System.open_out_gen + [Open_wronly; Open_creat; Open_trunc; Open_binary] 0o600 fspath in + output_string oc magic; output_string oc "\n"; flush oc; + state := + Some { oc = oc; count = 0; size = Uutil.Filesize.zero; + last = ""; queue = [] } + with Sys_error error -> + debug (fun () -> Util.msg "could not open cache file %s: %s\n" + (System.fspathToDebugString fspath) error) + end + end + +(****) + +(* Enqueue a fingerprint to be written to disk. *) + +let maxCount = 5000 +let maxSize = Uutil.Filesize.ofInt (100 * 1024 * 1024) + +let save path res = + match !state with + None -> + () + | Some state -> + let (info, _) = res in + let l = Props.length info.Fileinfo.desc in + state.size <- Uutil.Filesize.add state.size l; + state.count <- state.count + 1; + let (l, s) = compress state path in + state.queue <- (l, s, res) :: state.queue; + if state.count > maxCount || state.size > maxSize then write state + +(****) + +(* Check whether a fingerprint is in the in-memory cache and store it + to the on-disk cache in any case. *) + +(* HACK: we disable fastcheck for Excel (and MPP) files, as Excel + sometimes modifies a file without updating the time stamp. *) +let excelFile path = + let s = Path.toString path in + Util.endswith s ".xls" + || Util.endswith s ".mpp" + +let dataClearlyUnchanged fastCheck path info desc stamp = + fastCheck + && + Props.same_time info.Fileinfo.desc desc + && + Props.length info.Fileinfo.desc = Props.length desc + && + not (excelFile path) + && + match stamp with + Fileinfo.InodeStamp inode -> + info.Fileinfo.inode = inode + | Fileinfo.CtimeStamp ctime -> + (* BCP [Apr 07]: This doesn't work -- ctimes are unreliable + under windows. :-( + info.Fileinfo.ctime = ctime *) + true + +let ressClearlyUnchanged fastCheck info ress dataClearlyUnchanged = + fastCheck + && + Osx.ressUnchanged ress info.Fileinfo.osX.Osx.ressInfo + None dataClearlyUnchanged + +let clearlyUnchanged fastCheck path newInfo oldInfo = + let du = + dataClearlyUnchanged fastCheck path newInfo + oldInfo.Fileinfo.desc (Fileinfo.stamp oldInfo) + in + du && ressClearlyUnchanged fastCheck newInfo (Fileinfo.ressStamp oldInfo) du + +let fingerprint fastCheck currfspath path info optDig = + let res = + try + let (oldInfo, _) as res = PathTbl.find tbl (Path.toString path) in + if not (clearlyUnchanged fastCheck path info oldInfo) then + raise Not_found; + debug (fun () -> Util.msg "cache hit for path %s\n" + (Path.toDebugString path)); + res + with Not_found -> + debug (fun () -> Util.msg "cache miss for path %s\n" + (Path.toDebugString path)); + Os.safeFingerprint currfspath path info optDig + in + save path res; + res Added: trunk/src/fpcache.mli =================================================================== --- trunk/src/fpcache.mli (rev 0) +++ trunk/src/fpcache.mli 2010-01-06 21:20:12 UTC (rev 393) @@ -0,0 +1,20 @@ +(* Unison file synchronizer: src/fpcache.mli *) +(* Copyright 1999-2010, Benjamin C. Pierce (see COPYING for details) *) + +(* Initialize the cache *) +val init : bool -> System.fspath -> unit + +(* Close the cache file and clear the in-memory cache *) +val finish : unit -> unit + +(* Get the fingerprint of a file, possibly from the cache *) +val fingerprint : + bool -> Fspath.t -> Path.local -> Fileinfo.t -> Os.fullfingerprint option -> + Fileinfo.t * Os.fullfingerprint + +val dataClearlyUnchanged : + bool -> Path.local -> Fileinfo.t -> Props.t -> Fileinfo.stamp -> bool +val ressClearlyUnchanged : + bool -> Fileinfo.t -> 'a Osx.ressInfo -> bool -> bool +(* Is that a file for which fast checking is disabled? *) +val excelFile : Path.local -> bool Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-05 15:42:43 UTC (rev 392) +++ trunk/src/mkProjectInfo.ml 2010-01-06 21:20:12 UTC (rev 393) @@ -65,7 +65,7 @@ Str.matched_group 1 str;; let extract_int re str = int_of_string (extract_str re str);; -let revisionString = "$Rev: 388$";; +let revisionString = "$Rev: 393$";; let pointVersion = if String.length revisionString > 5 then Scanf.sscanf revisionString "$Rev: %d " (fun x -> x) - pointVersionOrigin else (* Determining the pointVersionOrigin in bzr is kind of tricky: @@ -87,7 +87,3 @@ Printf.printf "VERSION=%d.%d.%d\n" majorVersion minorVersion pointVersion;; Printf.printf "NAME=%s\n" projectName;; - - - - Modified: trunk/src/path.ml =================================================================== --- trunk/src/path.ml 2010-01-05 15:42:43 UTC (rev 392) +++ trunk/src/path.ml 2010-01-06 21:20:12 UTC (rev 393) @@ -192,6 +192,7 @@ (* No need to perform case normalization on local paths *) let hash p = Hashtbl.hash p +let equal (p1 : local) (p2 : local) = p1 = p2 (* Pref controlling whether symlinks are followed. *) let followPred = Pred.create "follow" Modified: trunk/src/path.mli =================================================================== --- trunk/src/path.mli 2010-01-05 15:42:43 UTC (rev 392) +++ trunk/src/path.mli 2010-01-06 21:20:12 UTC (rev 393) @@ -31,6 +31,7 @@ val addPrefixToFinalName : local -> string -> local val compare : t -> t -> int +val equal : local -> local -> bool val hash : local -> int val followLink : local -> bool Modified: trunk/src/update.ml =================================================================== --- trunk/src/update.ml 2010-01-05 15:42:43 UTC (rev 392) +++ trunk/src/update.ml 2010-01-06 21:20:12 UTC (rev 393) @@ -165,7 +165,7 @@ (* ----- *) (* The status of an archive *) -type archiveVersion = MainArch | NewArch | ScratchArch | Lock +type archiveVersion = MainArch | NewArch | ScratchArch | Lock | FPCache let showArchiveName = Prefs.createBool "showarchive" false @@ -201,7 +201,8 @@ let archiveName fspath (v: archiveVersion): string * string = let n = archiveHash fspath in let temp = match v with - MainArch -> "ar" | NewArch -> "tm" | ScratchArch -> "sc" | Lock -> "lk" + MainArch -> "ar" | NewArch -> "tm" | ScratchArch -> "sc" + | Lock -> "lk" | FPCache -> "fp" in (Printf.sprintf "%s%s" temp n, thisRootsGlobalName fspath) @@ -1191,13 +1192,6 @@ oldInfoOf archive) end -(* HACK: we disable fastcheck for Excel (and MPP) files on Windows, as Excel - sometimes modifies a file without updating the time stamp. *) -let excelFile path = - let s = Path.toString path in - Util.endswith s ".xls" - || Util.endswith s ".mpp" - (* Check whether a file has changed has changed, by comparing its digest and properties against [archDesc], [archDig], and [archStamp]. Returns a pair (optArch, ui) where [optArch] is *not* None when the file remains @@ -1226,27 +1220,10 @@ (Uutil.Filesize.toString (Props.length info.Fileinfo.desc)); Util.msg "\n"); let dataClearlyUnchanged = - fastCheck - && - Props.same_time info.Fileinfo.desc archDesc - && - Props.length info.Fileinfo.desc = Props.length archDesc - && - not (excelFile path) - && - match archStamp with - Fileinfo.InodeStamp inode -> - info.Fileinfo.inode = inode - | Fileinfo.CtimeStamp ctime -> - (* BCP [Apr 07]: This doesn't work -- ctimes are unreliable - under windows. :-( - info.Fileinfo.ctime = ctime *) - true in + Fpcache.dataClearlyUnchanged fastCheck path info archDesc archStamp in let ressClearlyUnchanged = - fastCheck - && - Osx.ressUnchanged archRess info.Fileinfo.osX.Osx.ressInfo - None dataClearlyUnchanged in + Fpcache.ressClearlyUnchanged fastCheck info archRess dataClearlyUnchanged + in if dataClearlyUnchanged && ressClearlyUnchanged then begin Xferhint.insertEntry currfspath path archDig; None, checkPropChange info archive archDesc @@ -1254,7 +1231,7 @@ debugverbose (fun() -> Util.msg " Double-check possibly updated file\n"); showStatusAddLength info; let (info, newDigest) = - Os.safeFingerprint currfspath path info + Fpcache.fingerprint fastCheck currfspath path info (if dataClearlyUnchanged then Some archDig else None) in Xferhint.insertEntry currfspath path newDigest; debug (fun() -> Util.msg " archive digest = %s current digest = %s\n" @@ -1492,7 +1469,9 @@ None, begin showStatusAddLength info; - let (info, dig) = Os.safeFingerprint currfspath path info None in + let (info, dig) = + Fpcache.fingerprint + fastCheckInfos.fastCheck currfspath path info None in Xferhint.insertEntry currfspath path dig; Updates (File (info.Fileinfo.desc, ContentsUpdated (dig, Fileinfo.stamp info, @@ -1582,8 +1561,12 @@ dirFastCheck = useFastChecking () && Util.osType = `Unix; dirStamp = dirStamp } in + let (cacheFilename, _) = archiveName fspath FPCache in + let cacheFile = Os.fileInUnisonDir cacheFilename in + Fpcache.init fastCheckInfos.fastCheck cacheFile; let (arch, ui) = buildUpdateRec archive fspath here fastCheckInfos in + Fpcache.finish (); (begin match arch with None -> archive | Some arch -> arch @@ -2067,7 +2050,7 @@ && Props.length desc = Props.length oldDesc && - not (excelFile path) + not (Fpcache.excelFile path) && Osx.ressUnchanged oldRess ress None true Modified: trunk/src/update.mli =================================================================== --- trunk/src/update.mli 2010-01-05 15:42:43 UTC (rev 392) +++ trunk/src/update.mli 2010-01-06 21:20:12 UTC (rev 393) @@ -64,9 +64,6 @@ (* Are we checking fast, or carefully? *) val useFastChecking : unit -> bool -(* Is that a file for which fast checking is disabled? *) -val excelFile : Path.local -> bool - (* Print the archive to the current formatter (see Format) *) val showArchive: archive -> unit From vouillon at seas.upenn.edu Wed Jan 6 17:13:20 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Wed, 6 Jan 2010 17:13:20 -0500 Subject: [Unison-hackers] [unison-svn] r394 - trunk/src Message-ID: <201001062213.o06MDLsB011501@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-06 17:13:17 -0500 (Wed, 06 Jan 2010) New Revision: 394 Modified: trunk/src/RECENTNEWS trunk/src/mkProjectInfo.ml trunk/src/uigtk2.ml Log: * GTK UI: - do not reposition the file list when updating the detail view anymore - added Ctrl+Q as a shortcut for quitting besides just the key Q Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-06 21:20:12 UTC (rev 393) +++ trunk/src/RECENTNEWS 2010-01-06 22:13:17 UTC (rev 394) @@ -1,5 +1,13 @@ CHANGES FROM VERSION 2.38.5 +* GTK UI: + - do not reposition the file list when updating the detail view + anymore + - added Ctrl+Q as a shortcut for quitting besides just the key Q + +------------------------------- +CHANGES FROM VERSION 2.38.5 + * Implemented an on-disk file fingerprint cache to speed-up update detection after a crash: this way, Unison does not have do recompute all the file fingerprints from scratch. Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-06 21:20:12 UTC (rev 393) +++ trunk/src/mkProjectInfo.ml 2010-01-06 22:13:17 UTC (rev 394) @@ -87,3 +87,4 @@ Printf.printf "VERSION=%d.%d.%d\n" majorVersion minorVersion pointVersion;; Printf.printf "NAME=%s\n" projectName;; + Modified: trunk/src/uigtk2.ml =================================================================== --- trunk/src/uigtk2.ml 2010-01-06 21:20:12 UTC (rev 393) +++ trunk/src/uigtk2.ml 2010-01-06 22:13:17 UTC (rev 394) @@ -2926,7 +2926,7 @@ None -> detailsWindow#buffer#set_text "" | Some row -> - makeRowVisible row; +(* makeRowVisible row;*) let (formated, details) = match !theState.(row).whatHappened with | Some(Util.Failed(s), _) -> @@ -3010,6 +3010,7 @@ mainWindow#unselect_all (); mainWindow#select i 0; delayUpdates := false; + makeRowVisible i; updateDetails () end in @@ -4117,10 +4118,13 @@ ~callback:(fun _ -> statWin#show ()) "Show _Statistics"); ignore (fileMenu#add_separator ()); - ignore (fileMenu#add_image_item - ~key:GdkKeysyms._q ~callback:safeExit - ~image:((GMisc.image ~stock:`QUIT ~icon_size:`MENU ())#coerce) - "_Quit"); + let quit = + fileMenu#add_image_item + ~key:GdkKeysyms._q ~callback:safeExit + ~image:((GMisc.image ~stock:`QUIT ~icon_size:`MENU ())#coerce) + "_Quit" + in + quit#add_accelerator ~group:accel_group ~modi:[`CONTROL] GdkKeysyms._q; (********************************************************************* Expert menu From vouillon at seas.upenn.edu Thu Jan 7 12:52:05 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Thu, 7 Jan 2010 12:52:05 -0500 Subject: [Unison-hackers] [unison-svn] r395 - trunk/src Message-ID: <201001071752.o07Hq52T030472@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-07 12:52:04 -0500 (Thu, 07 Jan 2010) New Revision: 395 Modified: trunk/src/RECENTNEWS trunk/src/fpcache.ml trunk/src/mkProjectInfo.ml trunk/src/update.ml Log: * Fix the fingerprint cache so that it works also with multiple paths Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-06 22:13:17 UTC (rev 394) +++ trunk/src/RECENTNEWS 2010-01-07 17:52:04 UTC (rev 395) @@ -1,5 +1,10 @@ CHANGES FROM VERSION 2.38.5 +* Fix the fingerprint cache so that it works also with multiple paths + +------------------------------- +CHANGES FROM VERSION 2.38.5 + * GTK UI: - do not reposition the file list when updating the detail view anymore Modified: trunk/src/fpcache.ml =================================================================== --- trunk/src/fpcache.ml 2010-01-06 22:13:17 UTC (rev 394) +++ trunk/src/fpcache.ml 2010-01-07 17:52:04 UTC (rev 395) @@ -131,6 +131,8 @@ finish (); if fastCheck then begin begin try + debug (fun () -> Util.msg "opening cache file %s for input\n" + (System.fspathToDebugString fspath)); let ic = System.open_in_bin fspath in begin try let header = input_line ic in @@ -243,8 +245,9 @@ (Path.toDebugString path)); res with Not_found -> - debug (fun () -> Util.msg "cache miss for path %s\n" - (Path.toDebugString path)); + if fastCheck then + debug (fun () -> Util.msg "cache miss for path %s\n" + (Path.toDebugString path)); Os.safeFingerprint currfspath path info optDig in save path res; Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-06 22:13:17 UTC (rev 394) +++ trunk/src/mkProjectInfo.ml 2010-01-07 17:52:04 UTC (rev 395) @@ -88,3 +88,4 @@ Printf.printf "NAME=%s\n" projectName;; + Modified: trunk/src/update.ml =================================================================== --- trunk/src/update.ml 2010-01-06 22:13:17 UTC (rev 394) +++ trunk/src/update.ml 2010-01-07 17:52:04 UTC (rev 395) @@ -1549,24 +1549,12 @@ contents. The directory permissions along the path are also collected, in case we need to build the directory hierarchy on one side. *) -let rec buildUpdate archive fspath fullpath here path dirStamp = +let rec buildUpdate archive fspath fullpath here path dirStamp fastCheckInfos = match Path.deconstruct path with None -> showStatus here; - let fastCheckInfos = - { fastCheck = useFastChecking (); - (* Directory optimization is disabled under Windows, - as Windows does not update directory modification times - on FAT filesystems. *) - dirFastCheck = useFastChecking () && Util.osType = `Unix; - dirStamp = dirStamp } - in - let (cacheFilename, _) = archiveName fspath FPCache in - let cacheFile = Os.fileInUnisonDir cacheFilename in - Fpcache.init fastCheckInfos.fastCheck cacheFile; let (arch, ui) = buildUpdateRec archive fspath here fastCheckInfos in - Fpcache.finish (); (begin match arch with None -> archive | Some arch -> arch @@ -1634,7 +1622,7 @@ let (arch, updates, localPath, props) = buildUpdate archChild fspath fullpath (Path.child here name') path' - dirStamp + dirStamp fastCheckInfos in let children = if arch = NoArchive then otherChildren else @@ -1647,7 +1635,7 @@ let (arch, updates, localPath, props) = buildUpdate NoArchive fspath fullpath (Path.child here name') path' - dirStamp + dirStamp fastCheckInfos in assert (arch = NoArchive); (archive, updates, localPath, @@ -1713,6 +1701,17 @@ (* let t1 = Unix.gettimeofday () in *) + let fastCheckInfos = + { fastCheck = useFastChecking (); + (* Directory optimization is disabled under Windows, + as Windows does not update directory modification times + on FAT filesystems. *) + dirFastCheck = useFastChecking () && Util.osType = `Unix; + dirStamp = dirStamp } + in + let (cacheFilename, _) = archiveName fspath FPCache in + let cacheFile = Os.fileInUnisonDir cacheFilename in + Fpcache.init fastCheckInfos.fastCheck cacheFile; let (archive, updates) = Safelist.fold_right (fun path (arch, upd) -> @@ -1720,11 +1719,13 @@ (arch, (translatePathLocal fspath path, NoUpdates, []) :: upd) else let (arch', ui, localPath, props) = - buildUpdate arch fspath path Path.empty path dirStamp + buildUpdate + arch fspath path Path.empty path dirStamp fastCheckInfos in arch', (localPath, ui, props) :: upd) pathList (archive, []) in + Fpcache.finish (); (* let t2 = Unix.gettimeofday () in Format.eprintf "Update detection: %f at ." (t2 -. t1); From vouillon at seas.upenn.edu Thu Jan 7 14:16:05 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Thu, 7 Jan 2010 14:16:05 -0500 Subject: [Unison-hackers] [unison-svn] r396 - in trunk/src: . ubase Message-ID: <201001071916.o07JG6PW032071@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-07 14:16:05 -0500 (Thu, 07 Jan 2010) New Revision: 396 Modified: trunk/src/.depend trunk/src/RECENTNEWS trunk/src/abort.ml trunk/src/case.ml trunk/src/case.mli trunk/src/copy.ml trunk/src/fileinfo.ml trunk/src/mkProjectInfo.ml trunk/src/osx.ml trunk/src/ubase/prefs.ml trunk/src/ubase/prefs.mli trunk/src/uicommon.ml trunk/src/uicommon.mli trunk/src/uigtk2.ml trunk/src/update.ml Log: * Bumped version number: incompatible protocol changes * Resume copy of partially transferred files. * Unicode mode is now the default when one of the hosts is under Windows or MacOS. This may make upgrades a bit more painful (the archives cannot be reused), but this is a much saner default. * Fastcheck is now the default under Windows. People mostly use NTFS nowadays and the Unicode API provides an equivalent to inode numbers for this filesystem. * Unison now fails if in unicode case-insensitive mode but the archive mode is not known (this means that we are upgrading from an older version which did not support this mode) * Changed the type of trivalued preferences (true/false/default) to an enumerated type * Removed the "reusewindows" preference, which was not used anymore. * GTK UI: do not reposition the file list on focus change Modified: trunk/src/.depend =================================================================== --- trunk/src/.depend 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/.depend 2010-01-07 19:16:05 UTC (rev 396) @@ -13,7 +13,8 @@ lwt/lwt.cmi common.cmi fileutil.cmi: fingerprint.cmi: uutil.cmi path.cmi fspath.cmi -fpcache.cmi: +fpcache.cmi: system.cmi props.cmi path.cmi osx.cmi os.cmi fspath.cmi \ + fileinfo.cmi fs.cmi: system/system_intf.cmo fspath.cmi fspath.cmi: system.cmi path.cmi name.cmi globals.cmi: ubase/prefs.cmi pred.cmi path.cmi lwt/lwt.cmi common.cmi @@ -63,13 +64,13 @@ copy.cmo: xferhint.cmi uutil.cmi ubase/util.cmi update.cmi transfer.cmi \ ubase/trace.cmi ubase/safelist.cmi remote.cmi props.cmi ubase/prefs.cmi \ path.cmi osx.cmi os.cmi lwt/lwt_util.cmi lwt/lwt.cmi globals.cmi \ - fspath.cmi fs.cmi fileinfo.cmi external.cmi common.cmi clroot.cmi \ - bytearray.cmi abort.cmi copy.cmi + fspath.cmi fs.cmi fpcache.cmi fingerprint.cmi fileinfo.cmi external.cmi \ + common.cmi clroot.cmi bytearray.cmi abort.cmi copy.cmi copy.cmx: xferhint.cmx uutil.cmx ubase/util.cmx update.cmx transfer.cmx \ ubase/trace.cmx ubase/safelist.cmx remote.cmx props.cmx ubase/prefs.cmx \ path.cmx osx.cmx os.cmx lwt/lwt_util.cmx lwt/lwt.cmx globals.cmx \ - fspath.cmx fs.cmx fileinfo.cmx external.cmx common.cmx clroot.cmx \ - bytearray.cmx abort.cmx copy.cmi + fspath.cmx fs.cmx fpcache.cmx fingerprint.cmx fileinfo.cmx external.cmx \ + common.cmx clroot.cmx bytearray.cmx abort.cmx copy.cmi external.cmo: ubase/util.cmi system.cmi ubase/safelist.cmi lwt/lwt_util.cmi \ lwt/lwt_unix.cmi lwt/lwt.cmi external.cmi external.cmx: ubase/util.cmx system.cmx ubase/safelist.cmx lwt/lwt_util.cmx \ Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/RECENTNEWS 2010-01-07 19:16:05 UTC (rev 396) @@ -1,3 +1,23 @@ +CHANGES FROM VERSION 2.39.0 + +* Bumped version number: incompatible protocol changes + +* Resume copy of partially transferred files. +* Unicode mode is now the default when one of the hosts is under + Windows or MacOS. This may make upgrades a bit more painful (the + archives cannot be reused), but this is a much saner default. +* Fastcheck is now the default under Windows. People mostly use NTFS + nowadays and the Unicode API provides an equivalent to inode numbers + for this filesystem. +* Unison now fails if in unicode case-insensitive mode but the archive + mode is not known (this means that we are upgrading from an older + version which did not support this mode) +* Changed the type of trivalued preferences (true/false/default) to an + enumerated type +* Removed the "reusewindows" preference, which was not used anymore. +* GTK UI: do not reposition the file list on focus change + +------------------------------- CHANGES FROM VERSION 2.38.5 * Fix the fingerprint cache so that it works also with multiple paths Modified: trunk/src/abort.ml =================================================================== --- trunk/src/abort.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/abort.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -25,7 +25,7 @@ "This preference controls after how many errors Unison aborts a \ directory transfer. Setting it to a large number allows Unison \ to transfer most of a directory even when some files fail to be \ - copied. The default is 1. If the preference is set to high, \ + copied. The default is 1. If the preference is set too high, \ Unison may take a long time to abort in case of repeated \ failures (for instance, when the disk is full)." Modified: trunk/src/case.ml =================================================================== --- trunk/src/case.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/case.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -51,10 +51,10 @@ "*Pseudo-preference for internal use only" "" (* Whether we default to Unicode encoding on OSX and Windows *) -let defaultToUnicode = false +let defaultToUnicode = true let useUnicode b = - let pref = Prefs.readBoolWithDefault unicode in + let pref = Prefs.read unicode in pref = `True || (defaultToUnicode && pref = `Default && b) @@ -66,8 +66,8 @@ (* server with the rest of the prefs. *) let init b = Prefs.set someHostIsInsensitive - (Prefs.readBoolWithDefault caseInsensitiveMode = `True || - (Prefs.readBoolWithDefault caseInsensitiveMode = `Default && b)); + (Prefs.read caseInsensitiveMode = `True || + (Prefs.read caseInsensitiveMode = `Default && b)); Prefs.set unicodeEncoding (useUnicode b) (****) Modified: trunk/src/case.mli =================================================================== --- trunk/src/case.mli 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/case.mli 2010-01-07 19:16:05 UTC (rev 396) @@ -4,7 +4,7 @@ val unicodeEncoding : bool Prefs.t val useUnicodeAPI : unit -> bool -type mode +type mode = Sensitive | Insensitive | UnicodeInsensitive val ops : unit -> < mode : mode; modeDesc : string; (* Current mode *) Modified: trunk/src/copy.ml =================================================================== --- trunk/src/copy.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/copy.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -117,6 +117,44 @@ let fp' = Os.fingerprint fspathTo pathTo info in fp' = fp) +(* We slice the files in 1GB chunks because that's the limit for + Fingerprint.subfile on 32 bit architectures *) +let fingerprintLimit = Uutil.Filesize.ofInt64 1072693248L + +let rec fingerprintPrefix fspath path offset len accu = + if len = Uutil.Filesize.zero then accu else begin + let l = min len fingerprintLimit in + let fp = Fingerprint.subfile (Fspath.concat fspath path) offset l in + fingerprintPrefix fspath path + (Int64.add offset (Uutil.Filesize.toInt64 l)) (Uutil.Filesize.sub len l) + (fp :: accu) + end + +let fingerprintPrefixRemotely = + Remote.registerServerCmd + "fingerprintSubfile" + (fun _ (fspath, path, len) -> + Lwt.return (fingerprintPrefix fspath path 0L len [])) + +let appendThreshold = Uutil.Filesize.ofInt (1024 * 1024) + +let validFilePrefix connFrom fspathFrom pathFrom fspathTo pathTo info desc = + let len = Props.length info.Fileinfo.desc in + if + info.Fileinfo.typ = `FILE && + len >= appendThreshold && len < Props.length desc + then begin + Lwt.try_bind + (fun () -> + fingerprintPrefixRemotely connFrom (fspathFrom, pathFrom, len)) + (fun fpFrom -> + let fpTo = fingerprintPrefix fspathTo pathTo 0L len [] in + Lwt.return (if fpFrom = fpTo then Some len else None)) + (fun _ -> + Lwt.return None) + end else + Lwt.return None + type transferStatus = Success of Fileinfo.t | Failure of string @@ -163,8 +201,14 @@ let openFileIn fspath path kind = match kind with - `DATA -> Fs.open_in_bin (Fspath.concat fspath path) - | `RESS -> Osx.openRessIn fspath path + `DATA -> + Fs.open_in_bin (Fspath.concat fspath path) + | `DATA_APPEND len -> + let ch = Fs.open_in_bin (Fspath.concat fspath path) in + LargeFile.seek_in ch (Uutil.Filesize.toInt64 len); + ch + | `RESS -> + Osx.openRessIn fspath path let openFileOut fspath path kind len = match kind with @@ -189,6 +233,13 @@ in Unix.out_channel_of_descr fd end + | `DATA_APPEND len -> + let fullpath = Fspath.concat fspath path in + let perm = 0o600 in + let ch = Fs.open_out_gen [Open_wronly; Open_binary] perm fullpath in + Fs.chmod fullpath perm; + LargeFile.seek_out ch (Uutil.Filesize.toInt64 len); + ch | `RESS -> Osx.openRessOut fspath path len @@ -336,6 +387,11 @@ "processTransferInstruction" marshalTransferInstruction processTransferInstruction +let showPrefixProgress id kind = + match kind with + `DATA_APPEND len -> Uutil.showProgress id len "r" + | _ -> () + let compress conn (biOpt, fspathFrom, pathFrom, fileKind, sizeFrom, id, file_id) = Lwt.catch @@ -344,10 +400,11 @@ (fun processTransferInstructionRemotely -> (* We abort the file transfer on error if it has not already started *) - if fileKind = `DATA then Abort.check id; + if fileKind <> `RESS then Abort.check id; let infd = openFileIn fspathFrom pathFrom fileKind in lwt_protect (fun () -> + showPrefixProgress id fileKind; let showProgress count = Uutil.showProgress id (Uutil.Filesize.ofInt count) "r" in let compr = @@ -401,8 +458,9 @@ None -> (* We abort the file transfer on error if it has not already started *) - if kind = `DATA then Abort.check id; + if kind <> `RESS then Abort.check id; let fd = openFileOut fspath path kind len in + showPrefixProgress id kind; outfd := Some fd; fd | Some fd -> @@ -441,7 +499,7 @@ Uutil.Filesize.zero | `Update (destFileDataSize, destFileRessSize) -> match fileKind with - `DATA -> destFileDataSize + `DATA | `DATA_APPEND _ -> destFileDataSize | `RESS -> destFileRessSize in let useRsync = @@ -522,16 +580,27 @@ let reallyTransferFile connFrom fspathFrom pathFrom fspathTo pathTo realPathTo - update desc fp ress id = + update desc fp ress id tempInfo = debug (fun() -> Util.msg "reallyTransferFile(%s,%s) -> (%s,%s,%s,%s)\n" (Fspath.toDebugString fspathFrom) (Path.toString pathFrom) (Fspath.toDebugString fspathTo) (Path.toString pathTo) (Path.toString realPathTo) (Props.toString desc)); - removeOldTempFile fspathTo pathTo; + validFilePrefix connFrom fspathFrom pathFrom fspathTo pathTo tempInfo desc + >>= fun prefixLen -> + begin match prefixLen with + None -> + removeOldTempFile fspathTo pathTo + | Some len -> + debug + (fun() -> + Util.msg "Keeping %s bytes previously transferred for file %s\n" + (Uutil.Filesize.toString len) (Path.toString pathFrom)) + end; (* Data fork *) transferFileContents connFrom fspathFrom pathFrom fspathTo pathTo realPathTo update - `DATA (Props.length desc) id >>= fun () -> + (match prefixLen with None -> `DATA | Some l -> `DATA_APPEND l) + (Props.length desc) id >>= fun () -> transferRessourceForkAndSetFileinfo connFrom fspathFrom pathFrom fspathTo pathTo realPathTo update desc fp ress id @@ -703,8 +772,8 @@ else Prefs.read copyprog in - let extraquotes = Prefs.readBoolWithDefault copyquoterem = `True - || ( Prefs.readBoolWithDefault copyquoterem = `Default + let extraquotes = Prefs.read copyquoterem = `True + || ( Prefs.read copyquoterem = `Default && Util.findsubstring "rsync" prog <> None) in let addquotes root s = match root with @@ -738,7 +807,8 @@ let transferFileLocal connFrom (fspathFrom, pathFrom, fspathTo, pathTo, realPathTo, update, desc, fp, ress, id) = - let (info, isTransferred) = fileIsTransferred fspathTo pathTo desc fp ress in + let (tempInfo, isTransferred) = + fileIsTransferred fspathTo pathTo desc fp ress in if isTransferred then begin (* File is already fully transferred (from some interrupted previous transfer). *) @@ -752,7 +822,7 @@ Uutil.showProgress id len "alr"; setFileinfo fspathTo pathTo realPathTo update desc; Xferhint.insertEntry fspathTo pathTo fp; - Lwt.return (`DONE (Success info, Some msg)) + Lwt.return (`DONE (Success tempInfo, Some msg)) end else registerFileTransfer pathTo fp (fun () -> @@ -769,7 +839,7 @@ else begin reallyTransferFile connFrom fspathFrom pathFrom fspathTo pathTo realPathTo - update desc fp ress id >>= fun status -> + update desc fp ress id tempInfo >>= fun status -> Xferhint.insertEntry fspathTo pathTo fp; Lwt.return (`DONE (status, None)) end) Modified: trunk/src/fileinfo.ml =================================================================== --- trunk/src/fileinfo.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/fileinfo.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -36,8 +36,8 @@ let init b = Prefs.set symlinksAllowed - (Prefs.readBoolWithDefault allowSymlinks = `True || - (Prefs.readBoolWithDefault allowSymlinks = `Default && not b)) + (Prefs.read allowSymlinks = `True || + (Prefs.read allowSymlinks = `Default && not b)) type typ = [ `ABSENT | `FILE | `DIRECTORY | `SYMLINK ] Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/mkProjectInfo.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -5,8 +5,8 @@ let projectName = "unison" let majorVersion = 2 -let minorVersion = 38 -let pointVersionOrigin = 388 (* Revision that corresponds to point version 0 *) +let minorVersion = 39 +let pointVersionOrigin = 396 (* Revision that corresponds to point version 0 *) (* Documentation: This is a program to construct a version of the form Major.Minor.Point, @@ -65,7 +65,7 @@ Str.matched_group 1 str;; let extract_int re str = int_of_string (extract_str re str);; -let revisionString = "$Rev: 393$";; +let revisionString = "$Rev: 396$";; let pointVersion = if String.length revisionString > 5 then Scanf.sscanf revisionString "$Rev: %d " (fun x -> x) - pointVersionOrigin else (* Determining the pointVersionOrigin in bzr is kind of tricky: @@ -87,5 +87,3 @@ Printf.printf "VERSION=%d.%d.%d\n" majorVersion minorVersion pointVersion;; Printf.printf "NAME=%s\n" projectName;; - - Modified: trunk/src/osx.ml =================================================================== --- trunk/src/osx.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/osx.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -45,8 +45,8 @@ let init b = Prefs.set rsrc - (Prefs.readBoolWithDefault rsrcSync = `True || - (Prefs.readBoolWithDefault rsrcSync = `Default && b)) + (Prefs.read rsrcSync = `True || + (Prefs.read rsrcSync = `Default && b)) (****) Modified: trunk/src/ubase/prefs.ml =================================================================== --- trunk/src/ubase/prefs.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/ubase/prefs.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -197,8 +197,7 @@ (fun cell -> Uarg.String (fun s -> set cell (s::(fst !cell)))) let createBoolWithDefault name ?(local=false) doc fulldoc = - createPrefInternal name `BOOLDEF local "default" doc fulldoc -(* + createPrefInternal name `BOOLDEF local `Default doc fulldoc (fun v -> [match v with `True -> "true" | `False -> "false" @@ -213,15 +212,7 @@ | _ -> `False in set cell v)) -*) - (fun v -> [v]) (fun cell -> Uarg.String (fun s -> set cell s)) -let readBoolWithDefault p = - match read p with - "yes" | "true" -> `True - | "default" | "auto" -> `Default - | _ -> `False - (*****************************************************************************) (* Command-line parsing *) (*****************************************************************************) Modified: trunk/src/ubase/prefs.mli =================================================================== --- trunk/src/ubase/prefs.mli 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/ubase/prefs.mli 2010-01-07 19:16:05 UTC (rev 396) @@ -4,8 +4,6 @@ type 'a t val read : 'a t -> 'a -(*FIX: remove this function and change the type of the preferences instead*) -val readBoolWithDefault : string t -> [ `Default | `False | `True ] val set : 'a t -> 'a -> unit val name : 'a t -> string list @@ -57,7 +55,7 @@ -> ?local:bool (* whether it is local to the client *) -> string (* documentation string *) -> string (* full (tex) documentation string *) - -> string t + -> [`True|`False|`Default] t (* -> new preference value *) exception IllegalValue of string Modified: trunk/src/uicommon.ml =================================================================== --- trunk/src/uicommon.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/uicommon.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -55,19 +55,6 @@ ("Used to set the height (in lines) of the main window in the graphical " ^ "user interface.") -(*FIX: remove this option... *) -let reuseToplevelWindows = - Prefs.createBool "reusewindows" false - "*reuse top-level windows instead of making new ones" "" -(* Not sure if this should actually be made available to users... - ("When true, causes the graphical interface to re-use top-level windows " - ^ "(e.g., the small window that says ``Connecting...'') rather than " - ^ "destroying them and creating fresh ones. ") -*) -(* For convenience: *) -let _ = Prefs.alias reuseToplevelWindows "rw" - - let expert = Prefs.createBool "expert" false "*Enable some developers-only functionality in the UI" "" Modified: trunk/src/uicommon.mli =================================================================== --- trunk/src/uicommon.mli 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/uicommon.mli 2010-01-07 19:16:05 UTC (rev 396) @@ -19,9 +19,6 @@ (* User preference: How tall to make the main window in the GTK ui *) val mainWindowHeight : int Prefs.t -(* User preference: Should we reuse top-level windows as much as possible? *) -val reuseToplevelWindows : bool Prefs.t - (* User preference: Expert mode *) val expert : bool Prefs.t Modified: trunk/src/uigtk2.ml =================================================================== --- trunk/src/uigtk2.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/uigtk2.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -1467,14 +1467,16 @@ modification time. Nowadays, FAT is rarely used on working partitions. In most cases, we should be in Unicode mode. Thus, it seems sensible to always enable fastcheck. *) +(* let fastcheck = isLocal >> not >> (fun b -> b || Util.osType = `Win32) in +*) (* Unicode mode can be problematic when the source machine is under Windows and the remote machine is not, as Unison may have already been used using the legacy Latin 1 encoding. Cygwin also did not handle Unicode before version 1.7. *) let vb = GPack.vbox ~spacing:6 ~packing:(options#pack ~expand:false) () in - let askUnicode = - isLocal >> not >> fun b -> (b || Util.isCygwin) && Util.osType = `Win32 in + let askUnicode = React.const false in +(* isLocal >> not >> fun b -> (b || Util.isCygwin) && Util.osType = `Win32 in*) GtkReact.show vb askUnicode; adjustSize (GMisc.label ~xalign:0. ~line_wrap:true ~justify:`LEFT @@ -1505,9 +1507,11 @@ ignore (GButton.radio_button ~label:"_Latin 1" ~use_mnemonic:true ~group:unicodeButton#group ~packing:(hb#add) ()); +(* let unicode = React.lift2 (||) (askUnicode >> not) (GtkReact.toggle_button unicodeButton) in +*) let p = assistant#append_page ~title:"Specific Options" ~complete:true @@ -1560,12 +1564,15 @@ Printf.fprintf ch "root = %s\n" (Clroot.clroot2string secondRoot); if React.state compress && React.state kind = `SSH then Printf.fprintf ch "sshargs = -C\n"; +(* if React.state fastcheck then Printf.fprintf ch "fastcheck = true\n"; if React.state unicode then Printf.fprintf ch "unicode = true\n"; +*) if React.state fat then begin Printf.fprintf ch "ignorecase = true\n"; + Printf.fprintf ch "unicode = true\n"; Printf.fprintf ch "ignoreinodenumbers = true\n"; Printf.fprintf ch "links = false\n"; Printf.fprintf ch "perms = 0o200\n" @@ -2467,8 +2474,10 @@ GButton.button ~stock:`EDIT ~packing:(vb#pack ~expand:false) () in ignore (editButton#connect#clicked ~callback:(fun () -> match React.state selInfo with - None -> () - | Some ((p, _), _) -> editProfile t p)); + None -> + () + | Some ((p, _), _) -> + editProfile t p; fillLst (Some p))); GtkReact.set_sensitive editButton hasSel; let deleteProfile () = match React.state selInfo with @@ -2994,12 +3003,14 @@ let delayUpdates = ref false in let hasFocus = ref false in - let select i = + let select i scroll = if !hasFocus then begin (* If we have the focus, we move the focus row directely *) - let r = mainWindow#rows in - let p = if r < 2 then 0. else (float i +. 0.5) /. float (r - 1) in - mainWindow#scroll_vertical `JUMP (min p 1.); + if scroll then begin + let r = mainWindow#rows in + let p = if r < 2 then 0. else (float i +. 0.5) /. float (r - 1) in + mainWindow#scroll_vertical `JUMP (min p 1.) + end; if IntSet.is_empty !current then mainWindow#select i 0 end else begin (* If we don't have the focus, we just move the selection. @@ -3010,7 +3021,7 @@ mainWindow#unselect_all (); mainWindow#select i 0; delayUpdates := false; - makeRowVisible i; + if scroll then makeRowVisible i; updateDetails () end in @@ -3021,7 +3032,7 @@ otherwise the focus row is not drawn correctly. *) ignore (GMain.Idle.add (fun () -> begin match currentRow () with - Some i -> select i + Some i -> select i false | None -> () end; false)); @@ -3046,7 +3057,7 @@ match !theState.(i).ri.replicas with Different {direction = dir} when not (Prefs.read Uicommon.auto) || dir = Conflict -> - select i + select i true | _ -> loop (i + 1) in loop start in @@ -3149,7 +3160,7 @@ (match savedCurrent with None->"None" | Some(i) -> string_of_int i)); begin match savedCurrent with None -> selectSomethingIfPossible () - | Some idx -> select idx + | Some idx -> select idx true end; mainWindow#thaw (); updateDetails (); (* Do we need this line? *) Modified: trunk/src/update.ml =================================================================== --- trunk/src/update.ml 2010-01-07 17:52:04 UTC (rev 395) +++ trunk/src/update.ml 2010-01-07 19:16:05 UTC (rev 396) @@ -276,37 +276,44 @@ System.file_exists (Os.fileInUnisonDir name))) let checkArchiveCaseSensitivity l = + let error curMode archMode = + (* We cannot compute the archive name locally as it + currently depends on the os type *) + Globals.allRootsMap + (fun r -> archiveNameOnRoot r MainArch) >>= fun names -> + let l = + List.map + (fun (name, host, _) -> + Format.sprintf " archive %s on host %s" name host) + names + in + Lwt.fail + (Util.Fatal + (String.concat "\n" + ("Warning: incompatible case sensitivity settings." :: + Format.sprintf "Unison is currently in %s mode," curMode :: + Format.sprintf + "while the archives were created in %s mode." archMode :: + "You should either change Unison's setup or delete " :: + "the following archives from the .unison directories:" :: + l @ + ["Then, try again."]))) + in match l with - Some (_, magic) :: _ -> + Some (_, magic) :: _ when magic <> "" -> begin try let archMode = String.sub magic 0 (String.index magic '\000') in let curMode = (Case.ops ())#modeDesc in - if curMode <> archMode then begin - (* We cannot compute the archive name locally as it - currently depends on the os type *) - Globals.allRootsMap - (fun r -> archiveNameOnRoot r MainArch) >>= fun names -> - let l = - List.map - (fun (name, host, _) -> - Format.sprintf " archive %s on host %s" name host) - names - in - Lwt.fail - (Util.Fatal - (String.concat "\n" - ("Warning: incompatible case sensitivity settings." :: - Format.sprintf "Unison is currently in %s mode," curMode :: - Format.sprintf - "while the archives assume %s mode." archMode :: - "You should either change Unison's setup " :: - "or delete the following archives:" :: - l @ - ["Then, try again."]))) + if curMode <> archMode then + error curMode archMode + else + Lwt.return () + with Not_found -> + if (Case.ops ())#mode = Case.UnicodeInsensitive then begin + let curMode = (Case.ops ())#modeDesc in + error curMode "some non-Unicode" end else Lwt.return () - with Not_found -> - Lwt.return () end | _ -> Lwt.return () @@ -1026,8 +1033,8 @@ \\sectionref{fastcheck}{Fast Checking} for more information.") let useFastChecking () = - Prefs.readBoolWithDefault fastcheck = `True - || (Prefs.readBoolWithDefault fastcheck = `Default && Util.osType = `Unix) + Prefs.read fastcheck = `True + || (Prefs.read fastcheck = `Default (*&& Util.osType = `Unix*)) let immutable = Pred.create "immutable" ~advanced:true ("This preference specifies paths for directories whose \ From bcpierce at cis.upenn.edu Thu Jan 7 21:01:01 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Thu, 7 Jan 2010 21:01:01 -0500 Subject: [Unison-hackers] 2.39 bugs Message-ID: <590767AC-2320-4A15-81BB-167C4A2A1E8B@cis.upenn.edu> Couple of small things with 2.39: When I run with the OSX GUI, I get this message: Thread 5 killed on uncaught exception Util.Fatal("Warning: incompatible case sensitivity settings. Unison is currently in Unicode case insensitive mode, while the archives were created in Latin-1 case insensitive mode. You should either change Unison's setup or delete the following archives Problem 1: It doesn't tell which archives to delete! Problem 2: I get this message in the console, not the GUI. I guess the second is an older problem. - B From bcpierce at cis.upenn.edu Thu Jan 7 21:06:33 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Thu, 7 Jan 2010 21:06:33 -0500 Subject: [Unison-hackers] 2.39: -ignorearchives broken? Message-ID: <0BC33911-B5B4-4D6A-BCBA-412370BDB54E@cis.upenn.edu> After switching to the text UI, I tried using the -ignorearchives switch to avoid manually deleting the archives. But this fails: ... Press return to continue.[] [update] findOnRoot //BCPLaptop//Users/bcpierce [update] findOnRoot /Users/bcpierce [update] findLocal /Users/bcpierce Uncaught exception Not_found ~/current/unison/trunk/src> [server: update] findLocal /Users/bcpierce Uncaught exception Not_found - B From vouillon at seas.upenn.edu Fri Jan 8 05:51:57 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Fri, 8 Jan 2010 05:51:57 -0500 Subject: [Unison-hackers] [unison-svn] r397 - in trunk/src: . uimacnew uimacnew09 Message-ID: <201001081051.o08ApvTb020360@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-08 05:51:56 -0500 (Fri, 08 Jan 2010) New Revision: 397 Modified: trunk/src/RECENTNEWS trunk/src/mkProjectInfo.ml trunk/src/uimacbridgenew.ml trunk/src/uimacnew/MyController.m trunk/src/uimacnew09/MyController.m Log: * MacOS GUI: - improved exception handling (untested code, might not even compile) Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-07 19:16:05 UTC (rev 396) +++ trunk/src/RECENTNEWS 2010-01-08 10:51:56 UTC (rev 397) @@ -1,5 +1,11 @@ CHANGES FROM VERSION 2.39.0 +* MacOS GUI: + - improved exception handling (untested code, might not even compile) + +------------------------------- +CHANGES FROM VERSION 2.39.0 + * Bumped version number: incompatible protocol changes * Resume copy of partially transferred files. Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-07 19:16:05 UTC (rev 396) +++ trunk/src/mkProjectInfo.ml 2010-01-08 10:51:56 UTC (rev 397) @@ -87,3 +87,4 @@ Printf.printf "VERSION=%d.%d.%d\n" majorVersion minorVersion pointVersion;; Printf.printf "NAME=%s\n" projectName;; + Modified: trunk/src/uimacbridgenew.ml =================================================================== --- trunk/src/uimacbridgenew.ml 2010-01-07 19:16:05 UTC (rev 396) +++ trunk/src/uimacbridgenew.ml 2010-01-08 10:51:56 UTC (rev 397) @@ -76,6 +76,22 @@ ;; Callback.register "callbackThreadCreate" callbackThreadCreate;; +(* Defined in MyController.m; display the error message and exit *) +external displayFatalError : string -> unit = "fatalError";; + +let fatalError message = + Trace.log (message ^ "\n"); + displayFatalError message + +let doInOtherThread f = + Thread.create + (fun () -> + try + f () + with + Util.Transient s | Util.Fatal s -> fatalError s + | exn -> fatalError (Uicommon.exn2string exn)) + (* Defined in MyController.m, used to redisplay the table when the status for a row changes *) external reloadTable : int -> unit = "reloadTable";; @@ -225,11 +241,10 @@ (* Do this in another thread and return immedidately to free up main thread in cocoa *) let unisonInit1 profileName = - let doIt () = - let r = do_unisonInit1 profileName in - unisonInit1Complete r; - in - Thread.create doIt(); + doInOtherThread + (fun () -> + let r = do_unisonInit1 profileName in + unisonInit1Complete r) ;; Callback.register "unisonInit1" unisonInit1;; Callback.register "openConnectionPrompt" Remote.openConnectionPrompt;; @@ -337,11 +352,10 @@ (* Do this in another thread and return immedidately to free up main thread in cocoa *) let unisonInit2 () = - let doIt () = - let r = do_unisonInit2 () in - unisonInit2Complete r; - in - Thread.create doIt(); + doInOtherThread + (fun () -> + let r = do_unisonInit2 () in + unisonInit2Complete r) ;; Callback.register "unisonInit2" unisonInit2;; @@ -377,7 +391,7 @@ Callback.register "unisonRiToRight" unisonRiToRight;; let unisonRiToFileSize ri = - (*FIX: will not work with files and directory larger than 1 GiB on + (*FIX: will not work with files and directories larger than 1 GiB on 32bit machines! *) Uutil.Filesize.toInt (riLength ri.ri);; Callback.register "unisonRiToFileSize" unisonRiToFileSize;; @@ -604,11 +618,10 @@ (* Do this in another thread and return immedidately to free up main thread in cocoa *) let unisonSynchronize () = - let doIt () = - do_unisonSynchronize (); - syncComplete (); - in - Thread.create doIt(); + doInOtherThread + (fun () -> + do_unisonSynchronize (); + syncComplete ()) ;; Callback.register "unisonSynchronize" unisonSynchronize;; Modified: trunk/src/uimacnew/MyController.m =================================================================== --- trunk/src/uimacnew/MyController.m 2010-01-07 19:16:05 UTC (rev 396) +++ trunk/src/uimacnew/MyController.m 2010-01-08 10:51:56 UTC (rev 397) @@ -999,6 +999,20 @@ return toolbarHeight; } +CAMLprim value fatalError(value s) +{ + NSString *str = [[NSString alloc] initWithUTF8String:String_val(s)]; + + [me performSelectorOnMainThread:@selector(fatalError:) withObject:str waitUntilDone:FALSE]; + [str release]; + return Val_unit; +} + +- (void)fatalError:(NSString *)msg { + NSRunAlertPanel(@"Fatal error", msg, @"Exit", nil, nil); + exit(1); +} + @end @implementation NSString (_UnisonUtil) Modified: trunk/src/uimacnew09/MyController.m =================================================================== --- trunk/src/uimacnew09/MyController.m 2010-01-07 19:16:05 UTC (rev 396) +++ trunk/src/uimacnew09/MyController.m 2010-01-08 10:51:56 UTC (rev 397) @@ -1081,6 +1081,20 @@ return toolbarHeight; } +CAMLprim value fatalError(value s) +{ + NSString *str = [[NSString alloc] initWithUTF8String:String_val(s)]; + + [me performSelectorOnMainThread:@selector(fatalError:) withObject:str waitUntilDone:FALSE]; + [str release]; + return Val_unit; +} + +- (void)fatalError:(NSString *)msg { + NSRunAlertPanel(@"Fatal error", msg, @"Exit", nil, nil); + exit(1); +} + @end @implementation NSString (_UnisonUtil) From Jerome.Vouillon at pps.jussieu.fr Fri Jan 8 05:55:00 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Fri, 8 Jan 2010 11:55:00 +0100 Subject: [Unison-hackers] 2.39 bugs In-Reply-To: <590767AC-2320-4A15-81BB-167C4A2A1E8B@cis.upenn.edu> References: <590767AC-2320-4A15-81BB-167C4A2A1E8B@cis.upenn.edu> Message-ID: <20100108105500.GA14930@pps.jussieu.fr> On Thu, Jan 07, 2010 at 09:01:01PM -0500, Benjamin Pierce wrote: > Couple of small things with 2.39: > > When I run with the OSX GUI, I get this message: > > Thread 5 killed on uncaught exception Util.Fatal("Warning: > incompatible case sensitivity settings. > Unison is currently in Unicode case insensitive mode, > while the archives were created in Latin-1 case insensitive mode. > You should either change Unison's setup or delete > the following archives > > Problem 1: It doesn't tell which archives to delete! > > Problem 2: I get this message in the console, not the GUI. > > I guess the second is an older problem. That's two sides of a same problem. Some exception are not caught. They are thus printed on the console by the Caml runtime. As the runtime format the message in a 256 bytes buffer, the message gets truncated. Hopefully, this is fixed now. -- Jerome From vouillon at seas.upenn.edu Fri Jan 8 10:18:06 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Fri, 8 Jan 2010 10:18:06 -0500 Subject: [Unison-hackers] [unison-svn] r398 - trunk/src Message-ID: <201001081518.o08FI6do026065@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-08 10:18:05 -0500 (Fri, 08 Jan 2010) New Revision: 398 Modified: trunk/src/RECENTNEWS trunk/src/fpcache.ml trunk/src/fpcache.mli trunk/src/mkProjectInfo.ml trunk/src/update.ml Log: * The "ignorearchives" preference now works. * When Unison detects that the archive case-sensitivity mode does not match the current settings, it populates the fingerprint cache using the archive contents. This way, changing the case-sensitivity mode should be reasonably fast. Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-08 10:51:56 UTC (rev 397) +++ trunk/src/RECENTNEWS 2010-01-08 15:18:05 UTC (rev 398) @@ -1,5 +1,14 @@ CHANGES FROM VERSION 2.39.0 +* The "ignorearchives" preference now works. +* When Unison detects that the archive case-sensitivity mode + does not match the current settings, it populates the fingerprint + cache using the archive contents. This way, changing the + case-sensitivity mode should be reasonably fast. + +------------------------------- +CHANGES FROM VERSION 2.39.0 + * MacOS GUI: - improved exception handling (untested code, might not even compile) Modified: trunk/src/fpcache.ml =================================================================== --- trunk/src/fpcache.ml 2010-01-08 10:51:56 UTC (rev 397) +++ trunk/src/fpcache.ml 2010-01-08 15:18:05 UTC (rev 398) @@ -31,7 +31,8 @@ (* Information for writing to the on-disk cache *) -type entry = int * string * (Fileinfo.t * Os.fullfingerprint) +type entry = + int * string * (Props.t * Os.fullfingerprint * Fileinfo.stamp * Osx.ressStamp) type state = { oc : out_channel; @@ -179,17 +180,17 @@ let maxCount = 5000 let maxSize = Uutil.Filesize.ofInt (100 * 1024 * 1024) -let save path res = +let save path v = match !state with None -> () | Some state -> - let (info, _) = res in - let l = Props.length info.Fileinfo.desc in + let (desc, _, _, _) = v in + let l = Props.length desc in state.size <- Uutil.Filesize.add state.size l; state.count <- state.count + 1; let (l, s) = compress state path in - state.queue <- (l, s, res) :: state.queue; + state.queue <- (l, s, v) :: state.queue; if state.count > maxCount || state.size > maxSize then write state (****) @@ -228,18 +229,20 @@ Osx.ressUnchanged ress info.Fileinfo.osX.Osx.ressInfo None dataClearlyUnchanged -let clearlyUnchanged fastCheck path newInfo oldInfo = +let clearlyUnchanged fastCheck path newInfo oldDesc oldStamp oldRess = let du = - dataClearlyUnchanged fastCheck path newInfo - oldInfo.Fileinfo.desc (Fileinfo.stamp oldInfo) + dataClearlyUnchanged fastCheck path newInfo oldDesc oldStamp in - du && ressClearlyUnchanged fastCheck newInfo (Fileinfo.ressStamp oldInfo) du + du && ressClearlyUnchanged fastCheck newInfo oldRess du let fingerprint fastCheck currfspath path info optDig = let res = try - let (oldInfo, _) as res = PathTbl.find tbl (Path.toString path) in - if not (clearlyUnchanged fastCheck path info oldInfo) then + let (oldDesc, oldDig, oldStamp, oldRess) as res = + PathTbl.find tbl (Path.toString path) in + if + not (clearlyUnchanged fastCheck path info oldDesc oldStamp oldRess) + then raise Not_found; debug (fun () -> Util.msg "cache hit for path %s\n" (Path.toDebugString path)); @@ -248,7 +251,8 @@ if fastCheck then debug (fun () -> Util.msg "cache miss for path %s\n" (Path.toDebugString path)); - Os.safeFingerprint currfspath path info optDig + let (info, dig) = Os.safeFingerprint currfspath path info optDig in + (info.Fileinfo.desc, dig, Fileinfo.stamp info, Fileinfo.ressStamp info) in save path res; res Modified: trunk/src/fpcache.mli =================================================================== --- trunk/src/fpcache.mli 2010-01-08 10:51:56 UTC (rev 397) +++ trunk/src/fpcache.mli 2010-01-08 15:18:05 UTC (rev 398) @@ -10,8 +10,15 @@ (* Get the fingerprint of a file, possibly from the cache *) val fingerprint : bool -> Fspath.t -> Path.local -> Fileinfo.t -> Os.fullfingerprint option -> - Fileinfo.t * Os.fullfingerprint + Props.t * Os.fullfingerprint * Fileinfo.stamp * Osx.ressStamp +(* Add an entry to the cache *) +val save : + Path.local -> + Props.t * Os.fullfingerprint * Fileinfo.stamp * Osx.ressStamp -> unit + +(****) + val dataClearlyUnchanged : bool -> Path.local -> Fileinfo.t -> Props.t -> Fileinfo.stamp -> bool val ressClearlyUnchanged : Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-08 10:51:56 UTC (rev 397) +++ trunk/src/mkProjectInfo.ml 2010-01-08 15:18:05 UTC (rev 398) @@ -88,3 +88,4 @@ Printf.printf "NAME=%s\n" projectName;; + Modified: trunk/src/update.ml =================================================================== --- trunk/src/update.ml 2010-01-08 10:51:56 UTC (rev 397) +++ trunk/src/update.ml 2010-01-08 15:18:05 UTC (rev 398) @@ -275,6 +275,22 @@ Os.myCanonicalHostName, System.file_exists (Os.fileInUnisonDir name))) +let compatibleCaseMode magic = + if magic = "" then `YES else + try + let archMode = String.sub magic 0 (String.index magic '\000') in + let curMode = (Case.ops ())#modeDesc in + if curMode <> archMode then + `NO (curMode, archMode) + else + `YES + with Not_found -> + if (Case.ops ())#mode = Case.UnicodeInsensitive then + let curMode = (Case.ops ())#modeDesc in + `NO (curMode, "some non-Unicode") + else + `YES + let checkArchiveCaseSensitivity l = let error curMode archMode = (* We cannot compute the archive name locally as it @@ -294,26 +310,17 @@ Format.sprintf "Unison is currently in %s mode," curMode :: Format.sprintf "while the archives were created in %s mode." archMode :: - "You should either change Unison's setup or delete " :: + "You should either change Unison's setup or delete" :: "the following archives from the .unison directories:" :: l @ - ["Then, try again."]))) + ["(or invoke Unison once with -ignorearchives flag)."; + "Then, try again."]))) in match l with - Some (_, magic) :: _ when magic <> "" -> - begin try - let archMode = String.sub magic 0 (String.index magic '\000') in - let curMode = (Case.ops ())#modeDesc in - if curMode <> archMode then - error curMode archMode - else - Lwt.return () - with Not_found -> - if (Case.ops ())#mode = Case.UnicodeInsensitive then begin - let curMode = (Case.ops ())#modeDesc in - error curMode "some non-Unicode" - end else - Lwt.return () + Some (_, magic) :: _ -> + begin match compatibleCaseMode magic with + `NO (curMode, archMode) -> error curMode archMode + | `YES -> Lwt.return () end | _ -> Lwt.return () @@ -575,6 +582,45 @@ (* Loading archives *) (*************************************************************************) +let ignoreArchives = + Prefs.createBool "ignorearchives" false + "!ignore existing archive files" + ("When this preference is set, Unison will ignore any existing " + ^ "archive files and behave as though it were being run for the first " + ^ "time on these replicas. It is " + ^ "not a good idea to set this option in a profile: it is intended for " + ^ "command-line use.") + +let rec populateCacheFromArchive path arch = + match arch with + ArchiveDir (_, children) -> + NameMap.iter + (fun nm ch -> populateCacheFromArchive (Path.child path nm) ch) + children + | ArchiveFile (desc, dig, stamp, ress) -> + Fpcache.save path (desc, dig, stamp, ress) + | ArchiveSymlink _ | NoArchive -> + () + +let setArchiveData thisRoot fspath (arch, hash, magic, properties) info = + setArchiveLocal thisRoot arch; + setArchivePropsLocal thisRoot properties; + Hashtbl.replace archiveInfoCache thisRoot info; + if compatibleCaseMode magic <> `YES then begin + let (cacheFilename, _) = archiveName fspath FPCache in + let cacheFile = Os.fileInUnisonDir cacheFilename in + Fpcache.init true cacheFile; + populateCacheFromArchive Path.empty arch; + Fpcache.finish () + end; + Lwt.return (Some (hash, magic)) + +let clearArchiveData thisRoot = + setArchiveLocal thisRoot NoArchive; + setArchivePropsLocal thisRoot Proplist.empty; + Hashtbl.remove archiveInfoCache thisRoot; + Lwt.return (Some (0, "")) + (* Load (main) root archive and cache it on the given server *) let loadArchiveOnRoot: Common.root -> bool -> (int * string) option Lwt.t = Remote.registerRootCmd @@ -582,7 +628,10 @@ (fun (fspath, optimistic) -> let (arcName,thisRoot) = archiveName fspath MainArch in let arcFspath = Os.fileInUnisonDir arcName in - if optimistic then begin + + if Prefs.read ignoreArchives then + clearArchiveData thisRoot + else if optimistic then begin let (newArcName, _) = archiveName fspath NewArch in if (* If the archive is not in a stable state, we need to @@ -605,14 +654,11 @@ Lwt.return (Some (0, "")) else begin match loadArchiveLocal arcFspath thisRoot with - Some (arch, hash, magic, properties) -> + Some archData -> let info' = Fileinfo.get' arcFspath in - if fileUnchanged info info' then begin - setArchiveLocal thisRoot arch; - setArchivePropsLocal thisRoot properties; - Hashtbl.replace archiveInfoCache thisRoot info; - Lwt.return (Some (hash, magic)) - end else + if fileUnchanged info info' then + setArchiveData thisRoot fspath archData info + else (* The archive was modified during loading. We fail. *) Lwt.return None | None -> @@ -621,18 +667,11 @@ end end else begin match loadArchiveLocal arcFspath thisRoot with - Some (arch, hash, magic, properties) -> - setArchiveLocal thisRoot arch; - setArchivePropsLocal thisRoot properties; - let info = Fileinfo.get' arcFspath in - Hashtbl.replace archiveInfoCache thisRoot info; - Lwt.return (Some (hash, magic)) + Some archData -> + setArchiveData thisRoot fspath archData (Fileinfo.get' arcFspath) | None -> (* No archive found *) - setArchiveLocal thisRoot NoArchive; - setArchivePropsLocal thisRoot Proplist.empty; - Hashtbl.remove archiveInfoCache thisRoot; - Lwt.return (Some (0, "")) + clearArchiveData thisRoot end) let dumpArchives = @@ -642,47 +681,34 @@ ^ "on each host, containing a text summary of the archive, immediately " ^ "after loading it.") -let ignoreArchives = - Prefs.createBool "ignorearchives" false - "!ignore existing archive files" - ("When this preference is set, Unison will ignore any existing " - ^ "archive files and behave as though it were being run for the first " - ^ "time on these replicas. It is " - ^ "not a good idea to set this option in a profile: it is intended for " - ^ "command-line use.") - (* For all roots (local or remote), load the archive and cache *) let loadArchives (optimistic: bool) : bool Lwt.t = - if Prefs.read ignoreArchives then begin - Lwt.return false - end else begin - Globals.allRootsMap (fun r -> loadArchiveOnRoot r optimistic) - >>= (fun checksums -> - let identicals = archivesIdentical checksums in - if not (optimistic || identicals) then - raise (Util.Fatal( - "Internal error: On-disk archives are not identical.\n" - ^ "\n" - ^ "This can happen when both machines have the same hostname.\n" - ^ "\n" - ^ "If this is not the case and you get this message repeatedly, please:\n" - ^ " a) Send a bug report to unison-users at yahoogroups.com (you may need" - ^ " to join the group before you will be allowed to post).\n" - ^ " b) Move the archive files on each machine to some other directory\n" - ^ " (in case they may be useful for debugging).\n" - ^ " The archive files on this machine are in the directory\n" - ^ (Printf.sprintf " %s\n" - (System.fspathToPrintString Os.unisonDir)) - ^ " and have names of the form\n" - ^ " arXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" - ^ " where the X's are a hexidecimal number .\n" - ^ " c) Run unison again to synchronize from scratch.\n")); - checkArchiveCaseSensitivity checksums >>= fun () -> - if Prefs.read dumpArchives then - Globals.allRootsMap (fun r -> dumpArchiveOnRoot r ()) - >>= (fun _ -> Lwt.return identicals) - else Lwt.return identicals) - end + Globals.allRootsMap (fun r -> loadArchiveOnRoot r optimistic) + >>= (fun checksums -> + let identicals = archivesIdentical checksums in + if not (optimistic || identicals) then + raise (Util.Fatal( + "Internal error: On-disk archives are not identical.\n" + ^ "\n" + ^ "This can happen when both machines have the same hostname.\n" + ^ "\n" + ^ "If this is not the case and you get this message repeatedly, please:\n" + ^ " a) Send a bug report to unison-users at yahoogroups.com (you may need" + ^ " to join the group before you will be allowed to post).\n" + ^ " b) Move the archive files on each machine to some other directory\n" + ^ " (in case they may be useful for debugging).\n" + ^ " The archive files on this machine are in the directory\n" + ^ (Printf.sprintf " %s\n" + (System.fspathToPrintString Os.unisonDir)) + ^ " and have names of the form\n" + ^ " arXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" + ^ " where the X's are a hexidecimal number .\n" + ^ " c) Run unison again to synchronize from scratch.\n")); + checkArchiveCaseSensitivity checksums >>= fun () -> + if Prefs.read dumpArchives then + Globals.allRootsMap (fun r -> dumpArchiveOnRoot r ()) + >>= (fun _ -> Lwt.return identicals) + else Lwt.return identicals) (*****************************************************************************) @@ -806,39 +832,6 @@ let exists = Safelist.exists (fun x -> x) let doArchiveCrashRecovery () = - let noArchives() = - foundArchives := false; - let expectedRoots = - String.concat "\n\t" (Safelist.map root2string (Globals.rootsList ())) in - Util.warn - ("No archive files were found for these roots, whose canonical names are:\n\t" - ^ expectedRoots ^ "\nThis can happen either\n" - ^ "because this is the first time you have synchronized these roots, \n" - ^ "or because you have upgraded Unison to a new version with a different\n" - ^ "archive format. \n\n" - ^ "Update detection may take a while on this run if the replicas are \n" - ^ "large.\n\n" - ^ "Unison will assume that the 'last synchronized state' of both replicas\n" - ^ "was completely empty. This means that any files that are different\n" - ^ "will be reported as conflicts, and any files that exist only on one\n" - ^ "replica will be judged as new and propagated to the other replica.\n" - ^ "If the two replicas are identical, then no changes will be reported.\n\n" - ^ "If you see this message repeatedly, it may be because one of your machines\n" - ^ "is getting its address from DHCP, which is causing its host name to change\n" - ^ "between synchronizations. See the documentation for the UNISONLOCALHOSTNAME\n" - ^ "environment variable for advice on how to correct this.\n" - ^ "\n" - ^ "Donations to the Unison project are gratefully accepted: \n" - ^ "http://www.cis.upenn.edu/~bcpierce/unison\n" - ^ "\n" - (* ^ "\nThe expected archive names were:\n" ^ expectedNames *) ); - Lwt.return () in - - (* See if we've been asked to ignore the archives *) - if Prefs.read ignoreArchives then - noArchives() - else - (* Check which hosts have copies of the old/new archive *) Globals.allRootsMap (fun r -> archivesExistOnRoot r ()) >>= (fun exl -> let oldnamesExist,newnamesExist = @@ -892,7 +885,32 @@ ["Please delete archive files as appropriate and try again\n"; "or invoke Unison with -ignorearchives flag."])))) else begin - noArchives() + foundArchives := false; + let expectedRoots = + String.concat "\n\t" (Safelist.map root2string (Globals.rootsList ())) in + Util.warn + ("No archive files were found for these roots, whose canonical names are:\n\t" + ^ expectedRoots ^ "\nThis can happen either\n" + ^ "because this is the first time you have synchronized these roots, \n" + ^ "or because you have upgraded Unison to a new version with a different\n" + ^ "archive format. \n\n" + ^ "Update detection may take a while on this run if the replicas are \n" + ^ "large.\n\n" + ^ "Unison will assume that the 'last synchronized state' of both replicas\n" + ^ "was completely empty. This means that any files that are different\n" + ^ "will be reported as conflicts, and any files that exist only on one\n" + ^ "replica will be judged as new and propagated to the other replica.\n" + ^ "If the two replicas are identical, then no changes will be reported.\n\n" + ^ "If you see this message repeatedly, it may be because one of your machines\n" + ^ "is getting its address from DHCP, which is causing its host name to change\n" + ^ "between synchronizations. See the documentation for the UNISONLOCALHOSTNAME\n" + ^ "environment variable for advice on how to correct this.\n" + ^ "\n" + ^ "Donations to the Unison project are gratefully accepted: \n" + ^ "http://www.cis.upenn.edu/~bcpierce/unison\n" + ^ "\n" + (* ^ "\nThe expected archive names were:\n" ^ expectedNames *) ); + Lwt.return () end)) (************************************************************************* @@ -1185,18 +1203,16 @@ Props.time archDesc >= 631152000. (* Jan 1, 1990 *) (* Check whether a file's permissions have not changed *) -let isPropUnchanged info archiveDesc = - Props.similar info.Fileinfo.desc archiveDesc +let isPropUnchanged desc archiveDesc = Props.similar desc archiveDesc (* Handle file permission change *) -let checkPropChange info archive archDesc = - if isPropUnchanged info archDesc then begin +let checkPropChange desc archive archDesc = + if isPropUnchanged desc archDesc then begin debugverbose (fun() -> Util.msg " Unchanged file\n"); NoUpdates end else begin debug (fun() -> Util.msg " File permissions updated\n"); - Updates (File (info.Fileinfo.desc, ContentsSame), - oldInfoOf archive) + Updates (File (desc, ContentsSame), oldInfoOf archive) end (* Check whether a file has changed has changed, by comparing its digest and @@ -1233,11 +1249,11 @@ in if dataClearlyUnchanged && ressClearlyUnchanged then begin Xferhint.insertEntry currfspath path archDig; - None, checkPropChange info archive archDesc + None, checkPropChange info.Fileinfo.desc archive archDesc end else begin debugverbose (fun() -> Util.msg " Double-check possibly updated file\n"); showStatusAddLength info; - let (info, newDigest) = + let (newDesc, newDigest, newStamp, newRess) = Fpcache.fingerprint fastCheck currfspath path info (if dataClearlyUnchanged then Some archDig else None) in Xferhint.insertEntry currfspath path newDigest; @@ -1245,20 +1261,16 @@ (Os.fullfingerprint_to_string archDig) (Os.fullfingerprint_to_string newDigest)); if archDig = newDigest then begin - let newprops = Props.setTime archDesc (Props.time info.Fileinfo.desc) in - let newarch = - ArchiveFile - (newprops, archDig, Fileinfo.stamp info, Fileinfo.ressStamp info) in + let newprops = Props.setTime archDesc (Props.time newDesc) in + let newarch = ArchiveFile (newprops, archDig, newStamp, newRess) in debugverbose (fun() -> - Util.msg " Contents match: update archive with new time...%f\n" - (Props.time newprops)); - Some newarch, checkPropChange info archive archDesc + Util.msg " Contents match: update archive with new time...%f\n" + (Props.time newprops)); + Some newarch, checkPropChange newDesc archive archDesc end else begin debug (fun() -> Util.msg " Updated file\n"); None, - Updates (File (info.Fileinfo.desc, - ContentsUpdated (newDigest, Fileinfo.stamp info, - Fileinfo.ressStamp info)), + Updates (File (newDesc, ContentsUpdated (newDigest, newStamp, newRess)), oldInfoOf archive) end end @@ -1476,13 +1488,11 @@ None, begin showStatusAddLength info; - let (info, dig) = + let (desc, dig, stamp, ress) = Fpcache.fingerprint fastCheckInfos.fastCheck currfspath path info None in Xferhint.insertEntry currfspath path dig; - Updates (File (info.Fileinfo.desc, - ContentsUpdated (dig, Fileinfo.stamp info, - Fileinfo.ressStamp info)), + Updates (File (desc, ContentsUpdated (dig, stamp, ress)), oldInfoOf archive) end (* --- *) @@ -1504,7 +1514,7 @@ | (`DIRECTORY, ArchiveDir (archDesc, prevChildren)) -> debugverbose (fun() -> Util.msg " buildUpdate -> Directory\n"); let (permchange, desc) = - if isPropUnchanged info archDesc then + if isPropUnchanged info.Fileinfo.desc archDesc then (PropsSame, archDesc) else (PropsUpdated, info.Fileinfo.desc) in From bcpierce at cis.upenn.edu Sun Jan 10 09:57:56 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Sun, 10 Jan 2010 09:57:56 -0500 Subject: [Unison-hackers] OSX GUI Message-ID: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> The GUI is not working for me at the moment -- it displays "Connecting..." forever and prints nothing (even when invoked from command line with "-debug all"). Anybody else seeing this? - B From mgross at informatik.uni-bremen.de Sun Jan 10 10:06:28 2010 From: mgross at informatik.uni-bremen.de (=?iso-8859-1?Q?Markus_Gro=DF?=) Date: Sun, 10 Jan 2010 16:06:28 +0100 Subject: [Unison-hackers] OSX GUI In-Reply-To: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> References: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> Message-ID: <6F0831DB-6784-4010-A642-47D14621CDD4@informatik.uni-bremen.de> On 10.01.2010, at 15:57, Benjamin Pierce wrote: > The GUI is not working for me at the moment -- it displays > "Connecting..." forever and prints nothing (even when invoked from > command line with "-debug all"). Anybody else seeing this? I have the same problem. I then tried the command-line version. Because of the case-insensitivity changes Unison wanted to rebuild the cache-files. However during this rebuild it got an "Index out of bounds" exception and terminated. - Markus From bcpierce at cis.upenn.edu Sun Jan 10 10:07:47 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Sun, 10 Jan 2010 10:07:47 -0500 Subject: [Unison-hackers] OSX GUI In-Reply-To: <6F0831DB-6784-4010-A642-47D14621CDD4@informatik.uni-bremen.de> References: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> <6F0831DB-6784-4010-A642-47D14621CDD4@informatik.uni-bremen.de> Message-ID: <7CF72B75-EDA0-476E-B112-C8959F7938CA@cis.upenn.edu> > The GUI is not working for me at the moment -- it displays >> "Connecting..." forever and prints nothing (even when invoked from >> command line with "-debug all"). Anybody else seeing this? > > I have the same problem. > I then tried the command-line version. Because of the case- > insensitivity changes Unison wanted to rebuild the cache-files. > However during this rebuild it got an "Index out of bounds" > exception and terminated. Hum -- I've been able to rebuild archives with no problem. Is the exception repeatable? (And, if so, does it happen soon enough that running with "-debug all" would not take forever?) - B From Jerome.Vouillon at pps.jussieu.fr Sun Jan 10 10:13:11 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Sun, 10 Jan 2010 16:13:11 +0100 Subject: [Unison-hackers] OSX GUI In-Reply-To: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> References: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> Message-ID: <20100110151311.GA1480@pps.jussieu.fr> On Sun, Jan 10, 2010 at 09:57:56AM -0500, Benjamin Pierce wrote: > The GUI is not working for me at the moment -- it displays > "Connecting..." forever and prints nothing (even when invoked from > command line with "-debug all"). Anybody else seeing this? Which version of Ocaml are you using? -- Jerome From Jerome.Vouillon at pps.jussieu.fr Sun Jan 10 10:17:49 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Sun, 10 Jan 2010 16:17:49 +0100 Subject: [Unison-hackers] OSX GUI In-Reply-To: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> References: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> Message-ID: <20100110151749.GA1523@pps.jussieu.fr> On Sun, Jan 10, 2010 at 09:57:56AM -0500, Benjamin Pierce wrote: > The GUI is not working for me at the moment -- it displays > "Connecting..." forever and prints nothing (even when invoked from > command line with "-debug all"). Anybody else seeing this? Try the patch below... -- Jerome Index: uimacbridgenew.ml =================================================================== --- uimacbridgenew.ml (r?vision 398) +++ uimacbridgenew.ml (copie de travail) @@ -91,6 +91,7 @@ with Util.Transient s | Util.Fatal s -> fatalError s | exn -> fatalError (Uicommon.exn2string exn)) + () (* Defined in MyController.m, used to redisplay the table when the status for a row changes *) From bcpierce at cis.upenn.edu Sun Jan 10 10:24:54 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Sun, 10 Jan 2010 10:24:54 -0500 Subject: [Unison-hackers] OSX GUI In-Reply-To: <20100110151311.GA1480@pps.jussieu.fr> References: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> <20100110151311.GA1480@pps.jussieu.fr> Message-ID: <8369CF86-BC33-4AE1-8FDA-266F0AE7E8F2@cis.upenn.edu> 3.11.2+rc1 On Jan 10, 2010, at 10:13 AM, Jerome Vouillon wrote: > On Sun, Jan 10, 2010 at 09:57:56AM -0500, Benjamin Pierce wrote: >> The GUI is not working for me at the moment -- it displays >> "Connecting..." forever and prints nothing (even when invoked from >> command line with "-debug all"). Anybody else seeing this? > > Which version of Ocaml are you using? > > -- Jerome > _______________________________________________ > Unison-hackers mailing list > Unison-hackers at lists.seas.upenn.edu > http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers From bcpierce at cis.upenn.edu Sun Jan 10 10:28:43 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Sun, 10 Jan 2010 10:28:43 -0500 Subject: [Unison-hackers] OSX GUI In-Reply-To: <20100110151749.GA1523@pps.jussieu.fr> References: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> <20100110151749.GA1523@pps.jussieu.fr> Message-ID: <3D495B9F-3F47-4469-8E40-C12C2EBD5E35@cis.upenn.edu> Much better! On Jan 10, 2010, at 10:17 AM, Jerome Vouillon wrote: > On Sun, Jan 10, 2010 at 09:57:56AM -0500, Benjamin Pierce wrote: >> The GUI is not working for me at the moment -- it displays >> "Connecting..." forever and prints nothing (even when invoked from >> command line with "-debug all"). Anybody else seeing this? > > Try the patch below... > > -- Jerome > > Index: uimacbridgenew.ml > =================================================================== > --- uimacbridgenew.ml (r?vision 398) > +++ uimacbridgenew.ml (copie de travail) > @@ -91,6 +91,7 @@ > with > Util.Transient s | Util.Fatal s -> fatalError s > | exn -> fatalError (Uicommon.exn2string exn)) > + () > > (* Defined in MyController.m, used to redisplay the table > when the status for a row changes *) > _______________________________________________ > Unison-hackers mailing list > Unison-hackers at lists.seas.upenn.edu > http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers > From bcpierce at seas.upenn.edu Sun Jan 10 10:30:19 2010 From: bcpierce at seas.upenn.edu (bcpierce@seas.upenn.edu) Date: Sun, 10 Jan 2010 10:30:19 -0500 Subject: [Unison-hackers] [unison-svn] r399 - in trunk/src: . ubase Message-ID: <201001101530.o0AFUKX2016998@yaws.seas.upenn.edu> Author: bcpierce Date: 2010-01-10 10:30:18 -0500 (Sun, 10 Jan 2010) New Revision: 399 Modified: trunk/src/RECENTNEWS trunk/src/mkProjectInfo.ml trunk/src/stasher.mli trunk/src/ubase/depend trunk/src/uimacbridgenew.ml trunk/src/update.ml Log: * Back out some minimal support we'd added for checking out Unison sources via Bazaar. * Small fix for OSX GUI Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-08 15:18:05 UTC (rev 398) +++ trunk/src/RECENTNEWS 2010-01-10 15:30:18 UTC (rev 399) @@ -1,5 +1,14 @@ CHANGES FROM VERSION 2.39.0 +* Back out some minimal support we'd added for checking out Unison sources via Bazaar. + +* Small fix for OSX GUI + + + +------------------------------- +CHANGES FROM VERSION 2.39.0 + * The "ignorearchives" preference now works. * When Unison detects that the archive case-sensitivity mode does not match the current settings, it populates the fingerprint Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-08 15:18:05 UTC (rev 398) +++ trunk/src/mkProjectInfo.ml 2010-01-10 15:30:18 UTC (rev 399) @@ -42,22 +42,7 @@ (* ---------------------------------------------------------------------- *) (* You shouldn't need to edit below. *) -(* run the bzr tool to get version information for bzr branches *) -exception BzrException of Unix.process_status;; -let bzr args = - let bzr = (try Sys.getenv "BZR" with Not_found -> "bzr") in - let cmd = bzr ^ " " ^ args in - let inc = Unix.open_process_in cmd in - let buf = Buffer.create 16 in - (try - while true do - Buffer.add_channel buf inc 1 - done - with End_of_file -> ()); - let status = Unix.close_process_in inc in - match status with - Unix.WEXITED 0 -> Buffer.contents buf - | _ -> raise (BzrException status);; +let revisionString = "$Rev: 396$";; (* extract a substring using a regular expression *) let extract_str re str = @@ -65,27 +50,53 @@ Str.matched_group 1 str;; let extract_int re str = int_of_string (extract_str re str);; -let revisionString = "$Rev: 396$";; -let pointVersion = if String.length revisionString > 5 -then Scanf.sscanf revisionString "$Rev: %d " (fun x -> x) - pointVersionOrigin -else (* Determining the pointVersionOrigin in bzr is kind of tricky: - - The mentioned revision number might not be part of this branch - - The mentioned revision number might be rhs of some merge - - The bzr-svn plugin might be outdated or not installed at all +(* BCP (1/10): This bit was added to help with getting Unison via bazaar, but it + was never used much and I'm not confident it's working. I'll comment it out + for now, but if it hasn't been needed or fixed in a few months, the next + person that edits this file should delete it... - On the whole, getting this to work seems too much effort for now. - So we'll simply use the revno as is as the point version, - and revisit offsetting them if unison should ever move its trunk to bzr. + (* run the bzr tool to get version information for bzr branches *) + exception BzrException of Unix.process_status;; + let bzr args = + let bzr = (try Sys.getenv "BZR" with Not_found -> "bzr") in + let cmd = bzr ^ " " ^ args in + let inc = Unix.open_process_in cmd in + let buf = Buffer.create 16 in + (try + while true do + Buffer.add_channel buf inc 1 + done + with End_of_file -> ()); + let status = Unix.close_process_in inc in + match status with + Unix.WEXITED 0 -> Buffer.contents buf + | _ -> raise (BzrException status);; - let pvo = extract_int "^revno:[ \t]*\\([0-9]+\\)[ \t]*$" - (bzr ("log -r svn:" ^ - string_of_int pointVersionOrigin)) in - *) - extract_int "^\\([0-9]+\\)$" (bzr "revno") (* - pvo *);; + let pointVersion = if String.length revisionString > 5 + then Scanf.sscanf revisionString "$Rev: %d " (fun x -> x) - pointVersionOrigin + else (* Determining the pointVersionOrigin in bzr is kind of tricky: + - The mentioned revision number might not be part of this branch + - The mentioned revision number might be rhs of some merge + - The bzr-svn plugin might be outdated or not installed at all + On the whole, getting this to work seems too much effort for now. + So we'll simply use the revno as is as the point version, + and revisit offsetting them if unison should ever move its trunk to bzr. + + let pvo = extract_int "^revno:[ \t]*\\([0-9]+\\)[ \t]*$" + (bzr ("log -r svn:" ^ + string_of_int pointVersionOrigin)) in + *) + extract_int "^\\([0-9]+\\)$" (bzr "revno") (* - pvo *);; +*) + +let pointVersion = + Scanf.sscanf revisionString "$Rev: %d " (fun x -> x) - pointVersionOrigin;; + Printf.printf "MAJORVERSION=%d.%d\n" majorVersion minorVersion;; Printf.printf "VERSION=%d.%d.%d\n" majorVersion minorVersion pointVersion;; Printf.printf "NAME=%s\n" projectName;; + Modified: trunk/src/stasher.mli =================================================================== --- trunk/src/stasher.mli 2010-01-08 15:18:05 UTC (rev 398) +++ trunk/src/stasher.mli 2010-01-10 15:30:18 UTC (rev 399) @@ -6,7 +6,7 @@ (* as archives for mergeable files. *) (* Make a backup copy of a file, if needed; if the third parameter is - `AndRemove, then the file is either backed up by renaming or + `AndRemove, then the file is either backed up by renaming or else deleted if no backup is needed. *) val backup: Fspath.t -> Path.local -> Modified: trunk/src/ubase/depend =================================================================== --- trunk/src/ubase/depend 2010-01-08 15:18:05 UTC (rev 398) +++ trunk/src/ubase/depend 2010-01-10 15:30:18 UTC (rev 399) @@ -16,12 +16,5 @@ uprintf.cmx: uprintf.cmi util.cmo: uprintf.cmi safelist.cmi util.cmi util.cmx: uprintf.cmx safelist.cmx util.cmi -myMap.cmi: prefs.cmi: util.cmi -proplist.cmi: -rx.cmi: -safelist.cmi: trace.cmi: prefs.cmi -uarg.cmi: -uprintf.cmi: -util.cmi: Modified: trunk/src/uimacbridgenew.ml =================================================================== --- trunk/src/uimacbridgenew.ml 2010-01-08 15:18:05 UTC (rev 398) +++ trunk/src/uimacbridgenew.ml 2010-01-10 15:30:18 UTC (rev 399) @@ -91,6 +91,7 @@ with Util.Transient s | Util.Fatal s -> fatalError s | exn -> fatalError (Uicommon.exn2string exn)) + () (* Defined in MyController.m, used to redisplay the table when the status for a row changes *) Modified: trunk/src/update.ml =================================================================== --- trunk/src/update.ml 2010-01-08 15:18:05 UTC (rev 398) +++ trunk/src/update.ml 2010-01-10 15:30:18 UTC (rev 399) @@ -693,7 +693,7 @@ ^ "This can happen when both machines have the same hostname.\n" ^ "\n" ^ "If this is not the case and you get this message repeatedly, please:\n" - ^ " a) Send a bug report to unison-users at yahoogroups.com (you may need" + ^ " a) Send a bug report to unison-users at yahoogroups.com (you may need\n" ^ " to join the group before you will be allowed to post).\n" ^ " b) Move the archive files on each machine to some other directory\n" ^ " (in case they may be useful for debugging).\n" From alan.schmitt at polytechnique.org Sun Jan 10 10:43:02 2010 From: alan.schmitt at polytechnique.org (Alan Schmitt) Date: Sun, 10 Jan 2010 16:43:02 +0100 Subject: [Unison-hackers] OSX GUI In-Reply-To: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> References: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> Message-ID: <25ec8ca61001100743pba80a8at1514f691f24b01a6@mail.gmail.com> On Sun, Jan 10, 2010 at 3:57 PM, Benjamin Pierce wrote: > The GUI is not working for me at the moment -- it displays > "Connecting..." forever and prints nothing (even when invoked from > command line with "-debug all"). ?Anybody else seeing this? Some error messages are sent to the Console, you could try looking there to see if there is something. Alan From Jerome.Vouillon at pps.jussieu.fr Sun Jan 10 10:49:12 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Sun, 10 Jan 2010 16:49:12 +0100 Subject: [Unison-hackers] OSX GUI In-Reply-To: <25ec8ca61001100743pba80a8at1514f691f24b01a6@mail.gmail.com> References: <3270E198-2A51-43EE-AC82-0F23691D666C@cis.upenn.edu> <25ec8ca61001100743pba80a8at1514f691f24b01a6@mail.gmail.com> Message-ID: <20100110154911.GB1810@pps.jussieu.fr> On Sun, Jan 10, 2010 at 04:43:02PM +0100, Alan Schmitt wrote: > On Sun, Jan 10, 2010 at 3:57 PM, Benjamin Pierce wrote: > > The GUI is not working for me at the moment -- it displays > > "Connecting..." forever and prints nothing (even when invoked from > > command line with "-debug all"). ?Anybody else seeing this? > > Some error messages are sent to the Console, you could try looking > there to see if there is something. Well, actually that was my fix to this very issue (errors messages being to sent to the Console) that was broken... -- Jerome From Jerome.Vouillon at pps.jussieu.fr Sun Jan 10 11:21:17 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Sun, 10 Jan 2010 17:21:17 +0100 Subject: [Unison-hackers] Performance of the OSX GUI? Message-ID: <20100110162117.GA2026@pps.jussieu.fr> I have some concerns regarding the performance of the OSX GUI. Indeed, method reloadTable in file MyController.m is invoked very often during synchronization. This method eventually invokes the NSTableView method reloadData, which forces redraw of all the visible portion of the NSTableView. The GTK UI was doing something similar and the performance hit was fairly high. Also, here is what the documentation says: This method forces redraw of all the visible cells in the receiver. If you want to update the value in a single cell, column, or row, it is more efficient to use frameOfCellAtColumn:row:, rectOfColumn:, or rectOfRow: in conjunction with setNeedsDisplayInRect: (NSView). If you just want to update the scroller, use noteNumberOfRowsChanged; if the height of a set of rows changes, use noteHeightOfRowsWithIndexesChanged:. A good way to test whether this is an issue or not is by putting a directory "foo" with a lot of small files at toplevel on one replica and an empty directory of the same name on the other replica (the two replicas should be local). The GUI should not take much longer to synchronize this directory than the text UI (accurate start and finish time of the synchronization can be found in the unison log). Can someone give it a try? -- Jerome From vouillon at seas.upenn.edu Sun Jan 10 17:53:01 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Sun, 10 Jan 2010 17:53:01 -0500 Subject: [Unison-hackers] [unison-svn] r400 - in trunk/src: . system system/win ubase uimacnew uimacnew09 Message-ID: <201001102253.o0AMr3a1026196@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-10 17:52:59 -0500 (Sun, 10 Jan 2010) New Revision: 400 Modified: trunk/src/RECENTNEWS trunk/src/fpcache.ml trunk/src/mkProjectInfo.ml trunk/src/os.ml trunk/src/osx.ml trunk/src/props.ml trunk/src/remote.ml trunk/src/system/system_win.ml trunk/src/system/win/system_impl.ml trunk/src/ubase/util.ml trunk/src/uimacbridgenew.ml trunk/src/uimacnew/Bridge.h trunk/src/uimacnew/Bridge.m trunk/src/uimacnew/ImageAndTextCell.m trunk/src/uimacnew/MyController.m trunk/src/uimacnew/ProfileController.m trunk/src/uimacnew/ReconItem.h trunk/src/uimacnew/ReconItem.m trunk/src/uimacnew/ReconTableView.m trunk/src/uimacnew/UnisonToolbar.h trunk/src/uimacnew/main.m trunk/src/uimacnew09/Bridge.h trunk/src/uimacnew09/Bridge.m trunk/src/uimacnew09/ImageAndTextCell.m trunk/src/uimacnew09/MyController.m trunk/src/uimacnew09/ProfileController.m trunk/src/uimacnew09/ReconItem.h trunk/src/uimacnew09/ReconItem.m trunk/src/uimacnew09/main.m Log: * Mac GUIs (NEED TESTING): - use doubles rather than ints for file sizes to prevent overflows - should now be 64 bit clean (the Growl framework is not up to date, though) - fixes from uimacnew09 copied back to uimacnew - made the bridge between Objective C and Ocaml code GC friendly (it was allocating ML values and putting them in an array which was not registered with the GC) * Windows: only use long UNC path for accessing replicas (as '..' is not handled with this format of paths, but can be useful) * Bumped fingerprint cache magic number (the format was changed in revision 398) Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/RECENTNEWS 2010-01-10 22:52:59 UTC (rev 400) @@ -1,5 +1,21 @@ CHANGES FROM VERSION 2.39.0 +* Mac GUIs (NEED TESTING): + - use doubles rather than ints for file sizes to prevent overflows + - should now be 64 bit clean (the Growl framework is not up to date, + though) + - fixes from uimacnew09 copied back to uimacnew + - made the bridge between Objective C and Ocaml code GC friendly + (it was allocating ML values and putting them in an array which + was not registered with the GC) +* Windows: only use long UNC path for accessing replicas (as '..' is + not handled with this format of paths, but can be useful) +* Bumped fingerprint cache magic number (the format was changed in + revision 398) + +------------------------------- +CHANGES FROM VERSION 2.39.0 + * Back out some minimal support we'd added for checking out Unison sources via Bazaar. * Small fix for OSX GUI Modified: trunk/src/fpcache.ml =================================================================== --- trunk/src/fpcache.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/fpcache.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -59,7 +59,7 @@ let compress state path = let s = state.last in let p = Path.toString path in - let l = String.length s in + let l = min (String.length p) (String.length s) in let i = ref 0 in while !i < l && p.[!i] = s.[!i] do incr i done; state.last <- p; @@ -126,7 +126,7 @@ closeOut st | None -> () -let magic = "Unison fingerprint cache format 1" +let magic = "Unison fingerprint cache format 2" let init fastCheck fspath = finish (); Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/mkProjectInfo.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -100,3 +100,4 @@ + Modified: trunk/src/os.ml =================================================================== --- trunk/src/os.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/os.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -292,14 +292,7 @@ genericName (* build a fspath representing an archive child path whose name is given *) -let fileInUnisonDir str = - begin try - ignore (Name.fromString str) - with Invalid_argument _ -> - raise (Util.Transient - ("Ill-formed name of file in UNISON directory: "^str)) - end; - System.fspathConcat unisonDir str +let fileInUnisonDir str = System.fspathConcat unisonDir str (* Make sure archive directory exists *) let createUnisonDir() = @@ -316,9 +309,9 @@ (*****************************************************************************) (* Truncate a filename to at most [l] bytes, making sure of not - truncating an UTF-8 character *) + truncating an UTF-8 character. Assumption: [String.length s > l] *) let rec truncate_filename s l = - if l >= 0 && Char.code s.[l] land 0xC0 = 0x80 then + if l > 0 && Char.code s.[l] land 0xC0 = 0x80 then truncate_filename s (l - 1) else String.sub s 0 l Modified: trunk/src/osx.ml =================================================================== --- trunk/src/osx.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/osx.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -206,7 +206,7 @@ (* Remove trailing zeroes *) let trim s = let rec trim_rec s pos = - if s.[pos - 1] = '\000' then + if pos > 0 && s.[pos - 1] = '\000' then trim_rec s (pos - 1) else String.sub s 0 pos Modified: trunk/src/props.ml =================================================================== --- trunk/src/props.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/props.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -614,7 +614,8 @@ let toString t = match t with - Some s when s.[0] = 'F' && String.sub (s ^ zeroes) 1 8 <> zeroes -> + Some s when String.length s > 0 && s.[0] = 'F' && + String.sub (s ^ zeroes) 1 8 <> zeroes -> let s = s ^ zeroes in " " ^ String.escaped (String.sub s 1 4) ^ " " ^ String.escaped (String.sub s 5 4) Modified: trunk/src/remote.ml =================================================================== --- trunk/src/remote.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/remote.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -987,10 +987,6 @@ negociateFlowControl conn; Lwt.return conn) -let inetAddr host = - let targetHostEntry = Unix.gethostbyname host in - targetHostEntry.Unix.h_addr_list.(0) - let rec findFirst f l = match l with [] -> None Modified: trunk/src/system/system_win.ml =================================================================== --- trunk/src/system/system_win.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/system/system_win.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -22,6 +22,8 @@ *) +module M (P : sig val useLongUNCPaths : bool end) = struct + type fspath = string let fspathFromString f = f @@ -43,7 +45,9 @@ let winRootRx = Rx.rx "[a-zA-Z]:[/\\].*" let winUncRx = Rx.rx "[/\\][/\\][^/\\]+[/\\][^/\\]+[/\\].*" let extendedPath f = - if Rx.match_string winRootRx f then + if not P.useLongUNCPaths then + f + else if Rx.match_string winRootRx f then fixPath ("\\\\?\\" ^ f) else if Rx.match_string winUncRx f then fixPath ("\\\\?\\UNC" ^ String.sub f 1 (String.length f - 1)) @@ -318,3 +322,5 @@ rawTerminal = (fun () -> setConsoleMode 0x19; setConsoleOutputCP 65001); startReading = (fun () -> setConsoleMode 0x18); stopReading = (fun () -> setConsoleMode 0x19) } + +end Modified: trunk/src/system/win/system_impl.ml =================================================================== --- trunk/src/system/win/system_impl.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/system/win/system_impl.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -15,7 +15,7 @@ along with this program. If not, see . *) -module System = System_win +module System = System_win.M (struct let useLongUNCPaths = false end) module Fs = struct @@ -28,7 +28,7 @@ let c3 f1 f2 v1 v2 v3 = if !unicode then f1 v1 v2 v3 else f2 v1 v2 v3 module G = System_generic - module W = System_win + module W = System_win.M (struct let useLongUNCPaths = true end) type fspath = string Modified: trunk/src/ubase/util.ml =================================================================== --- trunk/src/ubase/util.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/ubase/util.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -383,6 +383,7 @@ if l = 0 || s.[l - 1] <> '\r' then s else String.sub s 0 (l - 1) +(* FIX: quadratic! *) let rec trimWhitespace s = let l = String.length s in if l=0 then s Modified: trunk/src/uimacbridgenew.ml =================================================================== --- trunk/src/uimacbridgenew.ml 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacbridgenew.ml 2010-01-10 22:52:59 UTC (rev 400) @@ -392,9 +392,7 @@ Callback.register "unisonRiToRight" unisonRiToRight;; let unisonRiToFileSize ri = - (*FIX: will not work with files and directories larger than 1 GiB on - 32bit machines! *) - Uutil.Filesize.toInt (riLength ri.ri);; + Uutil.Filesize.toFloat (riLength ri.ri);; Callback.register "unisonRiToFileSize" unisonRiToFileSize;; let unisonRiToFileType ri = @@ -450,9 +448,7 @@ Callback.register "unisonRiToProgress" unisonRiToProgress;; let unisonRiToBytesTransferred ri = - (*FIX: will not work when transferring more than 1 GiB on 32bit - machines! *) - Uutil.Filesize.toInt ri.bytesTransferred;; + Uutil.Filesize.toFloat ri.bytesTransferred;; Callback.register "unisonRiToBytesTransferred" unisonRiToBytesTransferred;; (* --------------------------------------------------- *) Modified: trunk/src/uimacnew/Bridge.h =================================================================== --- trunk/src/uimacnew/Bridge.h 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/Bridge.h 2010-01-10 22:52:59 UTC (rev 400) @@ -24,14 +24,14 @@ Args/return values are converted to/from C/OCaml according to the supplied type signture string. Type codes are: x - void (for return type) - i - int + i - long s - char * S - NSString * + N - NSNumber * @ - OCamlValue (see below) - v - unwrapped OCaml value (deprecated -- unsafe!) Examples: - int count = (int)ocamlCall("iS", "lengthOfString", @"Some String"); + long count = (long)ocamlCall("iS", "lengthOfString", @"Some String"); (void)ocamlCall("x", "someVoidOCamlFunction"); @@ -42,17 +42,17 @@ // Wrapper/proxy for unconverted OCaml values @interface OCamlValue : NSObject { - int _v; + long _v; } -- initWithValue:(int)v; +- initWithValue:(long)v; -- (void *)getField:(int)i withType:(char)t; +- (void *)getField:(long)i withType:(char)t; // get value by position. See ocamlCall for list of type conversion codes -- (int)count; +- (long)count; // count of items in array -- (int)value; +- (long)value; // returns Ocaml value directly -- not safe to use except in direct callback from OCaml // (i.e. in the OCaml thread) @end Modified: trunk/src/uimacnew/Bridge.m =================================================================== --- trunk/src/uimacnew/Bridge.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/Bridge.m 2010-01-10 22:52:59 UTC (rev 400) @@ -95,7 +95,10 @@ // NSLog(@"*** caml_init complete!"); } -- (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldLogException:(NSException *)exception mask:(unsigned int)aMask +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +typedef unsigned int NSUInteger; +#endif +- (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldLogException:(NSException *)exception mask:(NSUInteger)aMask { // if (![[exception name] isEqual:@"OCamlException"]) return YES; @@ -121,7 +124,7 @@ // Field access value *valueP; - int fieldIndex; + long fieldIndex; char fieldType; // Return values @@ -139,9 +142,11 @@ // Our OCaml callback server thread -- waits for call then makes them // Called from thread spawned from OCaml -CAMLprim value bridgeThreadWait(int ignore) +CAMLprim value bridgeThreadWait(value ignore) { - value args[10]; + CAMLparam0(); + CAMLlocal1 (args); + args = caml_alloc_tuple(3); // NSLog(@"*** bridgeThreadWait init! (%d) Taking lock...", pthread_self()); while (TRUE) { @@ -168,6 +173,7 @@ char retType = 'v'; value e = Val_unit; if (cs->opCode == SafeCall) { + int i; char *fname = va_arg(cs->args, char *); value *f = caml_named_value(fname); // varargs with C-based args -- convert them to OCaml values based on type code string @@ -179,33 +185,31 @@ switch (*p) { case 's': str = va_arg(cs->args, const char *); - args[argCount] = caml_copy_string(str); + Store_field (args, argCount, caml_copy_string(str)); break; case 'S': str = [va_arg(cs->args, NSString *) UTF8String]; - args[argCount] = caml_copy_string(str); + Store_field (args, argCount, caml_copy_string(str)); break; - case 'n': - // leak? - args[argCount] = *caml_named_value(va_arg(cs->args, char *)); - break; case 'i': - args[argCount] = Val_int(va_arg(cs->args, int)); + Store_field (args, argCount, Val_long(va_arg(cs->args, long))); break; - case 'v': - args[argCount] = va_arg(cs->args, value); - break; case '@': - args[argCount] = [va_arg(cs->args, OCamlValue *) value]; + Store_field (args, argCount, [va_arg(cs->args, OCamlValue *) value]); break; + default: + NSCAssert1(0, @"Unknown input type '%c'", *p); + break; } argCount++; + NSCAssert(argCount <= 3, @"More than 3 arguments"); } // Call OCaml -- TODO: add support for > 3 args - if (argCount == 3) e = caml_callback3_exn(*f,args[0],args[1],args[2]); - else if (argCount == 2) e = caml_callback2_exn(*f,args[0],args[1]); - else if (argCount == 1) e = caml_callback_exn(*f,args[0]); + if (argCount == 3) e = caml_callback3_exn(*f,Field(args,0),Field(args,1),Field(args,2)); + else if (argCount == 2) e = caml_callback2_exn(*f,Field(args,0),Field(args,1)); + else if (argCount == 1) e = caml_callback_exn(*f,Field(args,0)); else e = caml_callback_exn(*f,Val_unit); + for (i = 0; i < argCount; i++) Store_field (args, i, Val_unit); } else if (cs->opCode == OldCall) { // old style (unsafe) version where OCaml values were passed directly from C thread if (cs->argCount == 3) e = caml_callback3_exn(cs->call,cs->a1,cs->a2,cs->a3); @@ -213,8 +217,8 @@ else e = caml_callback_exn(cs->call,cs->a1); retType = 'v'; } else if (cs->opCode == FieldAccess) { - int index = cs->fieldIndex; - e = (index == -1) ? Val_int(Wosize_val(*cs->valueP)) : Field(*cs->valueP, cs->fieldIndex); + long index = cs->fieldIndex; + e = (index == -1) ? Val_long(Wosize_val(*cs->valueP)) : Field(*cs->valueP, index); retType = cs->fieldType; } @@ -223,30 +227,38 @@ cs->ret = e; // OCaml return type -- unsafe... if (!Is_exception_result(e)) { switch (retType) { - case 's': - *((char **)&cs->retV) = (e == Val_unit) ? NULL : String_val(e); - break; case 'S': *((NSString **)&cs->retV) = (e == Val_unit) ? NULL : [[NSString alloc] initWithUTF8String:String_val(e)]; cs->_autorelease = TRUE; break; + case 'N': + if (Is_long (e)) { + *((NSNumber **)&cs->retV) = [[NSNumber alloc] initWithLong:Long_val(e)]; + } else { + *((NSNumber **)&cs->retV) = [[NSNumber alloc] initWithDouble:Double_val(e)]; + } + cs->_autorelease = TRUE; + break; case '@': *((NSObject **)&cs->retV) = (e == Val_unit) ? NULL : [[OCamlValue alloc] initWithValue:e]; cs->_autorelease = TRUE; break; - case 'v': - *((value *)&cs->retV) = e; - break; case 'i': - *((int *)&cs->retV) = Int_val(e); + *((long *)&cs->retV) = Long_val(e); break; + case 'x': + break; + default: + NSCAssert1(0, @"Unknown return type '%c'", retType); + break; } } if (Is_exception_result(e)) { // get exception string -- it will get thrown back in the calling thread value *f = caml_named_value("unisonExnInfo"); - cs->exception = String_val(caml_callback(*f,Extract_exception(e))); + // We leak memory here... + cs->exception = strdup(String_val(caml_callback(*f,Extract_exception(e)))); } [pool release]; @@ -260,7 +272,7 @@ pthread_mutex_unlock(&global_res_lock); } // Never get here... - return Val_unit; + CAMLreturn (Val_unit); } void *_passCall(CallState *cs) @@ -292,20 +304,18 @@ void *ocamlCall(const char *argTypes, ...) { - va_list ap; - va_start(ap, argTypes); CallState cs; cs.opCode = SafeCall; cs.exception = NULL; cs.argTypes = argTypes; - cs.args = ap; + va_start(cs.args, argTypes); void * res = _passCall(&cs); - va_end(ap); + va_end(cs.args); return res; } -void *getField(value *vP, int index, char type) +void *getField(value *vP, long index, char type) { CallState cs; cs.opCode = FieldAccess; @@ -318,7 +328,7 @@ @implementation OCamlValue -- initWithValue:(int)v +- initWithValue:(long)v { [super init]; _v = v; @@ -326,17 +336,17 @@ return self; } -- (int)count +- (long)count { - return (int)getField((value *)&_v, -1, 'i'); + return (long)getField((value *) &_v, -1, 'i'); } -- (void *)getField:(int)i withType:(char)t +- (void *)getField:(long)i withType:(char)t { - return getField((value *)&_v, i, t); + return getField((value *)&_v, i, t); } -- (int)value +- (long)value { // Unsafe to use! return _v; Modified: trunk/src/uimacnew/ImageAndTextCell.m =================================================================== --- trunk/src/uimacnew/ImageAndTextCell.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/ImageAndTextCell.m 2010-01-10 22:52:59 UTC (rev 400) @@ -88,7 +88,10 @@ [super editWithFrame: textFrame inView: controlView editor:textObj delegate:anObject event: theEvent]; } -- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(int)selStart length:(int)selLength { +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +typedef int NSInteger; +#endif +- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength { NSRect textFrame, imageFrame; NSDivideRect (aRect, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge); [super selectWithFrame: textFrame inView: controlView editor:textObj delegate:anObject start:selStart length:selLength]; Modified: trunk/src/uimacnew/MyController.m =================================================================== --- trunk/src/uimacnew/MyController.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/MyController.m 2010-01-10 22:52:59 UTC (rev 400) @@ -31,7 +31,9 @@ // BCP (11/09): Added per Onne Gorter: // if user closes main window, terminate app, instead of keeping an empty app around with no window -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { return YES; } +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { + return YES; +} - (id)init { @@ -166,8 +168,7 @@ } /* Only valid once a profile has been selected */ -- (NSString *)profile -{ +- (NSString *)profile { return myProfile; } @@ -176,8 +177,7 @@ [aProfile retain]; [myProfile release]; myProfile = aProfile; - [mainWindow setTitle: - [NSString stringWithFormat:@"Unison: %@", myProfile]]; + [mainWindow setTitle: [NSString stringWithFormat:@"Unison: %@", myProfile]]; } - (IBAction)restartButton:(id)sender @@ -217,7 +217,7 @@ { [tableView reloadData]; if (shouldResetSelection) { - [tableView selectRow:0 byExtendingSelection:NO]; + [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; shouldResetSelection = NO; } [updatesView setNeedsDisplay:YES]; @@ -268,6 +268,7 @@ CAMLprim value unisonInit1Complete(value v) { + id pool = [[NSAutoreleasePool alloc] init]; if (v == Val_unit) { NSLog(@"Connected."); [me->preconn release]; @@ -278,7 +279,7 @@ me->preconn = [[OCamlValue alloc] initWithValue:Field(v,0)]; // value of Some [me performSelectorOnMainThread:@selector(unisonInit1Complete:) withObject:nil waitUntilDone:FALSE]; } - + [pool release]; return Val_unit; } @@ -310,7 +311,7 @@ { // FIX: some prompts don't ask for password, need to look at it NSLog(@"Got the prompt: '%@'",prompt); - if ((int)ocamlCall("iS", "unisonPasswordMsg", prompt)) { + if ((long)ocamlCall("iS", "unisonPasswordMsg", prompt)) { [passwordPrompt setStringValue:@"Please enter your password"]; [NSApp beginSheet:passwordWindow modalForWindow:mainWindow @@ -319,7 +320,7 @@ contextInfo:nil]; return; } - if ((int)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { + if ((long)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { [passwordPrompt setStringValue:@"Please enter your passphrase"]; [NSApp beginSheet:passwordWindow modalForWindow:mainWindow @@ -328,7 +329,7 @@ contextInfo:nil]; return; } - if ((int)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { + if ((long)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { int i = NSRunAlertPanel(@"New host",prompt,@"Yes",@"No",nil); if (i == NSAlertDefaultReturn) { ocamlCall("x at s", "openConnectionReply", preconn, "yes"); @@ -466,7 +467,9 @@ CAMLprim value unisonInit2Complete(value v) { + id pool = [[NSAutoreleasePool alloc] init]; [me performSelectorOnMainThread:@selector(afterUpdate:) withObject:[[OCamlValue alloc] initWithValue:v] waitUntilDone:FALSE]; + [pool release]; return Val_unit; } @@ -502,7 +505,9 @@ CAMLprim value syncComplete() { + id pool = [[NSAutoreleasePool alloc] init]; [me performSelectorOnMainThread:@selector(afterSync:) withObject:nil waitUntilDone:FALSE]; + [pool release]; return Val_unit; } @@ -517,10 +522,12 @@ CAMLprim value reloadTable(value row) { + id pool = [[NSAutoreleasePool alloc] init]; // NSLog(@"OCaml says... ReloadTable: %i", Int_val(row)); NSNumber *num = [[NSNumber alloc] initWithInt:Int_val(row)]; [me performSelectorOnMainThread:@selector(reloadTable:) withObject:num waitUntilDone:FALSE]; [num release]; + [pool release]; return Val_unit; } @@ -556,9 +563,9 @@ [(ImageAndTextCell*)cell setImage:[item fileIcon]]; // For parents, format the file count into the text - int fileCount = [item fileCount]; + long fileCount = [item fileCount]; if (fileCount > 1) { - NSString *countString = [NSString stringWithFormat:@" (%i files)", fileCount]; + NSString *countString = [NSString stringWithFormat:@" (%ld files)", fileCount]; NSString *fullString = [(NSString *)[cell objectValue] stringByAppendingString:countString]; NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithString:fullString]; @@ -623,7 +630,7 @@ { [reconItems release]; reconItems = [[NSMutableArray alloc] init]; - int i, n =[caml_reconItems count]; + long i, n =[caml_reconItems count]; for (i=0; istatusText performSelectorOnMainThread:@selector(setStringValue:) withObject:str waitUntilDone:FALSE]; + [pool release]; return Val_unit; } Modified: trunk/src/uimacnew/ProfileController.m =================================================================== --- trunk/src/uimacnew/ProfileController.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/ProfileController.m 2010-01-10 22:52:59 UTC (rev 400) @@ -13,7 +13,11 @@ - (void)initProfiles { NSString *directory = unisonDirectory(); +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 NSArray *files = [[NSFileManager defaultManager] directoryContentsAtPath:directory]; +#else + NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directory error:nil]; +#endif unsigned int count = [files count]; unsigned int i,j; @@ -31,7 +35,7 @@ } } if (j > 0) - [tableView selectRow:0 byExtendingSelection:NO]; + [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; } - (void)awakeFromNib @@ -39,7 +43,7 @@ // start with the default profile selected [self initProfiles]; if (defaultIndex >= 0) - [tableView selectRow:defaultIndex byExtendingSelection:NO]; + [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:defaultIndex] byExtendingSelection:NO]; // on awake the scroll bar is inactive, but after adding profiles we might need it; // reloadData makes it happen. Q: is setNeedsDisplay more efficient? [tableView reloadData]; Modified: trunk/src/uimacnew/ReconItem.h =================================================================== --- trunk/src/uimacnew/ReconItem.h 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/ReconItem.h 2010-01-10 22:52:59 UTC (rev 400) @@ -12,8 +12,8 @@ BOOL selected; NSImage *direction; NSString *directionSortString; - int fileSize; - int bytesTransferred; + double fileSize; + double bytesTransferred; BOOL resolved; } - (BOOL)selected; @@ -24,10 +24,10 @@ - (NSString *)right; - (NSImage *)direction; - (NSImage *)fileIcon; -- (int)fileCount; -- (int)fileSize; +- (long)fileCount; +- (double)fileSize; - (NSString *)fileSizeString; -- (int)bytesTransferred; +- (double)bytesTransferred; - (NSString *)bytesTransferredString; - (void)setDirection:(char *)d; - (void) doAction:(unichar)action; @@ -64,15 +64,15 @@ NSString *progress; NSString *details; OCamlValue *ri; // an ocaml Common.reconItem - int index; // index in Ri list + long index; // index in Ri list } -- initWithRiAndIndex:(OCamlValue *)v index:(int)i; +- initWithRiAndIndex:(OCamlValue *)v index:(long)i; @end @interface ParentReconItem : ReconItem { NSMutableArray *_children; - int fileCount; + long fileCount; } - (void)addChild:(ReconItem *)item nested:(BOOL)useNesting; - (void)sortUsingDescriptors:(NSArray *)sortDescriptors; Modified: trunk/src/uimacnew/ReconItem.m =================================================================== --- trunk/src/uimacnew/ReconItem.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/ReconItem.m 2010-01-10 22:52:59 UTC (rev 400) @@ -9,8 +9,8 @@ [super init]; selected = NO; // NB only used/updated during sorts. Not a // reliable indicator of whether item is selected - fileSize = -1; - bytesTransferred = -1; + fileSize = -1.; + bytesTransferred = -1.; return self; } @@ -129,30 +129,29 @@ } -- (int)computeFileSize +- (double)computeFileSize { - return 0; + return 0.; } -- (int)bytesTransferred +- (double)bytesTransferred { - return 0; + return 0.; } -- (int)fileCount +- (long)fileCount { return 1; } -- (int)fileSize +- (double)fileSize { - if (fileSize == -1) fileSize = [self computeFileSize]; + if (fileSize == -1.) fileSize = [self computeFileSize]; return fileSize; } -- (NSString *)formatFileSize:(int)intSize +- (NSString *)formatFileSize:(double)size { - float size = (float)intSize; if (size == 0) return @"--"; if (size < 1024) return @"< 1KB"; // return [NSString stringWithFormat:@"%i bytes", size]; size /= 1024; @@ -175,8 +174,8 @@ - (NSNumber *)percentTransferred { - int size = [self computeFileSize]; - return (size > 0) ? [NSNumber numberWithFloat:(((float)[self bytesTransferred]) / (float)size) * 100.0] + double size = [self computeFileSize]; + return (size > 0) ? [NSNumber numberWithDouble:([self bytesTransferred] / (size) * 100.0)] : nil; } @@ -379,8 +378,8 @@ - (BOOL)transferInProgress { - int soFar = [self bytesTransferred]; - return (soFar > 0) && (soFar != [self fileSize]); + double soFar = [self bytesTransferred]; + return (soFar > 0) && (soFar < [self fileSize]); } - (void)resetProgress @@ -390,7 +389,7 @@ - (NSString *)progressString { NSString *progress = [self progress]; - if ([progress length] == 0 || [progress hasSuffix:@"%"]) + if ([progress length] == 0. || [progress hasSuffix:@"%"]) progress = [self transferInProgress] ? [self bytesTransferredString] : @""; else if ([progress isEqual:@"done"]) progress = @""; return progress; @@ -443,7 +442,7 @@ // --- Leaf items -- actually corresponding to ReconItems in OCaml @implementation LeafReconItem -- initWithRiAndIndex:(OCamlValue *)v index:(int)i +- initWithRiAndIndex:(OCamlValue *)v index:(long)i { [super init]; ri = [v retain]; @@ -482,17 +481,17 @@ return right; } -- (int)computeFileSize +- (double)computeFileSize { - return (int)ocamlCall("i@", "unisonRiToFileSize", ri); + return [(NSNumber *)ocamlCall("N@", "unisonRiToFileSize", ri) doubleValue]; } -- (int)bytesTransferred +- (double)bytesTransferred { - if (bytesTransferred == -1) { + if (bytesTransferred == -1.) { // need to force to fileSize if done, otherwise may not match up to 100% bytesTransferred = ([[self progress] isEqual:@"done"]) ? [self fileSize] - : (int)ocamlCall("i@", "unisonRiToBytesTransferred", ri); + : [(NSNumber*)ocamlCall("N@", "unisonRiToBytesTransferred", ri) doubleValue]; } return bytesTransferred; } @@ -535,7 +534,7 @@ { // Get rid of the memoized progress because we expect it to change [self willChange]; - bytesTransferred = -1; + bytesTransferred = -1.; [progress release]; // Force update now so we get the result while the OCaml thread is available @@ -559,12 +558,12 @@ - (BOOL)isConflict { - return ((int)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); + return ((long)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); } - (BOOL)changedFromDefault { - return ((int)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); + return ((long)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); } - (void)revertDirection @@ -575,7 +574,7 @@ - (BOOL)canDiff { - return ((int)ocamlCall("i@", "canDiff", ri) ? YES : NO); + return ((long)ocamlCall("i@", "canDiff", ri) ? YES : NO); } - (void)showDiffs @@ -715,7 +714,7 @@ // [directionSortString autorelease]; direction = nil; directionSortString = nil; - bytesTransferred = -1; + bytesTransferred = -1.; // fileSize = -1; // resolved = NO; @@ -748,7 +747,7 @@ } // Rollup methods -- (int)fileCount +- (long)fileCount { if (fileCount == 0) { int i = [_children count]; @@ -760,9 +759,9 @@ return fileCount; } -- (int)computeFileSize +- (double)computeFileSize { - int size = 0; + double size = 0; int i = [_children count]; while (i--) { ReconItem *child = [_children objectAtIndex:i]; @@ -771,10 +770,10 @@ return size; } -- (int)bytesTransferred +- (double)bytesTransferred { - if (bytesTransferred == -1) { - bytesTransferred = 0; + if (bytesTransferred == -1.) { + bytesTransferred = 0.; int i = [_children count]; while (i--) { ReconItem *child = [_children objectAtIndex:i]; Modified: trunk/src/uimacnew/ReconTableView.m =================================================================== --- trunk/src/uimacnew/ReconTableView.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/ReconTableView.m 2010-01-10 22:52:59 UTC (rev 400) @@ -11,12 +11,16 @@ #import "MyController.h" @implementation NSOutlineView (_UnisonExtras) + - (NSArray *)selectedObjects { NSMutableArray *result = [NSMutableArray array]; - NSEnumerator *e = [self selectedRowEnumerator]; - NSNumber *n; - while (n = [e nextObject]) [result addObject:[self itemAtRow:[n intValue]]]; + NSIndexSet *set = [self selectedRowIndexes]; + NSUInteger index = [set firstIndex]; + while (index != NSNotFound) { + [result addObject:[self itemAtRow:index]]; + index = [set indexGreaterThanIndex: index]; + } return result; } @@ -136,8 +140,9 @@ last = item; } if (last) { // something was selected - last = [[self dataSource] updateForIgnore:last]; - [self selectRow:[self rowForItem:last] byExtendingSelection:NO]; + MyController* controller = (MyController*) [self dataSource]; + last = [controller updateForIgnore:last]; + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:[self rowForItem:last]] byExtendingSelection:NO]; [self reloadData]; } } @@ -171,7 +176,7 @@ int nextRow = [self rowForItem:last] + 1; if (numSelected == 1 && [self numberOfRows] > nextRow && c!='d') { // Move to next row, unless already at last row, or if more than one row selected - [self selectRow:nextRow byExtendingSelection:NO]; + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:nextRow] byExtendingSelection:NO]; [self scrollRowToVisible:nextRow]; } [self reloadData]; @@ -206,12 +211,13 @@ - (IBAction)selectConflicts:(id)sender { [self deselectAll:self]; - NSMutableArray *reconItems = [[self dataSource] reconItems]; + MyController* controller = (MyController*) [self dataSource]; + NSMutableArray *reconItems = [controller reconItems]; int i = 0; for (; i < [reconItems count]; i++) { ReconItem *item = [reconItems objectAtIndex:i]; if ([item isConflict]) - [self selectRow:[self rowForItem:item] byExtendingSelection:YES]; + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:[self rowForItem:item]] byExtendingSelection:YES]; } } Modified: trunk/src/uimacnew/UnisonToolbar.h =================================================================== --- trunk/src/uimacnew/UnisonToolbar.h 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/UnisonToolbar.h 2010-01-10 22:52:59 UTC (rev 400) @@ -12,6 +12,9 @@ @class ReconTableView, MyController; @interface UnisonToolbar : NSToolbar +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) + +#endif { ReconTableView* tableView; MyController* myController; Modified: trunk/src/uimacnew/main.m =================================================================== --- trunk/src/uimacnew/main.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew/main.m 2010-01-10 22:52:59 UTC (rev 400) @@ -11,6 +11,7 @@ int main(int argc, const char *argv[]) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int i; /* When you click-start or use the open command, the program is invoked with @@ -34,22 +35,19 @@ !strcmp(argv[i],"-server") || !strcmp(argv[i],"-socket") || !strcmp(argv[i],"-ui")) { - /* We install an autorelease pool here because there might be callbacks - from ocaml to objc code */ NSLog(@"Calling nonGuiStartup"); - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @try { ocamlCall("x", "unisonNonGuiStartup"); } @catch (NSException *ex) { NSLog(@"Uncaught exception: %@", [ex reason]); exit(1); } - [pool release]; /* If we get here without exiting first, the non GUI startup detected a -ui graphic or command-line profile, and we should in fact start the GUI. */ } } /* go! */ + [pool release]; return NSApplicationMain(argc, argv); } Modified: trunk/src/uimacnew09/Bridge.h =================================================================== --- trunk/src/uimacnew09/Bridge.h 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew09/Bridge.h 2010-01-10 22:52:59 UTC (rev 400) @@ -24,14 +24,14 @@ Args/return values are converted to/from C/OCaml according to the supplied type signture string. Type codes are: x - void (for return type) - i - int + i - long s - char * S - NSString * + N - NSNumber * @ - OCamlValue (see below) - v - unwrapped OCaml value (deprecated -- unsafe!) Examples: - int count = (int)ocamlCall("iS", "lengthOfString", @"Some String"); + long count = (long)ocamlCall("iS", "lengthOfString", @"Some String"); (void)ocamlCall("x", "someVoidOCamlFunction"); @@ -42,17 +42,17 @@ // Wrapper/proxy for unconverted OCaml values @interface OCamlValue : NSObject { - int _v; + long _v; } -- initWithValue:(int)v; +- initWithValue:(long)v; -- (void *)getField:(int)i withType:(char)t; +- (void *)getField:(long)i withType:(char)t; // get value by position. See ocamlCall for list of type conversion codes -- (int)count; +- (long)count; // count of items in array -- (int)value; +- (long)value; // returns Ocaml value directly -- not safe to use except in direct callback from OCaml // (i.e. in the OCaml thread) @end Modified: trunk/src/uimacnew09/Bridge.m =================================================================== --- trunk/src/uimacnew09/Bridge.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew09/Bridge.m 2010-01-10 22:52:59 UTC (rev 400) @@ -95,7 +95,10 @@ // NSLog(@"*** caml_init complete!"); } -- (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldLogException:(NSException *)exception mask:(unsigned int)aMask +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +typedef unsigned int NSUInteger; +#endif +- (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldLogException:(NSException *)exception mask:(NSUInteger)aMask { // if (![[exception name] isEqual:@"OCamlException"]) return YES; @@ -121,7 +124,7 @@ // Field access value *valueP; - int fieldIndex; + long fieldIndex; char fieldType; // Return values @@ -139,9 +142,11 @@ // Our OCaml callback server thread -- waits for call then makes them // Called from thread spawned from OCaml -CAMLprim value bridgeThreadWait(int ignore) +CAMLprim value bridgeThreadWait(value ignore) { - value args[10]; + CAMLparam0(); + CAMLlocal1 (args); + args = caml_alloc_tuple(3); // NSLog(@"*** bridgeThreadWait init! (%d) Taking lock...", pthread_self()); while (TRUE) { @@ -168,6 +173,7 @@ char retType = 'v'; value e = Val_unit; if (cs->opCode == SafeCall) { + int i; char *fname = va_arg(cs->args, char *); value *f = caml_named_value(fname); // varargs with C-based args -- convert them to OCaml values based on type code string @@ -179,33 +185,31 @@ switch (*p) { case 's': str = va_arg(cs->args, const char *); - args[argCount] = caml_copy_string(str); + Store_field (args, argCount, caml_copy_string(str)); break; case 'S': str = [va_arg(cs->args, NSString *) UTF8String]; - args[argCount] = caml_copy_string(str); + Store_field (args, argCount, caml_copy_string(str)); break; - case 'n': - // leak? - args[argCount] = *caml_named_value(va_arg(cs->args, char *)); - break; case 'i': - args[argCount] = Val_int(va_arg(cs->args, int)); + Store_field (args, argCount, Val_long(va_arg(cs->args, long))); break; - case 'v': - args[argCount] = va_arg(cs->args, value); - break; case '@': - args[argCount] = [va_arg(cs->args, OCamlValue *) value]; + Store_field (args, argCount, [va_arg(cs->args, OCamlValue *) value]); break; + default: + NSCAssert1(0, @"Unknown input type '%c'", *p); + break; } argCount++; + NSCAssert(argCount <= 3, @"More than 3 arguments"); } // Call OCaml -- TODO: add support for > 3 args - if (argCount == 3) e = caml_callback3_exn(*f,args[0],args[1],args[2]); - else if (argCount == 2) e = caml_callback2_exn(*f,args[0],args[1]); - else if (argCount == 1) e = caml_callback_exn(*f,args[0]); + if (argCount == 3) e = caml_callback3_exn(*f,Field(args,0),Field(args,1),Field(args,2)); + else if (argCount == 2) e = caml_callback2_exn(*f,Field(args,0),Field(args,1)); + else if (argCount == 1) e = caml_callback_exn(*f,Field(args,0)); else e = caml_callback_exn(*f,Val_unit); + for (i = 0; i < argCount; i++) Store_field (args, i, Val_unit); } else if (cs->opCode == OldCall) { // old style (unsafe) version where OCaml values were passed directly from C thread if (cs->argCount == 3) e = caml_callback3_exn(cs->call,cs->a1,cs->a2,cs->a3); @@ -213,8 +217,8 @@ else e = caml_callback_exn(cs->call,cs->a1); retType = 'v'; } else if (cs->opCode == FieldAccess) { - int index = cs->fieldIndex; - e = (index == -1) ? Val_int(Wosize_val(*cs->valueP)) : Field(*cs->valueP, cs->fieldIndex); + long index = cs->fieldIndex; + e = (index == -1) ? Val_long(Wosize_val(*cs->valueP)) : Field(*cs->valueP, index); retType = cs->fieldType; } @@ -223,30 +227,38 @@ cs->ret = e; // OCaml return type -- unsafe... if (!Is_exception_result(e)) { switch (retType) { - case 's': - *((char **)&cs->retV) = (e == Val_unit) ? NULL : String_val(e); - break; case 'S': *((NSString **)&cs->retV) = (e == Val_unit) ? NULL : [[NSString alloc] initWithUTF8String:String_val(e)]; cs->_autorelease = TRUE; break; + case 'N': + if (Is_long (e)) { + *((NSNumber **)&cs->retV) = [[NSNumber alloc] initWithLong:Long_val(e)]; + } else { + *((NSNumber **)&cs->retV) = [[NSNumber alloc] initWithDouble:Double_val(e)]; + } + cs->_autorelease = TRUE; + break; case '@': *((NSObject **)&cs->retV) = (e == Val_unit) ? NULL : [[OCamlValue alloc] initWithValue:e]; cs->_autorelease = TRUE; break; - case 'v': - *((value *)&cs->retV) = e; - break; case 'i': - *((int *)&cs->retV) = Int_val(e); + *((long *)&cs->retV) = Long_val(e); break; + case 'x': + break; + default: + NSCAssert1(0, @"Unknown return type '%c'", retType); + break; } } if (Is_exception_result(e)) { // get exception string -- it will get thrown back in the calling thread value *f = caml_named_value("unisonExnInfo"); - cs->exception = String_val(caml_callback(*f,Extract_exception(e))); + // We leak memory here... + cs->exception = strdup(String_val(caml_callback(*f,Extract_exception(e)))); } [pool release]; @@ -260,7 +272,7 @@ pthread_mutex_unlock(&global_res_lock); } // Never get here... - return Val_unit; + CAMLreturn (Val_unit); } void *_passCall(CallState *cs) @@ -292,20 +304,18 @@ void *ocamlCall(const char *argTypes, ...) { - va_list ap; - va_start(ap, argTypes); CallState cs; cs.opCode = SafeCall; cs.exception = NULL; cs.argTypes = argTypes; - cs.args = ap; + va_start(cs.args, argTypes); void * res = _passCall(&cs); - va_end(ap); + va_end(cs.args); return res; } -void *getField(value *vP, int index, char type) +void *getField(value *vP, long index, char type) { CallState cs; cs.opCode = FieldAccess; @@ -318,7 +328,7 @@ @implementation OCamlValue -- initWithValue:(int)v +- initWithValue:(long)v { [super init]; _v = v; @@ -326,17 +336,17 @@ return self; } -- (int)count +- (long)count { - return (int)getField((value *)&_v, -1, 'i'); + return (long)getField((value *) &_v, -1, 'i'); } -- (void *)getField:(int)i withType:(char)t +- (void *)getField:(long)i withType:(char)t { - return getField((value *)&_v, i, t); + return getField((value *)&_v, i, t); } -- (int)value +- (long)value { // Unsafe to use! return _v; Modified: trunk/src/uimacnew09/ImageAndTextCell.m =================================================================== --- trunk/src/uimacnew09/ImageAndTextCell.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew09/ImageAndTextCell.m 2010-01-10 22:52:59 UTC (rev 400) @@ -88,7 +88,10 @@ [super editWithFrame: textFrame inView: controlView editor:textObj delegate:anObject event: theEvent]; } -- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(int)selStart length:(int)selLength { +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +typedef int NSInteger; +#endif +- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength { NSRect textFrame, imageFrame; NSDivideRect (aRect, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge); [super selectWithFrame: textFrame inView: controlView editor:textObj delegate:anObject start:selStart length:selLength]; Modified: trunk/src/uimacnew09/MyController.m =================================================================== --- trunk/src/uimacnew09/MyController.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew09/MyController.m 2010-01-10 22:52:59 UTC (rev 400) @@ -395,7 +395,7 @@ { // FIX: some prompts don't ask for password, need to look at it NSLog(@"Got the prompt: '%@'",prompt); - if ((int)ocamlCall("iS", "unisonPasswordMsg", prompt)) { + if ((long)ocamlCall("iS", "unisonPasswordMsg", prompt)) { [passwordPrompt setStringValue:@"Please enter your password"]; [NSApp beginSheet:passwordWindow modalForWindow:mainWindow @@ -404,7 +404,7 @@ contextInfo:nil]; return; } - if ((int)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { + if ((long)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { [passwordPrompt setStringValue:@"Please enter your passphrase"]; [NSApp beginSheet:passwordWindow modalForWindow:mainWindow @@ -413,7 +413,7 @@ contextInfo:nil]; return; } - if ((int)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { + if ((long)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { int i = NSRunAlertPanel(@"New host",prompt,@"Yes",@"No",nil); if (i == NSAlertDefaultReturn) { ocamlCall("x at s", "openConnectionReply", preconn, "yes"); @@ -649,9 +649,9 @@ [(ImageAndTextCell*)cell setImage:[item fileIcon]]; // For parents, format the file count into the text - int fileCount = [item fileCount]; + long fileCount = [item fileCount]; if (fileCount > 1) { - NSString *countString = [NSString stringWithFormat:@" (%i files)", fileCount]; + NSString *countString = [NSString stringWithFormat:@" (%ld files)", fileCount]; NSString *fullString = [(NSString *)[cell objectValue] stringByAppendingString:countString]; NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithString:fullString]; @@ -716,7 +716,7 @@ { [reconItems release]; reconItems = [[NSMutableArray alloc] init]; - int i, n =[caml_reconItems count]; + long i, n =[caml_reconItems count]; for (i=0; i 0) ? [NSNumber numberWithFloat:(((float)[self bytesTransferred]) / (float)size) * 100.0] + double size = [self computeFileSize]; + return (size > 0) ? [NSNumber numberWithDouble:([self bytesTransferred] / (size) * 100.0)] : nil; } @@ -379,8 +378,8 @@ - (BOOL)transferInProgress { - int soFar = [self bytesTransferred]; - return (soFar > 0) && (soFar != [self fileSize]); + double soFar = [self bytesTransferred]; + return (soFar > 0) && (soFar < [self fileSize]); } - (void)resetProgress @@ -390,7 +389,7 @@ - (NSString *)progressString { NSString *progress = [self progress]; - if ([progress length] == 0 || [progress hasSuffix:@"%"]) + if ([progress length] == 0. || [progress hasSuffix:@"%"]) progress = [self transferInProgress] ? [self bytesTransferredString] : @""; else if ([progress isEqual:@"done"]) progress = @""; return progress; @@ -443,7 +442,7 @@ // --- Leaf items -- actually corresponding to ReconItems in OCaml @implementation LeafReconItem -- initWithRiAndIndex:(OCamlValue *)v index:(int)i +- initWithRiAndIndex:(OCamlValue *)v index:(long)i { [super init]; ri = [v retain]; @@ -482,17 +481,17 @@ return right; } -- (int)computeFileSize +- (double)computeFileSize { - return (int)ocamlCall("i@", "unisonRiToFileSize", ri); + return [(NSNumber *)ocamlCall("N@", "unisonRiToFileSize", ri) doubleValue]; } -- (int)bytesTransferred +- (double)bytesTransferred { - if (bytesTransferred == -1) { + if (bytesTransferred == -1.) { // need to force to fileSize if done, otherwise may not match up to 100% bytesTransferred = ([[self progress] isEqual:@"done"]) ? [self fileSize] - : (int)ocamlCall("i@", "unisonRiToBytesTransferred", ri); + : [(NSNumber*)ocamlCall("N@", "unisonRiToBytesTransferred", ri) doubleValue]; } return bytesTransferred; } @@ -535,7 +534,7 @@ { // Get rid of the memoized progress because we expect it to change [self willChange]; - bytesTransferred = -1; + bytesTransferred = -1.; [progress release]; // Force update now so we get the result while the OCaml thread is available @@ -559,12 +558,12 @@ - (BOOL)isConflict { - return ((int)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); + return ((long)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); } - (BOOL)changedFromDefault { - return ((int)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); + return ((long)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); } - (void)revertDirection @@ -575,7 +574,7 @@ - (BOOL)canDiff { - return ((int)ocamlCall("i@", "canDiff", ri) ? YES : NO); + return ((long)ocamlCall("i@", "canDiff", ri) ? YES : NO); } - (void)showDiffs @@ -715,7 +714,7 @@ // [directionSortString autorelease]; direction = nil; directionSortString = nil; - bytesTransferred = -1; + bytesTransferred = -1.; // fileSize = -1; // resolved = NO; @@ -748,7 +747,7 @@ } // Rollup methods -- (int)fileCount +- (long)fileCount { if (fileCount == 0) { int i = [_children count]; @@ -760,9 +759,9 @@ return fileCount; } -- (int)computeFileSize +- (double)computeFileSize { - int size = 0; + double size = 0; int i = [_children count]; while (i--) { ReconItem *child = [_children objectAtIndex:i]; @@ -771,10 +770,10 @@ return size; } -- (int)bytesTransferred +- (double)bytesTransferred { - if (bytesTransferred == -1) { - bytesTransferred = 0; + if (bytesTransferred == -1.) { + bytesTransferred = 0.; int i = [_children count]; while (i--) { ReconItem *child = [_children objectAtIndex:i]; Modified: trunk/src/uimacnew09/main.m =================================================================== --- trunk/src/uimacnew09/main.m 2010-01-10 15:30:18 UTC (rev 399) +++ trunk/src/uimacnew09/main.m 2010-01-10 22:52:59 UTC (rev 400) @@ -11,7 +11,7 @@ int main(int argc, const char *argv[]) { - id pool = [[NSAutoreleasePool alloc] init]; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int i; /* When you click-start or use the open command, the program is invoked with @@ -35,17 +35,13 @@ !strcmp(argv[i],"-server") || !strcmp(argv[i],"-socket") || !strcmp(argv[i],"-ui")) { - /* We install an autorelease pool here because there might be callbacks - from ocaml to objc code */ NSLog(@"Calling nonGuiStartup"); - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @try { ocamlCall("x", "unisonNonGuiStartup"); } @catch (NSException *ex) { NSLog(@"Uncaught exception: %@", [ex reason]); exit(1); } - [pool release]; /* If we get here without exiting first, the non GUI startup detected a -ui graphic or command-line profile, and we should in fact start the GUI. */ } From bcpierce at cis.upenn.edu Sun Jan 10 20:12:32 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Sun, 10 Jan 2010 20:12:32 -0500 Subject: [Unison-hackers] [unison-svn] r400 - in trunk/src: . system system/win ubase uimacnew uimacnew09 In-Reply-To: <201001102253.o0AMr3a1026196@yaws.seas.upenn.edu> References: <201001102253.o0AMr3a1026196@yaws.seas.upenn.edu> Message-ID: <39207FF6-4FBA-4720-9391-6CFADB2EDCEA@cis.upenn.edu> For the macnew09 version, I'm stuck on this compilation error -- any hints? - B /Developer/Library/PrivateFrameworks/DevToolsCore.framework/ Resources/pbxcp -exclude .DS_Store -exclude CVS -exclude .svn -strip- debug-symbols -resolve-src-symlinks /Users/bcpierce/current/unison/ trunk/src/uimacnew09/Frameworks/Growl.framework /Users/bcpierce/ current/unison/trunk/src/uimacnew09/build/Default/Unison.app/Contents/ Frameworks strip: for architecture x86_64 object: /Users/bcpierce/current/unison/ trunk/src/uimacnew09/Frameworks/Growl.framework/Growl malformed object (unknown load command 5) pbxcp: warning: couldn't strip: /Users/bcpierce/current/unison/trunk/ src/uimacnew09/build/Default/Unison.app/Contents/Frameworks/ Growl.framework/Growl: No such file or directory PBXCp build/Default/Unison.app/Contents/Frameworks/ BWToolkitFramework.framework Frameworks/BWToolkitFramework.framework cd /Users/bcpierce/current/unison/trunk/src/uimacnew09 /Developer/Library/PrivateFrameworks/DevToolsCore.framework/ Resources/pbxcp -exclude .DS_Store -exclude CVS -exclude .svn -strip- debug-symbols -resolve-src-symlinks /Users/bcpierce/current/unison/ trunk/src/uimacnew09/Frameworks/BWToolkitFramework.framework /Users/ bcpierce/current/unison/trunk/src/uimacnew09/build/Default/Unison.app/ Contents/Frameworks strip: for architecture x86_64 object: /Users/bcpierce/current/unison/ trunk/src/uimacnew09/Frameworks/BWToolkitFramework.framework/ BWToolkitFramework malformed object (unknown load command 5) pbxcp: warning: couldn't strip: /Users/bcpierce/current/unison/trunk/ src/uimacnew09/build/Default/Unison.app/Contents/Frameworks/ BWToolkitFramework.framework/BWToolkitFramework: No such file or directory ** BUILD FAILED ** The following build commands failed: uimac: PBXCp build/Default/Unison.app/Contents/Frameworks/Growl.framework Frameworks/Growl.framework PBXCp build/Default/Unison.app/Contents/Frameworks/ BWToolkitFramework.framework Frameworks/BWToolkitFramework.framework (2 failures) On Jan 10, 2010, at 5:53 PM, vouillon at seas.upenn.edu wrote: > Author: vouillon > Date: 2010-01-10 17:52:59 -0500 (Sun, 10 Jan 2010) > New Revision: 400 > > Modified: > trunk/src/RECENTNEWS > trunk/src/fpcache.ml > trunk/src/mkProjectInfo.ml > trunk/src/os.ml > trunk/src/osx.ml > trunk/src/props.ml > trunk/src/remote.ml > trunk/src/system/system_win.ml > trunk/src/system/win/system_impl.ml > trunk/src/ubase/util.ml > trunk/src/uimacbridgenew.ml > trunk/src/uimacnew/Bridge.h > trunk/src/uimacnew/Bridge.m > trunk/src/uimacnew/ImageAndTextCell.m > trunk/src/uimacnew/MyController.m > trunk/src/uimacnew/ProfileController.m > trunk/src/uimacnew/ReconItem.h > trunk/src/uimacnew/ReconItem.m > trunk/src/uimacnew/ReconTableView.m > trunk/src/uimacnew/UnisonToolbar.h > trunk/src/uimacnew/main.m > trunk/src/uimacnew09/Bridge.h > trunk/src/uimacnew09/Bridge.m > trunk/src/uimacnew09/ImageAndTextCell.m > trunk/src/uimacnew09/MyController.m > trunk/src/uimacnew09/ProfileController.m > trunk/src/uimacnew09/ReconItem.h > trunk/src/uimacnew09/ReconItem.m > trunk/src/uimacnew09/main.m > Log: > * Mac GUIs (NEED TESTING): > - use doubles rather than ints for file sizes to prevent overflows > - should now be 64 bit clean (the Growl framework is not up to date, > though) > - fixes from uimacnew09 copied back to uimacnew > - made the bridge between Objective C and Ocaml code GC friendly > (it was allocating ML values and putting them in an array which > was not registered with the GC) > * Windows: only use long UNC path for accessing replicas (as '..' is > not handled with this format of paths, but can be useful) > * Bumped fingerprint cache magic number (the format was changed in > revision 398) > > > Modified: trunk/src/RECENTNEWS > =================================================================== > --- trunk/src/RECENTNEWS 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/RECENTNEWS 2010-01-10 22:52:59 UTC (rev 400) > @@ -1,5 +1,21 @@ > CHANGES FROM VERSION 2.39.0 > > +* Mac GUIs (NEED TESTING): > + - use doubles rather than ints for file sizes to prevent overflows > + - should now be 64 bit clean (the Growl framework is not up to > date, > + though) > + - fixes from uimacnew09 copied back to uimacnew > + - made the bridge between Objective C and Ocaml code GC friendly > + (it was allocating ML values and putting them in an array which > + was not registered with the GC) > +* Windows: only use long UNC path for accessing replicas (as '..' is > + not handled with this format of paths, but can be useful) > +* Bumped fingerprint cache magic number (the format was changed in > + revision 398) > + > +------------------------------- > +CHANGES FROM VERSION 2.39.0 > + > * Back out some minimal support we'd added for checking out Unison > sources via Bazaar. > > * Small fix for OSX GUI > > Modified: trunk/src/fpcache.ml > =================================================================== > --- trunk/src/fpcache.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/fpcache.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -59,7 +59,7 @@ > let compress state path = > let s = state.last in > let p = Path.toString path in > - let l = String.length s in > + let l = min (String.length p) (String.length s) in > let i = ref 0 in > while !i < l && p.[!i] = s.[!i] do incr i done; > state.last <- p; > @@ -126,7 +126,7 @@ > closeOut st > | None -> () > > -let magic = "Unison fingerprint cache format 1" > +let magic = "Unison fingerprint cache format 2" > > let init fastCheck fspath = > finish (); > > Modified: trunk/src/mkProjectInfo.ml > =================================================================== > --- trunk/src/mkProjectInfo.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/mkProjectInfo.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -100,3 +100,4 @@ > > > > + > > Modified: trunk/src/os.ml > =================================================================== > --- trunk/src/os.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/os.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -292,14 +292,7 @@ > genericName > > (* build a fspath representing an archive child path whose name is > given *) > -let fileInUnisonDir str = > - begin try > - ignore (Name.fromString str) > - with Invalid_argument _ -> > - raise (Util.Transient > - ("Ill-formed name of file in UNISON directory: "^str)) > - end; > - System.fspathConcat unisonDir str > +let fileInUnisonDir str = System.fspathConcat unisonDir str > > (* Make sure archive directory > exists *) > let createUnisonDir() = > @@ -316,9 +309,9 @@ > (*****************************************************************************) > > (* Truncate a filename to at most [l] bytes, making sure of not > - truncating an UTF-8 character *) > + truncating an UTF-8 character. Assumption: [String.length s > > l] *) > let rec truncate_filename s l = > - if l >= 0 && Char.code s.[l] land 0xC0 = 0x80 then > + if l > 0 && Char.code s.[l] land 0xC0 = 0x80 then > truncate_filename s (l - 1) > else > String.sub s 0 l > > Modified: trunk/src/osx.ml > =================================================================== > --- trunk/src/osx.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/osx.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -206,7 +206,7 @@ > (* Remove trailing zeroes *) > let trim s = > let rec trim_rec s pos = > - if s.[pos - 1] = '\000' then > + if pos > 0 && s.[pos - 1] = '\000' then > trim_rec s (pos - 1) > else > String.sub s 0 pos > > Modified: trunk/src/props.ml > =================================================================== > --- trunk/src/props.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/props.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -614,7 +614,8 @@ > > let toString t = > match t with > - Some s when s.[0] = 'F' && String.sub (s ^ zeroes) 1 8 <> > zeroes -> > + Some s when String.length s > 0 && s.[0] = 'F' && > + String.sub (s ^ zeroes) 1 8 <> zeroes -> > let s = s ^ zeroes in > " " ^ String.escaped (String.sub s 1 4) ^ > " " ^ String.escaped (String.sub s 5 4) > > Modified: trunk/src/remote.ml > =================================================================== > --- trunk/src/remote.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/remote.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -987,10 +987,6 @@ > negociateFlowControl conn; > Lwt.return conn) > > -let inetAddr host = > - let targetHostEntry = Unix.gethostbyname host in > - targetHostEntry.Unix.h_addr_list.(0) > - > let rec findFirst f l = > match l with > [] -> None > > Modified: trunk/src/system/system_win.ml > =================================================================== > --- trunk/src/system/system_win.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/system/system_win.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -22,6 +22,8 @@ > > *) > > +module M (P : sig val useLongUNCPaths : bool end) = struct > + > type fspath = string > > let fspathFromString f = f > @@ -43,7 +45,9 @@ > let winRootRx = Rx.rx "[a-zA-Z]:[/\\].*" > let winUncRx = Rx.rx "[/\\][/\\][^/\\]+[/\\][^/\\]+[/\\].*" > let extendedPath f = > - if Rx.match_string winRootRx f then > + if not P.useLongUNCPaths then > + f > + else if Rx.match_string winRootRx f then > fixPath ("\\\\?\\" ^ f) > else if Rx.match_string winUncRx f then > fixPath ("\\\\?\\UNC" ^ String.sub f 1 (String.length f - 1)) > @@ -318,3 +322,5 @@ > rawTerminal = (fun () -> setConsoleMode 0x19; setConsoleOutputCP > 65001); > startReading = (fun () -> setConsoleMode 0x18); > stopReading = (fun () -> setConsoleMode 0x19) } > + > +end > > Modified: trunk/src/system/win/system_impl.ml > =================================================================== > --- trunk/src/system/win/system_impl.ml 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/system/win/system_impl.ml 2010-01-10 22:52:59 UTC (rev > 400) > @@ -15,7 +15,7 @@ > along with this program. If not, see >. > *) > > -module System = System_win > +module System = System_win.M (struct let useLongUNCPaths = false end) > > module Fs = struct > > @@ -28,7 +28,7 @@ > let c3 f1 f2 v1 v2 v3 = if !unicode then f1 v1 v2 v3 else f2 v1 v2 > v3 > > module G = System_generic > - module W = System_win > + module W = System_win.M (struct let useLongUNCPaths = true end) > > type fspath = string > > > Modified: trunk/src/ubase/util.ml > =================================================================== > --- trunk/src/ubase/util.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/ubase/util.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -383,6 +383,7 @@ > if l = 0 || s.[l - 1] <> '\r' then s else > String.sub s 0 (l - 1) > > +(* FIX: quadratic! *) > let rec trimWhitespace s = > let l = String.length s in > if l=0 then s > > Modified: trunk/src/uimacbridgenew.ml > =================================================================== > --- trunk/src/uimacbridgenew.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacbridgenew.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -392,9 +392,7 @@ > Callback.register "unisonRiToRight" unisonRiToRight;; > > let unisonRiToFileSize ri = > - (*FIX: will not work with files and directories larger than 1 GiB > on > - 32bit machines! *) > - Uutil.Filesize.toInt (riLength ri.ri);; > + Uutil.Filesize.toFloat (riLength ri.ri);; > Callback.register "unisonRiToFileSize" unisonRiToFileSize;; > > let unisonRiToFileType ri = > @@ -450,9 +448,7 @@ > Callback.register "unisonRiToProgress" unisonRiToProgress;; > > let unisonRiToBytesTransferred ri = > - (*FIX: will not work when transferring more than 1 GiB on 32bit > - machines! *) > - Uutil.Filesize.toInt ri.bytesTransferred;; > + Uutil.Filesize.toFloat ri.bytesTransferred;; > Callback.register "unisonRiToBytesTransferred" > unisonRiToBytesTransferred;; > > (* --------------------------------------------------- *) > > Modified: trunk/src/uimacnew/Bridge.h > =================================================================== > --- trunk/src/uimacnew/Bridge.h 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/Bridge.h 2010-01-10 22:52:59 UTC (rev 400) > @@ -24,14 +24,14 @@ > Args/return values are converted to/from C/OCaml according to the > supplied type signture string. Type codes are: > x - void (for return type) > - i - int > + i - long > s - char * > S - NSString * > + N - NSNumber * > @ - OCamlValue (see below) > - v - unwrapped OCaml value (deprecated -- unsafe!) > > Examples: > - int count = (int)ocamlCall("iS", "lengthOfString", @"Some String"); > + long count = (long)ocamlCall("iS", "lengthOfString", @"Some > String"); > > (void)ocamlCall("x", "someVoidOCamlFunction"); > > @@ -42,17 +42,17 @@ > > // Wrapper/proxy for unconverted OCaml values > @interface OCamlValue : NSObject { > - int _v; > + long _v; > } > -- initWithValue:(int)v; > +- initWithValue:(long)v; > > -- (void *)getField:(int)i withType:(char)t; > +- (void *)getField:(long)i withType:(char)t; > // get value by position. See ocamlCall for list of type > conversion codes > > -- (int)count; > +- (long)count; > // count of items in array > > -- (int)value; > +- (long)value; > // returns Ocaml value directly -- not safe to use except in direct > callback from OCaml > // (i.e. in the OCaml thread) > @end > > Modified: trunk/src/uimacnew/Bridge.m > =================================================================== > --- trunk/src/uimacnew/Bridge.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/Bridge.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -95,7 +95,10 @@ > // NSLog(@"*** caml_init complete!"); > } > > -- (BOOL)exceptionHandler:(NSExceptionHandler *)sender > shouldLogException:(NSException *)exception mask:(unsigned int)aMask > +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 > +typedef unsigned int NSUInteger; > +#endif > +- (BOOL)exceptionHandler:(NSExceptionHandler *)sender > shouldLogException:(NSException *)exception mask:(NSUInteger)aMask > { > // if (![[exception name] isEqual:@"OCamlException"]) return YES; > > @@ -121,7 +124,7 @@ > > // Field access > value *valueP; > - int fieldIndex; > + long fieldIndex; > char fieldType; > > // Return values > @@ -139,9 +142,11 @@ > > // Our OCaml callback server thread -- waits for call then makes them > // Called from thread spawned from OCaml > -CAMLprim value bridgeThreadWait(int ignore) > +CAMLprim value bridgeThreadWait(value ignore) > { > - value args[10]; > + CAMLparam0(); > + CAMLlocal1 (args); > + args = caml_alloc_tuple(3); > > // NSLog(@"*** bridgeThreadWait init! (%d) Taking lock...", > pthread_self()); > while (TRUE) { > @@ -168,6 +173,7 @@ > char retType = 'v'; > value e = Val_unit; > if (cs->opCode == SafeCall) { > + int i; > char *fname = va_arg(cs->args, char *); > value *f = caml_named_value(fname); > // varargs with C-based args -- convert them to OCaml values > based on type code string > @@ -179,33 +185,31 @@ > switch (*p) { > case 's': > str = va_arg(cs->args, const char *); > - args[argCount] = caml_copy_string(str); > + Store_field (args, argCount, caml_copy_string(str)); > break; > case 'S': > str = [va_arg(cs->args, NSString *) UTF8String]; > - args[argCount] = caml_copy_string(str); > + Store_field (args, argCount, caml_copy_string(str)); > break; > - case 'n': > - // leak? > - args[argCount] = *caml_named_value(va_arg(cs->args, char *)); > - break; > case 'i': > - args[argCount] = Val_int(va_arg(cs->args, int)); > + Store_field (args, > argCount, Val_long(va_arg(cs->args, long))); > break; > - case 'v': > - args[argCount] = va_arg(cs->args, value); > - break; > case '@': > - args[argCount] = [va_arg(cs->args, OCamlValue *) value]; > + Store_field (args, > argCount, [va_arg(cs->args, OCamlValue *) value]); > break; > + default: > + NSCAssert1(0, > @"Unknown input type '%c'", *p); > + break; > } > argCount++; > + NSCAssert(argCount <= 3, @"More > than 3 arguments"); > } > // Call OCaml -- TODO: add support for > 3 args > - if (argCount == 3) e = > caml_callback3_exn(*f,args[0],args[1],args[2]); > - else if (argCount == 2) e = > caml_callback2_exn(*f,args[0],args[1]); > - else if (argCount == 1) e = caml_callback_exn(*f,args[0]); > + if (argCount == 3) e = caml_callback3_exn(*f,Field(args, > 0),Field(args,1),Field(args,2)); > + else if (argCount == 2) e = caml_callback2_exn(*f,Field(args, > 0),Field(args,1)); > + else if (argCount == 1) e = caml_callback_exn(*f,Field(args,0)); > else e = caml_callback_exn(*f,Val_unit); > + for (i = 0; i < argCount; i++) Store_field > (args, i, Val_unit); > } else if (cs->opCode == OldCall) { > // old style (unsafe) version where OCaml values were passed > directly from C thread > if (cs->argCount == 3) e = caml_callback3_exn(cs->call,cs->a1,cs- > >a2,cs->a3); > @@ -213,8 +217,8 @@ > else e = caml_callback_exn(cs->call,cs->a1); > retType = 'v'; > } else if (cs->opCode == FieldAccess) { > - int index = cs->fieldIndex; > - e = (index == -1) ? Val_int(Wosize_val(*cs->valueP)) : Field(*cs- > >valueP, cs->fieldIndex); > + long index = cs->fieldIndex; > + e = (index == -1) ? Val_long(Wosize_val(*cs->valueP)) : > Field(*cs->valueP, index); > retType = cs->fieldType; > } > > @@ -223,30 +227,38 @@ > cs->ret = e; // OCaml return type -- unsafe... > if (!Is_exception_result(e)) { > switch (retType) { > - case 's': > - *((char **)&cs->retV) = (e == Val_unit) ? NULL : String_val(e); > - break; > case 'S': > *((NSString **)&cs->retV) = (e == Val_unit) ? NULL : [[NSString > alloc] initWithUTF8String:String_val(e)]; > cs->_autorelease = TRUE; > break; > + case 'N': > + if (Is_long (e)) { > + *((NSNumber **)&cs->retV) = [[NSNumber alloc] > initWithLong:Long_val(e)]; > + } else { > + *((NSNumber **)&cs->retV) = [[NSNumber alloc] > initWithDouble:Double_val(e)]; > + } > + cs->_autorelease = TRUE; > + break; > case '@': > *((NSObject **)&cs->retV) = (e == Val_unit) ? NULL : > [[OCamlValue alloc] initWithValue:e]; > cs->_autorelease = TRUE; > break; > - case 'v': > - *((value *)&cs->retV) = e; > - break; > case 'i': > - *((int *)&cs->retV) = Int_val(e); > + *((long *)&cs->retV) = Long_val(e); > break; > + case 'x': > + break; > + default: > + NSCAssert1(0, @"Unknown > return type '%c'", retType); > + break; > } > } > > if (Is_exception_result(e)) { > // get exception string -- it will get thrown back in the calling > thread > value *f = caml_named_value("unisonExnInfo"); > - cs->exception = > String_val(caml_callback(*f,Extract_exception(e))); > + // We leak memory here... > + cs->exception = > strdup(String_val(caml_callback(*f,Extract_exception(e)))); > } > > [pool release]; > @@ -260,7 +272,7 @@ > pthread_mutex_unlock(&global_res_lock); > } > // Never get here... > - return Val_unit; > + CAMLreturn (Val_unit); > } > > void *_passCall(CallState *cs) > @@ -292,20 +304,18 @@ > > void *ocamlCall(const char *argTypes, ...) > { > - va_list ap; > - va_start(ap, argTypes); > CallState cs; > cs.opCode = SafeCall; > cs.exception = NULL; > cs.argTypes = argTypes; > - cs.args = ap; > + va_start(cs.args, argTypes); > void * res = _passCall(&cs); > > - va_end(ap); > + va_end(cs.args); > return res; > } > > -void *getField(value *vP, int index, char type) > +void *getField(value *vP, long index, char type) > { > CallState cs; > cs.opCode = FieldAccess; > @@ -318,7 +328,7 @@ > > @implementation OCamlValue > > -- initWithValue:(int)v > +- initWithValue:(long)v > { > [super init]; > _v = v; > @@ -326,17 +336,17 @@ > return self; > } > > -- (int)count > +- (long)count > { > - return (int)getField((value *)&_v, -1, 'i'); > + return (long)getField((value *) &_v, -1, 'i'); > } > > -- (void *)getField:(int)i withType:(char)t > +- (void *)getField:(long)i withType:(char)t > { > - return getField((value *)&_v, i, t); > + return getField((value *)&_v, i, t); > } > > -- (int)value > +- (long)value > { > // Unsafe to use! > return _v; > > Modified: trunk/src/uimacnew/ImageAndTextCell.m > =================================================================== > --- trunk/src/uimacnew/ImageAndTextCell.m 2010-01-10 15:30:18 UTC > (rev 399) > +++ trunk/src/uimacnew/ImageAndTextCell.m 2010-01-10 22:52:59 UTC > (rev 400) > @@ -88,7 +88,10 @@ > [super editWithFrame: textFrame inView: controlView > editor:textObj delegate:anObject event: theEvent]; > } > > -- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView > editor:(NSText *)textObj delegate:(id)anObject start:(int)selStart > length:(int)selLength { > +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 > +typedef int NSInteger; > +#endif > +- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView > editor:(NSText *)textObj delegate:(id)anObject start: > (NSInteger)selStart length:(NSInteger)selLength { > NSRect textFrame, imageFrame; > NSDivideRect (aRect, &imageFrame, &textFrame, 3 + [image > size].width, NSMinXEdge); > [super selectWithFrame: textFrame inView: controlView > editor:textObj delegate:anObject start:selStart length:selLength]; > > Modified: trunk/src/uimacnew/MyController.m > =================================================================== > --- trunk/src/uimacnew/MyController.m 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/uimacnew/MyController.m 2010-01-10 22:52:59 UTC (rev > 400) > @@ -31,7 +31,9 @@ > > // BCP (11/09): Added per Onne Gorter: > // if user closes main window, terminate app, instead of keeping an > empty app around with no window > -- (BOOL)applicationShouldTerminateAfterLastWindowClosed: > (NSApplication *)theApplication { return YES; } > +- (BOOL)applicationShouldTerminateAfterLastWindowClosed: > (NSApplication *)theApplication { > + return YES; > +} > > - (id)init > { > @@ -166,8 +168,7 @@ > } > > /* Only valid once a profile has been selected */ > -- (NSString *)profile > -{ > +- (NSString *)profile { > return myProfile; > } > > @@ -176,8 +177,7 @@ > [aProfile retain]; > [myProfile release]; > myProfile = aProfile; > - [mainWindow setTitle: > - [NSString stringWithFormat:@"Unison: %@", myProfile]]; > + [mainWindow setTitle: [NSString stringWithFormat:@"Unison: %@", > myProfile]]; > } > > - (IBAction)restartButton:(id)sender > @@ -217,7 +217,7 @@ > { > [tableView reloadData]; > if (shouldResetSelection) { > - [tableView selectRow:0 byExtendingSelection:NO]; > + [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] > byExtendingSelection:NO]; > shouldResetSelection = NO; > } > [updatesView setNeedsDisplay:YES]; > @@ -268,6 +268,7 @@ > > CAMLprim value unisonInit1Complete(value v) > { > + id pool = [[NSAutoreleasePool alloc] init]; > if (v == Val_unit) { > NSLog(@"Connected."); > [me->preconn release]; > @@ -278,7 +279,7 @@ > me->preconn = [[OCamlValue alloc] initWithValue:Field(v,0)]; // > value of Some > [me performSelectorOnMainThread:@selector(unisonInit1Complete:) > withObject:nil waitUntilDone:FALSE]; > } > - > + [pool release]; > return Val_unit; > } > > @@ -310,7 +311,7 @@ > { > // FIX: some prompts don't ask for password, need to look at it > NSLog(@"Got the prompt: '%@'",prompt); > - if ((int)ocamlCall("iS", "unisonPasswordMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonPasswordMsg", prompt)) { > [passwordPrompt setStringValue:@"Please enter your password"]; > [NSApp beginSheet:passwordWindow > modalForWindow:mainWindow > @@ -319,7 +320,7 @@ > contextInfo:nil]; > return; > } > - if ((int)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { > [passwordPrompt setStringValue:@"Please enter your > passphrase"]; > [NSApp beginSheet:passwordWindow > modalForWindow:mainWindow > @@ -328,7 +329,7 @@ > contextInfo:nil]; > return; > } > - if ((int)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { > int i = NSRunAlertPanel(@"New host",prompt,@"Yes",@"No",nil); > if (i == NSAlertDefaultReturn) { > ocamlCall("x at s", "openConnectionReply", preconn, "yes"); > @@ -466,7 +467,9 @@ > > CAMLprim value unisonInit2Complete(value v) > { > + id pool = [[NSAutoreleasePool alloc] init]; > [me performSelectorOnMainThread:@selector(afterUpdate:) > withObject:[[OCamlValue alloc] initWithValue:v] waitUntilDone:FALSE]; > + [pool release]; > return Val_unit; > } > > @@ -502,7 +505,9 @@ > > CAMLprim value syncComplete() > { > + id pool = [[NSAutoreleasePool alloc] init]; > [me performSelectorOnMainThread:@selector(afterSync:) > withObject:nil waitUntilDone:FALSE]; > + [pool release]; > return Val_unit; > } > > @@ -517,10 +522,12 @@ > > CAMLprim value reloadTable(value row) > { > + id pool = [[NSAutoreleasePool alloc] init]; > // NSLog(@"OCaml says... ReloadTable: %i", Int_val(row)); > NSNumber *num = [[NSNumber alloc] initWithInt:Int_val(row)]; > [me performSelectorOnMainThread:@selector(reloadTable:) > withObject:num waitUntilDone:FALSE]; > [num release]; > + [pool release]; > return Val_unit; > } > > @@ -556,9 +563,9 @@ > [(ImageAndTextCell*)cell setImage:[item fileIcon]]; > > // For parents, format the file count into the text > - int fileCount = [item fileCount]; > + long fileCount = [item fileCount]; > if (fileCount > 1) { > - NSString *countString = [NSString stringWithFormat:@" (%i > files)", fileCount]; > + NSString *countString = [NSString stringWithFormat:@" (%ld > files)", fileCount]; > NSString *fullString = [(NSString *)[cell objectValue] > stringByAppendingString:countString]; > NSMutableAttributedString *as = [[NSMutableAttributedString > alloc] initWithString:fullString]; > > @@ -623,7 +630,7 @@ > { > [reconItems release]; > reconItems = [[NSMutableArray alloc] init]; > - int i, n =[caml_reconItems count]; > + long i, n =[caml_reconItems count]; > for (i=0; i LeafReconItem *item = [[LeafReconItem alloc] initWithRiAndIndex: > (id)[caml_reconItems getField:i withType:'@'] index:i]; > [reconItems addObject:item]; > @@ -712,7 +719,7 @@ > > - (id)updateForIgnore:(id)item > { > - int j = (int)ocamlCall("ii", "unisonUpdateForIgnore", > [reconItems indexOfObjectIdenticalTo:item]); > + long j = (long)ocamlCall("ii", "unisonUpdateForIgnore", > [reconItems indexOfObjectIdenticalTo:item]); > NSLog(@"Updating for ignore..."); > [self updateReconItems:(OCamlValue *)ocamlCall("@", > "unisonState")]; > return [reconItems objectAtIndex:j]; > @@ -721,10 +728,12 @@ > // A function called from ocaml > CAMLprim value displayStatus(value s) > { > + id pool = [[NSAutoreleasePool alloc] init]; > NSString *str = [[NSString alloc] initWithUTF8String:String_val(s)]; > // NSLog(@"displayStatus: %@", str); > [me performSelectorOnMainThread:@selector(statusTextSet:) > withObject:str waitUntilDone:FALSE]; > [str release]; > + [pool release]; > return Val_unit; > } > > @@ -738,31 +747,36 @@ > // Called from ocaml to dislpay progress bar > CAMLprim value displayGlobalProgress(value p) > { > + id pool = [[NSAutoreleasePool alloc] init]; > NSNumber *num = [[NSNumber alloc] initWithDouble:Double_val(p)]; > [me performSelectorOnMainThread:@selector(updateProgressBar:) > withObject:num waitUntilDone:FALSE]; > [num release]; > + [pool release]; > return Val_unit; > } > > // Called from ocaml to display diff > CAMLprim value displayDiff(value s, value s2) > { > + id pool = [[NSAutoreleasePool alloc] init]; > [me performSelectorOnMainThread:@selector(diffViewTextSet:) > withObject:[NSArray arrayWithObjects:[NSString > stringWithUTF8String:String_val(s)], > [NSString stringWithUTF8String:String_val(s2)], nil] > waitUntilDone:FALSE]; > + [pool release]; > return Val_unit; > } > > // Called from ocaml to display diff error messages > CAMLprim value displayDiffErr(value s) > { > + id pool = [[NSAutoreleasePool alloc] init]; > NSString * str = [NSString stringWithUTF8String:String_val(s)]; > - str = [[str componentsSeparatedByString:@"\n"] > - componentsJoinedByString:@" "]; > + str = [[str componentsSeparatedByString:@"\n"] > componentsJoinedByString:@" "]; > [me->statusText > performSelectorOnMainThread:@selector(setStringValue:) > withObject:str waitUntilDone:FALSE]; > + [pool release]; > return Val_unit; > } > > > Modified: trunk/src/uimacnew/ProfileController.m > =================================================================== > --- trunk/src/uimacnew/ProfileController.m 2010-01-10 15:30:18 UTC > (rev 399) > +++ trunk/src/uimacnew/ProfileController.m 2010-01-10 22:52:59 UTC > (rev 400) > @@ -13,7 +13,11 @@ > - (void)initProfiles > { > NSString *directory = unisonDirectory(); > +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 > NSArray *files = [[NSFileManager defaultManager] > directoryContentsAtPath:directory]; > +#else > + NSArray *files = [[NSFileManager defaultManager] > contentsOfDirectoryAtPath:directory error:nil]; > +#endif > unsigned int count = [files count]; > unsigned int i,j; > > @@ -31,7 +35,7 @@ > } > } > if (j > 0) > - [tableView selectRow:0 byExtendingSelection:NO]; > + [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex: > 0] byExtendingSelection:NO]; > } > > - (void)awakeFromNib > @@ -39,7 +43,7 @@ > // start with the default profile selected > [self initProfiles]; > if (defaultIndex >= 0) > - [tableView selectRow:defaultIndex byExtendingSelection:NO]; > + [tableView selectRowIndexes:[NSIndexSet > indexSetWithIndex:defaultIndex] byExtendingSelection:NO]; > // on awake the scroll bar is inactive, but after adding > profiles we might need it; > // reloadData makes it happen. Q: is setNeedsDisplay more > efficient? > [tableView reloadData]; > > Modified: trunk/src/uimacnew/ReconItem.h > =================================================================== > --- trunk/src/uimacnew/ReconItem.h 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/ReconItem.h 2010-01-10 22:52:59 UTC (rev 400) > @@ -12,8 +12,8 @@ > BOOL selected; > NSImage *direction; > NSString *directionSortString; > - int fileSize; > - int bytesTransferred; > + double fileSize; > + double bytesTransferred; > BOOL resolved; > } > - (BOOL)selected; > @@ -24,10 +24,10 @@ > - (NSString *)right; > - (NSImage *)direction; > - (NSImage *)fileIcon; > -- (int)fileCount; > -- (int)fileSize; > +- (long)fileCount; > +- (double)fileSize; > - (NSString *)fileSizeString; > -- (int)bytesTransferred; > +- (double)bytesTransferred; > - (NSString *)bytesTransferredString; > - (void)setDirection:(char *)d; > - (void) doAction:(unichar)action; > @@ -64,15 +64,15 @@ > NSString *progress; > NSString *details; > OCamlValue *ri; // an ocaml Common.reconItem > - int index; // index in Ri list > + long index; // index in Ri list > } > -- initWithRiAndIndex:(OCamlValue *)v index:(int)i; > +- initWithRiAndIndex:(OCamlValue *)v index:(long)i; > @end > > @interface ParentReconItem : ReconItem > { > NSMutableArray *_children; > - int fileCount; > + long fileCount; > } > - (void)addChild:(ReconItem *)item nested:(BOOL)useNesting; > - (void)sortUsingDescriptors:(NSArray *)sortDescriptors; > > Modified: trunk/src/uimacnew/ReconItem.m > =================================================================== > --- trunk/src/uimacnew/ReconItem.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/ReconItem.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -9,8 +9,8 @@ > [super init]; > selected = NO; // NB only used/updated during sorts. Not a > // reliable indicator of whether item is selected > - fileSize = -1; > - bytesTransferred = -1; > + fileSize = -1.; > + bytesTransferred = -1.; > return self; > } > > @@ -129,30 +129,29 @@ > } > > > -- (int)computeFileSize > +- (double)computeFileSize > { > - return 0; > + return 0.; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - return 0; > + return 0.; > } > > -- (int)fileCount > +- (long)fileCount > { > return 1; > } > > -- (int)fileSize > +- (double)fileSize > { > - if (fileSize == -1) fileSize = [self computeFileSize]; > + if (fileSize == -1.) fileSize = [self computeFileSize]; > return fileSize; > } > > -- (NSString *)formatFileSize:(int)intSize > +- (NSString *)formatFileSize:(double)size > { > - float size = (float)intSize; > if (size == 0) return @"--"; > if (size < 1024) return @"< 1KB"; // return [NSString > stringWithFormat:@"%i bytes", size]; > size /= 1024; > @@ -175,8 +174,8 @@ > > - (NSNumber *)percentTransferred > { > - int size = [self computeFileSize]; > - return (size > 0) ? [NSNumber numberWithFloat:(((float)[self > bytesTransferred]) / (float)size) * 100.0] > + double size = [self computeFileSize]; > + return (size > 0) ? [NSNumber numberWithDouble:([self > bytesTransferred] / (size) * 100.0)] > : nil; > } > > @@ -379,8 +378,8 @@ > > - (BOOL)transferInProgress > { > - int soFar = [self bytesTransferred]; > - return (soFar > 0) && (soFar != [self fileSize]); > + double soFar = [self bytesTransferred]; > + return (soFar > 0) && (soFar < [self fileSize]); > } > > - (void)resetProgress > @@ -390,7 +389,7 @@ > - (NSString *)progressString > { > NSString *progress = [self progress]; > - if ([progress length] == 0 || [progress hasSuffix:@"%"]) > + if ([progress length] == 0. || [progress hasSuffix:@"%"]) > progress = [self transferInProgress] ? [self > bytesTransferredString] : @""; > else if ([progress isEqual:@"done"]) progress = @""; > return progress; > @@ -443,7 +442,7 @@ > // --- Leaf items -- actually corresponding to ReconItems in OCaml > @implementation LeafReconItem > > -- initWithRiAndIndex:(OCamlValue *)v index:(int)i > +- initWithRiAndIndex:(OCamlValue *)v index:(long)i > { > [super init]; > ri = [v retain]; > @@ -482,17 +481,17 @@ > return right; > } > > -- (int)computeFileSize > +- (double)computeFileSize > { > - return (int)ocamlCall("i@", "unisonRiToFileSize", ri); > + return [(NSNumber *)ocamlCall("N@", "unisonRiToFileSize", ri) > doubleValue]; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - if (bytesTransferred == -1) { > + if (bytesTransferred == -1.) { > // need to force to fileSize if done, otherwise may not match up > to 100% > bytesTransferred = ([[self progress] isEqual:@"done"]) ? [self > fileSize] > - : (int)ocamlCall("i@", "unisonRiToBytesTransferred", ri); > + : [(NSNumber*)ocamlCall("N@", > "unisonRiToBytesTransferred", ri) doubleValue]; > } > return bytesTransferred; > } > @@ -535,7 +534,7 @@ > { > // Get rid of the memoized progress because we expect it to change > [self willChange]; > - bytesTransferred = -1; > + bytesTransferred = -1.; > [progress release]; > > // Force update now so we get the result while the OCaml thread is > available > @@ -559,12 +558,12 @@ > > - (BOOL)isConflict > { > - return ((int)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); > } > > - (BOOL)changedFromDefault > { > - return ((int)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); > } > > - (void)revertDirection > @@ -575,7 +574,7 @@ > > - (BOOL)canDiff > { > - return ((int)ocamlCall("i@", "canDiff", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "canDiff", ri) ? YES : NO); > } > > - (void)showDiffs > @@ -715,7 +714,7 @@ > // [directionSortString autorelease]; > direction = nil; > directionSortString = nil; > - bytesTransferred = -1; > + bytesTransferred = -1.; > // fileSize = -1; > // resolved = NO; > > @@ -748,7 +747,7 @@ > } > > // Rollup methods > -- (int)fileCount > +- (long)fileCount > { > if (fileCount == 0) { > int i = [_children count]; > @@ -760,9 +759,9 @@ > return fileCount; > } > > -- (int)computeFileSize > +- (double)computeFileSize > { > - int size = 0; > + double size = 0; > int i = [_children count]; > while (i--) { > ReconItem *child = [_children objectAtIndex:i]; > @@ -771,10 +770,10 @@ > return size; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - if (bytesTransferred == -1) { > - bytesTransferred = 0; > + if (bytesTransferred == -1.) { > + bytesTransferred = 0.; > int i = [_children count]; > while (i--) { > ReconItem *child = [_children objectAtIndex:i]; > > Modified: trunk/src/uimacnew/ReconTableView.m > =================================================================== > --- trunk/src/uimacnew/ReconTableView.m 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/uimacnew/ReconTableView.m 2010-01-10 22:52:59 UTC (rev > 400) > @@ -11,12 +11,16 @@ > #import "MyController.h" > > @implementation NSOutlineView (_UnisonExtras) > + > - (NSArray *)selectedObjects > { > NSMutableArray *result = [NSMutableArray array]; > - NSEnumerator *e = [self selectedRowEnumerator]; > - NSNumber *n; > - while (n = [e nextObject]) [result addObject:[self itemAtRow:[n > intValue]]]; > + NSIndexSet *set = [self selectedRowIndexes]; > + NSUInteger index = [set firstIndex]; > + while (index != NSNotFound) { > + [result addObject:[self itemAtRow:index]]; > + index = [set indexGreaterThanIndex: index]; > + } > return result; > } > > @@ -136,8 +140,9 @@ > last = item; > } > if (last) { // something was selected > - last = [[self dataSource] updateForIgnore:last]; > - [self selectRow:[self rowForItem:last] > byExtendingSelection:NO]; > + MyController* controller = (MyController*) [self dataSource]; > + last = [controller updateForIgnore:last]; > + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:[self > rowForItem:last]] byExtendingSelection:NO]; > [self reloadData]; > } > } > @@ -171,7 +176,7 @@ > int nextRow = [self rowForItem:last] + 1; > if (numSelected == 1 && [self numberOfRows] > nextRow && c! > ='d') { > // Move to next row, unless already at last row, or if > more than one row selected > - [self selectRow:nextRow byExtendingSelection:NO]; > + [self selectRowIndexes:[NSIndexSet > indexSetWithIndex:nextRow] byExtendingSelection:NO]; > [self scrollRowToVisible:nextRow]; > } > [self reloadData]; > @@ -206,12 +211,13 @@ > - (IBAction)selectConflicts:(id)sender > { > [self deselectAll:self]; > - NSMutableArray *reconItems = [[self dataSource] reconItems]; > + MyController* controller = (MyController*) [self dataSource]; > + NSMutableArray *reconItems = [controller reconItems]; > int i = 0; > for (; i < [reconItems count]; i++) { > ReconItem *item = [reconItems objectAtIndex:i]; > if ([item isConflict]) > - [self selectRow:[self rowForItem:item] > byExtendingSelection:YES]; > + [self selectRowIndexes:[NSIndexSet indexSetWithIndex: > [self rowForItem:item]] byExtendingSelection:YES]; > } > } > > > Modified: trunk/src/uimacnew/UnisonToolbar.h > =================================================================== > --- trunk/src/uimacnew/UnisonToolbar.h 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/uimacnew/UnisonToolbar.h 2010-01-10 22:52:59 UTC (rev > 400) > @@ -12,6 +12,9 @@ > @class ReconTableView, MyController; > > @interface UnisonToolbar : NSToolbar > +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) > + > +#endif > { > ReconTableView* tableView; > MyController* myController; > > Modified: trunk/src/uimacnew/main.m > =================================================================== > --- trunk/src/uimacnew/main.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/main.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -11,6 +11,7 @@ > > int main(int argc, const char *argv[]) > { > + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; > int i; > > /* When you click-start or use the open command, the program is > invoked with > @@ -34,22 +35,19 @@ > !strcmp(argv[i],"-server") || > !strcmp(argv[i],"-socket") || > !strcmp(argv[i],"-ui")) { > - /* We install an autorelease pool here because there > might be callbacks > - from ocaml to objc code */ > NSLog(@"Calling nonGuiStartup"); > - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] > init]; > @try { > ocamlCall("x", "unisonNonGuiStartup"); > } @catch (NSException *ex) { > NSLog(@"Uncaught exception: %@", [ex reason]); > exit(1); > } > - [pool release]; > /* If we get here without exiting first, the non GUI > startup detected a > -ui graphic or command-line profile, and we should in > fact start the GUI. */ > } > } > > /* go! */ > + [pool release]; > return NSApplicationMain(argc, argv); > } > > Modified: trunk/src/uimacnew09/Bridge.h > =================================================================== > --- trunk/src/uimacnew09/Bridge.h 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/Bridge.h 2010-01-10 22:52:59 UTC (rev 400) > @@ -24,14 +24,14 @@ > Args/return values are converted to/from C/OCaml according to the > supplied type signture string. Type codes are: > x - void (for return type) > - i - int > + i - long > s - char * > S - NSString * > + N - NSNumber * > @ - OCamlValue (see below) > - v - unwrapped OCaml value (deprecated -- unsafe!) > > Examples: > - int count = (int)ocamlCall("iS", "lengthOfString", @"Some String"); > + long count = (long)ocamlCall("iS", "lengthOfString", @"Some > String"); > > (void)ocamlCall("x", "someVoidOCamlFunction"); > > @@ -42,17 +42,17 @@ > > // Wrapper/proxy for unconverted OCaml values > @interface OCamlValue : NSObject { > - int _v; > + long _v; > } > -- initWithValue:(int)v; > +- initWithValue:(long)v; > > -- (void *)getField:(int)i withType:(char)t; > +- (void *)getField:(long)i withType:(char)t; > // get value by position. See ocamlCall for list of type > conversion codes > > -- (int)count; > +- (long)count; > // count of items in array > > -- (int)value; > +- (long)value; > // returns Ocaml value directly -- not safe to use except in direct > callback from OCaml > // (i.e. in the OCaml thread) > @end > > Modified: trunk/src/uimacnew09/Bridge.m > =================================================================== > --- trunk/src/uimacnew09/Bridge.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/Bridge.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -95,7 +95,10 @@ > // NSLog(@"*** caml_init complete!"); > } > > -- (BOOL)exceptionHandler:(NSExceptionHandler *)sender > shouldLogException:(NSException *)exception mask:(unsigned int)aMask > +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 > +typedef unsigned int NSUInteger; > +#endif > +- (BOOL)exceptionHandler:(NSExceptionHandler *)sender > shouldLogException:(NSException *)exception mask:(NSUInteger)aMask > { > // if (![[exception name] isEqual:@"OCamlException"]) return YES; > > @@ -121,7 +124,7 @@ > > // Field access > value *valueP; > - int fieldIndex; > + long fieldIndex; > char fieldType; > > // Return values > @@ -139,9 +142,11 @@ > > // Our OCaml callback server thread -- waits for call then makes them > // Called from thread spawned from OCaml > -CAMLprim value bridgeThreadWait(int ignore) > +CAMLprim value bridgeThreadWait(value ignore) > { > - value args[10]; > + CAMLparam0(); > + CAMLlocal1 (args); > + args = caml_alloc_tuple(3); > > // NSLog(@"*** bridgeThreadWait init! (%d) Taking lock...", > pthread_self()); > while (TRUE) { > @@ -168,6 +173,7 @@ > char retType = 'v'; > value e = Val_unit; > if (cs->opCode == SafeCall) { > + int i; > char *fname = va_arg(cs->args, char *); > value *f = caml_named_value(fname); > // varargs with C-based args -- convert them to OCaml values > based on type code string > @@ -179,33 +185,31 @@ > switch (*p) { > case 's': > str = va_arg(cs->args, const char *); > - args[argCount] = caml_copy_string(str); > + Store_field (args, argCount, caml_copy_string(str)); > break; > case 'S': > str = [va_arg(cs->args, NSString *) UTF8String]; > - args[argCount] = caml_copy_string(str); > + Store_field (args, argCount, caml_copy_string(str)); > break; > - case 'n': > - // leak? > - args[argCount] = *caml_named_value(va_arg(cs->args, char *)); > - break; > case 'i': > - args[argCount] = Val_int(va_arg(cs->args, int)); > + Store_field (args, > argCount, Val_long(va_arg(cs->args, long))); > break; > - case 'v': > - args[argCount] = va_arg(cs->args, value); > - break; > case '@': > - args[argCount] = [va_arg(cs->args, OCamlValue *) value]; > + Store_field (args, > argCount, [va_arg(cs->args, OCamlValue *) value]); > break; > + default: > + NSCAssert1(0, > @"Unknown input type '%c'", *p); > + break; > } > argCount++; > + NSCAssert(argCount <= 3, @"More > than 3 arguments"); > } > // Call OCaml -- TODO: add support for > 3 args > - if (argCount == 3) e = > caml_callback3_exn(*f,args[0],args[1],args[2]); > - else if (argCount == 2) e = > caml_callback2_exn(*f,args[0],args[1]); > - else if (argCount == 1) e = caml_callback_exn(*f,args[0]); > + if (argCount == 3) e = caml_callback3_exn(*f,Field(args, > 0),Field(args,1),Field(args,2)); > + else if (argCount == 2) e = caml_callback2_exn(*f,Field(args, > 0),Field(args,1)); > + else if (argCount == 1) e = caml_callback_exn(*f,Field(args,0)); > else e = caml_callback_exn(*f,Val_unit); > + for (i = 0; i < argCount; i++) Store_field > (args, i, Val_unit); > } else if (cs->opCode == OldCall) { > // old style (unsafe) version where OCaml values were passed > directly from C thread > if (cs->argCount == 3) e = caml_callback3_exn(cs->call,cs->a1,cs- > >a2,cs->a3); > @@ -213,8 +217,8 @@ > else e = caml_callback_exn(cs->call,cs->a1); > retType = 'v'; > } else if (cs->opCode == FieldAccess) { > - int index = cs->fieldIndex; > - e = (index == -1) ? Val_int(Wosize_val(*cs->valueP)) : Field(*cs- > >valueP, cs->fieldIndex); > + long index = cs->fieldIndex; > + e = (index == -1) ? Val_long(Wosize_val(*cs->valueP)) : > Field(*cs->valueP, index); > retType = cs->fieldType; > } > > @@ -223,30 +227,38 @@ > cs->ret = e; // OCaml return type -- unsafe... > if (!Is_exception_result(e)) { > switch (retType) { > - case 's': > - *((char **)&cs->retV) = (e == Val_unit) ? NULL : String_val(e); > - break; > case 'S': > *((NSString **)&cs->retV) = (e == Val_unit) ? NULL : [[NSString > alloc] initWithUTF8String:String_val(e)]; > cs->_autorelease = TRUE; > break; > + case 'N': > + if (Is_long (e)) { > + *((NSNumber **)&cs->retV) = [[NSNumber alloc] > initWithLong:Long_val(e)]; > + } else { > + *((NSNumber **)&cs->retV) = [[NSNumber alloc] > initWithDouble:Double_val(e)]; > + } > + cs->_autorelease = TRUE; > + break; > case '@': > *((NSObject **)&cs->retV) = (e == Val_unit) ? NULL : > [[OCamlValue alloc] initWithValue:e]; > cs->_autorelease = TRUE; > break; > - case 'v': > - *((value *)&cs->retV) = e; > - break; > case 'i': > - *((int *)&cs->retV) = Int_val(e); > + *((long *)&cs->retV) = Long_val(e); > break; > + case 'x': > + break; > + default: > + NSCAssert1(0, @"Unknown > return type '%c'", retType); > + break; > } > } > > if (Is_exception_result(e)) { > // get exception string -- it will get thrown back in the calling > thread > value *f = caml_named_value("unisonExnInfo"); > - cs->exception = > String_val(caml_callback(*f,Extract_exception(e))); > + // We leak memory here... > + cs->exception = > strdup(String_val(caml_callback(*f,Extract_exception(e)))); > } > > [pool release]; > @@ -260,7 +272,7 @@ > pthread_mutex_unlock(&global_res_lock); > } > // Never get here... > - return Val_unit; > + CAMLreturn (Val_unit); > } > > void *_passCall(CallState *cs) > @@ -292,20 +304,18 @@ > > void *ocamlCall(const char *argTypes, ...) > { > - va_list ap; > - va_start(ap, argTypes); > CallState cs; > cs.opCode = SafeCall; > cs.exception = NULL; > cs.argTypes = argTypes; > - cs.args = ap; > + va_start(cs.args, argTypes); > void * res = _passCall(&cs); > > - va_end(ap); > + va_end(cs.args); > return res; > } > > -void *getField(value *vP, int index, char type) > +void *getField(value *vP, long index, char type) > { > CallState cs; > cs.opCode = FieldAccess; > @@ -318,7 +328,7 @@ > > @implementation OCamlValue > > -- initWithValue:(int)v > +- initWithValue:(long)v > { > [super init]; > _v = v; > @@ -326,17 +336,17 @@ > return self; > } > > -- (int)count > +- (long)count > { > - return (int)getField((value *)&_v, -1, 'i'); > + return (long)getField((value *) &_v, -1, 'i'); > } > > -- (void *)getField:(int)i withType:(char)t > +- (void *)getField:(long)i withType:(char)t > { > - return getField((value *)&_v, i, t); > + return getField((value *)&_v, i, t); > } > > -- (int)value > +- (long)value > { > // Unsafe to use! > return _v; > > Modified: trunk/src/uimacnew09/ImageAndTextCell.m > =================================================================== > --- trunk/src/uimacnew09/ImageAndTextCell.m 2010-01-10 15:30:18 UTC > (rev 399) > +++ trunk/src/uimacnew09/ImageAndTextCell.m 2010-01-10 22:52:59 UTC > (rev 400) > @@ -88,7 +88,10 @@ > [super editWithFrame: textFrame inView: controlView > editor:textObj delegate:anObject event: theEvent]; > } > > -- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView > editor:(NSText *)textObj delegate:(id)anObject start:(int)selStart > length:(int)selLength { > +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 > +typedef int NSInteger; > +#endif > +- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView > editor:(NSText *)textObj delegate:(id)anObject start: > (NSInteger)selStart length:(NSInteger)selLength { > NSRect textFrame, imageFrame; > NSDivideRect (aRect, &imageFrame, &textFrame, 3 + [image > size].width, NSMinXEdge); > [super selectWithFrame: textFrame inView: controlView > editor:textObj delegate:anObject start:selStart length:selLength]; > > Modified: trunk/src/uimacnew09/MyController.m > =================================================================== > --- trunk/src/uimacnew09/MyController.m 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/uimacnew09/MyController.m 2010-01-10 22:52:59 UTC (rev > 400) > @@ -395,7 +395,7 @@ > { > // FIX: some prompts don't ask for password, need to look at it > NSLog(@"Got the prompt: '%@'",prompt); > - if ((int)ocamlCall("iS", "unisonPasswordMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonPasswordMsg", prompt)) { > [passwordPrompt setStringValue:@"Please enter your password"]; > [NSApp beginSheet:passwordWindow > modalForWindow:mainWindow > @@ -404,7 +404,7 @@ > contextInfo:nil]; > return; > } > - if ((int)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { > [passwordPrompt setStringValue:@"Please enter your > passphrase"]; > [NSApp beginSheet:passwordWindow > modalForWindow:mainWindow > @@ -413,7 +413,7 @@ > contextInfo:nil]; > return; > } > - if ((int)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { > int i = NSRunAlertPanel(@"New host",prompt,@"Yes",@"No",nil); > if (i == NSAlertDefaultReturn) { > ocamlCall("x at s", "openConnectionReply", preconn, "yes"); > @@ -649,9 +649,9 @@ > [(ImageAndTextCell*)cell setImage:[item fileIcon]]; > > // For parents, format the file count into the text > - int fileCount = [item fileCount]; > + long fileCount = [item fileCount]; > if (fileCount > 1) { > - NSString *countString = [NSString stringWithFormat:@" (%i > files)", fileCount]; > + NSString *countString = [NSString stringWithFormat:@" (%ld > files)", fileCount]; > NSString *fullString = [(NSString *)[cell objectValue] > stringByAppendingString:countString]; > NSMutableAttributedString *as = [[NSMutableAttributedString > alloc] initWithString:fullString]; > > @@ -716,7 +716,7 @@ > { > [reconItems release]; > reconItems = [[NSMutableArray alloc] init]; > - int i, n =[caml_reconItems count]; > + long i, n =[caml_reconItems count]; > for (i=0; i LeafReconItem *item = [[LeafReconItem alloc] initWithRiAndIndex: > (id)[caml_reconItems getField:i withType:'@'] index:i]; > [reconItems addObject:item]; > @@ -805,7 +805,7 @@ > > - (id)updateForIgnore:(id)item > { > - int j = (int)ocamlCall("ii", "unisonUpdateForIgnore", > [reconItems indexOfObjectIdenticalTo:item]); > + long j = (long)ocamlCall("ii", "unisonUpdateForIgnore", > [reconItems indexOfObjectIdenticalTo:item]); > NSLog(@"Updating for ignore..."); > [self updateReconItems:(OCamlValue *)ocamlCall("@", > "unisonState")]; > return [reconItems objectAtIndex:j]; > > Modified: trunk/src/uimacnew09/ProfileController.m > =================================================================== > --- trunk/src/uimacnew09/ProfileController.m 2010-01-10 15:30:18 UTC > (rev 399) > +++ trunk/src/uimacnew09/ProfileController.m 2010-01-10 22:52:59 UTC > (rev 400) > @@ -13,7 +13,11 @@ > - (void)initProfiles > { > NSString *directory = unisonDirectory(); > +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 > + NSArray *files = [[NSFileManager defaultManager] > directoryContentsAtPath:directory]; > +#else > NSArray *files = [[NSFileManager defaultManager] > contentsOfDirectoryAtPath:directory error:nil]; > +#endif > unsigned int count = [files count]; > unsigned int i,j; > > > Modified: trunk/src/uimacnew09/ReconItem.h > =================================================================== > --- trunk/src/uimacnew09/ReconItem.h 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/ReconItem.h 2010-01-10 22:52:59 UTC (rev 400) > @@ -12,8 +12,8 @@ > BOOL selected; > NSImage *direction; > NSString *directionSortString; > - int fileSize; > - int bytesTransferred; > + double fileSize; > + double bytesTransferred; > BOOL resolved; > } > - (BOOL)selected; > @@ -24,10 +24,10 @@ > - (NSString *)right; > - (NSImage *)direction; > - (NSImage *)fileIcon; > -- (int)fileCount; > -- (int)fileSize; > +- (long)fileCount; > +- (double)fileSize; > - (NSString *)fileSizeString; > -- (int)bytesTransferred; > +- (double)bytesTransferred; > - (NSString *)bytesTransferredString; > - (void)setDirection:(char *)d; > - (void) doAction:(unichar)action; > @@ -64,15 +64,15 @@ > NSString *progress; > NSString *details; > OCamlValue *ri; // an ocaml Common.reconItem > - int index; // index in Ri list > + long index; // index in Ri list > } > -- initWithRiAndIndex:(OCamlValue *)v index:(int)i; > +- initWithRiAndIndex:(OCamlValue *)v index:(long)i; > @end > > @interface ParentReconItem : ReconItem > { > NSMutableArray *_children; > - int fileCount; > + long fileCount; > } > - (void)addChild:(ReconItem *)item nested:(BOOL)useNesting; > - (void)sortUsingDescriptors:(NSArray *)sortDescriptors; > > Modified: trunk/src/uimacnew09/ReconItem.m > =================================================================== > --- trunk/src/uimacnew09/ReconItem.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/ReconItem.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -9,8 +9,8 @@ > [super init]; > selected = NO; // NB only used/updated during sorts. Not a > // reliable indicator of whether item is selected > - fileSize = -1; > - bytesTransferred = -1; > + fileSize = -1.; > + bytesTransferred = -1.; > return self; > } > > @@ -129,30 +129,29 @@ > } > > > -- (int)computeFileSize > +- (double)computeFileSize > { > - return 0; > + return 0.; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - return 0; > + return 0.; > } > > -- (int)fileCount > +- (long)fileCount > { > return 1; > } > > -- (int)fileSize > +- (double)fileSize > { > - if (fileSize == -1) fileSize = [self computeFileSize]; > + if (fileSize == -1.) fileSize = [self computeFileSize]; > return fileSize; > } > > -- (NSString *)formatFileSize:(int)intSize > +- (NSString *)formatFileSize:(double)size > { > - float size = (float)intSize; > if (size == 0) return @"--"; > if (size < 1024) return @"< 1KB"; // return [NSString > stringWithFormat:@"%i bytes", size]; > size /= 1024; > @@ -175,8 +174,8 @@ > > - (NSNumber *)percentTransferred > { > - int size = [self computeFileSize]; > - return (size > 0) ? [NSNumber numberWithFloat:(((float)[self > bytesTransferred]) / (float)size) * 100.0] > + double size = [self computeFileSize]; > + return (size > 0) ? [NSNumber numberWithDouble:([self > bytesTransferred] / (size) * 100.0)] > : nil; > } > > @@ -379,8 +378,8 @@ > > - (BOOL)transferInProgress > { > - int soFar = [self bytesTransferred]; > - return (soFar > 0) && (soFar != [self fileSize]); > + double soFar = [self bytesTransferred]; > + return (soFar > 0) && (soFar < [self fileSize]); > } > > - (void)resetProgress > @@ -390,7 +389,7 @@ > - (NSString *)progressString > { > NSString *progress = [self progress]; > - if ([progress length] == 0 || [progress hasSuffix:@"%"]) > + if ([progress length] == 0. || [progress hasSuffix:@"%"]) > progress = [self transferInProgress] ? [self > bytesTransferredString] : @""; > else if ([progress isEqual:@"done"]) progress = @""; > return progress; > @@ -443,7 +442,7 @@ > // --- Leaf items -- actually corresponding to ReconItems in OCaml > @implementation LeafReconItem > > -- initWithRiAndIndex:(OCamlValue *)v index:(int)i > +- initWithRiAndIndex:(OCamlValue *)v index:(long)i > { > [super init]; > ri = [v retain]; > @@ -482,17 +481,17 @@ > return right; > } > > -- (int)computeFileSize > +- (double)computeFileSize > { > - return (int)ocamlCall("i@", "unisonRiToFileSize", ri); > + return [(NSNumber *)ocamlCall("N@", "unisonRiToFileSize", ri) > doubleValue]; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - if (bytesTransferred == -1) { > + if (bytesTransferred == -1.) { > // need to force to fileSize if done, otherwise may not match up > to 100% > bytesTransferred = ([[self progress] isEqual:@"done"]) ? [self > fileSize] > - : (int)ocamlCall("i@", "unisonRiToBytesTransferred", ri); > + : [(NSNumber*)ocamlCall("N@", > "unisonRiToBytesTransferred", ri) doubleValue]; > } > return bytesTransferred; > } > @@ -535,7 +534,7 @@ > { > // Get rid of the memoized progress because we expect it to change > [self willChange]; > - bytesTransferred = -1; > + bytesTransferred = -1.; > [progress release]; > > // Force update now so we get the result while the OCaml thread is > available > @@ -559,12 +558,12 @@ > > - (BOOL)isConflict > { > - return ((int)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); > } > > - (BOOL)changedFromDefault > { > - return ((int)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); > } > > - (void)revertDirection > @@ -575,7 +574,7 @@ > > - (BOOL)canDiff > { > - return ((int)ocamlCall("i@", "canDiff", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "canDiff", ri) ? YES : NO); > } > > - (void)showDiffs > @@ -715,7 +714,7 @@ > // [directionSortString autorelease]; > direction = nil; > directionSortString = nil; > - bytesTransferred = -1; > + bytesTransferred = -1.; > // fileSize = -1; > // resolved = NO; > > @@ -748,7 +747,7 @@ > } > > // Rollup methods > -- (int)fileCount > +- (long)fileCount > { > if (fileCount == 0) { > int i = [_children count]; > @@ -760,9 +759,9 @@ > return fileCount; > } > > -- (int)computeFileSize > +- (double)computeFileSize > { > - int size = 0; > + double size = 0; > int i = [_children count]; > while (i--) { > ReconItem *child = [_children objectAtIndex:i]; > @@ -771,10 +770,10 @@ > return size; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - if (bytesTransferred == -1) { > - bytesTransferred = 0; > + if (bytesTransferred == -1.) { > + bytesTransferred = 0.; > int i = [_children count]; > while (i--) { > ReconItem *child = [_children objectAtIndex:i]; > > Modified: trunk/src/uimacnew09/main.m > =================================================================== > --- trunk/src/uimacnew09/main.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/main.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -11,7 +11,7 @@ > > int main(int argc, const char *argv[]) > { > - id pool = [[NSAutoreleasePool alloc] init]; > + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; > int i; > > /* When you click-start or use the open command, the program is > invoked with > @@ -35,17 +35,13 @@ > !strcmp(argv[i],"-server") || > !strcmp(argv[i],"-socket") || > !strcmp(argv[i],"-ui")) { > - /* We install an autorelease pool here because there might be > callbacks > - from ocaml to objc code */ > NSLog(@"Calling nonGuiStartup"); > - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; > @try { > ocamlCall("x", "unisonNonGuiStartup"); > } @catch (NSException *ex) { > NSLog(@"Uncaught exception: %@", [ex reason]); > exit(1); > } > - [pool release]; > /* If we get here without exiting first, the non GUI startup > detected a > -ui graphic or command-line profile, and we should in fact > start the GUI. */ > } > > _______________________________________________ > Unison-hackers mailing list > Unison-hackers at lists.seas.upenn.edu > http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers From bcpierce at cis.upenn.edu Sun Jan 10 20:38:38 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Sun, 10 Jan 2010 20:38:38 -0500 Subject: [Unison-hackers] [unison-svn] r400 - in trunk/src: . system system/win ubase uimacnew uimacnew09 In-Reply-To: <201001102253.o0AMr3a1026196@yaws.seas.upenn.edu> References: <201001102253.o0AMr3a1026196@yaws.seas.upenn.edu> Message-ID: The uimacnew version seems to work here. - B On Jan 10, 2010, at 5:53 PM, vouillon at seas.upenn.edu wrote: > Author: vouillon > Date: 2010-01-10 17:52:59 -0500 (Sun, 10 Jan 2010) > New Revision: 400 > > Modified: > trunk/src/RECENTNEWS > trunk/src/fpcache.ml > trunk/src/mkProjectInfo.ml > trunk/src/os.ml > trunk/src/osx.ml > trunk/src/props.ml > trunk/src/remote.ml > trunk/src/system/system_win.ml > trunk/src/system/win/system_impl.ml > trunk/src/ubase/util.ml > trunk/src/uimacbridgenew.ml > trunk/src/uimacnew/Bridge.h > trunk/src/uimacnew/Bridge.m > trunk/src/uimacnew/ImageAndTextCell.m > trunk/src/uimacnew/MyController.m > trunk/src/uimacnew/ProfileController.m > trunk/src/uimacnew/ReconItem.h > trunk/src/uimacnew/ReconItem.m > trunk/src/uimacnew/ReconTableView.m > trunk/src/uimacnew/UnisonToolbar.h > trunk/src/uimacnew/main.m > trunk/src/uimacnew09/Bridge.h > trunk/src/uimacnew09/Bridge.m > trunk/src/uimacnew09/ImageAndTextCell.m > trunk/src/uimacnew09/MyController.m > trunk/src/uimacnew09/ProfileController.m > trunk/src/uimacnew09/ReconItem.h > trunk/src/uimacnew09/ReconItem.m > trunk/src/uimacnew09/main.m > Log: > * Mac GUIs (NEED TESTING): > - use doubles rather than ints for file sizes to prevent overflows > - should now be 64 bit clean (the Growl framework is not up to date, > though) > - fixes from uimacnew09 copied back to uimacnew > - made the bridge between Objective C and Ocaml code GC friendly > (it was allocating ML values and putting them in an array which > was not registered with the GC) > * Windows: only use long UNC path for accessing replicas (as '..' is > not handled with this format of paths, but can be useful) > * Bumped fingerprint cache magic number (the format was changed in > revision 398) > > > Modified: trunk/src/RECENTNEWS > =================================================================== > --- trunk/src/RECENTNEWS 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/RECENTNEWS 2010-01-10 22:52:59 UTC (rev 400) > @@ -1,5 +1,21 @@ > CHANGES FROM VERSION 2.39.0 > > +* Mac GUIs (NEED TESTING): > + - use doubles rather than ints for file sizes to prevent overflows > + - should now be 64 bit clean (the Growl framework is not up to > date, > + though) > + - fixes from uimacnew09 copied back to uimacnew > + - made the bridge between Objective C and Ocaml code GC friendly > + (it was allocating ML values and putting them in an array which > + was not registered with the GC) > +* Windows: only use long UNC path for accessing replicas (as '..' is > + not handled with this format of paths, but can be useful) > +* Bumped fingerprint cache magic number (the format was changed in > + revision 398) > + > +------------------------------- > +CHANGES FROM VERSION 2.39.0 > + > * Back out some minimal support we'd added for checking out Unison > sources via Bazaar. > > * Small fix for OSX GUI > > Modified: trunk/src/fpcache.ml > =================================================================== > --- trunk/src/fpcache.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/fpcache.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -59,7 +59,7 @@ > let compress state path = > let s = state.last in > let p = Path.toString path in > - let l = String.length s in > + let l = min (String.length p) (String.length s) in > let i = ref 0 in > while !i < l && p.[!i] = s.[!i] do incr i done; > state.last <- p; > @@ -126,7 +126,7 @@ > closeOut st > | None -> () > > -let magic = "Unison fingerprint cache format 1" > +let magic = "Unison fingerprint cache format 2" > > let init fastCheck fspath = > finish (); > > Modified: trunk/src/mkProjectInfo.ml > =================================================================== > --- trunk/src/mkProjectInfo.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/mkProjectInfo.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -100,3 +100,4 @@ > > > > + > > Modified: trunk/src/os.ml > =================================================================== > --- trunk/src/os.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/os.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -292,14 +292,7 @@ > genericName > > (* build a fspath representing an archive child path whose name is > given *) > -let fileInUnisonDir str = > - begin try > - ignore (Name.fromString str) > - with Invalid_argument _ -> > - raise (Util.Transient > - ("Ill-formed name of file in UNISON directory: "^str)) > - end; > - System.fspathConcat unisonDir str > +let fileInUnisonDir str = System.fspathConcat unisonDir str > > (* Make sure archive directory > exists *) > let createUnisonDir() = > @@ -316,9 +309,9 @@ > (*****************************************************************************) > > (* Truncate a filename to at most [l] bytes, making sure of not > - truncating an UTF-8 character *) > + truncating an UTF-8 character. Assumption: [String.length s > > l] *) > let rec truncate_filename s l = > - if l >= 0 && Char.code s.[l] land 0xC0 = 0x80 then > + if l > 0 && Char.code s.[l] land 0xC0 = 0x80 then > truncate_filename s (l - 1) > else > String.sub s 0 l > > Modified: trunk/src/osx.ml > =================================================================== > --- trunk/src/osx.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/osx.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -206,7 +206,7 @@ > (* Remove trailing zeroes *) > let trim s = > let rec trim_rec s pos = > - if s.[pos - 1] = '\000' then > + if pos > 0 && s.[pos - 1] = '\000' then > trim_rec s (pos - 1) > else > String.sub s 0 pos > > Modified: trunk/src/props.ml > =================================================================== > --- trunk/src/props.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/props.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -614,7 +614,8 @@ > > let toString t = > match t with > - Some s when s.[0] = 'F' && String.sub (s ^ zeroes) 1 8 <> > zeroes -> > + Some s when String.length s > 0 && s.[0] = 'F' && > + String.sub (s ^ zeroes) 1 8 <> zeroes -> > let s = s ^ zeroes in > " " ^ String.escaped (String.sub s 1 4) ^ > " " ^ String.escaped (String.sub s 5 4) > > Modified: trunk/src/remote.ml > =================================================================== > --- trunk/src/remote.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/remote.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -987,10 +987,6 @@ > negociateFlowControl conn; > Lwt.return conn) > > -let inetAddr host = > - let targetHostEntry = Unix.gethostbyname host in > - targetHostEntry.Unix.h_addr_list.(0) > - > let rec findFirst f l = > match l with > [] -> None > > Modified: trunk/src/system/system_win.ml > =================================================================== > --- trunk/src/system/system_win.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/system/system_win.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -22,6 +22,8 @@ > > *) > > +module M (P : sig val useLongUNCPaths : bool end) = struct > + > type fspath = string > > let fspathFromString f = f > @@ -43,7 +45,9 @@ > let winRootRx = Rx.rx "[a-zA-Z]:[/\\].*" > let winUncRx = Rx.rx "[/\\][/\\][^/\\]+[/\\][^/\\]+[/\\].*" > let extendedPath f = > - if Rx.match_string winRootRx f then > + if not P.useLongUNCPaths then > + f > + else if Rx.match_string winRootRx f then > fixPath ("\\\\?\\" ^ f) > else if Rx.match_string winUncRx f then > fixPath ("\\\\?\\UNC" ^ String.sub f 1 (String.length f - 1)) > @@ -318,3 +322,5 @@ > rawTerminal = (fun () -> setConsoleMode 0x19; setConsoleOutputCP > 65001); > startReading = (fun () -> setConsoleMode 0x18); > stopReading = (fun () -> setConsoleMode 0x19) } > + > +end > > Modified: trunk/src/system/win/system_impl.ml > =================================================================== > --- trunk/src/system/win/system_impl.ml 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/system/win/system_impl.ml 2010-01-10 22:52:59 UTC (rev > 400) > @@ -15,7 +15,7 @@ > along with this program. If not, see >. > *) > > -module System = System_win > +module System = System_win.M (struct let useLongUNCPaths = false end) > > module Fs = struct > > @@ -28,7 +28,7 @@ > let c3 f1 f2 v1 v2 v3 = if !unicode then f1 v1 v2 v3 else f2 v1 v2 > v3 > > module G = System_generic > - module W = System_win > + module W = System_win.M (struct let useLongUNCPaths = true end) > > type fspath = string > > > Modified: trunk/src/ubase/util.ml > =================================================================== > --- trunk/src/ubase/util.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/ubase/util.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -383,6 +383,7 @@ > if l = 0 || s.[l - 1] <> '\r' then s else > String.sub s 0 (l - 1) > > +(* FIX: quadratic! *) > let rec trimWhitespace s = > let l = String.length s in > if l=0 then s > > Modified: trunk/src/uimacbridgenew.ml > =================================================================== > --- trunk/src/uimacbridgenew.ml 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacbridgenew.ml 2010-01-10 22:52:59 UTC (rev 400) > @@ -392,9 +392,7 @@ > Callback.register "unisonRiToRight" unisonRiToRight;; > > let unisonRiToFileSize ri = > - (*FIX: will not work with files and directories larger than 1 GiB > on > - 32bit machines! *) > - Uutil.Filesize.toInt (riLength ri.ri);; > + Uutil.Filesize.toFloat (riLength ri.ri);; > Callback.register "unisonRiToFileSize" unisonRiToFileSize;; > > let unisonRiToFileType ri = > @@ -450,9 +448,7 @@ > Callback.register "unisonRiToProgress" unisonRiToProgress;; > > let unisonRiToBytesTransferred ri = > - (*FIX: will not work when transferring more than 1 GiB on 32bit > - machines! *) > - Uutil.Filesize.toInt ri.bytesTransferred;; > + Uutil.Filesize.toFloat ri.bytesTransferred;; > Callback.register "unisonRiToBytesTransferred" > unisonRiToBytesTransferred;; > > (* --------------------------------------------------- *) > > Modified: trunk/src/uimacnew/Bridge.h > =================================================================== > --- trunk/src/uimacnew/Bridge.h 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/Bridge.h 2010-01-10 22:52:59 UTC (rev 400) > @@ -24,14 +24,14 @@ > Args/return values are converted to/from C/OCaml according to the > supplied type signture string. Type codes are: > x - void (for return type) > - i - int > + i - long > s - char * > S - NSString * > + N - NSNumber * > @ - OCamlValue (see below) > - v - unwrapped OCaml value (deprecated -- unsafe!) > > Examples: > - int count = (int)ocamlCall("iS", "lengthOfString", @"Some String"); > + long count = (long)ocamlCall("iS", "lengthOfString", @"Some > String"); > > (void)ocamlCall("x", "someVoidOCamlFunction"); > > @@ -42,17 +42,17 @@ > > // Wrapper/proxy for unconverted OCaml values > @interface OCamlValue : NSObject { > - int _v; > + long _v; > } > -- initWithValue:(int)v; > +- initWithValue:(long)v; > > -- (void *)getField:(int)i withType:(char)t; > +- (void *)getField:(long)i withType:(char)t; > // get value by position. See ocamlCall for list of type > conversion codes > > -- (int)count; > +- (long)count; > // count of items in array > > -- (int)value; > +- (long)value; > // returns Ocaml value directly -- not safe to use except in direct > callback from OCaml > // (i.e. in the OCaml thread) > @end > > Modified: trunk/src/uimacnew/Bridge.m > =================================================================== > --- trunk/src/uimacnew/Bridge.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/Bridge.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -95,7 +95,10 @@ > // NSLog(@"*** caml_init complete!"); > } > > -- (BOOL)exceptionHandler:(NSExceptionHandler *)sender > shouldLogException:(NSException *)exception mask:(unsigned int)aMask > +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 > +typedef unsigned int NSUInteger; > +#endif > +- (BOOL)exceptionHandler:(NSExceptionHandler *)sender > shouldLogException:(NSException *)exception mask:(NSUInteger)aMask > { > // if (![[exception name] isEqual:@"OCamlException"]) return YES; > > @@ -121,7 +124,7 @@ > > // Field access > value *valueP; > - int fieldIndex; > + long fieldIndex; > char fieldType; > > // Return values > @@ -139,9 +142,11 @@ > > // Our OCaml callback server thread -- waits for call then makes them > // Called from thread spawned from OCaml > -CAMLprim value bridgeThreadWait(int ignore) > +CAMLprim value bridgeThreadWait(value ignore) > { > - value args[10]; > + CAMLparam0(); > + CAMLlocal1 (args); > + args = caml_alloc_tuple(3); > > // NSLog(@"*** bridgeThreadWait init! (%d) Taking lock...", > pthread_self()); > while (TRUE) { > @@ -168,6 +173,7 @@ > char retType = 'v'; > value e = Val_unit; > if (cs->opCode == SafeCall) { > + int i; > char *fname = va_arg(cs->args, char *); > value *f = caml_named_value(fname); > // varargs with C-based args -- convert them to OCaml values > based on type code string > @@ -179,33 +185,31 @@ > switch (*p) { > case 's': > str = va_arg(cs->args, const char *); > - args[argCount] = caml_copy_string(str); > + Store_field (args, argCount, caml_copy_string(str)); > break; > case 'S': > str = [va_arg(cs->args, NSString *) UTF8String]; > - args[argCount] = caml_copy_string(str); > + Store_field (args, argCount, caml_copy_string(str)); > break; > - case 'n': > - // leak? > - args[argCount] = *caml_named_value(va_arg(cs->args, char *)); > - break; > case 'i': > - args[argCount] = Val_int(va_arg(cs->args, int)); > + Store_field (args, > argCount, Val_long(va_arg(cs->args, long))); > break; > - case 'v': > - args[argCount] = va_arg(cs->args, value); > - break; > case '@': > - args[argCount] = [va_arg(cs->args, OCamlValue *) value]; > + Store_field (args, > argCount, [va_arg(cs->args, OCamlValue *) value]); > break; > + default: > + NSCAssert1(0, > @"Unknown input type '%c'", *p); > + break; > } > argCount++; > + NSCAssert(argCount <= 3, @"More > than 3 arguments"); > } > // Call OCaml -- TODO: add support for > 3 args > - if (argCount == 3) e = > caml_callback3_exn(*f,args[0],args[1],args[2]); > - else if (argCount == 2) e = > caml_callback2_exn(*f,args[0],args[1]); > - else if (argCount == 1) e = caml_callback_exn(*f,args[0]); > + if (argCount == 3) e = caml_callback3_exn(*f,Field(args, > 0),Field(args,1),Field(args,2)); > + else if (argCount == 2) e = caml_callback2_exn(*f,Field(args, > 0),Field(args,1)); > + else if (argCount == 1) e = caml_callback_exn(*f,Field(args,0)); > else e = caml_callback_exn(*f,Val_unit); > + for (i = 0; i < argCount; i++) Store_field > (args, i, Val_unit); > } else if (cs->opCode == OldCall) { > // old style (unsafe) version where OCaml values were passed > directly from C thread > if (cs->argCount == 3) e = caml_callback3_exn(cs->call,cs->a1,cs- > >a2,cs->a3); > @@ -213,8 +217,8 @@ > else e = caml_callback_exn(cs->call,cs->a1); > retType = 'v'; > } else if (cs->opCode == FieldAccess) { > - int index = cs->fieldIndex; > - e = (index == -1) ? Val_int(Wosize_val(*cs->valueP)) : Field(*cs- > >valueP, cs->fieldIndex); > + long index = cs->fieldIndex; > + e = (index == -1) ? Val_long(Wosize_val(*cs->valueP)) : > Field(*cs->valueP, index); > retType = cs->fieldType; > } > > @@ -223,30 +227,38 @@ > cs->ret = e; // OCaml return type -- unsafe... > if (!Is_exception_result(e)) { > switch (retType) { > - case 's': > - *((char **)&cs->retV) = (e == Val_unit) ? NULL : String_val(e); > - break; > case 'S': > *((NSString **)&cs->retV) = (e == Val_unit) ? NULL : [[NSString > alloc] initWithUTF8String:String_val(e)]; > cs->_autorelease = TRUE; > break; > + case 'N': > + if (Is_long (e)) { > + *((NSNumber **)&cs->retV) = [[NSNumber alloc] > initWithLong:Long_val(e)]; > + } else { > + *((NSNumber **)&cs->retV) = [[NSNumber alloc] > initWithDouble:Double_val(e)]; > + } > + cs->_autorelease = TRUE; > + break; > case '@': > *((NSObject **)&cs->retV) = (e == Val_unit) ? NULL : > [[OCamlValue alloc] initWithValue:e]; > cs->_autorelease = TRUE; > break; > - case 'v': > - *((value *)&cs->retV) = e; > - break; > case 'i': > - *((int *)&cs->retV) = Int_val(e); > + *((long *)&cs->retV) = Long_val(e); > break; > + case 'x': > + break; > + default: > + NSCAssert1(0, @"Unknown > return type '%c'", retType); > + break; > } > } > > if (Is_exception_result(e)) { > // get exception string -- it will get thrown back in the calling > thread > value *f = caml_named_value("unisonExnInfo"); > - cs->exception = > String_val(caml_callback(*f,Extract_exception(e))); > + // We leak memory here... > + cs->exception = > strdup(String_val(caml_callback(*f,Extract_exception(e)))); > } > > [pool release]; > @@ -260,7 +272,7 @@ > pthread_mutex_unlock(&global_res_lock); > } > // Never get here... > - return Val_unit; > + CAMLreturn (Val_unit); > } > > void *_passCall(CallState *cs) > @@ -292,20 +304,18 @@ > > void *ocamlCall(const char *argTypes, ...) > { > - va_list ap; > - va_start(ap, argTypes); > CallState cs; > cs.opCode = SafeCall; > cs.exception = NULL; > cs.argTypes = argTypes; > - cs.args = ap; > + va_start(cs.args, argTypes); > void * res = _passCall(&cs); > > - va_end(ap); > + va_end(cs.args); > return res; > } > > -void *getField(value *vP, int index, char type) > +void *getField(value *vP, long index, char type) > { > CallState cs; > cs.opCode = FieldAccess; > @@ -318,7 +328,7 @@ > > @implementation OCamlValue > > -- initWithValue:(int)v > +- initWithValue:(long)v > { > [super init]; > _v = v; > @@ -326,17 +336,17 @@ > return self; > } > > -- (int)count > +- (long)count > { > - return (int)getField((value *)&_v, -1, 'i'); > + return (long)getField((value *) &_v, -1, 'i'); > } > > -- (void *)getField:(int)i withType:(char)t > +- (void *)getField:(long)i withType:(char)t > { > - return getField((value *)&_v, i, t); > + return getField((value *)&_v, i, t); > } > > -- (int)value > +- (long)value > { > // Unsafe to use! > return _v; > > Modified: trunk/src/uimacnew/ImageAndTextCell.m > =================================================================== > --- trunk/src/uimacnew/ImageAndTextCell.m 2010-01-10 15:30:18 UTC > (rev 399) > +++ trunk/src/uimacnew/ImageAndTextCell.m 2010-01-10 22:52:59 UTC > (rev 400) > @@ -88,7 +88,10 @@ > [super editWithFrame: textFrame inView: controlView > editor:textObj delegate:anObject event: theEvent]; > } > > -- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView > editor:(NSText *)textObj delegate:(id)anObject start:(int)selStart > length:(int)selLength { > +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 > +typedef int NSInteger; > +#endif > +- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView > editor:(NSText *)textObj delegate:(id)anObject start: > (NSInteger)selStart length:(NSInteger)selLength { > NSRect textFrame, imageFrame; > NSDivideRect (aRect, &imageFrame, &textFrame, 3 + [image > size].width, NSMinXEdge); > [super selectWithFrame: textFrame inView: controlView > editor:textObj delegate:anObject start:selStart length:selLength]; > > Modified: trunk/src/uimacnew/MyController.m > =================================================================== > --- trunk/src/uimacnew/MyController.m 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/uimacnew/MyController.m 2010-01-10 22:52:59 UTC (rev > 400) > @@ -31,7 +31,9 @@ > > // BCP (11/09): Added per Onne Gorter: > // if user closes main window, terminate app, instead of keeping an > empty app around with no window > -- (BOOL)applicationShouldTerminateAfterLastWindowClosed: > (NSApplication *)theApplication { return YES; } > +- (BOOL)applicationShouldTerminateAfterLastWindowClosed: > (NSApplication *)theApplication { > + return YES; > +} > > - (id)init > { > @@ -166,8 +168,7 @@ > } > > /* Only valid once a profile has been selected */ > -- (NSString *)profile > -{ > +- (NSString *)profile { > return myProfile; > } > > @@ -176,8 +177,7 @@ > [aProfile retain]; > [myProfile release]; > myProfile = aProfile; > - [mainWindow setTitle: > - [NSString stringWithFormat:@"Unison: %@", myProfile]]; > + [mainWindow setTitle: [NSString stringWithFormat:@"Unison: %@", > myProfile]]; > } > > - (IBAction)restartButton:(id)sender > @@ -217,7 +217,7 @@ > { > [tableView reloadData]; > if (shouldResetSelection) { > - [tableView selectRow:0 byExtendingSelection:NO]; > + [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] > byExtendingSelection:NO]; > shouldResetSelection = NO; > } > [updatesView setNeedsDisplay:YES]; > @@ -268,6 +268,7 @@ > > CAMLprim value unisonInit1Complete(value v) > { > + id pool = [[NSAutoreleasePool alloc] init]; > if (v == Val_unit) { > NSLog(@"Connected."); > [me->preconn release]; > @@ -278,7 +279,7 @@ > me->preconn = [[OCamlValue alloc] initWithValue:Field(v,0)]; // > value of Some > [me performSelectorOnMainThread:@selector(unisonInit1Complete:) > withObject:nil waitUntilDone:FALSE]; > } > - > + [pool release]; > return Val_unit; > } > > @@ -310,7 +311,7 @@ > { > // FIX: some prompts don't ask for password, need to look at it > NSLog(@"Got the prompt: '%@'",prompt); > - if ((int)ocamlCall("iS", "unisonPasswordMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonPasswordMsg", prompt)) { > [passwordPrompt setStringValue:@"Please enter your password"]; > [NSApp beginSheet:passwordWindow > modalForWindow:mainWindow > @@ -319,7 +320,7 @@ > contextInfo:nil]; > return; > } > - if ((int)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { > [passwordPrompt setStringValue:@"Please enter your > passphrase"]; > [NSApp beginSheet:passwordWindow > modalForWindow:mainWindow > @@ -328,7 +329,7 @@ > contextInfo:nil]; > return; > } > - if ((int)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { > int i = NSRunAlertPanel(@"New host",prompt,@"Yes",@"No",nil); > if (i == NSAlertDefaultReturn) { > ocamlCall("x at s", "openConnectionReply", preconn, "yes"); > @@ -466,7 +467,9 @@ > > CAMLprim value unisonInit2Complete(value v) > { > + id pool = [[NSAutoreleasePool alloc] init]; > [me performSelectorOnMainThread:@selector(afterUpdate:) > withObject:[[OCamlValue alloc] initWithValue:v] waitUntilDone:FALSE]; > + [pool release]; > return Val_unit; > } > > @@ -502,7 +505,9 @@ > > CAMLprim value syncComplete() > { > + id pool = [[NSAutoreleasePool alloc] init]; > [me performSelectorOnMainThread:@selector(afterSync:) > withObject:nil waitUntilDone:FALSE]; > + [pool release]; > return Val_unit; > } > > @@ -517,10 +522,12 @@ > > CAMLprim value reloadTable(value row) > { > + id pool = [[NSAutoreleasePool alloc] init]; > // NSLog(@"OCaml says... ReloadTable: %i", Int_val(row)); > NSNumber *num = [[NSNumber alloc] initWithInt:Int_val(row)]; > [me performSelectorOnMainThread:@selector(reloadTable:) > withObject:num waitUntilDone:FALSE]; > [num release]; > + [pool release]; > return Val_unit; > } > > @@ -556,9 +563,9 @@ > [(ImageAndTextCell*)cell setImage:[item fileIcon]]; > > // For parents, format the file count into the text > - int fileCount = [item fileCount]; > + long fileCount = [item fileCount]; > if (fileCount > 1) { > - NSString *countString = [NSString stringWithFormat:@" (%i > files)", fileCount]; > + NSString *countString = [NSString stringWithFormat:@" (%ld > files)", fileCount]; > NSString *fullString = [(NSString *)[cell objectValue] > stringByAppendingString:countString]; > NSMutableAttributedString *as = [[NSMutableAttributedString > alloc] initWithString:fullString]; > > @@ -623,7 +630,7 @@ > { > [reconItems release]; > reconItems = [[NSMutableArray alloc] init]; > - int i, n =[caml_reconItems count]; > + long i, n =[caml_reconItems count]; > for (i=0; i LeafReconItem *item = [[LeafReconItem alloc] initWithRiAndIndex: > (id)[caml_reconItems getField:i withType:'@'] index:i]; > [reconItems addObject:item]; > @@ -712,7 +719,7 @@ > > - (id)updateForIgnore:(id)item > { > - int j = (int)ocamlCall("ii", "unisonUpdateForIgnore", > [reconItems indexOfObjectIdenticalTo:item]); > + long j = (long)ocamlCall("ii", "unisonUpdateForIgnore", > [reconItems indexOfObjectIdenticalTo:item]); > NSLog(@"Updating for ignore..."); > [self updateReconItems:(OCamlValue *)ocamlCall("@", > "unisonState")]; > return [reconItems objectAtIndex:j]; > @@ -721,10 +728,12 @@ > // A function called from ocaml > CAMLprim value displayStatus(value s) > { > + id pool = [[NSAutoreleasePool alloc] init]; > NSString *str = [[NSString alloc] initWithUTF8String:String_val(s)]; > // NSLog(@"displayStatus: %@", str); > [me performSelectorOnMainThread:@selector(statusTextSet:) > withObject:str waitUntilDone:FALSE]; > [str release]; > + [pool release]; > return Val_unit; > } > > @@ -738,31 +747,36 @@ > // Called from ocaml to dislpay progress bar > CAMLprim value displayGlobalProgress(value p) > { > + id pool = [[NSAutoreleasePool alloc] init]; > NSNumber *num = [[NSNumber alloc] initWithDouble:Double_val(p)]; > [me performSelectorOnMainThread:@selector(updateProgressBar:) > withObject:num waitUntilDone:FALSE]; > [num release]; > + [pool release]; > return Val_unit; > } > > // Called from ocaml to display diff > CAMLprim value displayDiff(value s, value s2) > { > + id pool = [[NSAutoreleasePool alloc] init]; > [me performSelectorOnMainThread:@selector(diffViewTextSet:) > withObject:[NSArray arrayWithObjects:[NSString > stringWithUTF8String:String_val(s)], > [NSString stringWithUTF8String:String_val(s2)], nil] > waitUntilDone:FALSE]; > + [pool release]; > return Val_unit; > } > > // Called from ocaml to display diff error messages > CAMLprim value displayDiffErr(value s) > { > + id pool = [[NSAutoreleasePool alloc] init]; > NSString * str = [NSString stringWithUTF8String:String_val(s)]; > - str = [[str componentsSeparatedByString:@"\n"] > - componentsJoinedByString:@" "]; > + str = [[str componentsSeparatedByString:@"\n"] > componentsJoinedByString:@" "]; > [me->statusText > performSelectorOnMainThread:@selector(setStringValue:) > withObject:str waitUntilDone:FALSE]; > + [pool release]; > return Val_unit; > } > > > Modified: trunk/src/uimacnew/ProfileController.m > =================================================================== > --- trunk/src/uimacnew/ProfileController.m 2010-01-10 15:30:18 UTC > (rev 399) > +++ trunk/src/uimacnew/ProfileController.m 2010-01-10 22:52:59 UTC > (rev 400) > @@ -13,7 +13,11 @@ > - (void)initProfiles > { > NSString *directory = unisonDirectory(); > +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 > NSArray *files = [[NSFileManager defaultManager] > directoryContentsAtPath:directory]; > +#else > + NSArray *files = [[NSFileManager defaultManager] > contentsOfDirectoryAtPath:directory error:nil]; > +#endif > unsigned int count = [files count]; > unsigned int i,j; > > @@ -31,7 +35,7 @@ > } > } > if (j > 0) > - [tableView selectRow:0 byExtendingSelection:NO]; > + [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex: > 0] byExtendingSelection:NO]; > } > > - (void)awakeFromNib > @@ -39,7 +43,7 @@ > // start with the default profile selected > [self initProfiles]; > if (defaultIndex >= 0) > - [tableView selectRow:defaultIndex byExtendingSelection:NO]; > + [tableView selectRowIndexes:[NSIndexSet > indexSetWithIndex:defaultIndex] byExtendingSelection:NO]; > // on awake the scroll bar is inactive, but after adding > profiles we might need it; > // reloadData makes it happen. Q: is setNeedsDisplay more > efficient? > [tableView reloadData]; > > Modified: trunk/src/uimacnew/ReconItem.h > =================================================================== > --- trunk/src/uimacnew/ReconItem.h 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/ReconItem.h 2010-01-10 22:52:59 UTC (rev 400) > @@ -12,8 +12,8 @@ > BOOL selected; > NSImage *direction; > NSString *directionSortString; > - int fileSize; > - int bytesTransferred; > + double fileSize; > + double bytesTransferred; > BOOL resolved; > } > - (BOOL)selected; > @@ -24,10 +24,10 @@ > - (NSString *)right; > - (NSImage *)direction; > - (NSImage *)fileIcon; > -- (int)fileCount; > -- (int)fileSize; > +- (long)fileCount; > +- (double)fileSize; > - (NSString *)fileSizeString; > -- (int)bytesTransferred; > +- (double)bytesTransferred; > - (NSString *)bytesTransferredString; > - (void)setDirection:(char *)d; > - (void) doAction:(unichar)action; > @@ -64,15 +64,15 @@ > NSString *progress; > NSString *details; > OCamlValue *ri; // an ocaml Common.reconItem > - int index; // index in Ri list > + long index; // index in Ri list > } > -- initWithRiAndIndex:(OCamlValue *)v index:(int)i; > +- initWithRiAndIndex:(OCamlValue *)v index:(long)i; > @end > > @interface ParentReconItem : ReconItem > { > NSMutableArray *_children; > - int fileCount; > + long fileCount; > } > - (void)addChild:(ReconItem *)item nested:(BOOL)useNesting; > - (void)sortUsingDescriptors:(NSArray *)sortDescriptors; > > Modified: trunk/src/uimacnew/ReconItem.m > =================================================================== > --- trunk/src/uimacnew/ReconItem.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/ReconItem.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -9,8 +9,8 @@ > [super init]; > selected = NO; // NB only used/updated during sorts. Not a > // reliable indicator of whether item is selected > - fileSize = -1; > - bytesTransferred = -1; > + fileSize = -1.; > + bytesTransferred = -1.; > return self; > } > > @@ -129,30 +129,29 @@ > } > > > -- (int)computeFileSize > +- (double)computeFileSize > { > - return 0; > + return 0.; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - return 0; > + return 0.; > } > > -- (int)fileCount > +- (long)fileCount > { > return 1; > } > > -- (int)fileSize > +- (double)fileSize > { > - if (fileSize == -1) fileSize = [self computeFileSize]; > + if (fileSize == -1.) fileSize = [self computeFileSize]; > return fileSize; > } > > -- (NSString *)formatFileSize:(int)intSize > +- (NSString *)formatFileSize:(double)size > { > - float size = (float)intSize; > if (size == 0) return @"--"; > if (size < 1024) return @"< 1KB"; // return [NSString > stringWithFormat:@"%i bytes", size]; > size /= 1024; > @@ -175,8 +174,8 @@ > > - (NSNumber *)percentTransferred > { > - int size = [self computeFileSize]; > - return (size > 0) ? [NSNumber numberWithFloat:(((float)[self > bytesTransferred]) / (float)size) * 100.0] > + double size = [self computeFileSize]; > + return (size > 0) ? [NSNumber numberWithDouble:([self > bytesTransferred] / (size) * 100.0)] > : nil; > } > > @@ -379,8 +378,8 @@ > > - (BOOL)transferInProgress > { > - int soFar = [self bytesTransferred]; > - return (soFar > 0) && (soFar != [self fileSize]); > + double soFar = [self bytesTransferred]; > + return (soFar > 0) && (soFar < [self fileSize]); > } > > - (void)resetProgress > @@ -390,7 +389,7 @@ > - (NSString *)progressString > { > NSString *progress = [self progress]; > - if ([progress length] == 0 || [progress hasSuffix:@"%"]) > + if ([progress length] == 0. || [progress hasSuffix:@"%"]) > progress = [self transferInProgress] ? [self > bytesTransferredString] : @""; > else if ([progress isEqual:@"done"]) progress = @""; > return progress; > @@ -443,7 +442,7 @@ > // --- Leaf items -- actually corresponding to ReconItems in OCaml > @implementation LeafReconItem > > -- initWithRiAndIndex:(OCamlValue *)v index:(int)i > +- initWithRiAndIndex:(OCamlValue *)v index:(long)i > { > [super init]; > ri = [v retain]; > @@ -482,17 +481,17 @@ > return right; > } > > -- (int)computeFileSize > +- (double)computeFileSize > { > - return (int)ocamlCall("i@", "unisonRiToFileSize", ri); > + return [(NSNumber *)ocamlCall("N@", "unisonRiToFileSize", ri) > doubleValue]; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - if (bytesTransferred == -1) { > + if (bytesTransferred == -1.) { > // need to force to fileSize if done, otherwise may not match up > to 100% > bytesTransferred = ([[self progress] isEqual:@"done"]) ? [self > fileSize] > - : (int)ocamlCall("i@", "unisonRiToBytesTransferred", ri); > + : [(NSNumber*)ocamlCall("N@", > "unisonRiToBytesTransferred", ri) doubleValue]; > } > return bytesTransferred; > } > @@ -535,7 +534,7 @@ > { > // Get rid of the memoized progress because we expect it to change > [self willChange]; > - bytesTransferred = -1; > + bytesTransferred = -1.; > [progress release]; > > // Force update now so we get the result while the OCaml thread is > available > @@ -559,12 +558,12 @@ > > - (BOOL)isConflict > { > - return ((int)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); > } > > - (BOOL)changedFromDefault > { > - return ((int)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); > } > > - (void)revertDirection > @@ -575,7 +574,7 @@ > > - (BOOL)canDiff > { > - return ((int)ocamlCall("i@", "canDiff", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "canDiff", ri) ? YES : NO); > } > > - (void)showDiffs > @@ -715,7 +714,7 @@ > // [directionSortString autorelease]; > direction = nil; > directionSortString = nil; > - bytesTransferred = -1; > + bytesTransferred = -1.; > // fileSize = -1; > // resolved = NO; > > @@ -748,7 +747,7 @@ > } > > // Rollup methods > -- (int)fileCount > +- (long)fileCount > { > if (fileCount == 0) { > int i = [_children count]; > @@ -760,9 +759,9 @@ > return fileCount; > } > > -- (int)computeFileSize > +- (double)computeFileSize > { > - int size = 0; > + double size = 0; > int i = [_children count]; > while (i--) { > ReconItem *child = [_children objectAtIndex:i]; > @@ -771,10 +770,10 @@ > return size; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - if (bytesTransferred == -1) { > - bytesTransferred = 0; > + if (bytesTransferred == -1.) { > + bytesTransferred = 0.; > int i = [_children count]; > while (i--) { > ReconItem *child = [_children objectAtIndex:i]; > > Modified: trunk/src/uimacnew/ReconTableView.m > =================================================================== > --- trunk/src/uimacnew/ReconTableView.m 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/uimacnew/ReconTableView.m 2010-01-10 22:52:59 UTC (rev > 400) > @@ -11,12 +11,16 @@ > #import "MyController.h" > > @implementation NSOutlineView (_UnisonExtras) > + > - (NSArray *)selectedObjects > { > NSMutableArray *result = [NSMutableArray array]; > - NSEnumerator *e = [self selectedRowEnumerator]; > - NSNumber *n; > - while (n = [e nextObject]) [result addObject:[self itemAtRow:[n > intValue]]]; > + NSIndexSet *set = [self selectedRowIndexes]; > + NSUInteger index = [set firstIndex]; > + while (index != NSNotFound) { > + [result addObject:[self itemAtRow:index]]; > + index = [set indexGreaterThanIndex: index]; > + } > return result; > } > > @@ -136,8 +140,9 @@ > last = item; > } > if (last) { // something was selected > - last = [[self dataSource] updateForIgnore:last]; > - [self selectRow:[self rowForItem:last] > byExtendingSelection:NO]; > + MyController* controller = (MyController*) [self dataSource]; > + last = [controller updateForIgnore:last]; > + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:[self > rowForItem:last]] byExtendingSelection:NO]; > [self reloadData]; > } > } > @@ -171,7 +176,7 @@ > int nextRow = [self rowForItem:last] + 1; > if (numSelected == 1 && [self numberOfRows] > nextRow && c! > ='d') { > // Move to next row, unless already at last row, or if > more than one row selected > - [self selectRow:nextRow byExtendingSelection:NO]; > + [self selectRowIndexes:[NSIndexSet > indexSetWithIndex:nextRow] byExtendingSelection:NO]; > [self scrollRowToVisible:nextRow]; > } > [self reloadData]; > @@ -206,12 +211,13 @@ > - (IBAction)selectConflicts:(id)sender > { > [self deselectAll:self]; > - NSMutableArray *reconItems = [[self dataSource] reconItems]; > + MyController* controller = (MyController*) [self dataSource]; > + NSMutableArray *reconItems = [controller reconItems]; > int i = 0; > for (; i < [reconItems count]; i++) { > ReconItem *item = [reconItems objectAtIndex:i]; > if ([item isConflict]) > - [self selectRow:[self rowForItem:item] > byExtendingSelection:YES]; > + [self selectRowIndexes:[NSIndexSet indexSetWithIndex: > [self rowForItem:item]] byExtendingSelection:YES]; > } > } > > > Modified: trunk/src/uimacnew/UnisonToolbar.h > =================================================================== > --- trunk/src/uimacnew/UnisonToolbar.h 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/uimacnew/UnisonToolbar.h 2010-01-10 22:52:59 UTC (rev > 400) > @@ -12,6 +12,9 @@ > @class ReconTableView, MyController; > > @interface UnisonToolbar : NSToolbar > +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) > + > +#endif > { > ReconTableView* tableView; > MyController* myController; > > Modified: trunk/src/uimacnew/main.m > =================================================================== > --- trunk/src/uimacnew/main.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew/main.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -11,6 +11,7 @@ > > int main(int argc, const char *argv[]) > { > + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; > int i; > > /* When you click-start or use the open command, the program is > invoked with > @@ -34,22 +35,19 @@ > !strcmp(argv[i],"-server") || > !strcmp(argv[i],"-socket") || > !strcmp(argv[i],"-ui")) { > - /* We install an autorelease pool here because there > might be callbacks > - from ocaml to objc code */ > NSLog(@"Calling nonGuiStartup"); > - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] > init]; > @try { > ocamlCall("x", "unisonNonGuiStartup"); > } @catch (NSException *ex) { > NSLog(@"Uncaught exception: %@", [ex reason]); > exit(1); > } > - [pool release]; > /* If we get here without exiting first, the non GUI > startup detected a > -ui graphic or command-line profile, and we should in > fact start the GUI. */ > } > } > > /* go! */ > + [pool release]; > return NSApplicationMain(argc, argv); > } > > Modified: trunk/src/uimacnew09/Bridge.h > =================================================================== > --- trunk/src/uimacnew09/Bridge.h 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/Bridge.h 2010-01-10 22:52:59 UTC (rev 400) > @@ -24,14 +24,14 @@ > Args/return values are converted to/from C/OCaml according to the > supplied type signture string. Type codes are: > x - void (for return type) > - i - int > + i - long > s - char * > S - NSString * > + N - NSNumber * > @ - OCamlValue (see below) > - v - unwrapped OCaml value (deprecated -- unsafe!) > > Examples: > - int count = (int)ocamlCall("iS", "lengthOfString", @"Some String"); > + long count = (long)ocamlCall("iS", "lengthOfString", @"Some > String"); > > (void)ocamlCall("x", "someVoidOCamlFunction"); > > @@ -42,17 +42,17 @@ > > // Wrapper/proxy for unconverted OCaml values > @interface OCamlValue : NSObject { > - int _v; > + long _v; > } > -- initWithValue:(int)v; > +- initWithValue:(long)v; > > -- (void *)getField:(int)i withType:(char)t; > +- (void *)getField:(long)i withType:(char)t; > // get value by position. See ocamlCall for list of type > conversion codes > > -- (int)count; > +- (long)count; > // count of items in array > > -- (int)value; > +- (long)value; > // returns Ocaml value directly -- not safe to use except in direct > callback from OCaml > // (i.e. in the OCaml thread) > @end > > Modified: trunk/src/uimacnew09/Bridge.m > =================================================================== > --- trunk/src/uimacnew09/Bridge.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/Bridge.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -95,7 +95,10 @@ > // NSLog(@"*** caml_init complete!"); > } > > -- (BOOL)exceptionHandler:(NSExceptionHandler *)sender > shouldLogException:(NSException *)exception mask:(unsigned int)aMask > +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 > +typedef unsigned int NSUInteger; > +#endif > +- (BOOL)exceptionHandler:(NSExceptionHandler *)sender > shouldLogException:(NSException *)exception mask:(NSUInteger)aMask > { > // if (![[exception name] isEqual:@"OCamlException"]) return YES; > > @@ -121,7 +124,7 @@ > > // Field access > value *valueP; > - int fieldIndex; > + long fieldIndex; > char fieldType; > > // Return values > @@ -139,9 +142,11 @@ > > // Our OCaml callback server thread -- waits for call then makes them > // Called from thread spawned from OCaml > -CAMLprim value bridgeThreadWait(int ignore) > +CAMLprim value bridgeThreadWait(value ignore) > { > - value args[10]; > + CAMLparam0(); > + CAMLlocal1 (args); > + args = caml_alloc_tuple(3); > > // NSLog(@"*** bridgeThreadWait init! (%d) Taking lock...", > pthread_self()); > while (TRUE) { > @@ -168,6 +173,7 @@ > char retType = 'v'; > value e = Val_unit; > if (cs->opCode == SafeCall) { > + int i; > char *fname = va_arg(cs->args, char *); > value *f = caml_named_value(fname); > // varargs with C-based args -- convert them to OCaml values > based on type code string > @@ -179,33 +185,31 @@ > switch (*p) { > case 's': > str = va_arg(cs->args, const char *); > - args[argCount] = caml_copy_string(str); > + Store_field (args, argCount, caml_copy_string(str)); > break; > case 'S': > str = [va_arg(cs->args, NSString *) UTF8String]; > - args[argCount] = caml_copy_string(str); > + Store_field (args, argCount, caml_copy_string(str)); > break; > - case 'n': > - // leak? > - args[argCount] = *caml_named_value(va_arg(cs->args, char *)); > - break; > case 'i': > - args[argCount] = Val_int(va_arg(cs->args, int)); > + Store_field (args, > argCount, Val_long(va_arg(cs->args, long))); > break; > - case 'v': > - args[argCount] = va_arg(cs->args, value); > - break; > case '@': > - args[argCount] = [va_arg(cs->args, OCamlValue *) value]; > + Store_field (args, > argCount, [va_arg(cs->args, OCamlValue *) value]); > break; > + default: > + NSCAssert1(0, > @"Unknown input type '%c'", *p); > + break; > } > argCount++; > + NSCAssert(argCount <= 3, @"More > than 3 arguments"); > } > // Call OCaml -- TODO: add support for > 3 args > - if (argCount == 3) e = > caml_callback3_exn(*f,args[0],args[1],args[2]); > - else if (argCount == 2) e = > caml_callback2_exn(*f,args[0],args[1]); > - else if (argCount == 1) e = caml_callback_exn(*f,args[0]); > + if (argCount == 3) e = caml_callback3_exn(*f,Field(args, > 0),Field(args,1),Field(args,2)); > + else if (argCount == 2) e = caml_callback2_exn(*f,Field(args, > 0),Field(args,1)); > + else if (argCount == 1) e = caml_callback_exn(*f,Field(args,0)); > else e = caml_callback_exn(*f,Val_unit); > + for (i = 0; i < argCount; i++) Store_field > (args, i, Val_unit); > } else if (cs->opCode == OldCall) { > // old style (unsafe) version where OCaml values were passed > directly from C thread > if (cs->argCount == 3) e = caml_callback3_exn(cs->call,cs->a1,cs- > >a2,cs->a3); > @@ -213,8 +217,8 @@ > else e = caml_callback_exn(cs->call,cs->a1); > retType = 'v'; > } else if (cs->opCode == FieldAccess) { > - int index = cs->fieldIndex; > - e = (index == -1) ? Val_int(Wosize_val(*cs->valueP)) : Field(*cs- > >valueP, cs->fieldIndex); > + long index = cs->fieldIndex; > + e = (index == -1) ? Val_long(Wosize_val(*cs->valueP)) : > Field(*cs->valueP, index); > retType = cs->fieldType; > } > > @@ -223,30 +227,38 @@ > cs->ret = e; // OCaml return type -- unsafe... > if (!Is_exception_result(e)) { > switch (retType) { > - case 's': > - *((char **)&cs->retV) = (e == Val_unit) ? NULL : String_val(e); > - break; > case 'S': > *((NSString **)&cs->retV) = (e == Val_unit) ? NULL : [[NSString > alloc] initWithUTF8String:String_val(e)]; > cs->_autorelease = TRUE; > break; > + case 'N': > + if (Is_long (e)) { > + *((NSNumber **)&cs->retV) = [[NSNumber alloc] > initWithLong:Long_val(e)]; > + } else { > + *((NSNumber **)&cs->retV) = [[NSNumber alloc] > initWithDouble:Double_val(e)]; > + } > + cs->_autorelease = TRUE; > + break; > case '@': > *((NSObject **)&cs->retV) = (e == Val_unit) ? NULL : > [[OCamlValue alloc] initWithValue:e]; > cs->_autorelease = TRUE; > break; > - case 'v': > - *((value *)&cs->retV) = e; > - break; > case 'i': > - *((int *)&cs->retV) = Int_val(e); > + *((long *)&cs->retV) = Long_val(e); > break; > + case 'x': > + break; > + default: > + NSCAssert1(0, @"Unknown > return type '%c'", retType); > + break; > } > } > > if (Is_exception_result(e)) { > // get exception string -- it will get thrown back in the calling > thread > value *f = caml_named_value("unisonExnInfo"); > - cs->exception = > String_val(caml_callback(*f,Extract_exception(e))); > + // We leak memory here... > + cs->exception = > strdup(String_val(caml_callback(*f,Extract_exception(e)))); > } > > [pool release]; > @@ -260,7 +272,7 @@ > pthread_mutex_unlock(&global_res_lock); > } > // Never get here... > - return Val_unit; > + CAMLreturn (Val_unit); > } > > void *_passCall(CallState *cs) > @@ -292,20 +304,18 @@ > > void *ocamlCall(const char *argTypes, ...) > { > - va_list ap; > - va_start(ap, argTypes); > CallState cs; > cs.opCode = SafeCall; > cs.exception = NULL; > cs.argTypes = argTypes; > - cs.args = ap; > + va_start(cs.args, argTypes); > void * res = _passCall(&cs); > > - va_end(ap); > + va_end(cs.args); > return res; > } > > -void *getField(value *vP, int index, char type) > +void *getField(value *vP, long index, char type) > { > CallState cs; > cs.opCode = FieldAccess; > @@ -318,7 +328,7 @@ > > @implementation OCamlValue > > -- initWithValue:(int)v > +- initWithValue:(long)v > { > [super init]; > _v = v; > @@ -326,17 +336,17 @@ > return self; > } > > -- (int)count > +- (long)count > { > - return (int)getField((value *)&_v, -1, 'i'); > + return (long)getField((value *) &_v, -1, 'i'); > } > > -- (void *)getField:(int)i withType:(char)t > +- (void *)getField:(long)i withType:(char)t > { > - return getField((value *)&_v, i, t); > + return getField((value *)&_v, i, t); > } > > -- (int)value > +- (long)value > { > // Unsafe to use! > return _v; > > Modified: trunk/src/uimacnew09/ImageAndTextCell.m > =================================================================== > --- trunk/src/uimacnew09/ImageAndTextCell.m 2010-01-10 15:30:18 UTC > (rev 399) > +++ trunk/src/uimacnew09/ImageAndTextCell.m 2010-01-10 22:52:59 UTC > (rev 400) > @@ -88,7 +88,10 @@ > [super editWithFrame: textFrame inView: controlView > editor:textObj delegate:anObject event: theEvent]; > } > > -- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView > editor:(NSText *)textObj delegate:(id)anObject start:(int)selStart > length:(int)selLength { > +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 > +typedef int NSInteger; > +#endif > +- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView > editor:(NSText *)textObj delegate:(id)anObject start: > (NSInteger)selStart length:(NSInteger)selLength { > NSRect textFrame, imageFrame; > NSDivideRect (aRect, &imageFrame, &textFrame, 3 + [image > size].width, NSMinXEdge); > [super selectWithFrame: textFrame inView: controlView > editor:textObj delegate:anObject start:selStart length:selLength]; > > Modified: trunk/src/uimacnew09/MyController.m > =================================================================== > --- trunk/src/uimacnew09/MyController.m 2010-01-10 15:30:18 UTC (rev > 399) > +++ trunk/src/uimacnew09/MyController.m 2010-01-10 22:52:59 UTC (rev > 400) > @@ -395,7 +395,7 @@ > { > // FIX: some prompts don't ask for password, need to look at it > NSLog(@"Got the prompt: '%@'",prompt); > - if ((int)ocamlCall("iS", "unisonPasswordMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonPasswordMsg", prompt)) { > [passwordPrompt setStringValue:@"Please enter your password"]; > [NSApp beginSheet:passwordWindow > modalForWindow:mainWindow > @@ -404,7 +404,7 @@ > contextInfo:nil]; > return; > } > - if ((int)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonPassphraseMsg", prompt)) { > [passwordPrompt setStringValue:@"Please enter your > passphrase"]; > [NSApp beginSheet:passwordWindow > modalForWindow:mainWindow > @@ -413,7 +413,7 @@ > contextInfo:nil]; > return; > } > - if ((int)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { > + if ((long)ocamlCall("iS", "unisonAuthenticityMsg", prompt)) { > int i = NSRunAlertPanel(@"New host",prompt,@"Yes",@"No",nil); > if (i == NSAlertDefaultReturn) { > ocamlCall("x at s", "openConnectionReply", preconn, "yes"); > @@ -649,9 +649,9 @@ > [(ImageAndTextCell*)cell setImage:[item fileIcon]]; > > // For parents, format the file count into the text > - int fileCount = [item fileCount]; > + long fileCount = [item fileCount]; > if (fileCount > 1) { > - NSString *countString = [NSString stringWithFormat:@" (%i > files)", fileCount]; > + NSString *countString = [NSString stringWithFormat:@" (%ld > files)", fileCount]; > NSString *fullString = [(NSString *)[cell objectValue] > stringByAppendingString:countString]; > NSMutableAttributedString *as = [[NSMutableAttributedString > alloc] initWithString:fullString]; > > @@ -716,7 +716,7 @@ > { > [reconItems release]; > reconItems = [[NSMutableArray alloc] init]; > - int i, n =[caml_reconItems count]; > + long i, n =[caml_reconItems count]; > for (i=0; i LeafReconItem *item = [[LeafReconItem alloc] initWithRiAndIndex: > (id)[caml_reconItems getField:i withType:'@'] index:i]; > [reconItems addObject:item]; > @@ -805,7 +805,7 @@ > > - (id)updateForIgnore:(id)item > { > - int j = (int)ocamlCall("ii", "unisonUpdateForIgnore", > [reconItems indexOfObjectIdenticalTo:item]); > + long j = (long)ocamlCall("ii", "unisonUpdateForIgnore", > [reconItems indexOfObjectIdenticalTo:item]); > NSLog(@"Updating for ignore..."); > [self updateReconItems:(OCamlValue *)ocamlCall("@", > "unisonState")]; > return [reconItems objectAtIndex:j]; > > Modified: trunk/src/uimacnew09/ProfileController.m > =================================================================== > --- trunk/src/uimacnew09/ProfileController.m 2010-01-10 15:30:18 UTC > (rev 399) > +++ trunk/src/uimacnew09/ProfileController.m 2010-01-10 22:52:59 UTC > (rev 400) > @@ -13,7 +13,11 @@ > - (void)initProfiles > { > NSString *directory = unisonDirectory(); > +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 > + NSArray *files = [[NSFileManager defaultManager] > directoryContentsAtPath:directory]; > +#else > NSArray *files = [[NSFileManager defaultManager] > contentsOfDirectoryAtPath:directory error:nil]; > +#endif > unsigned int count = [files count]; > unsigned int i,j; > > > Modified: trunk/src/uimacnew09/ReconItem.h > =================================================================== > --- trunk/src/uimacnew09/ReconItem.h 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/ReconItem.h 2010-01-10 22:52:59 UTC (rev 400) > @@ -12,8 +12,8 @@ > BOOL selected; > NSImage *direction; > NSString *directionSortString; > - int fileSize; > - int bytesTransferred; > + double fileSize; > + double bytesTransferred; > BOOL resolved; > } > - (BOOL)selected; > @@ -24,10 +24,10 @@ > - (NSString *)right; > - (NSImage *)direction; > - (NSImage *)fileIcon; > -- (int)fileCount; > -- (int)fileSize; > +- (long)fileCount; > +- (double)fileSize; > - (NSString *)fileSizeString; > -- (int)bytesTransferred; > +- (double)bytesTransferred; > - (NSString *)bytesTransferredString; > - (void)setDirection:(char *)d; > - (void) doAction:(unichar)action; > @@ -64,15 +64,15 @@ > NSString *progress; > NSString *details; > OCamlValue *ri; // an ocaml Common.reconItem > - int index; // index in Ri list > + long index; // index in Ri list > } > -- initWithRiAndIndex:(OCamlValue *)v index:(int)i; > +- initWithRiAndIndex:(OCamlValue *)v index:(long)i; > @end > > @interface ParentReconItem : ReconItem > { > NSMutableArray *_children; > - int fileCount; > + long fileCount; > } > - (void)addChild:(ReconItem *)item nested:(BOOL)useNesting; > - (void)sortUsingDescriptors:(NSArray *)sortDescriptors; > > Modified: trunk/src/uimacnew09/ReconItem.m > =================================================================== > --- trunk/src/uimacnew09/ReconItem.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/ReconItem.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -9,8 +9,8 @@ > [super init]; > selected = NO; // NB only used/updated during sorts. Not a > // reliable indicator of whether item is selected > - fileSize = -1; > - bytesTransferred = -1; > + fileSize = -1.; > + bytesTransferred = -1.; > return self; > } > > @@ -129,30 +129,29 @@ > } > > > -- (int)computeFileSize > +- (double)computeFileSize > { > - return 0; > + return 0.; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - return 0; > + return 0.; > } > > -- (int)fileCount > +- (long)fileCount > { > return 1; > } > > -- (int)fileSize > +- (double)fileSize > { > - if (fileSize == -1) fileSize = [self computeFileSize]; > + if (fileSize == -1.) fileSize = [self computeFileSize]; > return fileSize; > } > > -- (NSString *)formatFileSize:(int)intSize > +- (NSString *)formatFileSize:(double)size > { > - float size = (float)intSize; > if (size == 0) return @"--"; > if (size < 1024) return @"< 1KB"; // return [NSString > stringWithFormat:@"%i bytes", size]; > size /= 1024; > @@ -175,8 +174,8 @@ > > - (NSNumber *)percentTransferred > { > - int size = [self computeFileSize]; > - return (size > 0) ? [NSNumber numberWithFloat:(((float)[self > bytesTransferred]) / (float)size) * 100.0] > + double size = [self computeFileSize]; > + return (size > 0) ? [NSNumber numberWithDouble:([self > bytesTransferred] / (size) * 100.0)] > : nil; > } > > @@ -379,8 +378,8 @@ > > - (BOOL)transferInProgress > { > - int soFar = [self bytesTransferred]; > - return (soFar > 0) && (soFar != [self fileSize]); > + double soFar = [self bytesTransferred]; > + return (soFar > 0) && (soFar < [self fileSize]); > } > > - (void)resetProgress > @@ -390,7 +389,7 @@ > - (NSString *)progressString > { > NSString *progress = [self progress]; > - if ([progress length] == 0 || [progress hasSuffix:@"%"]) > + if ([progress length] == 0. || [progress hasSuffix:@"%"]) > progress = [self transferInProgress] ? [self > bytesTransferredString] : @""; > else if ([progress isEqual:@"done"]) progress = @""; > return progress; > @@ -443,7 +442,7 @@ > // --- Leaf items -- actually corresponding to ReconItems in OCaml > @implementation LeafReconItem > > -- initWithRiAndIndex:(OCamlValue *)v index:(int)i > +- initWithRiAndIndex:(OCamlValue *)v index:(long)i > { > [super init]; > ri = [v retain]; > @@ -482,17 +481,17 @@ > return right; > } > > -- (int)computeFileSize > +- (double)computeFileSize > { > - return (int)ocamlCall("i@", "unisonRiToFileSize", ri); > + return [(NSNumber *)ocamlCall("N@", "unisonRiToFileSize", ri) > doubleValue]; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - if (bytesTransferred == -1) { > + if (bytesTransferred == -1.) { > // need to force to fileSize if done, otherwise may not match up > to 100% > bytesTransferred = ([[self progress] isEqual:@"done"]) ? [self > fileSize] > - : (int)ocamlCall("i@", "unisonRiToBytesTransferred", ri); > + : [(NSNumber*)ocamlCall("N@", > "unisonRiToBytesTransferred", ri) doubleValue]; > } > return bytesTransferred; > } > @@ -535,7 +534,7 @@ > { > // Get rid of the memoized progress because we expect it to change > [self willChange]; > - bytesTransferred = -1; > + bytesTransferred = -1.; > [progress release]; > > // Force update now so we get the result while the OCaml thread is > available > @@ -559,12 +558,12 @@ > > - (BOOL)isConflict > { > - return ((int)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "unisonRiIsConflict", ri) ? YES : NO); > } > > - (BOOL)changedFromDefault > { > - return ((int)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "changedFromDefault", ri) ? YES : NO); > } > > - (void)revertDirection > @@ -575,7 +574,7 @@ > > - (BOOL)canDiff > { > - return ((int)ocamlCall("i@", "canDiff", ri) ? YES : NO); > + return ((long)ocamlCall("i@", "canDiff", ri) ? YES : NO); > } > > - (void)showDiffs > @@ -715,7 +714,7 @@ > // [directionSortString autorelease]; > direction = nil; > directionSortString = nil; > - bytesTransferred = -1; > + bytesTransferred = -1.; > // fileSize = -1; > // resolved = NO; > > @@ -748,7 +747,7 @@ > } > > // Rollup methods > -- (int)fileCount > +- (long)fileCount > { > if (fileCount == 0) { > int i = [_children count]; > @@ -760,9 +759,9 @@ > return fileCount; > } > > -- (int)computeFileSize > +- (double)computeFileSize > { > - int size = 0; > + double size = 0; > int i = [_children count]; > while (i--) { > ReconItem *child = [_children objectAtIndex:i]; > @@ -771,10 +770,10 @@ > return size; > } > > -- (int)bytesTransferred > +- (double)bytesTransferred > { > - if (bytesTransferred == -1) { > - bytesTransferred = 0; > + if (bytesTransferred == -1.) { > + bytesTransferred = 0.; > int i = [_children count]; > while (i--) { > ReconItem *child = [_children objectAtIndex:i]; > > Modified: trunk/src/uimacnew09/main.m > =================================================================== > --- trunk/src/uimacnew09/main.m 2010-01-10 15:30:18 UTC (rev 399) > +++ trunk/src/uimacnew09/main.m 2010-01-10 22:52:59 UTC (rev 400) > @@ -11,7 +11,7 @@ > > int main(int argc, const char *argv[]) > { > - id pool = [[NSAutoreleasePool alloc] init]; > + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; > int i; > > /* When you click-start or use the open command, the program is > invoked with > @@ -35,17 +35,13 @@ > !strcmp(argv[i],"-server") || > !strcmp(argv[i],"-socket") || > !strcmp(argv[i],"-ui")) { > - /* We install an autorelease pool here because there might be > callbacks > - from ocaml to objc code */ > NSLog(@"Calling nonGuiStartup"); > - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; > @try { > ocamlCall("x", "unisonNonGuiStartup"); > } @catch (NSException *ex) { > NSLog(@"Uncaught exception: %@", [ex reason]); > exit(1); > } > - [pool release]; > /* If we get here without exiting first, the non GUI startup > detected a > -ui graphic or command-line profile, and we should in fact > start the GUI. */ > } > > _______________________________________________ > Unison-hackers mailing list > Unison-hackers at lists.seas.upenn.edu > http://lists.seas.upenn.edu/mailman/listinfo/unison-hackers From mgross at informatik.uni-bremen.de Mon Jan 11 02:49:51 2010 From: mgross at informatik.uni-bremen.de (=?iso-8859-1?Q?Markus_Gro=DF?=) Date: Mon, 11 Jan 2010 08:49:51 +0100 Subject: [Unison-hackers] [unison-svn] r400 - in trunk/src: . system system/win ubase uimacnew uimacnew09 In-Reply-To: <39207FF6-4FBA-4720-9391-6CFADB2EDCEA@cis.upenn.edu> References: <201001102253.o0AMr3a1026196@yaws.seas.upenn.edu> <39207FF6-4FBA-4720-9391-6CFADB2EDCEA@cis.upenn.edu> Message-ID: <7350E55C-F892-4676-BA8F-B055E0D5F9A2@informatik.uni-bremen.de> On 11.01.2010, at 02:12, Benjamin Pierce wrote: > For the macnew09 version, I'm stuck on this compilation error -- any > hints? Maybe we can get it working if you re-add the frameworks. Please try: - Delete the two frameworks from the Xcode project and also delete their folders in the "Frameworks" folder. - Then download the two frameworks here: http://growl.googlecode.com/files/Growl-1.2-SDK.dmg http://brandonwalkin.com/blog/bwtfiles/BWToolkit.zip - Copy the Growl.framework and BWToolkit.framework to the "uimacnew09/Frameworks" folder (or just to the "uimacnew09" folder, perhabs the Framework-subfolder is making trouble) - Add the two frameworks in the Xcode project (make sure they are also in the "Copy files" build-phase of the Unison-target) - If you get any "Growl.h not found" errors, enter the "uimacnew09/Frameworks" path to "Framework Search Paths" in the Xcode project info (you might want to check the "recursive" box when doing this). Hope that will work... Cheers, Markus > /Developer/Library/PrivateFrameworks/DevToolsCore.framework/ > Resources/pbxcp -exclude .DS_Store -exclude CVS -exclude .svn -strip- > debug-symbols -resolve-src-symlinks /Users/bcpierce/current/unison/ > trunk/src/uimacnew09/Frameworks/Growl.framework /Users/bcpierce/ > current/unison/trunk/src/uimacnew09/build/Default/Unison.app/Contents/ > Frameworks > strip: for architecture x86_64 object: /Users/bcpierce/current/unison/ > trunk/src/uimacnew09/Frameworks/Growl.framework/Growl malformed object > (unknown load command 5) > pbxcp: warning: couldn't strip: /Users/bcpierce/current/unison/trunk/ > src/uimacnew09/build/Default/Unison.app/Contents/Frameworks/ > Growl.framework/Growl: No such file or directory > > PBXCp build/Default/Unison.app/Contents/Frameworks/ > BWToolkitFramework.framework Frameworks/BWToolkitFramework.framework > cd /Users/bcpierce/current/unison/trunk/src/uimacnew09 > /Developer/Library/PrivateFrameworks/DevToolsCore.framework/ > Resources/pbxcp -exclude .DS_Store -exclude CVS -exclude .svn -strip- > debug-symbols -resolve-src-symlinks /Users/bcpierce/current/unison/ > trunk/src/uimacnew09/Frameworks/BWToolkitFramework.framework /Users/ > bcpierce/current/unison/trunk/src/uimacnew09/build/Default/Unison.app/ > Contents/Frameworks > strip: for architecture x86_64 object: /Users/bcpierce/current/unison/ > trunk/src/uimacnew09/Frameworks/BWToolkitFramework.framework/ > BWToolkitFramework malformed object (unknown load command 5) > pbxcp: warning: couldn't strip: /Users/bcpierce/current/unison/trunk/ > src/uimacnew09/build/Default/Unison.app/Contents/Frameworks/ > BWToolkitFramework.framework/BWToolkitFramework: No such file or > directory > ** BUILD FAILED ** > > The following build commands failed: > uimac: > PBXCp build/Default/Unison.app/Contents/Frameworks/Growl.framework > Frameworks/Growl.framework > PBXCp build/Default/Unison.app/Contents/Frameworks/ > BWToolkitFramework.framework Frameworks/BWToolkitFramework.framework > (2 failures) From Jerome.Vouillon at pps.jussieu.fr Mon Jan 11 05:01:43 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Mon, 11 Jan 2010 11:01:43 +0100 Subject: [Unison-hackers] Performance of the OSX GUI? In-Reply-To: <20100110162117.GA2026@pps.jussieu.fr> References: <20100110162117.GA2026@pps.jussieu.fr> Message-ID: <20100111100143.GA11385@pps.jussieu.fr> On Sun, Jan 10, 2010 at 05:21:17PM +0100, Jerome Vouillon wrote: > A good way to test whether this is an issue or not is by putting a > directory "foo" with a lot of small files at toplevel on one replica > and an empty directory of the same name on the other replica (the two > replicas should be local). The GUI should not take much longer to > synchronize this directory than the text UI (accurate start and finish > time of the synchronization can be found in the unison log). Can > someone give it a try? For instance, one can take the trunk directory of Unison (subversion directories and object files included). Synchronizing replicas /tmp/u and /tmp/v, the directories can be populated as follows: cp -a ~/unison-branches/trunk/src /tmp/u && mkdir /tmp/v/src After synchronization, the directories can be cleaned-up by running the following command and then performing update detection once: rm -rf /tmp/u/src /tmp/v/src After performing several synchronizations, one can get the starting and finishing times of the propagation phases by running: grep UNISON ~/unison.log On my machine, the text UI takes 0.45s (best of 4 runs) to propagate changes. The 2.32 GTK UI takes 2.6s while the 2.39 GTK UI takes 0.50s (about 5 time faster). -- Jerome From mgross at informatik.uni-bremen.de Mon Jan 11 05:39:02 2010 From: mgross at informatik.uni-bremen.de (=?iso-8859-1?Q?Markus_Gro=DF?=) Date: Mon, 11 Jan 2010 11:39:02 +0100 Subject: [Unison-hackers] Performance of the OSX GUI? In-Reply-To: <20100111100143.GA11385@pps.jussieu.fr> References: <20100110162117.GA2026@pps.jussieu.fr> <20100111100143.GA11385@pps.jussieu.fr> Message-ID: <0E98F9B1-0CAC-404D-9336-1B42CBFABE0F@informatik.uni-bremen.de> > On my machine, the text UI takes 0.45s (best of 4 runs) to propagate > changes. The 2.32 GTK UI takes 2.6s while the 2.39 GTK UI takes > 0.50s (about 5 time faster). Using revision 400 from trunk and doing 4 runs each. Text ui: 1.53s, 1.63s, 1.6s, 1.71s Macnew09: 2.81s, 1.57s, 1.57s, 1.8s If you take out the first run of the gui, the text and gui version seem to be equally fast. - Markus From Jerome.Vouillon at pps.jussieu.fr Mon Jan 11 06:00:32 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Mon, 11 Jan 2010 12:00:32 +0100 Subject: [Unison-hackers] Performance of the OSX GUI? In-Reply-To: <0E98F9B1-0CAC-404D-9336-1B42CBFABE0F@informatik.uni-bremen.de> References: <20100110162117.GA2026@pps.jussieu.fr> <20100111100143.GA11385@pps.jussieu.fr> <0E98F9B1-0CAC-404D-9336-1B42CBFABE0F@informatik.uni-bremen.de> Message-ID: <20100111110031.GA12119@pps.jussieu.fr> On Mon, Jan 11, 2010 at 11:39:02AM +0100, Markus Gro? wrote: > If you take out the first run of the gui, the text and gui version > seem to be equally fast. So we don't have any improvement to do. Great! Thanks a lot! -- Jerome From bcpierce at cis.upenn.edu Thu Jan 14 08:44:59 2010 From: bcpierce at cis.upenn.edu (Benjamin Pierce) Date: Thu, 14 Jan 2010 08:44:59 -0500 Subject: [Unison-hackers] Invalid_argument? Message-ID: <5BCE4FC6-DCE9-48E3-9A6B-C4EF9EE0EE5D@cis.upenn.edu> Just today, I'm persistently (but not yet quite repeatably) getting Uncaught exception Invalid_argument(_) apparently sometime during the transfer phase. Is anyone else seeing this? - B From Jerome.Vouillon at pps.jussieu.fr Thu Jan 14 09:08:36 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Thu, 14 Jan 2010 15:08:36 +0100 Subject: [Unison-hackers] Invalid_argument? In-Reply-To: <5BCE4FC6-DCE9-48E3-9A6B-C4EF9EE0EE5D@cis.upenn.edu> References: <5BCE4FC6-DCE9-48E3-9A6B-C4EF9EE0EE5D@cis.upenn.edu> Message-ID: <20100114140836.GA18363@pps.jussieu.fr> On Thu, Jan 14, 2010 at 08:44:59AM -0500, Benjamin Pierce wrote: > Just today, I'm persistently (but not yet quite repeatably) getting > > Uncaught exception Invalid_argument(_) > > apparently sometime during the transfer phase. Is anyone else seeing > this? I have never seen this. Is that with the GUI? Do you get a fatal error window? Is it the exact error message? I'm surprised the string is not printed. You should add a case for Invalid_argument in Uicommon.exn2string. -- Jerome From vouillon at seas.upenn.edu Fri Jan 15 03:29:27 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Fri, 15 Jan 2010 03:29:27 -0500 Subject: [Unison-hackers] [unison-svn] r401 - in trunk/src: . lwt Message-ID: <201001150829.o0F8TSu4010394@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-15 03:29:26 -0500 (Fri, 15 Jan 2010) New Revision: 401 Modified: trunk/src/.depend trunk/src/RECENTNEWS trunk/src/lwt/lwt_unix.ml trunk/src/lwt/lwt_unix.mli trunk/src/mkProjectInfo.ml trunk/src/os.ml trunk/src/os.mli trunk/src/remote.ml trunk/src/terminal.ml trunk/src/terminal.mli trunk/src/uicommon.ml trunk/src/uigtk2.ml trunk/src/update.ml Log: * GTK UI: - take into account the "height" preference again - the internal list of selected reconciler item was not always in sync with what was displayed (GTK bug?); workaround implemented * Do not display "Looking for change" messages during propagation (when checking the targe is unchanged) but only during update detection * Now use an opaque datatype for file descriptors in Lwt Modified: trunk/src/.depend =================================================================== --- trunk/src/.depend 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/.depend 2010-01-15 08:29:26 UTC (rev 401) @@ -32,7 +32,7 @@ stasher.cmi: update.cmi ubase/prefs.cmi path.cmi os.cmi fspath.cmi strings.cmi: system.cmi: system/system_intf.cmo -terminal.cmi: +terminal.cmi: lwt/lwt_unix.cmi test.cmi: transfer.cmi: uutil.cmi lwt/lwt.cmi bytearray.cmi transport.cmi: uutil.cmi lwt/lwt.cmi common.cmi @@ -113,6 +113,8 @@ globals.cmx: ubase/util.cmx ubase/trace.cmx ubase/safelist.cmx remote.cmx \ ubase/prefs.cmx pred.cmx path.cmx os.cmx name.cmx lwt/lwt_util.cmx \ lwt/lwt_unix.cmx lwt/lwt.cmx common.cmx clroot.cmx globals.cmi +library_info.cmo: +library_info.cmx: linkgtk.cmo: uigtk.cmi main.cmo linkgtk.cmx: uigtk.cmx main.cmx linkgtk2.cmo: uigtk2.cmi main.cmo @@ -185,10 +187,10 @@ strings.cmx: strings.cmi system.cmo: system.cmi system.cmx: system.cmi -terminal.cmo: system.cmi ubase/safelist.cmi ubase/rx.cmi lwt/lwt_unix.cmi \ - lwt/lwt.cmi terminal.cmi -terminal.cmx: system.cmx ubase/safelist.cmx ubase/rx.cmx lwt/lwt_unix.cmx \ - lwt/lwt.cmx terminal.cmi +terminal.cmo: system.cmi ubase/rx.cmi lwt/lwt_unix.cmi lwt/lwt.cmi \ + terminal.cmi +terminal.cmx: system.cmx ubase/rx.cmx lwt/lwt_unix.cmx lwt/lwt.cmx \ + terminal.cmi test.cmo: uutil.cmi ubase/util.cmi update.cmi uicommon.cmi transport.cmi \ ubase/trace.cmi stasher.cmi ubase/safelist.cmi remote.cmi recon.cmi \ ubase/prefs.cmi path.cmi os.cmi lwt/lwt_util.cmi lwt/lwt_unix.cmi \ Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/RECENTNEWS 2010-01-15 08:29:26 UTC (rev 401) @@ -1,3 +1,14 @@ +CHANGES FROM VERSION 2.39.4 + +* GTK UI: + - take into account the "height" preference again + - the internal list of selected reconciler item was not always in + sync with what was displayed (GTK bug?); workaround implemented +* Do not display "Looking for change" messages during propagation + (when checking the targe is unchanged) but only during update detection +* Now use an opaque datatype for file descriptors in Lwt + +------------------------------- CHANGES FROM VERSION 2.39.0 * Mac GUIs (NEED TESTING): Modified: trunk/src/lwt/lwt_unix.ml =================================================================== --- trunk/src/lwt/lwt_unix.ml 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/lwt/lwt_unix.ml 2010-01-15 08:29:26 UTC (rev 401) @@ -57,6 +57,10 @@ | _ -> () +type file_descr = Unix.file_descr + +let of_unix_file_descr fd = if not windows_hack then Unix.set_nonblock fd; fd + let inputs = ref [] let outputs = ref [] let wait_children = ref [] @@ -225,18 +229,32 @@ | e -> Lwt.fail e +(* let pipe () = - let (out_fd, in_fd) as fd_pair = Unix.pipe() in + let (in_fd, out_fd) as fd_pair = Unix.pipe() in if not windows_hack then begin Unix.set_nonblock in_fd; Unix.set_nonblock out_fd end; - Lwt.return fd_pair + fd_pair +*) +let pipe_in () = + let (in_fd, out_fd) as fd_pair = Unix.pipe() in + if not windows_hack then + Unix.set_nonblock in_fd; + fd_pair + +let pipe_out () = + let (in_fd, out_fd) as fd_pair = Unix.pipe() in + if not windows_hack then + Unix.set_nonblock out_fd; + fd_pair + let socket dom typ proto = let s = Unix.socket dom typ proto in if not windows_hack then Unix.set_nonblock s; - Lwt.return s + s let socketpair dom typ proto = let (s1, s2) as spair = Unix.socketpair dom typ proto in @@ -245,6 +263,12 @@ end; Lwt.return spair +let bind = Unix.bind +let setsockopt = Unix.setsockopt +let listen = Unix.listen +let close = Unix.close +let set_close_on_exec = Unix.set_close_on_exec + let accept ch = let res = Lwt.wait () in inputs := (ch, `Accept res) :: !inputs; @@ -398,29 +422,29 @@ | id -> Hashtbl.add popen_processes proc id let open_process_in cmd = - Lwt.bind (pipe ()) (fun (in_read, in_write) -> + let (in_read, in_write) = pipe_in () in let inchan = Unix.in_channel_of_descr in_read in open_proc cmd (Process_in inchan) Unix.stdin in_write [in_read]; Unix.close in_write; - Lwt.return inchan) + Lwt.return inchan let open_process_out cmd = - Lwt.bind (pipe ()) (fun (out_read, out_write) -> + let (out_read, out_write) = pipe_out () in let outchan = Unix.out_channel_of_descr out_write in open_proc cmd (Process_out outchan) out_read Unix.stdout [out_write]; Unix.close out_read; - Lwt.return outchan) + Lwt.return outchan let open_process cmd = - Lwt.bind (pipe ()) (fun (in_read, in_write) -> - Lwt.bind (pipe ()) (fun (out_read, out_write) -> + let (in_read, in_write) = pipe_in () in + let (out_read, out_write) = pipe_out () in let inchan = Unix.in_channel_of_descr in_read in let outchan = Unix.out_channel_of_descr out_write in open_proc cmd (Process(inchan, outchan)) out_read in_write [in_read; out_write]; Unix.close out_read; Unix.close in_write; - Lwt.return (inchan, outchan))) + Lwt.return (inchan, outchan) (* FIX: Subprocesses that use /dev/tty to print things on the terminal will NOT have this output captured and returned to the caller of this @@ -430,7 +454,7 @@ principle, by writing a little C code that opens /dev/tty and then uses the TIOCNOTTY ioctl control to detach the terminal. *) -let open_proc_full cmd env proc output input error toclose = +let open_proc_full cmd env proc input output error toclose = match Unix.fork () with 0 -> Unix.dup2 input Unix.stdin; Unix.close input; Unix.dup2 output Unix.stdout; Unix.close output; @@ -440,18 +464,18 @@ | id -> Hashtbl.add popen_processes proc id let open_process_full cmd env = - Lwt.bind (pipe ()) (fun (in_read, in_write) -> - Lwt.bind (pipe ()) (fun (out_read, out_write) -> - Lwt.bind (pipe ()) (fun (err_read, err_write) -> - let inchan = Unix.out_channel_of_descr in_write in - let outchan = Unix.in_channel_of_descr out_read in + let (in_read, in_write) = pipe_in () in + let (out_read, out_write) = pipe_out () in + let (err_read, err_write) = pipe_in () in + let inchan = Unix.in_channel_of_descr in_read in + let outchan = Unix.out_channel_of_descr out_write in let errchan = Unix.in_channel_of_descr err_read in - open_proc_full cmd env (Process_full(outchan, inchan, errchan)) - out_write in_read err_write [in_write; out_read; err_read]; - Unix.close out_write; - Unix.close in_read; + open_proc_full cmd env (Process_full(inchan, outchan, errchan)) + out_read in_write err_write [in_write; out_read; err_read]; + Unix.close out_read; + Unix.close in_write; Unix.close err_write; - Lwt.return (outchan, inchan, errchan)))) + Lwt.return (inchan, outchan, errchan) let find_proc_id fun_name proc = try Modified: trunk/src/lwt/lwt_unix.mli =================================================================== --- trunk/src/lwt/lwt_unix.mli 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/lwt/lwt_unix.mli 2010-01-15 08:29:26 UTC (rev 401) @@ -30,16 +30,28 @@ this library, you must first turn them into non-blocking mode using [Unix.set_nonblock]. *) -val read : Unix.file_descr -> string -> int -> int -> int Lwt.t -val write : Unix.file_descr -> string -> int -> int -> int Lwt.t -val pipe : unit -> (Unix.file_descr * Unix.file_descr) Lwt.t +type file_descr + +val of_unix_file_descr : Unix.file_descr -> file_descr + +val read : file_descr -> string -> int -> int -> int Lwt.t +val write : file_descr -> string -> int -> int -> int Lwt.t +val wait_read : file_descr -> unit Lwt.t +val wait_write : file_descr -> unit Lwt.t +val pipe_in : unit -> file_descr * Unix.file_descr +val pipe_out : unit -> Unix.file_descr * file_descr val socket : - Unix.socket_domain -> Unix.socket_type -> int -> Unix.file_descr Lwt.t + Unix.socket_domain -> Unix.socket_type -> int -> file_descr val socketpair : Unix.socket_domain -> Unix.socket_type -> int -> - (Unix.file_descr * Unix.file_descr) Lwt.t -val accept : Unix.file_descr -> (Unix.file_descr * Unix.sockaddr) Lwt.t -val connect : Unix.file_descr -> Unix.sockaddr -> unit Lwt.t + (file_descr * file_descr) Lwt.t +val bind : file_descr -> Unix.sockaddr -> unit +val setsockopt : file_descr -> Unix.socket_bool_option -> bool -> unit +val accept : file_descr -> (file_descr * Unix.sockaddr) Lwt.t +val connect : file_descr -> Unix.sockaddr -> unit Lwt.t +val listen : file_descr -> int -> unit +val close : file_descr -> unit +val set_close_on_exec : file_descr -> unit val wait : unit -> (int * Unix.process_status) Lwt.t val waitpid : Unix.wait_flag list -> int -> (int * Unix.process_status) Lwt.t Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/mkProjectInfo.ml 2010-01-15 08:29:26 UTC (rev 401) @@ -42,7 +42,7 @@ (* ---------------------------------------------------------------------- *) (* You shouldn't need to edit below. *) -let revisionString = "$Rev: 396$";; +let revisionString = "$Rev: 400$";; (* extract a substring using a regular expression *) let extract_str re str = @@ -101,3 +101,4 @@ + Modified: trunk/src/os.ml =================================================================== --- trunk/src/os.ml 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/os.ml 2010-01-15 08:29:26 UTC (rev 401) @@ -342,19 +342,3 @@ let tempPath ?(fresh=true) fspath path = genTempPath fresh fspath path tempFilePrefix !tempFileSuffix - -(*****************************************************************************) -(* INTERRUPTED SYSTEM CALLS *) -(*****************************************************************************) -(* Needed because in lwt/lwt_unix.ml we set a signal handler for SIG_CHLD, - which means that slow system calls can be interrupted to handle - SIG_CHLD. We want to restart these system calls. It would be much - better to do this using SA_RESTART, however, ocaml's Unix module does - not support this, probably because it isn't nicely portable. *) -let accept fd = - let rec loop () = - try Unix.accept fd - with Unix.Unix_error(Unix.EINTR,_,_) -> loop() in - loop() - - Modified: trunk/src/os.mli =================================================================== --- trunk/src/os.mli 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/os.mli 2010-01-15 08:29:26 UTC (rev 401) @@ -45,7 +45,3 @@ Fspath.t -> Path.local -> (* coordinates of file to fingerprint *) Fileinfo.t -> (* old fileinfo *) fullfingerprint (* current fingerprint *) - -(* Versions of system calls that will restart when interrupted by - signal handling *) -val accept : Unix.file_descr -> (Unix.file_descr * Unix.sockaddr) Modified: trunk/src/remote.ml =================================================================== --- trunk/src/remote.ml 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/remote.ml 2010-01-15 08:29:26 UTC (rev 401) @@ -42,8 +42,8 @@ Threads behave in a very controlled way: they only perform possibly blocking I/Os through the remote module, and never call Lwt_unix.yield. This mean that when one side gives up its right to - write, we know that no longer how much we wait, it would not have - any thing to write. This ensures that there will be no deadlock. + write, we know that no matter how long we wait, it will not have + anything to write. This ensures that there is no deadlock. A more robust protocol would be to give up write permission whenever idle (not just after having sent at least one message). But then, there is the risk that the two sides exchange spurious @@ -110,7 +110,7 @@ (* I/O buffers *) type ioBuffer = - { channel : Unix.file_descr; + { channel : Lwt_unix.file_descr; buffer : string; mutable length : int } @@ -368,10 +368,6 @@ (* Initialize the connection *) let setupIO isServer inCh outCh = - if not windowsHack then begin - Unix.set_nonblock inCh; - Unix.set_nonblock outCh - end; makeConnection isServer inCh outCh (* XXX *) @@ -989,67 +985,81 @@ let rec findFirst f l = match l with - [] -> None - | x :: r -> match f x with + [] -> Lwt.return None + | x :: r -> f x >>= fun v -> + match v with None -> findFirst f r - | Some _ as v -> v + | Some _ as v -> Lwt.return v let buildSocket host port kind = let attemptCreation ai = - try - let socket = - Unix.socket ai.Unix.ai_family ai.Unix.ai_socktype ai.Unix.ai_protocol - in - try - begin match kind with - `Connect -> - (* Connect to the remote host *) - Unix.connect socket ai.Unix.ai_addr - | `Bind -> - (* Allow reuse of local addresses for bind *) - Unix.setsockopt socket Unix.SO_REUSEADDR true; - (* Bind the socket to portnum on the local host *) - Unix.bind socket ai.Unix.ai_addr; - (* Start listening, allow up to 1 pending request *) - Unix.listen socket 1 - end; - Some socket - with Unix.Unix_error _ as e -> - Unix.close socket; - raise e - with Unix.Unix_error (error, _, _) -> - begin match error with - Unix.EAFNOSUPPORT | Unix.EPROTONOSUPPORT | Unix.EINVAL -> - () - | _ -> - let msg = - match kind with - `Connect -> - Printf.sprintf "Can't connect to server (%s:%s): %s" - host port (Unix.error_message error) - | `Bind -> - Printf.sprintf - "Can't bind socket to port %s at address [%s]: %s\n" - port - (match ai.Unix.ai_addr with - Unix.ADDR_INET (addr, _) -> Unix.string_of_inet_addr addr - | _ -> assert false) - (Unix.error_message error) - in - Util.warn msg - end; - None + Lwt.catch + (fun () -> + let socket = + Lwt_unix.socket + ai.Unix.ai_family ai.Unix.ai_socktype ai.Unix.ai_protocol + in + Lwt.catch + (fun () -> + begin match kind with + `Connect -> + (* Connect (synchronously) to the remote host *) + Lwt_unix.connect socket ai.Unix.ai_addr + | `Bind -> + (* Allow reuse of local addresses for bind *) + Lwt_unix.setsockopt socket Unix.SO_REUSEADDR true; + (* Bind the socket to portnum on the local host *) + Lwt_unix.bind socket ai.Unix.ai_addr; + (* Start listening, allow up to 1 pending request *) + Lwt_unix.listen socket 1; + Lwt.return () + end >>= fun () -> + Lwt.return (Some socket)) + (fun e -> + match e with + Unix.Unix_error _ -> + Lwt_unix.close socket; + Lwt.fail e + | _ -> + Lwt.fail e)) + (fun e -> + match e with + Unix.Unix_error (error, _, _) -> + begin match error with + Unix.EAFNOSUPPORT | Unix.EPROTONOSUPPORT | Unix.EINVAL -> + () + | _ -> + let msg = + match kind with + `Connect -> + Printf.sprintf "Can't connect to server (%s:%s): %s" + host port (Unix.error_message error) + | `Bind -> + Printf.sprintf + "Can't bind socket to port %s at address [%s]: %s\n" + port + (match ai.Unix.ai_addr with + Unix.ADDR_INET (addr, _) -> + Unix.string_of_inet_addr addr + | _ -> + assert false) + (Unix.error_message error) + in + Util.warn msg + end; + Lwt.return None + | _ -> + Lwt.fail e) in let options = match kind with `Connect -> [ Unix.AI_SOCKTYPE Unix.SOCK_STREAM ] | `Bind -> [ Unix.AI_SOCKTYPE Unix.SOCK_STREAM ; Unix.AI_PASSIVE ] in - match - findFirst attemptCreation (Unix.getaddrinfo host port options) - with + findFirst attemptCreation (Unix.getaddrinfo host port options) >>= fun res -> + match res with Some socket -> - socket + Lwt.return socket | None -> let msg = match kind with @@ -1063,12 +1073,11 @@ Printf.sprintf "Can't bind socket to port %s on host %s" port host in - raise (Util.Fatal msg) + Lwt.fail (Util.Fatal msg) let buildSocketConnection host port = - Util.convertUnixErrorsToFatal "canonizeRoot" (fun () -> - let socket = buildSocket host port `Connect in - initConnection socket socket) + buildSocket host port `Connect >>= fun socket -> + initConnection socket socket let buildShellConnection shell host userOpt portOpt rootName termInteract = let remoteCmd = @@ -1109,13 +1118,13 @@ Safelist.concat (Safelist.map (fun s -> Util.splitIntoWords s ' ') preargs) in let argsarray = Array.of_list args in - let (i1,o1) = Unix.pipe() in - let (i2,o2) = Unix.pipe() in + let (i1,o1) = Lwt_unix.pipe_out () in + let (i2,o2) = Lwt_unix.pipe_in () in (* We need to make sure that there is only one reader and one writer by pipe, so that, when one side of the connection dies, the other side receives an EOF or a SIGPIPE. *) - Unix.set_close_on_exec i2; - Unix.set_close_on_exec o1; + Lwt_unix.set_close_on_exec i2; + Lwt_unix.set_close_on_exec o1; (* We add CYGWIN=binmode to the environment before calling ssh because the cygwin implementation on Windows sometimes puts the pipe in text mode (which does end of line @@ -1212,11 +1221,11 @@ terminal interaction might be required (for ssh password) *) type preconnection = (Unix.file_descr + * Lwt_unix.file_descr + * Lwt_unix.file_descr * Unix.file_descr - * Unix.file_descr - * Unix.file_descr * string option - * Unix.file_descr option + * Lwt_unix.file_descr option * Clroot.clroot * int) let openConnectionStart clroot = @@ -1293,13 +1302,13 @@ Safelist.concat (Safelist.map (fun s -> Util.splitIntoWords s ' ') preargs) in let argsarray = Array.of_list args in - let (i1,o1) = Unix.pipe() in - let (i2,o2) = Unix.pipe() in + let (i1,o1) = Lwt_unix.pipe_out() in + let (i2,o2) = Lwt_unix.pipe_in() in (* We need to make sure that there is only one reader and one writer by pipe, so that, when one side of the connection dies, the other side receives an EOF or a SIGPIPE. *) - Unix.set_close_on_exec i2; - Unix.set_close_on_exec o1; + Lwt_unix.set_close_on_exec i2; + Lwt_unix.set_close_on_exec o1; (* We add CYGWIN=binmode to the environment before calling ssh because the cygwin implementation on Windows sometimes puts the pipe in text mode (which does end of line @@ -1325,8 +1334,9 @@ let openConnectionReply = function (i1,i2,o1,o2,s,Some fdTerm,clroot,pid) -> (fun response -> - (* FIX: should loop on write, watch for EINTR, etc. *) - ignore(Unix.write fdTerm (response ^ "\n") 0 (String.length response + 1))) + (* FIX: should loop until everything is written... *) + ignore (Lwt_unix.run (Lwt_unix.write fdTerm (response ^ "\n") 0 + (String.length response + 1)))) | _ -> (fun _ -> ()) let openConnectionEnd (i1,i2,o1,o2,s,_,clroot,pid) = @@ -1344,11 +1354,12 @@ let openConnectionCancel (i1,i2,o1,o2,s,fdopt,clroot,pid) = try Unix.kill pid Sys.sigkill with Unix.Unix_error _ -> (); try Unix.close i1 with Unix.Unix_error _ -> (); - try Unix.close i2 with Unix.Unix_error _ -> (); - try Unix.close o1 with Unix.Unix_error _ -> (); + try Lwt_unix.close i2 with Unix.Unix_error _ -> (); + try Lwt_unix.close o1 with Unix.Unix_error _ -> (); try Unix.close o2 with Unix.Unix_error _ -> (); match fdopt with - None -> () | Some fd -> (try Unix.close fd with Unix.Unix_error _ -> ()) + None -> () + | Some fd -> (try Lwt_unix.close fd with Unix.Unix_error _ -> ()) (****************************************************************************) (* SERVER-MODE COMMAND PROCESSING LOOP *) @@ -1372,9 +1383,9 @@ (* Send header indicating to the client that it has successfully connected to the server *) let conn = setupIO true in_ch out_ch in - try - Lwt_unix.run - (dump conn [(Bytearray.of_string connectionHeader, 0, + Lwt.catch + (fun e -> + dump conn [(Bytearray.of_string connectionHeader, 0, String.length connectionHeader)] >>= (fun () -> (* Set the local warning printer to make an RPC to the client and @@ -1385,9 +1396,13 @@ Some (fun str -> Lwt_unix.run (forwardMsgToClient conn str)); receive conn >>= Lwt.wait)) -(* debug (fun () -> Util.msg "Should never happen\n") *) - with Util.Fatal "Lost connection with the server" -> - debug (fun () -> Util.msg "Connection closed by the client\n") + (fun e -> + match e with + Util.Fatal "Lost connection with the server" -> + debug (fun () -> Util.msg "Connection closed by the client\n"); + Lwt.return () + | _ -> + Lwt.fail e) let killServer = Prefs.createBool "killserver" false @@ -1408,23 +1423,25 @@ for a request. Each request is processed by commandLoop. When a session finishes, the server waits for another request. *) let waitOnPort hostOpt port = - Util.convertUnixErrorsToFatal - "waiting on port" + Util.convertUnixErrorsToFatal "waiting on port" (fun () -> - let host = match hostOpt with - Some host -> host - | None -> "" in - let listening = buildSocket host port `Bind in - Util.msg "server started\n"; - while - (* Accept a connection *) - let (connected,_) = Os.accept listening in - Unix.setsockopt connected Unix.SO_KEEPALIVE true; - commandLoop connected connected; - (* The client has closed its end of the connection *) - begin try Unix.close connected with Unix.Unix_error _ -> () end; - not (Prefs.read killServer) - do () done) + Lwt_unix.run + (let host = match hostOpt with + Some host -> host + | None -> "" in + buildSocket host port `Bind >>= fun listening -> + Util.msg "server started\n"; + let rec handleClients () = + (* Accept a connection *) + Lwt_unix.accept listening >>= fun (connected,_) -> + Lwt_unix.setsockopt connected Unix.SO_KEEPALIVE true; + commandLoop connected connected >>= fun () -> + (* The client has closed its end of the connection *) + begin try Lwt_unix.close connected with Unix.Unix_error _ -> () end; + if Prefs.read killServer then Lwt.return () else + handleClients () + in + handleClients ())) let beAServer () = begin try @@ -1437,4 +1454,7 @@ "Environment variable HOME unbound: \ executing server in current directory\n" end; - commandLoop Unix.stdin Unix.stdout + Lwt_unix.run + (commandLoop + (Lwt_unix.of_unix_file_descr Unix.stdin) + (Lwt_unix.of_unix_file_descr Unix.stdout)) Modified: trunk/src/terminal.ml =================================================================== --- trunk/src/terminal.ml 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/terminal.ml 2010-01-15 08:29:26 UTC (rev 401) @@ -209,41 +209,36 @@ end | childPid -> Unix.close slaveFd; - (Some masterFd, childPid) + (Some (Lwt_unix.of_unix_file_descr masterFd), childPid) end -let rec select a b c d = - try Unix.select a b c d - with Unix.Unix_error(Unix.EINTR,_,_) -> select a b c d +let (>>=) = Lwt.bind (* Wait until there is input. If there is terminal input s, return Some s. Otherwise, return None. *) let rec termInput fdTerm fdInput = - let (ready,_,_) = select [fdTerm;fdInput] [] [] (-1.0) in - if not(Safelist.exists (fun x -> x=fdTerm) ready) then None else - (* there's input waiting on the terminal *) - (* read a line of input *) - let msg = - let n = 1024 in (* Assume length of input from terminal < n *) - let s = String.create n in - let howmany = - let rec loop() = - try Unix.read fdTerm s 0 n - with Unix.Unix_error(Unix.EINTR,_,_) -> loop() in - loop() in - if howmany <= 0 then "" else - String.sub s 0 howmany in - let len = String.length msg in - if len = 0 then None (* the terminal has been closed *) - else if len = 2 && msg.[0] = '\r' && msg.[1] = '\n' then - termInput fdTerm fdInput - else Some msg + let buf = String.create 10000 in + let rec readPrompt () = + Lwt_unix.read fdTerm buf 0 10000 >>= fun len -> + if len = 0 then + (* The remote end is dead *) + Lwt.return None + else + let query = String.sub buf 0 len in + if query = "\r\n" then + readPrompt () + else + Lwt.return (Some query) + in + let connectionEstablished () = + Lwt_unix.wait_read fdInput >>= fun () -> Lwt.return None + in + Lwt_unix.run + (Lwt.choose + [readPrompt (); connectionEstablished ()]) -let (>>=) = Lwt.bind - (* Read messages from the terminal and use the callback to get an answer *) let handlePasswordRequests fdTerm callback = - Unix.set_nonblock fdTerm; let buf = String.create 10000 in let rec loop () = Lwt_unix.read fdTerm buf 0 10000 >>= (fun len -> Modified: trunk/src/terminal.mli =================================================================== --- trunk/src/terminal.mli 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/terminal.mli 2010-01-15 08:29:26 UTC (rev 401) @@ -5,17 +5,17 @@ val create_session : string -> string array -> Unix.file_descr -> Unix.file_descr -> Unix.file_descr -> - Unix.file_descr option * int + Lwt_unix.file_descr option * int (* termInput fdTerm fdInput Wait until there is input on at least one file descriptor. If there is terminal input s, return Some s. Otherwise, return None. *) val termInput : - Unix.file_descr -> Unix.file_descr -> string option + Lwt_unix.file_descr -> Lwt_unix.file_descr -> string option val handlePasswordRequests : - Unix.file_descr -> (string -> string) -> unit + Lwt_unix.file_descr -> (string -> string) -> unit (* For recognizing messages from OpenSSH *) val password : string -> bool Modified: trunk/src/uicommon.ml =================================================================== --- trunk/src/uicommon.ml 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/uicommon.ml 2010-01-15 08:29:26 UTC (rev 401) @@ -50,7 +50,7 @@ (* This has to be here rather than in uigtk.ml, because it is part of what gets sent to the server at startup *) let mainWindowHeight = - Prefs.createInt "height" 20 + Prefs.createInt "height" 15 "!height (in lines) of main window in graphical interface" ("Used to set the height (in lines) of the main window in the graphical " ^ "user interface.") Modified: trunk/src/uigtk2.ml =================================================================== --- trunk/src/uigtk2.ml 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/uigtk2.ml 2010-01-15 08:29:26 UTC (rev 401) @@ -1996,11 +1996,13 @@ ~renderer:(GTree.cell_renderer_text [], ["text", c_name]) ())); let hiddenPrefs = ["auto"; "doc"; "silent"; "terse"; "testserver"; "version"] in + let shownPrefs = + ["label"; "key"] in let insert (store : #GTree.list_store) all = List.iter (fun nm -> if - all || + all || List.mem nm shownPrefs || (let (_, _, basic) = Prefs.documentation nm in basic && not (List.mem nm hiddenPrefs)) then begin @@ -2379,7 +2381,7 @@ in let hb = GPack.hbox ~spacing:12 ~packing:(lvb#add) () in let sw = - GBin.scrolled_window ~packing:(hb#pack ~expand:true) ~height:200 + GBin.scrolled_window ~packing:(hb#pack ~expand:true) ~height:300 ~shadow_type:`IN ~hpolicy:`AUTOMATIC ~vpolicy:`AUTOMATIC () in let cols = new GTree.column_list in let c_name = cols#add Gobject.Data.string in @@ -2612,7 +2614,7 @@ let summaryBox ~parent ~title ~message ~f = let t = GWindow.dialog ~parent ~border_width:6 ~modal:true ~no_separator:true - ~allow_grow:false () in + ~allow_grow:false ~focus_on_map:false () in t#vbox#set_spacing 12; let h1 = GPack.hbox ~border_width:6 ~spacing:12 ~packing:t#vbox#pack () in ignore (GMisc.image ~stock:`DIALOG_INFO ~icon_size:`DIALOG @@ -2752,13 +2754,21 @@ (********************************************************************* Create the main window *********************************************************************) + let mainWindowSW = + GBin.scrolled_window ~packing:(toplevelVBox#pack ~expand:true) + ~hpolicy:`AUTOMATIC ~vpolicy:`AUTOMATIC () + in + let sizeMainWindow () = + let ctx = mainWindowSW#misc#pango_context in + let metrics = ctx#get_metrics () in + let h = GPango.to_pixels (metrics#ascent+metrics#descent) in + mainWindowSW#misc#set_size_request + ~height:((h + 1) * (Prefs.read Uicommon.mainWindowHeight + 1) + 10) () + in let mainWindow = - let sw = - GBin.scrolled_window ~packing:(toplevelVBox#pack ~expand:true) - ~height:(Prefs.read Uicommon.mainWindowHeight * 12) - ~hpolicy:`AUTOMATIC ~vpolicy:`AUTOMATIC () in GList.clist ~columns:5 ~titles_show:true - ~selection_mode:`MULTIPLE ~packing:sw#add () in + ~selection_mode:`MULTIPLE ~packing:mainWindowSW#add () + in (* let cols = new GTree.column_list in let c_replica1 = cols#add Gobject.Data.string in @@ -2808,7 +2818,8 @@ ~title_active:false ~auto_resize:true ~title:data i) [| " " ^ Unicode.protect (String.sub s 0 12) ^ " "; " Action "; " " ^ Unicode.protect (String.sub s 15 12) ^ " "; " Status "; - " Path" |] + " Path" |]; + sizeMainWindow () in setMainWindowColumnHeaders " "; @@ -3749,6 +3760,7 @@ let loadProfile p reload = debug (fun()-> Util.msg "Loading profile %s..." p); + Trace.status "Loading profile"; Uicommon.initPrefs p (fun () -> if not reload then displayWaitMessage ()) getFirstRoot getSecondRoot termInteract; @@ -3787,7 +3799,23 @@ () end in + let updateCurrent () = + let n = mainWindow#rows in + (* This has quadratic complexity, thus we only do it when + the list is not too long... *) + if n < 300 then begin + current := IntSet.empty; + for i = 0 to n -1 do + if mainWindow#get_row_state i = `SELECTED then + current := IntSet.add i !current + done + end + in let doAction f = + (* FIX: when the window does not have the focus, we are not notified + immediately from changes to the list of selected items. So, we + update our view of the current selection here. *) + updateCurrent (); match currentRow () with Some i -> doActionOnRow f i; @@ -3823,12 +3851,10 @@ ~callback:leftAction ()); (* actionBar#insert_space ();*) grAdd grAction - (actionBar#insert_button -(* ~icon:((GMisc.pixmap mergeLogoBlack())#coerce)*) - ~icon:((GMisc.image ~stock:`ADD ())#coerce) - ~text:"Merge" - ~tooltip:"Merge selected files" - ~callback:mergeAction ()); + (actionBar#insert_button ~text:"Skip" + ~icon:((GMisc.image ~stock:`NO ())#coerce) + ~tooltip:"Skip selected items" + ~callback:questionAction ()); (* actionBar#insert_space ();*) grAdd grAction (actionBar#insert_button @@ -3840,10 +3866,12 @@ ~callback:rightAction ()); (* actionBar#insert_space ();*) grAdd grAction - (actionBar#insert_button ~text:"Skip" - ~icon:((GMisc.image ~stock:`NO ())#coerce) - ~tooltip:"Skip selected items" - ~callback:questionAction ()); + (actionBar#insert_button +(* ~icon:((GMisc.pixmap mergeLogoBlack())#coerce)*) + ~icon:((GMisc.image ~stock:`ADD ())#coerce) + ~text:"Merge" + ~tooltip:"Merge selected files" + ~callback:mergeAction ()); (********************************************************************* Diff / merge buttons Modified: trunk/src/update.ml =================================================================== --- trunk/src/update.ml 2010-01-10 22:52:59 UTC (rev 400) +++ trunk/src/update.ml 2010-01-15 08:29:26 UTC (rev 401) @@ -1065,10 +1065,11 @@ let immutablenot = Pred.create "immutablenot" ~advanced:true ("This preference overrides {\\tt immutable}.") -type fastCheckInfos = +type scanInfo = { fastCheck : bool; dirFastCheck : bool; - dirStamp : Props.dirChangedStamp } + dirStamp : Props.dirChangedStamp; + showStatus : bool } (** Status display **) @@ -1083,8 +1084,7 @@ finished its own update detection and can receive and acknowledge the status display message -- thus effectively serializing the client and server! *) -let showStatusAddLength info = - if not !Trace.runningasserver then begin +let showStatusAddLength scanInfo info = let len1 = Props.length info.Fileinfo.desc in let len2 = Osx.ressLength info.Fileinfo.osX.Osx.ressInfo in if len1 >= bigFileLengthFS || len2 >= bigFileLengthFS then @@ -1093,21 +1093,18 @@ fileLength := min bigFileLength (!fileLength + Uutil.Filesize.toInt len1 + Uutil.Filesize.toInt len2) - end -let showStatus path = - if not !Trace.runningasserver then begin +let showStatus scanInfo path = fileLength := !fileLength + smallFileLength; if !fileLength >= bigFileLength then begin fileLength := 0; let t = Unix.gettimeofday () in if t -. !t0 > 0.05 then begin - Uutil.showUpdateStatus (Path.toString path); -(*Trace.statusDetail ("scanning... " ^ Path.toString path);*) + if scanInfo.showStatus then + Uutil.showUpdateStatus (Path.toString path); t0 := t end end - end let showStatusDir path = () @@ -1158,7 +1155,7 @@ (* Check whether the directory contents is different from what is in the archive *) let directoryCheckContentUnchanged - currfspath path info archDesc childUpdates fastCheckInfos = + currfspath path info archDesc childUpdates scanInfo = if noChildChange childUpdates && @@ -1169,7 +1166,7 @@ let (archDesc, updated) = let inode = match Fileinfo.stamp info with Fileinfo.InodeStamp i -> i | _ -> 0 in - Props.setDirChangeFlag archDesc fastCheckInfos.dirStamp inode in + Props.setDirChangeFlag archDesc scanInfo.dirStamp inode in let updated = updated || not (Props.same_time info.Fileinfo.desc archDesc) in if updated then @@ -1188,12 +1185,12 @@ end (* Check whether the list of children of a directory is clearly unchanged *) -let dirContentsClearlyUnchanged info archDesc fastCheckInfos = - fastCheckInfos.dirFastCheck +let dirContentsClearlyUnchanged info archDesc scanInfo = + scanInfo.dirFastCheck && let inode = match Fileinfo.stamp info with Fileinfo.InodeStamp i -> i | _ -> 0 in - Props.dirMarkedUnchanged archDesc fastCheckInfos.dirStamp inode + Props.dirMarkedUnchanged archDesc scanInfo.dirStamp inode && Props.same_time info.Fileinfo.desc archDesc && @@ -1222,7 +1219,7 @@ series functions to compute the _old_ archive with updated time stamp (thus, there will no false update the next time) *) let checkContentsChange - currfspath path info archive archDesc archDig archStamp archRess fastCheck + currfspath path info archive archDesc archDig archStamp archRess scanInfo : archive option * Common.updateItem = debug (fun () -> @@ -1242,6 +1239,7 @@ (Uutil.Filesize.toString (Props.length archDesc)) (Uutil.Filesize.toString (Props.length info.Fileinfo.desc)); Util.msg "\n"); + let fastCheck = scanInfo.fastCheck in let dataClearlyUnchanged = Fpcache.dataClearlyUnchanged fastCheck path info archDesc archStamp in let ressClearlyUnchanged = @@ -1252,7 +1250,7 @@ None, checkPropChange info.Fileinfo.desc archive archDesc end else begin debugverbose (fun() -> Util.msg " Double-check possibly updated file\n"); - showStatusAddLength info; + showStatusAddLength scanInfo info; let (newDesc, newDigest, newStamp, newRess) = Fpcache.fingerprint fastCheck currfspath path info (if dataClearlyUnchanged then Some archDig else None) in @@ -1335,7 +1333,7 @@ remain unchanged, the second a named list of updates; also returns whether the directory is now empty *) let rec buildUpdateChildren - fspath path (archChi: archive NameMap.t) unchangedChildren fastCheckInfos + fspath path (archChi: archive NameMap.t) unchangedChildren scanInfo : archive NameMap.t option * (Name.t * Common.updateItem) list * bool * bool = @@ -1361,9 +1359,9 @@ let archUpdated = ref false in let handleChild nm archive = let path' = Path.child path nm in - showStatus path'; + showStatus scanInfo path'; let (arch,uiChild) = - buildUpdateRec archive fspath path' fastCheckInfos in + buildUpdateRec archive fspath path' scanInfo in if uiChild <> NoUpdates then updates := (nm, uiChild) :: !updates; match arch with @@ -1389,7 +1387,7 @@ (Path.toString path')); archive end else begin - showStatus path'; + showStatus scanInfo path'; match status with `Ok | `Abs -> if skip && archive <> NoArchive && status <> `Abs then begin @@ -1402,7 +1400,7 @@ archive end else begin let (arch,uiChild) = - buildUpdateRec archive fspath path' fastCheckInfos in + buildUpdateRec archive fspath path' scanInfo in if uiChild <> NoUpdates then updates := (nm, uiChild) :: !updates; match arch with @@ -1465,7 +1463,7 @@ ((if !archUpdated then Some newChi else None), Safelist.rev !updates, emptied, !hasIgnoredChildren) -and buildUpdateRec archive currfspath path fastCheckInfos = +and buildUpdateRec archive currfspath path scanInfo = try debug (fun() -> Util.msg "buildUpdate: %s\n" @@ -1482,15 +1480,15 @@ | (`FILE, ArchiveFile (archDesc, archDig, archStamp, archRess)) -> checkContentsChange currfspath path info archive - archDesc archDig archStamp archRess fastCheckInfos.fastCheck + archDesc archDig archStamp archRess scanInfo | (`FILE, _) -> debug (fun() -> Util.msg " buildUpdate -> Updated file\n"); None, begin - showStatusAddLength info; + showStatusAddLength scanInfo info; let (desc, dig, stamp, ress) = Fpcache.fingerprint - fastCheckInfos.fastCheck currfspath path info None in + scanInfo.fastCheck currfspath path info None in Xferhint.insertEntry currfspath path dig; Updates (File (desc, ContentsUpdated (dig, stamp, ress)), oldInfoOf archive) @@ -1519,10 +1517,10 @@ else (PropsUpdated, info.Fileinfo.desc) in let unchanged = - dirContentsClearlyUnchanged info archDesc fastCheckInfos in + dirContentsClearlyUnchanged info archDesc scanInfo in let (newChildren, childUpdates, emptied, hasIgnoredChildren) = buildUpdateChildren - currfspath path prevChildren unchanged fastCheckInfos in + currfspath path prevChildren unchanged scanInfo in let (archDesc, updated) = (* If the archive contain ignored children, we cannot use it to skip reading the directory contents from the filesystem. @@ -1535,7 +1533,7 @@ ignored and are now ignored.) *) if hasIgnoredChildren then (archDesc, true) else directoryCheckContentUnchanged - currfspath path info archDesc childUpdates fastCheckInfos in + currfspath path info archDesc childUpdates scanInfo in (begin match newChildren with Some ch -> Some (ArchiveDir (archDesc, ch)) @@ -1552,7 +1550,7 @@ debug (fun() -> Util.msg " buildUpdate -> New directory\n"); let (newChildren, childUpdates, _, _) = buildUpdateChildren - currfspath path NameMap.empty false fastCheckInfos in + currfspath path NameMap.empty false scanInfo in (None, Updates (Dir (info.Fileinfo.desc, childUpdates, PropsUpdated, false), oldInfoOf archive)) @@ -1566,12 +1564,12 @@ contents. The directory permissions along the path are also collected, in case we need to build the directory hierarchy on one side. *) -let rec buildUpdate archive fspath fullpath here path dirStamp fastCheckInfos = +let rec buildUpdate archive fspath fullpath here path dirStamp scanInfo = match Path.deconstruct path with None -> - showStatus here; + showStatus scanInfo here; let (arch, ui) = - buildUpdateRec archive fspath here fastCheckInfos in + buildUpdateRec archive fspath here scanInfo in (begin match arch with None -> archive | Some arch -> arch @@ -1639,7 +1637,7 @@ let (arch, updates, localPath, props) = buildUpdate archChild fspath fullpath (Path.child here name') path' - dirStamp fastCheckInfos + dirStamp scanInfo in let children = if arch = NoArchive then otherChildren else @@ -1652,7 +1650,7 @@ let (arch, updates, localPath, props) = buildUpdate NoArchive fspath fullpath (Path.child here name') path' - dirStamp fastCheckInfos + dirStamp scanInfo in assert (arch = NoArchive); (archive, updates, localPath, @@ -1718,17 +1716,18 @@ (* let t1 = Unix.gettimeofday () in *) - let fastCheckInfos = + let scanInfo = { fastCheck = useFastChecking (); (* Directory optimization is disabled under Windows, as Windows does not update directory modification times on FAT filesystems. *) dirFastCheck = useFastChecking () && Util.osType = `Unix; - dirStamp = dirStamp } + dirStamp = dirStamp; + showStatus = not !Trace.runningasserver } in let (cacheFilename, _) = archiveName fspath FPCache in let cacheFile = Os.fileInUnisonDir cacheFilename in - Fpcache.init fastCheckInfos.fastCheck cacheFile; + Fpcache.init scanInfo.fastCheck cacheFile; let (archive, updates) = Safelist.fold_right (fun path (arch, upd) -> @@ -1737,7 +1736,7 @@ else let (arch', ui, localPath, props) = buildUpdate - arch fspath path Path.empty path dirStamp fastCheckInfos + arch fspath path Path.empty path dirStamp scanInfo in arch', (localPath, ui, props) :: upd) pathList (archive, []) @@ -2173,11 +2172,12 @@ state of the replica... *) let archive = updateArchiveRec ui archive in (* ...and check that this is a good description of what's out in the world *) - let fastCheckInfos = + let scanInfo = { fastCheck = false; dirFastCheck = false; - dirStamp = Props.changedDirStamp } + dirStamp = Props.changedDirStamp; + showStatus = false } in - let (_, uiNew) = buildUpdateRec archive fspath localPath fastCheckInfos in + let (_, uiNew) = buildUpdateRec archive fspath localPath scanInfo in markPossiblyUpdatedRec fspath pathInArchive uiNew; explainUpdate pathInArchive uiNew; archive From vouillon at seas.upenn.edu Fri Jan 15 10:28:25 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Fri, 15 Jan 2010 10:28:25 -0500 Subject: [Unison-hackers] [unison-svn] r402 - in trunk/src: . ubase Message-ID: <201001151528.o0FFSQZ4022976@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-15 10:28:23 -0500 (Fri, 15 Jan 2010) New Revision: 402 Modified: trunk/src/RECENTNEWS trunk/src/copy.ml trunk/src/external.ml trunk/src/external.mli trunk/src/fileinfo.ml trunk/src/files.ml trunk/src/globals.ml trunk/src/globals.mli trunk/src/mkProjectInfo.ml trunk/src/path.ml trunk/src/props.ml trunk/src/recon.ml trunk/src/remote.ml trunk/src/ubase/prefs.ml trunk/src/ubase/prefs.mli trunk/src/ubase/util.ml trunk/src/uicommon.ml trunk/src/uigtk2.ml trunk/src/uimacbridge.ml trunk/src/uimacbridgenew.ml trunk/src/update.ml Log: * New preferences "noupdate=root", "nodeletion=root", "nocreation=root" that prevent Unison from performing files updates, deletions or creations on the given root. * GTK UI: do not reload the preference file before a new update detection if it is unchanged * Limit the number of simultaneous external copy program ("copymax" preference) Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/RECENTNEWS 2010-01-15 15:28:23 UTC (rev 402) @@ -1,5 +1,16 @@ CHANGES FROM VERSION 2.39.4 +* New preferences "noupdate=root", "nodeletion=root", "nocreation=root" + that prevent Unison from performing files updates, deletions or + creations on the given root. +* GTK UI: do not reload the preference file before a new update + detection if it is unchanged +* Limit the number of simultaneous external copy program + ("copymax" preference) + +------------------------------- +CHANGES FROM VERSION 2.39.4 + * GTK UI: - take into account the "height" preference again - the internal list of selected reconciler item was not always in Modified: trunk/src/copy.ml =================================================================== --- trunk/src/copy.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/copy.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -693,6 +693,12 @@ ^ "added if the value of {\\tt copyprog} contains the string " ^ "{\\tt rsync}.") +let copymax = + Prefs.createInt "copymax" ~local:true 1 + "!maximum number of simultaneous copyprog transfers" + ("A number indicating how many instances of the external copying utility \ + Unison is allowed to run simultaneously (default to 1).") + let formatConnectionInfo root = match root with Common.Local, _ -> "" @@ -762,6 +768,8 @@ Remote.registerRootCmdWithConnection "finishExternalTransfer" finishExternalTransferLocal +let copyprogReg = Lwt_util.make_region 1 + let transferFileUsingExternalCopyprog rootFrom pathFrom rootTo fspathTo pathTo realPathTo update desc fp ress id useExistingTarget = @@ -791,7 +799,9 @@ ^ (Uutil.quotes fromSpec) ^ " " ^ (Uutil.quotes toSpec) in Trace.log (Printf.sprintf "%s\n" cmd); - let _,log = External.runExternalProgram cmd in + Lwt_util.resize_region copyprogReg (Prefs.read copymax); + Lwt_util.run_in_region copyprogReg 1 + (fun () -> External.runExternalProgram cmd) >>= fun (_, log) -> debug (fun() -> let l = Util.trimWhitespace log in Util.msg "transferFileUsingExternalCopyprog %s: returned...\n%s%s" Modified: trunk/src/external.ml =================================================================== --- trunk/src/external.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/external.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -74,8 +74,8 @@ "\n\n" ^ Util.process_status_to_string returnValue else "") in - (returnValue,mergeResultLog) - end else Lwt_unix.run ( + Lwt.return (returnValue,mergeResultLog) + end else let (out, ipt, err) as desc = System.open_process_full cmd in let out = Lwt_unix.intern_in_channel out in let err = Lwt_unix.intern_in_channel err in @@ -94,4 +94,4 @@ then "" else "\n\n" ^ Util.process_status_to_string returnValue))) (* Stop typechechecker from complaining about non-exhaustive pattern above *) - | _ -> assert false)) + | _ -> assert false) Modified: trunk/src/external.mli =================================================================== --- trunk/src/external.mli 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/external.mli 2010-01-15 15:28:23 UTC (rev 402) @@ -1,5 +1,5 @@ (* Unison file synchronizer: src/external.mli *) (* Copyright 1999-2009, Benjamin C. Pierce (see COPYING for details) *) -val runExternalProgram : string -> Unix.process_status * string +val runExternalProgram : string -> (Unix.process_status * string) Lwt.t val readChannelTillEof : in_channel -> string Modified: trunk/src/fileinfo.ml =================================================================== --- trunk/src/fileinfo.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/fileinfo.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -150,14 +150,13 @@ let ignoreInodeNumbers = Prefs.createBool "ignoreinodenumbers" false - "!Use creation times for detecting updates" + "!ignore inode number changes when detecting updates" ("When set to true, this preference makes Unison not take advantage \ - of inode numbers during fast update detection even when running \ - on a Unix system. This switch should be used with care, as it \ + of inode numbers during fast update detection. \ + This switch should be used with care, as it \ is less safe than the standard update detection method, but it \ can be useful for synchronizing VFAT filesystems (which do not \ - support inode numbers) mounted on Unix systems. \ - The {\\tt fastcheck} option should also be set to true.") + support inode numbers) mounted on Unix systems.") let _ = Prefs.alias ignoreInodeNumbers "pretendwin" let stamp info = Modified: trunk/src/files.ml =================================================================== --- trunk/src/files.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/files.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -580,7 +580,7 @@ let diffCmd = Prefs.createString "diff" "diff -u CURRENT2 CURRENT1" - "!command for showing differences between files" + "!set command for showing differences between files" ("This preference can be used to control the name and command-line " ^ "arguments of the system " ^ "utility used to generate displays of file differences. The default " @@ -873,7 +873,8 @@ (Fspath.quotes (Fspath.concat workingDirForMerge newarch)) in Trace.log (Printf.sprintf "Merge command: %s\n" cmd); - let returnValue, mergeResultLog = External.runExternalProgram cmd in + let returnValue, mergeResultLog = + Lwt_unix.run (External.runExternalProgram cmd) in Trace.log (Printf.sprintf "Merge result (%s):\n%s\n" (showStatus returnValue) mergeResultLog); Modified: trunk/src/globals.ml =================================================================== --- trunk/src/globals.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/globals.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -40,14 +40,13 @@ ^ "that, if Unison is invoked later with a slightly different name " ^ "for the same root, it will be able to locate the correct archives.") -let setRawRoots l = - Prefs.set rawroots l +let setRawRoots l = Prefs.set rawroots (Safelist.rev l) -let rawRoots () = Prefs.read rawroots +let rawRoots () = Safelist.rev (Prefs.read rawroots) -let rootsInitialName () = +let rawRootPair () = match rawRoots () with - [r2; r1] -> (r1, r2) + [r1; r2] -> (r1, r2) | _ -> assert false let theroots = ref [] @@ -67,7 +66,7 @@ cont >>= (fun l -> return (r' :: l)))) roots (return []) >>= (fun roots' -> - theroots := Safelist.rev roots'; + theroots := roots'; return ()) (* Alternate interface, should replace old interface eventually *) @@ -76,8 +75,8 @@ let roots = rawRoots () in theroots := Safelist.map Remote.canonize ((Safelist.map Clroot.parseRoot) roots); - theroots := Safelist.rev !theroots (* Not sure why this is needed... *) - + theroots := !theroots + let roots () = match !theroots with [root1;root2] -> (root1,root2) Modified: trunk/src/globals.mli =================================================================== --- trunk/src/globals.mli 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/globals.mli 2010-01-15 15:28:23 UTC (rev 402) @@ -8,6 +8,7 @@ (* line *) val rawRoots : unit -> string list val setRawRoots : string list -> unit +val rawRootPair : unit -> string * string (* Parse and canonize roots from their raw names *) val installRoots : (string -> string -> string) option -> unit Lwt.t Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/mkProjectInfo.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -102,3 +102,4 @@ + Modified: trunk/src/path.ml =================================================================== --- trunk/src/path.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/path.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -195,7 +195,7 @@ let equal (p1 : local) (p2 : local) = p1 = p2 (* Pref controlling whether symlinks are followed. *) -let followPred = Pred.create "follow" +let followPred = Pred.create ~advanced:true "follow" ("Including the preference \\texttt{-follow \\ARG{pathspec}} causes Unison to \ treat symbolic links matching \\ARG{pathspec} as `invisible' and \ behave as if the object pointed to by the link had appeared literally \ Modified: trunk/src/props.ml =================================================================== --- trunk/src/props.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/props.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -188,7 +188,7 @@ let dontChmod = Prefs.createBool "dontchmod" false - "!When set, never use the chmod system call" + "!when set, never use the chmod system call" ( "By default, Unison uses the 'chmod' system call to set the permission bits" ^ " of files after it has copied them. But in some circumstances (and under " ^ " some operating systems), the chmod call always fails. Setting this " @@ -544,7 +544,7 @@ let cmd = "/usr/local/bin/sudo -u root /usr/bin/touch -m -a -t " ^ tstr ^ " " ^ Fspath.quotes abspath in Util.msg "Running external program to set utimes:\n %s\n" cmd; - let (r,_) = External.runExternalProgram cmd in + let (r,_) = Lwt_unix.run (External.runExternalProgram cmd) in if r<>(Unix.WEXITED 0) then raise (Util.Transient "External time-setting command failed") end else Fs.utimes abspath v v) Modified: trunk/src/recon.ml =================================================================== --- trunk/src/recon.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/recon.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -68,9 +68,7 @@ if root="older" then `Older else if root="newer" then `Newer else - let roots = Safelist.rev (Globals.rawRoots()) in - let r1 = Safelist.nth roots 0 in - let r2 = Safelist.nth roots 1 in + let (r1, r2) = Globals.rawRootPair () in debug (fun() -> Printf.eprintf "root2direction called to choose %s from %s and %s\n" root r1 r2); @@ -82,7 +80,7 @@ let forceRoot: string Prefs.t = Prefs.createString "force" "" - "force changes from this replica to the other" + "!force changes from this replica to the other" ("Including the preference \\texttt{-force \\ARG{root}} causes Unison to " ^ "resolve all differences (even non-conflicting changes) in favor of " ^ "\\ARG{root}. " @@ -114,7 +112,7 @@ let preferRoot: string Prefs.t = Prefs.createString "prefer" "" - "choose this replica's version for conflicting changes" + "!choose this replica's version for conflicting changes" ("Including the preference \\texttt{-prefer \\ARG{root}} causes Unison always to " ^ "resolve conflicts in favor of \\ARG{root}, rather than asking for " ^ "guidance from the user. (The syntax of \\ARG{root} is the same as " @@ -158,6 +156,71 @@ else ("",`Prefer) +let actionKind fromRc toRc = + let fromTyp = fromRc.typ in + let toTyp = toRc.typ in + if fromTyp = toTyp then `UPDATE else + if toTyp = `ABSENT then `CREATION else + `DELETION + +type prefs = { noDeletion : bool; noUpdate: bool; noCreation : bool } + +let shouldCancel rc1 rc2 prefs = + match actionKind rc1 rc2 with + `UPDATE -> prefs.noUpdate + | `DELETION -> prefs.noUpdate || prefs.noDeletion + | `CREATION -> prefs.noCreation + +let filterRi prefs1 prefs2 ri = + match ri.replicas with + Problem _ -> + () + | Different diff -> + if + match diff.direction with + Replica1ToReplica2 -> shouldCancel diff.rc1 diff.rc2 prefs2 + | Replica2ToReplica1 -> shouldCancel diff.rc2 diff.rc1 prefs1 + | Conflict | Merge -> false + then + diff.direction <- Conflict + +let noDeletion = + Prefs.createStringList "nodeletion" ~local:true + "prevent file deletions on one replica" + ("Including the preference \\texttt{-nodeletion \\ARG{root}} prevents \ + Unison from performing any file deletion on root \\ARG{root}.\n\ + This preference can be included twice, once for each root, if you \ + want to prevent any creation.") + +let noUpdate = + Prefs.createStringList "noupdate" ~local:true + "prevent file updates and deletions on one replica" + ("Including the preference \\texttt{-noupdate \\ARG{root}} prevents \ + Unison from performing any file update or deletion on root \ + \\ARG{root}.\n\ + This preference can be included twice, once for each root, if you \ + want to prevent any update.") + +let noCreation = + Prefs.createStringList "nocreation" ~local:true + "prevent file creations on one replica" + ("Including the preference \\texttt{-nocreation \\ARG{root}} prevents \ + Unison from performing any file creation on root \\ARG{root}.\n\ + This preference can be included twice, once for each root, if you \ + want to prevent any creation.") + +let filterRis ris = + let (root1, root2) = Globals.rawRootPair () in + let getPref root pref = List.mem root (Prefs.read pref) in + let getPrefs root = + { noDeletion = getPref root noDeletion; + noUpdate = getPref root noUpdate; + noCreation = getPref root noCreation } + in + let prefs1 = getPrefs root1 in + let prefs2 = getPrefs root2 in + Safelist.iter (fun ri -> filterRi prefs1 prefs2 ri) ris + (* Use the current values of the '-prefer ' and '-force ' *) (* preferences to override the reconciler's choices *) let overrideReconcilerChoices ris = @@ -171,13 +234,12 @@ if rootp<>"" then begin let dir = root2direction rootp in setDirection ri dir forcep - end) ris + end) ris; + filterRis ris (* Look up the preferred root and verify that it is OK (this is called at *) (* the beginning of the run, so that we don't have to wait to hear about *) (* errors *) -(* This should also check for the partial version, but this needs a way to *) -(* extract the associated values from a Pred.t *) let checkThatPreferredRootIsValid () = let test_root predname = function | "" -> () @@ -190,7 +252,23 @@ let (root,pred) = lookupPreferredRoot() in if root<>"" then test_root (match pred with `Force -> "force" | `Prefer -> "prefer") root; Safelist.iter (test_root "forcepartial") (Pred.extern_associated_strings forceRootPartial); - Safelist.iter (test_root "preferpartial") (Pred.extern_associated_strings preferRootPartial) + Safelist.iter (test_root "preferpartial") (Pred.extern_associated_strings preferRootPartial); + let checkPref pref prefName = + try + let root = + List.find (fun r -> not (List.mem r (Globals.rawRoots ()))) + (Prefs.read pref) + in + let (r1, r2) = Globals.rawRootPair () in + raise (Util.Fatal (Printf.sprintf + "%s (given as argument to '%s' preference)\n\ + is not one of the current roots:\n %s\n %s" root prefName r1 r2)) + with Not_found -> + () + in + checkPref noDeletion "nodeletion"; + checkPref noUpdate "noupdate"; + checkPref noCreation "nocreation" (* ------------------------------------------------------------------------- *) (* Main Reconciliation stuff *) Modified: trunk/src/remote.ml =================================================================== --- trunk/src/remote.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/remote.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -94,7 +94,8 @@ Unix.Unix_error(Unix.ECONNRESET, _, _) | Unix.Unix_error(Unix.EPIPE, _, _) (* Windows may also return the following errors... *) - | Unix.Unix_error(Unix.EINVAL, _, _) -> + | Unix.Unix_error(Unix.EINVAL, _, _) + | Unix.Unix_error(Unix.EUNKNOWNERR (-64), _, _) -> (* Client has closed its end of the connection *) lostConnection () | _ -> Modified: trunk/src/ubase/prefs.ml =================================================================== --- trunk/src/ubase/prefs.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/ubase/prefs.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -16,6 +16,7 @@ (* ------------------------------------------------------------------------- *) let profileName = ref None +let profileFiles = ref [] let profilePathname n = let f = Util.fileInUnisonDir n in @@ -27,6 +28,18 @@ None -> raise (Util.Transient("No preference file has been specified")) | Some(n) -> profilePathname n +let profileUnchanged () = + List.for_all + (fun (path, info) -> + try + let newInfo = System.stat path in + newInfo.Unix.LargeFile.st_kind = Unix.S_REG && + info.Unix.LargeFile.st_mtime = newInfo.Unix.LargeFile.st_mtime && + info.Unix.LargeFile.st_size = newInfo.Unix.LargeFile.st_size + with Unix.Unix_error _ -> + false) + !profileFiles + (* ------------------------------------------------------------------------- *) (* When preferences change, we need to dump them out to the file we loaded *) @@ -46,8 +59,9 @@ let addresetter f = resetters := f :: !resetters -let resetToDefaults () = Safelist.iter (fun f -> f()) !resetters - +let resetToDefaults () = + Safelist.iter (fun f -> f()) !resetters; profileFiles := [] + (* ------------------------------------------------------------------------- *) (* When the server starts up, we need to ship it the current state of all *) @@ -322,9 +336,13 @@ in the same order as in the file. *) let rec readAFile filename : (string * int * string * string) list = let chan = - try System.open_in_bin (profilePathname filename) - with Sys_error _ -> - raise(Util.Fatal(Printf.sprintf "Preference file %s not found" filename)) in + try + let path = profilePathname filename in + profileFiles := (path, System.stat path) :: !profileFiles; + System.open_in_bin path + with Unix.Unix_error _ | Sys_error _ -> + raise(Util.Fatal(Printf.sprintf "Preference file %s not found" filename)) + in let bom = "\xef\xbb\xbf" in (* BOM: UTF-8 byte-order mark *) let rec loop lines = match (try Some(input_line chan) with End_of_file -> None) with Modified: trunk/src/ubase/prefs.mli =================================================================== --- trunk/src/ubase/prefs.mli 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/ubase/prefs.mli 2010-01-15 15:28:23 UTC (rev 402) @@ -104,6 +104,9 @@ (* Calculate the full pathname of a preference file *) val profilePathname : string -> System.fspath +(* Check whether the profile file is unchanged *) +val profileUnchanged : unit -> bool + (* Add a new preference to the file on disk (the result is a diagnostic *) (* message that can be displayed to the user to verify where the new pref *) (* went) *) Modified: trunk/src/ubase/util.ml =================================================================== --- trunk/src/ubase/util.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/ubase/util.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -125,7 +125,11 @@ Unix.Unix_error(err,fnname,param) -> let s = "Error in " ^ m ^ ":\n" ^ (Unix.error_message err) - ^ " [" ^ fnname ^ "(" ^ param ^ ")]" in + ^ " [" ^ fnname ^ "(" ^ param ^ ")]%s" ^ + (match err with + Unix.EUNKNOWNERR n -> Format.sprintf " (code %d)" n + | _ -> "") + in debug "exn" (fun() -> msg "Converting a Unix error to %s:\n%s\n" kindStr s); reraise s Modified: trunk/src/uicommon.ml =================================================================== --- trunk/src/uicommon.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/uicommon.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -313,6 +313,15 @@ Sys.Break -> "Terminated!" | Util.Fatal(s) -> Printf.sprintf "Fatal error: %s" s | Util.Transient(s) -> Printf.sprintf "Error: %s" s + | Unix.Unix_error (err, fun_name, arg) -> + Printf.sprintf "Uncaught unix error: %s failed%s: %s%s" + fun_name + (if String.length arg > 0 then Format.sprintf " on \"%s\"" arg else "") + (Unix.error_message err) + (match err with + Unix.EUNKNOWNERR n -> Format.sprintf " (code %d)" n + | _ -> "") + | Invalid_argument s -> Printf.sprintf "Invalid argument: %s" s | other -> Printf.sprintf "Uncaught exception %s" (Printexc.to_string other) (* precondition: uc = File (Updates(_, ..) on both sides *) @@ -464,7 +473,7 @@ let r2 = match getSecondRoot() with None -> exit 0 | Some r -> r in (* Remember them for this run, ordering them so that the first will come out on the left in the UI *) - Globals.setRawRoots [r2;r1]; + Globals.setRawRoots [r1; r2]; (* Save them in the current profile *) ignore (Prefs.add "root" r1); ignore (Prefs.add "root" r2) @@ -477,14 +486,14 @@ let firstTime = ref(true) (* Roots given on the command line *) -let rawRoots = ref [] +let cmdLineRawRoots = ref [] (* BCP: WARNING: Some of the code from here is duplicated in uimacbridge...! *) let initPrefs ~profileName ~displayWaitMessage ~getFirstRoot ~getSecondRoot ~termInteract = (* Restore prefs to their default values, if necessary *) if not !firstTime then Prefs.resetToDefaults(); - Globals.setRawRoots !rawRoots; + Globals.setRawRoots !cmdLineRawRoots; (* Tell the preferences module the name of the profile *) Prefs.profileName := Some(profileName); @@ -644,9 +653,9 @@ match Util.StringMap.find "rest" args with [] -> () | [profile] -> clprofile := Some profile - | [root1;root2] -> rawRoots := [root1;root2] - | [root1;root2;profile] -> - rawRoots := [root1;root2]; + | [root2;root1] -> cmdLineRawRoots := [root1;root2] + | [root2;root1;profile] -> + cmdLineRawRoots := [root1;root2]; clprofile := Some profile | _ -> (reportError(Printf.sprintf @@ -664,7 +673,7 @@ (match !clprofile with None -> Util.msg "No profile given on command line" | Some s -> Printf.eprintf "Profile '%s' given on command line" s); - (match !rawRoots with + (match !cmdLineRawRoots with [] -> Util.msg "No roots given on command line" | [root1;root2] -> Printf.eprintf "Roots '%s' and '%s' given on command line" @@ -674,7 +683,7 @@ let profileName = begin match !clprofile with None -> - let clroots_given = !rawRoots <> [] in + let clroots_given = !cmdLineRawRoots <> [] in let n = if not(clroots_given) then begin (* Ask the user to choose a profile or create a new one. *) Modified: trunk/src/uigtk2.ml =================================================================== --- trunk/src/uigtk2.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/uigtk2.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -3768,9 +3768,14 @@ in let reloadProfile () = - match !Prefs.profileName with - None -> () - | Some(n) -> clearMainWindow (); loadProfile n true in + let n = + match !Prefs.profileName with + None -> assert false + | Some n -> n + in + clearMainWindow (); + if not (Prefs.profileUnchanged ()) then loadProfile n true + in let detectCmd () = getLock detectUpdatesAndReconcile; @@ -4026,7 +4031,7 @@ ~callback:(fun () -> doAction (fun ri _ -> Recon.setDirection ri `Older `Prefer)) - "Resolve conflicts in favor of least recently modified"); + "Resolve Conflicts in Favor of Least Recently Modified"); ignore (actionMenu#add_separator ()); grAdd grAction (actionMenu#add_item Modified: trunk/src/uimacbridge.ml =================================================================== --- trunk/src/uimacbridge.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/uimacbridge.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -79,8 +79,8 @@ match Util.StringMap.find "rest" args with [] -> () | [profile] -> clprofile := Some profile - | [root1;root2] -> Globals.setRawRoots [root1;root2] - | [root1;root2;profile] -> + | [root2;root1] -> Globals.setRawRoots [root1;root2] + | [root2;root1;profile] -> Globals.setRawRoots [root1;root2]; clprofile := Some profile | _ -> Modified: trunk/src/uimacbridgenew.ml =================================================================== --- trunk/src/uimacbridgenew.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/uimacbridgenew.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -150,8 +150,8 @@ match Util.StringMap.find "rest" args with [] -> () | [profile] -> clprofile := Some profile - | [root1;root2] -> Globals.setRawRoots [root1;root2] - | [root1;root2;profile] -> + | [root2;root1] -> Globals.setRawRoots [root1;root2] + | [root2;root1;profile] -> Globals.setRawRoots [root1;root2]; clprofile := Some profile | _ -> Modified: trunk/src/update.ml =================================================================== --- trunk/src/update.ml 2010-01-15 08:29:26 UTC (rev 401) +++ trunk/src/update.ml 2010-01-15 15:28:23 UTC (rev 402) @@ -524,9 +524,11 @@ && Props.same_time oldInfo.Fileinfo.desc newInfo.Fileinfo.desc && + Props.length oldInfo.Fileinfo.desc = Props.length newInfo.Fileinfo.desc + && match Fileinfo.stamp oldInfo, Fileinfo.stamp newInfo with Fileinfo.InodeStamp in1, Fileinfo.InodeStamp in2 -> in1 = in2 - | Fileinfo.CtimeStamp t1, Fileinfo.CtimeStamp t2 -> t1 = t2 + | Fileinfo.CtimeStamp _, Fileinfo.CtimeStamp _ -> true | _ -> false let archiveUnchanged fspath newInfo = @@ -1042,10 +1044,8 @@ this switch under Windows most of the time and occasionally \ run Unison once with {\\tt fastcheck} set to \ \\verb|false|, if you are \ - worried that Unison may have overlooked an update. The default \ - value of the preference is \\verb|auto|, which causes Unison to \ - use fast checking on Unix replicas (where it is safe) and slow \ - checking on Windows replicas. For backward compatibility, \ + worried that Unison may have overlooked an update. \ + For backward compatibility, \ \\verb|yes|, \\verb|no|, and \\verb|default| can be used in place \ of \\verb|true|, \\verb|false|, and \\verb|auto|. See \ \\sectionref{fastcheck}{Fast Checking} for more information.") @@ -1055,7 +1055,7 @@ || (Prefs.read fastcheck = `Default (*&& Util.osType = `Unix*)) let immutable = Pred.create "immutable" ~advanced:true - ("This preference specifies paths for directories whose \ + ("This preference specifies paths for directories whose \ immediate children are all immutable files --- i.e., once a file has been \ created, its contents never changes. When scanning for updates, \ Unison does not check whether these files have been modified; \ @@ -1063,13 +1063,13 @@ directories).") let immutablenot = Pred.create "immutablenot" ~advanced:true - ("This preference overrides {\\tt immutable}.") + ("This preference overrides {\\tt immutable}.") type scanInfo = - { fastCheck : bool; - dirFastCheck : bool; - dirStamp : Props.dirChangedStamp; - showStatus : bool } + { fastCheck : bool; + dirFastCheck : bool; + dirStamp : Props.dirChangedStamp; + showStatus : bool } (** Status display **) @@ -1085,26 +1085,26 @@ the status display message -- thus effectively serializing the client and server! *) let showStatusAddLength scanInfo info = - let len1 = Props.length info.Fileinfo.desc in - let len2 = Osx.ressLength info.Fileinfo.osX.Osx.ressInfo in + let len1 = Props.length info.Fileinfo.desc in + let len2 = Osx.ressLength info.Fileinfo.osX.Osx.ressInfo in if len1 >= bigFileLengthFS || len2 >= bigFileLengthFS then fileLength := bigFileLength else fileLength := min bigFileLength - (!fileLength + Uutil.Filesize.toInt len1 + Uutil.Filesize.toInt len2) + (!fileLength + Uutil.Filesize.toInt len1 + Uutil.Filesize.toInt len2) let showStatus scanInfo path = - fileLength := !fileLength + smallFileLength; - if !fileLength >= bigFileLength then begin - fileLength := 0; - let t = Unix.gettimeofday () in + fileLength := !fileLength + smallFileLength; + if !fileLength >= bigFileLength then begin + fileLength := 0; + let t = Unix.gettimeofday () in if t -. !t0 > 0.05 then begin if scanInfo.showStatus then Uutil.showUpdateStatus (Path.toString path); t0 := t end - end + end let showStatusDir path = () @@ -1114,11 +1114,11 @@ they are scanned -- but this seems worse: it prints far too much stuff. So I'm going to revert to the old version. *) (* -let showStatus path = () -let showStatusAddLength info = () -let showStatusDir path = + let showStatus path = () + let showStatusAddLength info = () + let showStatusDir path = if not !Trace.runningasserver then begin - Trace.statusDetail ("scanning... " ^ Path.toString path); + Trace.statusDetail ("scanning... " ^ Path.toString path); end *) From vouillon at seas.upenn.edu Fri Jan 15 18:25:09 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Fri, 15 Jan 2010 18:25:09 -0500 Subject: [Unison-hackers] [unison-svn] r403 - trunk/src Message-ID: <201001152325.o0FNPAx3002833@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-15 18:25:09 -0500 (Fri, 15 Jan 2010) New Revision: 403 Modified: trunk/src/Makefile trunk/src/RECENTNEWS trunk/src/mkProjectInfo.ml trunk/src/pred.ml trunk/src/pred.mli trunk/src/recon.ml trunk/src/uicommon.ml trunk/src/uimacbridge.ml trunk/src/uimacbridgenew.ml Log: * Implemented 'partial' versions of 'noupdate', 'nodeletion' and 'nocreation' * Check sooner (before connecting to another machine) that the roots given as argument to all these preference are well-formed Modified: trunk/src/Makefile =================================================================== --- trunk/src/Makefile 2010-01-15 15:28:23 UTC (rev 402) +++ trunk/src/Makefile 2010-01-15 23:25:09 UTC (rev 403) @@ -60,11 +60,11 @@ # NAME, VERSION, and MAJORVERSION, automatically generated -include Makefile.ProjectInfo -Makefile.ProjectInfo: mkProjectInfo $(wildcard ../.bzr/branch/last-revision) - ./mkProjectInfo > $@ +Makefile.ProjectInfo: mkProjectInfo.ml $(wildcard ../.bzr/branch/last-revision) + ocaml str.cma unix.cma ./mkProjectInfo.ml > $@ -mkProjectInfo: mkProjectInfo.ml - ocamlc -o $@ unix.cma str.cma $^ +#mkProjectInfo: mkProjectInfo.ml +# ocamlc -o $@ unix.cma str.cma $^ clean:: $(RM) mkProjectInfo Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-15 15:28:23 UTC (rev 402) +++ trunk/src/RECENTNEWS 2010-01-15 23:25:09 UTC (rev 403) @@ -1,3 +1,9 @@ +CHANGES FROM VERSION 2.39.6 + +* Implemented 'partial' versions of 'noupdate', 'nodeletion' and 'nocreation' +* Check sooner (before connecting to another machine) that the roots + given as argument to all these preference are well-formed +------------------------------- CHANGES FROM VERSION 2.39.4 * New preferences "noupdate=root", "nodeletion=root", "nocreation=root" Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-15 15:28:23 UTC (rev 402) +++ trunk/src/mkProjectInfo.ml 2010-01-15 23:25:09 UTC (rev 403) @@ -42,7 +42,7 @@ (* ---------------------------------------------------------------------- *) (* You shouldn't need to edit below. *) -let revisionString = "$Rev: 400$";; +let revisionString = "$Rev: 402$";; (* extract a substring using a regular expression *) let extract_str re str = @@ -97,9 +97,3 @@ Printf.printf "VERSION=%d.%d.%d\n" majorVersion minorVersion pointVersion;; Printf.printf "NAME=%s\n" projectName;; - - - - - - Modified: trunk/src/pred.ml =================================================================== --- trunk/src/pred.ml 2010-01-15 15:28:23 UTC (rev 402) +++ trunk/src/pred.ml 2010-01-15 23:25:09 UTC (rev 403) @@ -88,9 +88,9 @@ end in (compiled, v) -let create name ?(advanced=false) fulldoc = +let create name ?(local=false) ?(advanced=false) fulldoc = let pref = - Prefs.create name [] + Prefs.create name ~local [] ((if advanced then "!" else "") ^ "add a pattern to the " ^ name ^ " list") fulldoc @@ -111,15 +111,17 @@ let pref = Prefs.read p.pref in let compiledList = Safelist.map compile_pattern (Safelist.append p.default pref) in let compiled = Rx.alt (Safelist.map fst compiledList) in + let handleCase rx = + if (Case.ops())#caseInsensitiveMatch then Rx.case_insensitive rx + else rx + in let strings = Safelist.filterMap (fun (rx,vo) -> match vo with None -> None - | Some v -> Some (rx,v)) + | Some v -> Some (handleCase rx,v)) compiledList in - p.compiled <- - if (Case.ops())#caseInsensitiveMatch then Rx.case_insensitive compiled - else compiled; + p.compiled <- handleCase compiled; p.associated_strings <- strings; p.last_pref <- pref; p.last_def <- p.default; @@ -160,3 +162,9 @@ recompile_if_needed p; let s = (Case.ops())#normalizeMatchedString s in snd (Safelist.find (fun (rx,v) -> Rx.match_string rx s) p.associated_strings) + +let assoc_all p s = + recompile_if_needed p; + let s = (Case.ops())#normalizeMatchedString s in + Safelist.map snd + (Safelist.filter (fun (rx,v) -> Rx.match_string rx s) p.associated_strings) Modified: trunk/src/pred.mli =================================================================== --- trunk/src/pred.mli 2010-01-15 15:28:23 UTC (rev 402) +++ trunk/src/pred.mli 2010-01-15 23:25:09 UTC (rev 403) @@ -30,7 +30,7 @@ (* Create a new predicate and register it with the preference module. The first arg is the name of the predicate; the second is full (latex) documentation. *) -val create : string -> ?advanced:bool -> string -> t +val create : string -> ?local:bool -> ?advanced:bool -> string -> t (* Check whether a given path matches one of the default or current patterns *) val test : t -> string -> bool @@ -39,6 +39,9 @@ if no pattern with an associated string matches. *) val assoc : t -> string -> string +(* Return all strings associated to a matching pattern. *) +val assoc_all : t -> string -> string list + (* Add list of default patterns to the existing list. (These patterns are remembered even when the associated preference is cleared). *) val addDefaultPatterns : t -> string list -> unit Modified: trunk/src/recon.ml =================================================================== --- trunk/src/recon.ml 2010-01-15 15:28:23 UTC (rev 402) +++ trunk/src/recon.ml 2010-01-15 23:25:09 UTC (rev 403) @@ -96,7 +96,7 @@ let forceRootPartial: Pred.t = Pred.create "forcepartial" ~advanced:true - ("Including the preference \\texttt{forcepartial \\ARG{PATHSPEC} -> \\ARG{root}} causes Unison to " + ("Including the preference \\texttt{forcepartial = \\ARG{PATHSPEC} -> \\ARG{root}} causes Unison to " ^ "resolve all differences (even non-conflicting changes) in favor of " ^ "\\ARG{root} for the files in \\ARG{PATHSPEC} (see \\sectionref{pathspec}{Path Specification} " ^ "for more information). " @@ -124,7 +124,7 @@ let preferRootPartial: Pred.t = Pred.create "preferpartial" ~advanced:true - ("Including the preference \\texttt{preferpartial \\ARG{PATHSPEC} -> \\ARG{root}} " + ("Including the preference \\texttt{preferpartial = \\ARG{PATHSPEC} -> \\ARG{root}} " ^ "causes Unison always to " ^ "resolve conflicts in favor of \\ARG{root}, rather than asking for " ^ "guidance from the user, for the files in \\ARG{PATHSPEC} (see " @@ -156,39 +156,11 @@ else ("",`Prefer) -let actionKind fromRc toRc = - let fromTyp = fromRc.typ in - let toTyp = toRc.typ in - if fromTyp = toTyp then `UPDATE else - if toTyp = `ABSENT then `CREATION else - `DELETION - -type prefs = { noDeletion : bool; noUpdate: bool; noCreation : bool } - -let shouldCancel rc1 rc2 prefs = - match actionKind rc1 rc2 with - `UPDATE -> prefs.noUpdate - | `DELETION -> prefs.noUpdate || prefs.noDeletion - | `CREATION -> prefs.noCreation - -let filterRi prefs1 prefs2 ri = - match ri.replicas with - Problem _ -> - () - | Different diff -> - if - match diff.direction with - Replica1ToReplica2 -> shouldCancel diff.rc1 diff.rc2 prefs2 - | Replica2ToReplica1 -> shouldCancel diff.rc2 diff.rc1 prefs1 - | Conflict | Merge -> false - then - diff.direction <- Conflict - let noDeletion = Prefs.createStringList "nodeletion" ~local:true "prevent file deletions on one replica" ("Including the preference \\texttt{-nodeletion \\ARG{root}} prevents \ - Unison from performing any file deletion on root \\ARG{root}.\n\ + Unison from performing any file deletion on root \\ARG{root}.\n\n\ This preference can be included twice, once for each root, if you \ want to prevent any creation.") @@ -197,7 +169,7 @@ "prevent file updates and deletions on one replica" ("Including the preference \\texttt{-noupdate \\ARG{root}} prevents \ Unison from performing any file update or deletion on root \ - \\ARG{root}.\n\ + \\ARG{root}.\n\n\ This preference can be included twice, once for each root, if you \ want to prevent any update.") @@ -205,21 +177,83 @@ Prefs.createStringList "nocreation" ~local:true "prevent file creations on one replica" ("Including the preference \\texttt{-nocreation \\ARG{root}} prevents \ - Unison from performing any file creation on root \\ARG{root}.\n\ + Unison from performing any file creation on root \\ARG{root}.\n\n\ This preference can be included twice, once for each root, if you \ want to prevent any creation.") +let noDeletionPartial = + Pred.create "nodeletionpartial" ~local:true ~advanced:true + ("Including the preference \ + \\texttt{nodeletionpartial = \\ARG{PATHSPEC} -> \\ARG{root}} prevents \ + Unison from performing any file deletion in \\ARG{PATHSPEC} \ + on root \\ARG{root} (see \\sectionref{pathspec}{Path Specification} \ + for more information).") + +let noUpdatePartial = + Pred.create "noupdatepartial" ~local:true ~advanced:true + ("Including the preference \ + \\texttt{noupdatepartial = \\ARG{PATHSPEC} -> \\ARG{root}} prevents \ + Unison from performing any file update or deletion in \ + \\ARG{PATHSPEC} on root \\ARG{root} (see \ + \\sectionref{pathspec}{Path Specification} for more information).") + +let noCreationPartial = + Pred.create "nocreationpartial" ~local:true ~advanced:true + ("Including the preference \ + \\texttt{nocreationpartial = \\ARG{PATHSPEC} -> \\ARG{root}} prevents \ + Unison from performing any file creation in \\ARG{PATHSPEC} \ + on root \\ARG{root} (see \\sectionref{pathspec}{Path Specification} \ + for more information).") + +let partialCancelPref actionKind = + match actionKind with + `DELETION -> noDeletionPartial + | `UPDATE -> noUpdatePartial + | `CREATION -> noCreationPartial + +let cancelPref actionKind = + match actionKind with + `DELETION -> noDeletion + | `UPDATE -> noUpdate + | `CREATION -> noCreation + +let actionKind fromRc toRc = + let fromTyp = fromRc.typ in + let toTyp = toRc.typ in + if fromTyp = toTyp then `UPDATE else + if toTyp = `ABSENT then `CREATION else + `DELETION + +let shouldCancel path rc1 rc2 root2 = + let test kind = + List.mem root2 (Prefs.read (cancelPref kind)) + || + List.mem root2 (Pred.assoc_all (partialCancelPref kind) path) + in + match actionKind rc1 rc2 with + `UPDATE -> test `UPDATE + | `DELETION -> test `UPDATE || test `DELETION + | `CREATION -> test `CREATION + +let filterRi root1 root2 ri = + match ri.replicas with + Problem _ -> + () + | Different diff -> + if + match diff.direction with + Replica1ToReplica2 -> + shouldCancel (Path.toString ri.path1) diff.rc1 diff.rc2 root2 + | Replica2ToReplica1 -> + shouldCancel (Path.toString ri.path1) diff.rc2 diff.rc1 root1 + | Conflict | Merge -> + false + then + diff.direction <- Conflict + let filterRis ris = let (root1, root2) = Globals.rawRootPair () in - let getPref root pref = List.mem root (Prefs.read pref) in - let getPrefs root = - { noDeletion = getPref root noDeletion; - noUpdate = getPref root noUpdate; - noCreation = getPref root noCreation } - in - let prefs1 = getPrefs root1 in - let prefs2 = getPrefs root2 in - Safelist.iter (fun ri -> filterRi prefs1 prefs2 ri) ris + Safelist.iter (fun ri -> filterRi root1 root2 ri) ris (* Use the current values of the '-prefer ' and '-force ' *) (* preferences to override the reconciler's choices *) @@ -253,11 +287,11 @@ if root<>"" then test_root (match pred with `Force -> "force" | `Prefer -> "prefer") root; Safelist.iter (test_root "forcepartial") (Pred.extern_associated_strings forceRootPartial); Safelist.iter (test_root "preferpartial") (Pred.extern_associated_strings preferRootPartial); - let checkPref pref prefName = + let checkPref extract (pref, prefName) = try let root = List.find (fun r -> not (List.mem r (Globals.rawRoots ()))) - (Prefs.read pref) + (extract pref) in let (r1, r2) = Globals.rawRootPair () in raise (Util.Fatal (Printf.sprintf @@ -266,9 +300,12 @@ with Not_found -> () in - checkPref noDeletion "nodeletion"; - checkPref noUpdate "noupdate"; - checkPref noCreation "nocreation" + List.iter (checkPref Prefs.read) + [noDeletion, "nodeletion"; noUpdate, "noupdate"; noCreation, "nocreation"]; + List.iter (checkPref Pred.extern_associated_strings) + [noDeletionPartial, "nodeletionpartial"; + noUpdatePartial, "noupdatepartial"; + noCreationPartial, "nocreationpartial"] (* ------------------------------------------------------------------------- *) (* Main Reconciliation stuff *) Modified: trunk/src/uicommon.ml =================================================================== --- trunk/src/uicommon.ml 2010-01-15 15:28:23 UTC (rev 402) +++ trunk/src/uicommon.ml 2010-01-15 23:25:09 UTC (rev 403) @@ -545,6 +545,8 @@ promptForRoots getFirstRoot getSecondRoot; end; + Recon.checkThatPreferredRootIsValid(); + (* The following step contacts the server, so warn the user it could take some time *) if not (Prefs.read contactquietly || Prefs.read Trace.terse) then @@ -597,8 +599,6 @@ Printf.eprintf " %s\n" (root2string r)) (Globals.rootsInCanonicalOrder()); Printf.eprintf "\n"); - - Recon.checkThatPreferredRootIsValid(); Lwt_unix.run (validateAndFixupPrefs () >>= Modified: trunk/src/uimacbridge.ml =================================================================== --- trunk/src/uimacbridge.ml 2010-01-15 15:28:23 UTC (rev 402) +++ trunk/src/uimacbridge.ml 2010-01-15 23:25:09 UTC (rev 403) @@ -153,6 +153,9 @@ Trace.debug "" (fun() -> Prefs.dumpPrefsToStderr() ); (* FIX: if no roots, ask the user *) + + Recon.checkThatPreferredRootIsValid(); + let localRoots,remoteRoots = Safelist.partition (function Clroot.ConnectLocal _ -> true | _ -> false) @@ -207,8 +210,6 @@ Printf.eprintf "\n" ); - Recon.checkThatPreferredRootIsValid(); - Lwt_unix.run (Uicommon.validateAndFixupPrefs () >>= Globals.propagatePrefs); Modified: trunk/src/uimacbridgenew.ml =================================================================== --- trunk/src/uimacbridgenew.ml 2010-01-15 15:28:23 UTC (rev 402) +++ trunk/src/uimacbridgenew.ml 2010-01-15 23:25:09 UTC (rev 403) @@ -224,6 +224,9 @@ Trace.debug "" (fun() -> Prefs.dumpPrefsToStderr() ); (* FIX: if no roots, ask the user *) + + Recon.checkThatPreferredRootIsValid(); + let localRoots,remoteRoots = Safelist.partition (function Clroot.ConnectLocal _ -> true | _ -> false) @@ -293,8 +296,6 @@ Printf.eprintf "\n" ); - Recon.checkThatPreferredRootIsValid(); - Lwt_unix.run (Uicommon.validateAndFixupPrefs () >>= Globals.propagatePrefs); From Jerome.Vouillon at pps.jussieu.fr Sat Jan 16 08:42:04 2010 From: Jerome.Vouillon at pps.jussieu.fr (Jerome Vouillon) Date: Sat, 16 Jan 2010 14:42:04 +0100 Subject: [Unison-hackers] Mac GUI: terminating when closing the main window Message-ID: <20100116134204.GA5959@pps.jussieu.fr> With the uimacnew GUI, when one presses the red button on the main window frame the application is not terminated: the window disappears but the menu remains. The code to address this is already there, but the interface file has not been updated accordingly. So, could someone open the file English.lproj/MainMenu.nib file with Interface Builder and make the controller object (of class MyController) a delegate of the Application object. As far as I can see, this is already in uimacnew09. -- Jerome From vouillon at seas.upenn.edu Tue Jan 19 04:18:16 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Tue, 19 Jan 2010 04:18:16 -0500 Subject: [Unison-hackers] [unison-svn] r404 - in trunk/src: . uimacnew uimacnew09 Message-ID: <201001190918.o0J9IHUD015940@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-19 04:18:15 -0500 (Tue, 19 Jan 2010) New Revision: 404 Modified: trunk/src/RECENTNEWS trunk/src/mkProjectInfo.ml trunk/src/osx.ml trunk/src/remote.ml trunk/src/uicommon.ml trunk/src/uigtk2.ml trunk/src/uimacnew/Bridge.m trunk/src/uimacnew/MyController.m trunk/src/uimacnew09/Bridge.m trunk/src/uimacnew09/MyController.m trunk/src/uitext.ml Log: * Made a server waiting on a socket more resilient to unexpected lost connections from the client. * Fixed possible race condition in half-duplex communication mode. * Minor other changes... Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/RECENTNEWS 2010-01-19 09:18:15 UTC (rev 404) @@ -1,5 +1,13 @@ CHANGES FROM VERSION 2.39.6 +* Made a server waiting on a socket more resilient to unexpected + lost connections from the client. +* Fixed possible race condition in half-duplex communication mode. +* Minor other changes... + +------------------------------- +CHANGES FROM VERSION 2.39.6 + * Implemented 'partial' versions of 'noupdate', 'nodeletion' and 'nocreation' * Check sooner (before connecting to another machine) that the roots given as argument to all these preference are well-formed Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/mkProjectInfo.ml 2010-01-19 09:18:15 UTC (rev 404) @@ -97,3 +97,4 @@ Printf.printf "VERSION=%d.%d.%d\n" majorVersion minorVersion pointVersion;; Printf.printf "NAME=%s\n" projectName;; + Modified: trunk/src/osx.ml =================================================================== --- trunk/src/osx.ml 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/osx.ml 2010-01-19 09:18:15 UTC (rev 404) @@ -15,6 +15,11 @@ along with this program. If not, see . *) +(* +See +http://www.opensource.apple.com/source/copyfile/copyfile-42/copyfile.c +*) + let debug = Trace.debug "osx" (****) Modified: trunk/src/remote.ml =================================================================== --- trunk/src/remote.ml 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/remote.ml 2010-01-19 09:18:15 UTC (rev 404) @@ -33,6 +33,10 @@ Scanf.sscanf Sys.ocaml_version "%d.%d" (fun maj min -> (maj = 3 && min >= 11) || maj > 3) +let _ = + if Sys.os_type = "Unix" then + ignore(Sys.set_signal Sys.sigpipe Sys.Signal_ignore) + (* Flow-control mechanism (only active under Windows). Only one side is allowed to send messages at any given time. @@ -95,7 +99,10 @@ | Unix.Unix_error(Unix.EPIPE, _, _) (* Windows may also return the following errors... *) | Unix.Unix_error(Unix.EINVAL, _, _) - | Unix.Unix_error(Unix.EUNKNOWNERR (-64), _, _) -> + | Unix.Unix_error(Unix.EUNKNOWNERR (-64), _, _) + (* ERROR_NETNAME_DELETED *) + | Unix.Unix_error(Unix.EUNKNOWNERR (-233), _, _) -> + (* ERROR_PIPE_NOT_CONNECTED *) (* Client has closed its end of the connection *) lostConnection () | _ -> @@ -113,14 +120,16 @@ type ioBuffer = { channel : Lwt_unix.file_descr; buffer : string; - mutable length : int } + mutable length : int; + mutable opened : bool } let bufferSize = 16384 (* No point in making this larger, as the Ocaml Unix library uses a buffer of this size *) let makeBuffer ch = - { channel = ch; buffer = String.create bufferSize; length = 0 } + { channel = ch; buffer = String.create bufferSize; + length = 0; opened = true } (****) @@ -176,7 +185,11 @@ let rec sendOutput conn = catchIoErrors (fun () -> - Lwt_unix.write conn.channel conn.buffer 0 conn.length >>= fun len -> + begin if conn.opened then + Lwt_unix.write conn.channel conn.buffer 0 conn.length + else + Lwt.return conn.length + end >>= fun len -> debugV (fun() -> Util.msg "dump: %s\n" (String.escaped (String.sub conn.buffer 0 len))); @@ -236,10 +249,6 @@ let rec performOutputRec q (kind, action, res) = action () >>= fun () -> - if kind = Last then begin - assert (q.canWrite); - if q.flowControl then q.canWrite <- false - end; Lwt.wakeup res (); popOutputQueues q @@ -321,6 +330,7 @@ (fun () -> if q.flowControl then begin debugE (fun() -> Util.msg "Sending write token\n"); + q.canWrite <- false; fillBuffer buf [encodeInt 0] >>= fun () -> flushBuffer buf end else @@ -975,8 +985,6 @@ (****) let initConnection in_ch out_ch = - if not windowsHack then - ignore(Sys.set_signal Sys.sigpipe Sys.Signal_ignore); let conn = setupIO false in_ch out_ch in checkHeader conn (Bytearray.create 1) 0 (String.length connectionHeader) >>= (fun () -> @@ -992,6 +1000,13 @@ None -> findFirst f r | Some _ as v -> Lwt.return v +let printAddr host addr = + match addr with + Unix.ADDR_UNIX s -> + assert false + | Unix.ADDR_INET (s, p) -> + Format.sprintf "%s[%s]:%d" host (Unix.string_of_inet_addr s) p + let buildSocket host port kind = let attemptCreation ai = Lwt.catch @@ -1033,8 +1048,9 @@ let msg = match kind with `Connect -> - Printf.sprintf "Can't connect to server (%s:%s): %s" - host port (Unix.error_message error) + Printf.sprintf "Can't connect to server %s: %s\n" + (printAddr host ai.Unix.ai_addr) + (Unix.error_message error) | `Bind -> Printf.sprintf "Can't bind socket to port %s at address [%s]: %s\n" @@ -1066,7 +1082,7 @@ match kind with `Connect -> Printf.sprintf - "Can't find the IP address of the server (%s:%s)" host port + "Failed to connect to the server on host %s:%s" host port | `Bind -> if host = "" then Printf.sprintf "Can't bind socket to port %s" port @@ -1401,7 +1417,17 @@ match e with Util.Fatal "Lost connection with the server" -> debug (fun () -> Util.msg "Connection closed by the client\n"); - Lwt.return () + (* We prevents new writes and wait for any current write to + terminate. As we don't have a good way to wait for the + writer to terminate, we just yield a bit. *) + let rec wait n = + if n = 0 then Lwt.return () else begin + Lwt_unix.yield () >>= fun () -> + wait (n - 1) + end + in + conn.outputBuffer.opened <- false; + wait 10 | _ -> Lwt.fail e) @@ -1426,23 +1452,27 @@ let waitOnPort hostOpt port = Util.convertUnixErrorsToFatal "waiting on port" (fun () -> - Lwt_unix.run - (let host = match hostOpt with - Some host -> host - | None -> "" in - buildSocket host port `Bind >>= fun listening -> - Util.msg "server started\n"; - let rec handleClients () = - (* Accept a connection *) - Lwt_unix.accept listening >>= fun (connected,_) -> - Lwt_unix.setsockopt connected Unix.SO_KEEPALIVE true; - commandLoop connected connected >>= fun () -> - (* The client has closed its end of the connection *) - begin try Lwt_unix.close connected with Unix.Unix_error _ -> () end; - if Prefs.read killServer then Lwt.return () else - handleClients () - in - handleClients ())) + let host = + match hostOpt with + Some host -> host + | None -> "" + in + let listening = Lwt_unix.run (buildSocket host port `Bind) in + Util.msg "server started\n"; + let rec handleClients () = + let (connected, _) = + Lwt_unix.run (Lwt_unix.accept listening) + in + Lwt_unix.setsockopt connected Unix.SO_KEEPALIVE true; + begin try + (* Accept a connection *) + Lwt_unix.run (commandLoop connected connected) + with Util.Fatal "Lost connection with the server" -> () end; + (* The client has closed its end of the connection *) + begin try Lwt_unix.close connected with Unix.Unix_error _ -> () end; + if not (Prefs.read killServer) then handleClients () + in + handleClients ()) let beAServer () = begin try Modified: trunk/src/uicommon.ml =================================================================== --- trunk/src/uicommon.ml 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/uicommon.ml 2010-01-19 09:18:15 UTC (rev 404) @@ -350,13 +350,13 @@ if dangerousPaths = [Path.empty] then "The root of one of the replicas has been completely emptied.\n\ Unison may delete everything in the other replica. (Set the \n\ - 'confirmbigdel' preference to false to disable this check.)" + 'confirmbigdel' preference to false to disable this check.)\n" else Printf.sprintf "The following paths have been completely emptied in one replica:\n \ %s\n\ Unison may delete everything below these paths in the other replica.\n - (Set the 'confirmbigdel' preference to false to disable this check.)" + (Set the 'confirmbigdel' preference to false to disable this check.)\n" (String.concat "\n " (Safelist.map (fun p -> "'" ^ (Path.toString p) ^ "'") dangerousPaths)) Modified: trunk/src/uigtk2.ml =================================================================== --- trunk/src/uigtk2.ml 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/uigtk2.ml 2010-01-19 09:18:15 UTC (rev 404) @@ -3672,6 +3672,8 @@ format skippedCount "skipped item" "" "s" in let message = + (if failureCount = 0 then "The synchronization was successful.\n\n" + else "") ^ "The replicas are not fully synchronized.\nThere was" ^ begin match infos with [] -> assert false Modified: trunk/src/uimacnew/Bridge.m =================================================================== --- trunk/src/uimacnew/Bridge.m 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/uimacnew/Bridge.m 2010-01-19 09:18:15 UTC (rev 404) @@ -38,7 +38,7 @@ pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t init_cond = PTHREAD_COND_INITIALIZER; -static BOOL doneInit = false; +static BOOL doneInit = NO; pthread_mutex_t global_call_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t global_call_cond = PTHREAD_COND_INITIALIZER; Modified: trunk/src/uimacnew/MyController.m =================================================================== --- trunk/src/uimacnew/MyController.m 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/uimacnew/MyController.m 2010-01-19 09:18:15 UTC (rev 404) @@ -74,7 +74,7 @@ [[tableView tableColumnWithIdentifier:@"path"] setDataCell:[[[ImageAndTextCell alloc] init] autorelease]]; // Custom progress cell - ProgressCell *progressCell = [[ProgressCell alloc] init]; + ProgressCell *progressCell = [[[ProgressCell alloc] init] autorelease]; [[tableView tableColumnWithIdentifier:@"percentTransferred"] setDataCell:progressCell]; /* Set up the version string in the about box. We use a custom Modified: trunk/src/uimacnew09/Bridge.m =================================================================== --- trunk/src/uimacnew09/Bridge.m 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/uimacnew09/Bridge.m 2010-01-19 09:18:15 UTC (rev 404) @@ -38,7 +38,7 @@ pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t init_cond = PTHREAD_COND_INITIALIZER; -static BOOL doneInit = false; +static BOOL doneInit = NO; pthread_mutex_t global_call_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t global_call_cond = PTHREAD_COND_INITIALIZER; Modified: trunk/src/uimacnew09/MyController.m =================================================================== --- trunk/src/uimacnew09/MyController.m 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/uimacnew09/MyController.m 2010-01-19 09:18:15 UTC (rev 404) @@ -93,7 +93,7 @@ [[tableView tableColumnWithIdentifier:@"path"] setDataCell:[[[ImageAndTextCell alloc] init] autorelease]]; // Custom progress cell - ProgressCell *progressCell = [[ProgressCell alloc] init]; + ProgressCell *progressCell = [[[ProgressCell alloc] init] autorelease]; [[tableView tableColumnWithIdentifier:@"percentTransferred"] setDataCell:progressCell]; /* Set up the version string in the about box. We use a custom Modified: trunk/src/uitext.ml =================================================================== --- trunk/src/uitext.ml 2010-01-15 23:25:09 UTC (rev 403) +++ trunk/src/uitext.ml 2010-01-19 09:18:15 UTC (rev 404) @@ -814,7 +814,8 @@ Uicommon.uiInit (fun s -> Util.msg "%s\n%s\n" Uicommon.shortUsageMsg s; exit 1) (fun s -> Util.msg "%s" Uicommon.shortUsageMsg; exit 1) - (fun () -> if Prefs.read silent then Prefs.set Trace.terse true; + (fun () -> setWarnPrinter(); + if Prefs.read silent then Prefs.set Trace.terse true; if not (Prefs.read silent) then Util.msg "%s\n" (Uicommon.contactingServerMsg())) (fun () -> Some "default") From schmitta at seas.upenn.edu Wed Jan 20 11:11:29 2010 From: schmitta at seas.upenn.edu (schmitta@seas.upenn.edu) Date: Wed, 20 Jan 2010 11:11:29 -0500 Subject: [Unison-hackers] [unison-svn] r405 - trunk/src/uimacnew/English.lproj/MainMenu.nib Message-ID: <201001201611.o0KGBUFP025905@yaws.seas.upenn.edu> Author: schmitta Date: 2010-01-20 11:11:27 -0500 (Wed, 20 Jan 2010) New Revision: 405 Modified: trunk/src/uimacnew/English.lproj/MainMenu.nib/keyedobjects.nib Log: Made "MyController" object a delegate of "Application" Modified: trunk/src/uimacnew/English.lproj/MainMenu.nib/keyedobjects.nib =================================================================== (Binary files differ) From alan.schmitt at polytechnique.org Wed Jan 20 11:12:18 2010 From: alan.schmitt at polytechnique.org (Alan Schmitt) Date: Wed, 20 Jan 2010 17:12:18 +0100 Subject: [Unison-hackers] Mac GUI: terminating when closing the main window In-Reply-To: <20100116134204.GA5959@pps.jussieu.fr> References: <20100116134204.GA5959@pps.jussieu.fr> Message-ID: <25ec8ca61001200812u39257c58t55095280388d5ab3@mail.gmail.com> On Sat, Jan 16, 2010 at 2:42 PM, Jerome Vouillon wrote: > > With the uimacnew GUI, when one presses the red button on the main > window frame the application is not terminated: ?the window disappears > but the menu remains. > > The code to address this is already there, but the interface file has > not been updated accordingly. ?So, could someone open the file > English.lproj/MainMenu.nib file with Interface Builder and make the > controller object (of class MyController) a delegate of the > Application object. I've done it, but haven't been able to test it yet. (With the new release of ocaml, I should be able to build unison again fairly soon.) Alan From vouillon at seas.upenn.edu Fri Jan 22 04:52:57 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Fri, 22 Jan 2010 04:52:57 -0500 Subject: [Unison-hackers] [unison-svn] r406 - in trunk: doc src src/lwt src/lwt/generic src/lwt/win src/system/win Message-ID: <201001220952.o0M9qwtH019070@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-22 04:52:57 -0500 (Fri, 22 Jan 2010) New Revision: 406 Added: trunk/src/lwt/generic/ trunk/src/lwt/generic/lwt_unix_impl.ml trunk/src/lwt/lwt_unix.ml trunk/src/lwt/lwt_unix_stubs.c trunk/src/lwt/win/ trunk/src/lwt/win/lwt_unix_impl.ml Removed: trunk/src/lwt/lwt_unix.ml Modified: trunk/doc/Makefile trunk/src/.depend trunk/src/Makefile.OCaml trunk/src/RECENTNEWS trunk/src/fingerprint.ml trunk/src/lwt/lwt_unix.mli trunk/src/mkProjectInfo.ml trunk/src/osx.ml trunk/src/osxsupport.c trunk/src/remote.ml trunk/src/system/win/ Log: * Fixed bug which made Unison ignore finder information and resource fork when compiled to 64bit on Mac OSX. * Use asynchronous I/O under Windows Modified: trunk/doc/Makefile =================================================================== --- trunk/doc/Makefile 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/doc/Makefile 2010-01-22 09:52:57 UTC (rev 406) @@ -34,7 +34,7 @@ @echo HEVEAPATH = $(HEVEAPATH) @(if [ ! -f prefs.tmp ]; then $(MAKE) prefs.tmp; fi) @(if [ ! -f prefsdocs.tmp ]; then $(MAKE) prefsdocs.tmp; fi) -ifdef HEVEA +ifeq ($(HEVEA),true) printf '$(TEXDIRECTIVES)\\textversiontrue\\draftfalse' \ > texdirectives.tex latex unison-manual.tex @@ -90,4 +90,4 @@ ../src/mkProjectInfo > $@ ../src/mkProjectInfo: ../src/mkProjectInfo.ml - ocamlc -o $@ $^ + ocamlc str.cma -o $@ $^ Modified: trunk/src/.depend =================================================================== --- trunk/src/.depend 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/.depend 2010-01-22 09:52:57 UTC (rev 406) @@ -7,7 +7,7 @@ fileinfo.cmi copy.cmi: uutil.cmi props.cmi path.cmi osx.cmi os.cmi lwt/lwt.cmi fspath.cmi \ fileinfo.cmi common.cmi -external.cmi: +external.cmi: lwt/lwt.cmi fileinfo.cmi: system.cmi props.cmi path.cmi osx.cmi fspath.cmi files.cmi: uutil.cmi system.cmi props.cmi path.cmi lwt/lwt_util.cmi \ lwt/lwt.cmi common.cmi @@ -154,9 +154,9 @@ pred.cmx: ubase/util.cmx ubase/safelist.cmx ubase/rx.cmx ubase/prefs.cmx \ case.cmx pred.cmi props.cmo: uutil.cmi ubase/util.cmi ubase/prefs.cmi path.cmi osx.cmi \ - fspath.cmi fs.cmi external.cmi props.cmi + lwt/lwt_unix.cmi fspath.cmi fs.cmi external.cmi props.cmi props.cmx: uutil.cmx ubase/util.cmx ubase/prefs.cmx path.cmx osx.cmx \ - fspath.cmx fs.cmx external.cmx props.cmi + lwt/lwt_unix.cmx fspath.cmx fs.cmx external.cmx props.cmi recon.cmo: ubase/util.cmi update.cmi tree.cmi ubase/trace.cmi sortri.cmi \ ubase/safelist.cmi props.cmi ubase/prefs.cmi pred.cmi path.cmi name.cmi \ globals.cmi fileinfo.cmi common.cmi recon.cmi @@ -295,8 +295,8 @@ fspath.cmx xferhint.cmi lwt/lwt.cmo: lwt/lwt.cmi lwt/lwt.cmx: lwt/lwt.cmi -lwt/lwt_unix.cmo: lwt/pqueue.cmi lwt/lwt.cmi lwt/lwt_unix.cmi -lwt/lwt_unix.cmx: lwt/pqueue.cmx lwt/lwt.cmx lwt/lwt_unix.cmi +lwt/lwt_unix.cmo: lwt/lwt_unix.cmi +lwt/lwt_unix.cmx: lwt/lwt_unix.cmi lwt/lwt_util.cmo: lwt/lwt.cmi lwt/lwt_util.cmi lwt/lwt_util.cmx: lwt/lwt.cmx lwt/lwt_util.cmi lwt/pqueue.cmo: lwt/pqueue.cmi @@ -350,6 +350,10 @@ lwt/example/editor.cmx: lwt/lwt_unix.cmx lwt/example/relay.cmo: lwt/lwt_unix.cmi lwt/lwt.cmi lwt/example/relay.cmx: lwt/lwt_unix.cmx lwt/lwt.cmx +lwt/generic/lwt_unix_impl.cmo: lwt/pqueue.cmi lwt/lwt.cmi +lwt/generic/lwt_unix_impl.cmx: lwt/pqueue.cmx lwt/lwt.cmx +lwt/win/lwt_unix_impl.cmo: lwt/pqueue.cmi lwt/lwt.cmi +lwt/win/lwt_unix_impl.cmx: lwt/pqueue.cmx lwt/lwt.cmx system/generic/system_impl.cmo: system/system_generic.cmo system/generic/system_impl.cmx: system/system_generic.cmx system/win/system_impl.cmo: system/system_win.cmo system/system_generic.cmo Modified: trunk/src/Makefile.OCaml =================================================================== --- trunk/src/Makefile.OCaml 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/Makefile.OCaml 2010-01-22 09:52:57 UTC (rev 406) @@ -5,6 +5,11 @@ #################################################################### ### Try to automatically guess OS +ifeq (${OSCOMP},cross) # Cross-compilation under Linux + OSARCH=win32gnuc + PATH := /usr/i586-mingw32msvc/bin:$(PATH) +endif + ifeq (${OSCOMP},cygwingnuc) # Define this if compiling with Cygwin GNU C OSARCH=win32gnuc ETAGS=/bin/etags @@ -86,7 +91,7 @@ INCLFLAGS=-I lwt -I ubase -I system CAMLFLAGS+=$(INCLFLAGS) -CAMLFLAGS+=-I system/$(SYSTEM) +CAMLFLAGS+=-I system/$(SYSTEM) -I lwt/$(SYSTEM) ifeq ($(OSARCH),win32) # Win32 system @@ -100,7 +105,7 @@ # issue." # CLIBS+=-cclib win32rc/unison.res # STATICLIBS+=-cclib win32rc/unison.res - COBJS+=system/system_win_stubs$(OBJ_EXT) + COBJS+=system/system_win_stubs$(OBJ_EXT) lwt/lwt_unix_stubs$(OBJ_EXT) WINOBJS=system/system_win.cmo SYSTEM=win CLIBS+=-cclib "-link win32rc/unison.res" @@ -113,7 +118,7 @@ ifeq ($(OSARCH),win32gnuc) CWD=. EXEC_EXT=.exe - COBJS+=system/system_win_stubs$(OBJ_EXT) + COBJS+=system/system_win_stubs$(OBJ_EXT) lwt/lwt_unix_stubs$(OBJ_EXT) WINOBJS=system/system_win.cmo SYSTEM=win CLIBS+=-cclib win32rc/unison.res.lib @@ -206,7 +211,8 @@ ubase/uprintf.cmo ubase/util.cmo ubase/uarg.cmo \ ubase/prefs.cmo ubase/trace.cmo ubase/proplist.cmo \ \ - lwt/pqueue.cmo lwt/lwt.cmo lwt/lwt_util.cmo lwt/lwt_unix.cmo \ + lwt/pqueue.cmo lwt/lwt.cmo lwt/lwt_util.cmo \ + lwt/$(SYSTEM)/lwt_unix_impl.cmo lwt/lwt_unix.cmo \ \ case.cmo pred.cmo uutil.cmo \ fileutil.cmo name.cmo path.cmo fspath.cmo fs.cmo fingerprint.cmo \ @@ -303,6 +309,8 @@ # Additional dependencied depending on the system system.cmo fspath.cmo fs.cmo: system/$(SYSTEM)/system_impl.cmo system.cmx fspath.cmx fs.cmx: system/$(SYSTEM)/system_impl.cmx +lwt/lwt_unix.cmo: lwt/$(SYSTEM)/lwt_unix_impl.cmo +lwt/lwt_unix.cmx: lwt/$(SYSTEM)/lwt_unix_impl.cmx ifeq ($(OSARCH), OpenBSD) ifeq ($(shell echo type ocamldot | ksh), file) Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/RECENTNEWS 2010-01-22 09:52:57 UTC (rev 406) @@ -1,5 +1,12 @@ CHANGES FROM VERSION 2.39.6 +* Fixed bug which made Unison ignore finder information and resource + fork when compiled to 64bit on Mac OSX. +* Use asynchronous I/O under Windows + +------------------------------- +CHANGES FROM VERSION 2.39.6 + * Made a server waiting on a socket more resilient to unexpected lost connections from the client. * Fixed possible race condition in half-duplex communication mode. Modified: trunk/src/fingerprint.ml =================================================================== --- trunk/src/fingerprint.ml 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/fingerprint.ml 2010-01-22 09:52:57 UTC (rev 406) @@ -84,6 +84,7 @@ if d == dummy then 1234577 else begin + assert (String.length d >= 3); Char.code (String.unsafe_get d 0) + (Char.code (String.unsafe_get d 1) lsl 8) + (Char.code (String.unsafe_get d 2) lsl 16) Property changes on: trunk/src/lwt/generic ___________________________________________________________________ Added: svn:ignore + *.cmx *.cmi *.cmo Copied: trunk/src/lwt/generic/lwt_unix_impl.ml (from rev 402, trunk/src/lwt/lwt_unix.ml) =================================================================== --- trunk/src/lwt/generic/lwt_unix_impl.ml (rev 0) +++ trunk/src/lwt/generic/lwt_unix_impl.ml 2010-01-22 09:52:57 UTC (rev 406) @@ -0,0 +1,508 @@ +(* +Non-blocking I/O and select does not (fully) work under Windows. +The libray therefore does not use them under Windows, and will +therefore have the following limitations: +- No read will be performed while there are some threads ready to run + or waiting to write; +- When a read is pending, everything else will be blocked: [sleep] + will not terminate and other reads will not be performed before + this read terminates; +- A write on a socket or a pipe can block the execution of the program + if the data are never consumed at the other end of the connection. + In particular, if both ends use this library and write at the same + time, this could result in a dead-lock. +- [connect] is blocking +*) +let windows_hack = Sys.os_type <> "Unix" +let recent_ocaml = + Scanf.sscanf Sys.ocaml_version "%d.%d" + (fun maj min -> (maj = 3 && min >= 11) || maj > 3) + +module SleepQueue = + Pqueue.Make (struct + type t = float * int * unit Lwt.t + let compare (t, i, _) (t', i', _) = + let c = compare t t' in + if c = 0 then i - i' else c + end) +let sleep_queue = ref SleepQueue.empty + +let event_counter = ref 0 + +let sleep d = + let res = Lwt.wait () in + incr event_counter; + let t = if d <= 0. then 0. else Unix.gettimeofday () +. d in + sleep_queue := + SleepQueue.add (t, !event_counter, res) !sleep_queue; + res + +let yield () = sleep 0. + +let get_time t = + if !t = -1. then t := Unix.gettimeofday (); + !t + +let in_the_past now t = + t = 0. || t <= get_time now + +let rec restart_threads imax now = + match + try Some (SleepQueue.find_min !sleep_queue) with Not_found -> None + with + Some (time, i, thr) when in_the_past now time && i - imax <= 0 -> + sleep_queue := SleepQueue.remove_min !sleep_queue; + Lwt.wakeup thr (); + restart_threads imax now + | _ -> + () + +type file_descr = Unix.file_descr + +let of_unix_file_descr fd = if not windows_hack then Unix.set_nonblock fd; fd + +let inputs = ref [] +let outputs = ref [] +let wait_children = ref [] + +let child_exited = ref false +let _ = + if not windows_hack then + ignore(Sys.signal Sys.sigchld (Sys.Signal_handle (fun _ -> child_exited := true))) + +let bad_fd fd = + try ignore (Unix.LargeFile.fstat fd); false with + Unix.Unix_error (_, _, _) -> + true + +let wrap_syscall queue fd cont syscall = + let res = + try + Some (syscall ()) + with + Exit + | Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK | Unix.EINTR), _, _) -> + (* EINTR because we are catching SIG_CHLD hence the system call + might be interrupted to handle the signal; this lets us restart + the system call eventually. *) + None + | e -> + queue := List.remove_assoc fd !queue; + Lwt.wakeup_exn cont e; + None + in + match res with + Some v -> + queue := List.remove_assoc fd !queue; + Lwt.wakeup cont v + | None -> + () + +let rec run thread = + match Lwt.poll thread with + Some v -> + v + | None -> + let next_event = + try + let (time, _, _) = SleepQueue.find_min !sleep_queue in Some time + with Not_found -> + None + in + let now = ref (-1.) in + let delay = + match next_event with + None -> -1. + | Some 0. -> 0. + | Some time -> max 0. (time -. get_time now) + in + let infds = List.map fst !inputs in + let outfds = List.map fst !outputs in + let (readers, writers, _) = + if windows_hack && not recent_ocaml then + let writers = outfds in + let readers = + if delay = 0. || writers <> [] then [] else infds in + (readers, writers, []) + else if infds = [] && outfds = [] && delay = 0. then + ([], [], []) + else + try + let res = Unix.select infds outfds [] delay in + if delay > 0. && !now <> -1. then now := !now +. delay; + res + with + Unix.Unix_error (Unix.EINTR, _, _) -> + ([], [], []) + | Unix.Unix_error (Unix.EBADF, _, _) -> + (List.filter bad_fd infds, List.filter bad_fd outfds, []) + | Unix.Unix_error (Unix.EPIPE, _, _) + when windows_hack && recent_ocaml -> + (* Workaround for a bug in Ocaml 3.11: select fails with an + EPIPE error when the file descriptor is remotely closed *) + (infds, [], []) + in + restart_threads !event_counter now; + List.iter + (fun fd -> + try + match List.assoc fd !inputs with + `Read (buf, pos, len, res) -> + wrap_syscall inputs fd res + (fun () -> Unix.read fd buf pos len) + | `Accept res -> + wrap_syscall inputs fd res + (fun () -> + let (s, _) as v = Unix.accept fd in + if not windows_hack then Unix.set_nonblock s; + v) + | `Wait res -> + wrap_syscall inputs fd res (fun () -> ()) + with Not_found -> + ()) + readers; + List.iter + (fun fd -> + try + match List.assoc fd !outputs with + `Write (buf, pos, len, res) -> + wrap_syscall outputs fd res + (fun () -> Unix.write fd buf pos len) + | `CheckSocket res -> + wrap_syscall outputs fd res + (fun () -> + try ignore (Unix.getpeername fd) with + Unix.Unix_error (Unix.ENOTCONN, _, _) -> + ignore (Unix.read fd " " 0 1)) + | `Wait res -> + wrap_syscall inputs fd res (fun () -> ()) + with Not_found -> + ()) + writers; + if !child_exited then begin + child_exited := false; + List.iter + (fun (id, (res, flags, pid)) -> + wrap_syscall wait_children id res + (fun () -> + let (pid', _) as v = Unix.waitpid flags pid in + if pid' = 0 then raise Exit; + v)) + !wait_children + end; + run thread + +(****) + +let wait_read ch = + let res = Lwt.wait () in + inputs := (ch, `Wait res) :: !inputs; + res + +let wait_write ch = + let res = Lwt.wait () in + outputs := (ch, `Wait res) :: !outputs; + res + +let read ch buf pos len = + try + if windows_hack then raise (Unix.Unix_error (Unix.EAGAIN, "", "")); + Lwt.return (Unix.read ch buf pos len) + with + Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) -> + let res = Lwt.wait () in + inputs := (ch, `Read (buf, pos, len, res)) :: !inputs; + res + | e -> + Lwt.fail e + +let write ch buf pos len = + try + if windows_hack && recent_ocaml then + raise (Unix.Unix_error (Unix.EAGAIN, "", "")); + Lwt.return (Unix.write ch buf pos len) + with + Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) -> + let res = Lwt.wait () in + outputs := (ch, `Write (buf, pos, len, res)) :: !outputs; + res + | e -> + Lwt.fail e + +(* +let pipe () = + let (in_fd, out_fd) as fd_pair = Unix.pipe() in + if not windows_hack then begin + Unix.set_nonblock in_fd; + Unix.set_nonblock out_fd + end; + fd_pair +*) + +let pipe_in () = + let (in_fd, out_fd) as fd_pair = Unix.pipe() in + if not windows_hack then + Unix.set_nonblock in_fd; + fd_pair + +let pipe_out () = + let (in_fd, out_fd) as fd_pair = Unix.pipe() in + if not windows_hack then + Unix.set_nonblock out_fd; + fd_pair + +let socket dom typ proto = + let s = Unix.socket dom typ proto in + if not windows_hack then Unix.set_nonblock s; + s + +let socketpair dom typ proto = + let (s1, s2) as spair = Unix.socketpair dom typ proto in + if not windows_hack then begin + Unix.set_nonblock s1; Unix.set_nonblock s2 + end; + Lwt.return spair + +let bind = Unix.bind +let setsockopt = Unix.setsockopt +let listen = Unix.listen +let close = Unix.close +let set_close_on_exec = Unix.set_close_on_exec + +let accept ch = + let res = Lwt.wait () in + inputs := (ch, `Accept res) :: !inputs; + res + +let check_socket ch = + let res = Lwt.wait () in + outputs := (ch, `CheckSocket res) :: !outputs; + res + +let connect s addr = + try + Unix.connect s addr; + Lwt.return () + with + Unix.Unix_error + ((Unix.EINPROGRESS | Unix.EWOULDBLOCK | Unix.EAGAIN), _, _) -> + check_socket s + | e -> + Lwt.fail e + +let ids = ref 0 +let new_id () = incr ids; !ids + +let _waitpid flags pid = + try + Lwt.return (Unix.waitpid flags pid) + with e -> + Lwt.fail e + +let waitpid flags pid = + if List.mem Unix.WNOHANG flags || windows_hack then + _waitpid flags pid + else + let flags = Unix.WNOHANG :: flags in + Lwt.bind (_waitpid flags pid) (fun ((pid', _) as res) -> + if pid' <> 0 then + Lwt.return res + else + let res = Lwt.wait () in + wait_children := (new_id (), (res, flags, pid)) :: !wait_children; + res) + +let wait () = waitpid [] (-1) + +let system cmd = + match Unix.fork () with + 0 -> Unix.execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |] + | id -> Lwt.bind (waitpid [] id) (fun (pid, status) -> Lwt.return status) + +(****) + +type lwt_in_channel = in_channel +type lwt_out_channel = out_channel + +let intern_in_channel ch = + Unix.set_nonblock (Unix.descr_of_in_channel ch); ch +let intern_out_channel ch = + Unix.set_nonblock (Unix.descr_of_out_channel ch); ch + + +let wait_inchan ic = wait_read (Unix.descr_of_in_channel ic) +let wait_outchan oc = wait_write (Unix.descr_of_out_channel oc) + +let rec input_char ic = + try + Lwt.return (Pervasives.input_char ic) + with + Sys_blocked_io -> + Lwt.bind (wait_inchan ic) (fun () -> input_char ic) + | e -> + Lwt.fail e + +let rec input ic s ofs len = + try + Lwt.return (Pervasives.input ic s ofs len) + with + Sys_blocked_io -> + Lwt.bind (wait_inchan ic) (fun () -> input ic s ofs len) + | e -> + Lwt.fail e + +let rec unsafe_really_input ic s ofs len = + if len <= 0 then + Lwt.return () + else begin + Lwt.bind (input ic s ofs len) (fun r -> + if r = 0 + then Lwt.fail End_of_file + else unsafe_really_input ic s (ofs+r) (len-r)) + end + +let really_input ic s ofs len = + if ofs < 0 || len < 0 || ofs > String.length s - len + then Lwt.fail (Invalid_argument "really_input") + else unsafe_really_input ic s ofs len + +let input_line ic = + let buf = ref (String.create 128) in + let pos = ref 0 in + let rec loop () = + if !pos = String.length !buf then begin + let newbuf = String.create (2 * !pos) in + String.blit !buf 0 newbuf 0 !pos; + buf := newbuf + end; + Lwt.bind (input_char ic) (fun c -> + if c = '\n' then + Lwt.return () + else begin + !buf.[!pos] <- c; + incr pos; + loop () + end) + in + Lwt.bind + (Lwt.catch loop + (fun e -> + match e with + End_of_file when !pos <> 0 -> + Lwt.return () + | _ -> + Lwt.fail e)) + (fun () -> + let res = String.create !pos in + String.blit !buf 0 res 0 !pos; + Lwt.return res) + +(****) + +type popen_process = + Process of in_channel * out_channel + | Process_in of in_channel + | Process_out of out_channel + | Process_full of in_channel * out_channel * in_channel + +let popen_processes = (Hashtbl.create 7 : (popen_process, int) Hashtbl.t) + +let open_proc cmd proc input output toclose = + match Unix.fork () with + 0 -> if input <> Unix.stdin then begin + Unix.dup2 input Unix.stdin; + Unix.close input + end; + if output <> Unix.stdout then begin + Unix.dup2 output Unix.stdout; + Unix.close output + end; + List.iter Unix.close toclose; + Unix.execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |] + | id -> Hashtbl.add popen_processes proc id + +let open_process_in cmd = + let (in_read, in_write) = pipe_in () in + let inchan = Unix.in_channel_of_descr in_read in + open_proc cmd (Process_in inchan) Unix.stdin in_write [in_read]; + Unix.close in_write; + Lwt.return inchan + +let open_process_out cmd = + let (out_read, out_write) = pipe_out () in + let outchan = Unix.out_channel_of_descr out_write in + open_proc cmd (Process_out outchan) out_read Unix.stdout [out_write]; + Unix.close out_read; + Lwt.return outchan + +let open_process cmd = + let (in_read, in_write) = pipe_in () in + let (out_read, out_write) = pipe_out () in + let inchan = Unix.in_channel_of_descr in_read in + let outchan = Unix.out_channel_of_descr out_write in + open_proc cmd (Process(inchan, outchan)) out_read in_write + [in_read; out_write]; + Unix.close out_read; + Unix.close in_write; + Lwt.return (inchan, outchan) + +(* FIX: Subprocesses that use /dev/tty to print things on the terminal + will NOT have this output captured and returned to the caller of this + function. There's an argument that this is correct, but if we are + running from a GUI the user may not be looking at any terminal and it + will appear that the process is just hanging. This can be fixed, in + principle, by writing a little C code that opens /dev/tty and then uses + the TIOCNOTTY ioctl control to detach the terminal. *) + +let open_proc_full cmd env proc input output error toclose = + match Unix.fork () with + 0 -> Unix.dup2 input Unix.stdin; Unix.close input; + Unix.dup2 output Unix.stdout; Unix.close output; + Unix.dup2 error Unix.stderr; Unix.close error; + List.iter Unix.close toclose; + Unix.execve "/bin/sh" [| "/bin/sh"; "-c"; cmd |] env + | id -> Hashtbl.add popen_processes proc id + +let open_process_full cmd env = + let (in_read, in_write) = pipe_in () in + let (out_read, out_write) = pipe_out () in + let (err_read, err_write) = pipe_in () in + let inchan = Unix.in_channel_of_descr in_read in + let outchan = Unix.out_channel_of_descr out_write in + let errchan = Unix.in_channel_of_descr err_read in + open_proc_full cmd env (Process_full(inchan, outchan, errchan)) + out_read in_write err_write [in_write; out_read; err_read]; + Unix.close out_read; + Unix.close in_write; + Unix.close err_write; + Lwt.return (inchan, outchan, errchan) + +let find_proc_id fun_name proc = + try + let pid = Hashtbl.find popen_processes proc in + Hashtbl.remove popen_processes proc; + pid + with Not_found -> + raise (Unix.Unix_error (Unix.EBADF, fun_name, "")) + +let close_process_in inchan = + let pid = find_proc_id "close_process_in" (Process_in inchan) in + close_in inchan; + Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) + +let close_process_out outchan = + let pid = find_proc_id "close_process_out" (Process_out outchan) in + close_out outchan; + Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) + +let close_process (inchan, outchan) = + let pid = find_proc_id "close_process" (Process(inchan, outchan)) in + close_in inchan; close_out outchan; + Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) + +let close_process_full (outchan, inchan, errchan) = + let pid = + find_proc_id "close_process_full" + (Process_full(outchan, inchan, errchan)) in + close_out inchan; close_in outchan; close_in errchan; + Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) Deleted: trunk/src/lwt/lwt_unix.ml =================================================================== --- trunk/src/lwt/lwt_unix.ml 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/lwt/lwt_unix.ml 2010-01-22 09:52:57 UTC (rev 406) @@ -1,508 +0,0 @@ -(* -Non-blocking I/O and select does not (fully) work under Windows. -The libray therefore does not use them under Windows, and will -therefore have the following limitations: -- No read will be performed while there are some threads ready to run - or waiting to write; -- When a read is pending, everything else will be blocked: [sleep] - will not terminate and other reads will not be performed before - this read terminates; -- A write on a socket or a pipe can block the execution of the program - if the data are never consumed at the other end of the connection. - In particular, if both ends use this library and write at the same - time, this could result in a dead-lock. -- [connect] is blocking -*) -let windows_hack = Sys.os_type <> "Unix" -let recent_ocaml = - Scanf.sscanf Sys.ocaml_version "%d.%d" - (fun maj min -> (maj = 3 && min >= 11) || maj > 3) - -module SleepQueue = - Pqueue.Make (struct - type t = float * int * unit Lwt.t - let compare (t, i, _) (t', i', _) = - let c = compare t t' in - if c = 0 then i - i' else c - end) -let sleep_queue = ref SleepQueue.empty - -let event_counter = ref 0 - -let sleep d = - let res = Lwt.wait () in - incr event_counter; - let t = if d <= 0. then 0. else Unix.gettimeofday () +. d in - sleep_queue := - SleepQueue.add (t, !event_counter, res) !sleep_queue; - res - -let yield () = sleep 0. - -let get_time t = - if !t = -1. then t := Unix.gettimeofday (); - !t - -let in_the_past now t = - t = 0. || t <= get_time now - -let rec restart_threads imax now = - match - try Some (SleepQueue.find_min !sleep_queue) with Not_found -> None - with - Some (time, i, thr) when in_the_past now time && i - imax <= 0 -> - sleep_queue := SleepQueue.remove_min !sleep_queue; - Lwt.wakeup thr (); - restart_threads imax now - | _ -> - () - -type file_descr = Unix.file_descr - -let of_unix_file_descr fd = if not windows_hack then Unix.set_nonblock fd; fd - -let inputs = ref [] -let outputs = ref [] -let wait_children = ref [] - -let child_exited = ref false -let _ = - if not windows_hack then - ignore(Sys.signal Sys.sigchld (Sys.Signal_handle (fun _ -> child_exited := true))) - -let bad_fd fd = - try ignore (Unix.LargeFile.fstat fd); false with - Unix.Unix_error (_, _, _) -> - true - -let wrap_syscall queue fd cont syscall = - let res = - try - Some (syscall ()) - with - Exit - | Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK | Unix.EINTR), _, _) -> - (* EINTR because we are catching SIG_CHLD hence the system call - might be interrupted to handle the signal; this lets us restart - the system call eventually. *) - None - | e -> - queue := List.remove_assoc fd !queue; - Lwt.wakeup_exn cont e; - None - in - match res with - Some v -> - queue := List.remove_assoc fd !queue; - Lwt.wakeup cont v - | None -> - () - -let rec run thread = - match Lwt.poll thread with - Some v -> - v - | None -> - let next_event = - try - let (time, _, _) = SleepQueue.find_min !sleep_queue in Some time - with Not_found -> - None - in - let now = ref (-1.) in - let delay = - match next_event with - None -> -1. - | Some 0. -> 0. - | Some time -> max 0. (time -. get_time now) - in - let infds = List.map fst !inputs in - let outfds = List.map fst !outputs in - let (readers, writers, _) = - if windows_hack && not recent_ocaml then - let writers = outfds in - let readers = - if delay = 0. || writers <> [] then [] else infds in - (readers, writers, []) - else if infds = [] && outfds = [] && delay = 0. then - ([], [], []) - else - try - let res = Unix.select infds outfds [] delay in - if delay > 0. && !now <> -1. then now := !now +. delay; - res - with - Unix.Unix_error (Unix.EINTR, _, _) -> - ([], [], []) - | Unix.Unix_error (Unix.EBADF, _, _) -> - (List.filter bad_fd infds, List.filter bad_fd outfds, []) - | Unix.Unix_error (Unix.EPIPE, _, _) - when windows_hack && recent_ocaml -> - (* Workaround for a bug in Ocaml 3.11: select fails with an - EPIPE error when the file descriptor is remotely closed *) - (infds, [], []) - in - restart_threads !event_counter now; - List.iter - (fun fd -> - try - match List.assoc fd !inputs with - `Read (buf, pos, len, res) -> - wrap_syscall inputs fd res - (fun () -> Unix.read fd buf pos len) - | `Accept res -> - wrap_syscall inputs fd res - (fun () -> - let (s, _) as v = Unix.accept fd in - if not windows_hack then Unix.set_nonblock s; - v) - | `Wait res -> - wrap_syscall inputs fd res (fun () -> ()) - with Not_found -> - ()) - readers; - List.iter - (fun fd -> - try - match List.assoc fd !outputs with - `Write (buf, pos, len, res) -> - wrap_syscall outputs fd res - (fun () -> Unix.write fd buf pos len) - | `CheckSocket res -> - wrap_syscall outputs fd res - (fun () -> - try ignore (Unix.getpeername fd) with - Unix.Unix_error (Unix.ENOTCONN, _, _) -> - ignore (Unix.read fd " " 0 1)) - | `Wait res -> - wrap_syscall inputs fd res (fun () -> ()) - with Not_found -> - ()) - writers; - if !child_exited then begin - child_exited := false; - List.iter - (fun (id, (res, flags, pid)) -> - wrap_syscall wait_children id res - (fun () -> - let (pid', _) as v = Unix.waitpid flags pid in - if pid' = 0 then raise Exit; - v)) - !wait_children - end; - run thread - -(****) - -let wait_read ch = - let res = Lwt.wait () in - inputs := (ch, `Wait res) :: !inputs; - res - -let wait_write ch = - let res = Lwt.wait () in - outputs := (ch, `Wait res) :: !outputs; - res - -let read ch buf pos len = - try - if windows_hack then raise (Unix.Unix_error (Unix.EAGAIN, "", "")); - Lwt.return (Unix.read ch buf pos len) - with - Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) -> - let res = Lwt.wait () in - inputs := (ch, `Read (buf, pos, len, res)) :: !inputs; - res - | e -> - Lwt.fail e - -let write ch buf pos len = - try - if windows_hack && recent_ocaml then - raise (Unix.Unix_error (Unix.EAGAIN, "", "")); - Lwt.return (Unix.write ch buf pos len) - with - Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) -> - let res = Lwt.wait () in - outputs := (ch, `Write (buf, pos, len, res)) :: !outputs; - res - | e -> - Lwt.fail e - -(* -let pipe () = - let (in_fd, out_fd) as fd_pair = Unix.pipe() in - if not windows_hack then begin - Unix.set_nonblock in_fd; - Unix.set_nonblock out_fd - end; - fd_pair -*) - -let pipe_in () = - let (in_fd, out_fd) as fd_pair = Unix.pipe() in - if not windows_hack then - Unix.set_nonblock in_fd; - fd_pair - -let pipe_out () = - let (in_fd, out_fd) as fd_pair = Unix.pipe() in - if not windows_hack then - Unix.set_nonblock out_fd; - fd_pair - -let socket dom typ proto = - let s = Unix.socket dom typ proto in - if not windows_hack then Unix.set_nonblock s; - s - -let socketpair dom typ proto = - let (s1, s2) as spair = Unix.socketpair dom typ proto in - if not windows_hack then begin - Unix.set_nonblock s1; Unix.set_nonblock s2 - end; - Lwt.return spair - -let bind = Unix.bind -let setsockopt = Unix.setsockopt -let listen = Unix.listen -let close = Unix.close -let set_close_on_exec = Unix.set_close_on_exec - -let accept ch = - let res = Lwt.wait () in - inputs := (ch, `Accept res) :: !inputs; - res - -let check_socket ch = - let res = Lwt.wait () in - outputs := (ch, `CheckSocket res) :: !outputs; - res - -let connect s addr = - try - Unix.connect s addr; - Lwt.return () - with - Unix.Unix_error - ((Unix.EINPROGRESS | Unix.EWOULDBLOCK | Unix.EAGAIN), _, _) -> - check_socket s - | e -> - Lwt.fail e - -let ids = ref 0 -let new_id () = incr ids; !ids - -let _waitpid flags pid = - try - Lwt.return (Unix.waitpid flags pid) - with e -> - Lwt.fail e - -let waitpid flags pid = - if List.mem Unix.WNOHANG flags || windows_hack then - _waitpid flags pid - else - let flags = Unix.WNOHANG :: flags in - Lwt.bind (_waitpid flags pid) (fun ((pid', _) as res) -> - if pid' <> 0 then - Lwt.return res - else - let res = Lwt.wait () in - wait_children := (new_id (), (res, flags, pid)) :: !wait_children; - res) - -let wait () = waitpid [] (-1) - -let system cmd = - match Unix.fork () with - 0 -> Unix.execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |] - | id -> Lwt.bind (waitpid [] id) (fun (pid, status) -> Lwt.return status) - -(****) - -type lwt_in_channel = in_channel -type lwt_out_channel = out_channel - -let intern_in_channel ch = - Unix.set_nonblock (Unix.descr_of_in_channel ch); ch -let intern_out_channel ch = - Unix.set_nonblock (Unix.descr_of_out_channel ch); ch - - -let wait_inchan ic = wait_read (Unix.descr_of_in_channel ic) -let wait_outchan oc = wait_write (Unix.descr_of_out_channel oc) - -let rec input_char ic = - try - Lwt.return (Pervasives.input_char ic) - with - Sys_blocked_io -> - Lwt.bind (wait_inchan ic) (fun () -> input_char ic) - | e -> - Lwt.fail e - -let rec input ic s ofs len = - try - Lwt.return (Pervasives.input ic s ofs len) - with - Sys_blocked_io -> - Lwt.bind (wait_inchan ic) (fun () -> input ic s ofs len) - | e -> - Lwt.fail e - -let rec unsafe_really_input ic s ofs len = - if len <= 0 then - Lwt.return () - else begin - Lwt.bind (input ic s ofs len) (fun r -> - if r = 0 - then Lwt.fail End_of_file - else unsafe_really_input ic s (ofs+r) (len-r)) - end - -let really_input ic s ofs len = - if ofs < 0 || len < 0 || ofs > String.length s - len - then Lwt.fail (Invalid_argument "really_input") - else unsafe_really_input ic s ofs len - -let input_line ic = - let buf = ref (String.create 128) in - let pos = ref 0 in - let rec loop () = - if !pos = String.length !buf then begin - let newbuf = String.create (2 * !pos) in - String.blit !buf 0 newbuf 0 !pos; - buf := newbuf - end; - Lwt.bind (input_char ic) (fun c -> - if c = '\n' then - Lwt.return () - else begin - !buf.[!pos] <- c; - incr pos; - loop () - end) - in - Lwt.bind - (Lwt.catch loop - (fun e -> - match e with - End_of_file when !pos <> 0 -> - Lwt.return () - | _ -> - Lwt.fail e)) - (fun () -> - let res = String.create !pos in - String.blit !buf 0 res 0 !pos; - Lwt.return res) - -(****) - -type popen_process = - Process of in_channel * out_channel - | Process_in of in_channel - | Process_out of out_channel - | Process_full of in_channel * out_channel * in_channel - -let popen_processes = (Hashtbl.create 7 : (popen_process, int) Hashtbl.t) - -let open_proc cmd proc input output toclose = - match Unix.fork () with - 0 -> if input <> Unix.stdin then begin - Unix.dup2 input Unix.stdin; - Unix.close input - end; - if output <> Unix.stdout then begin - Unix.dup2 output Unix.stdout; - Unix.close output - end; - List.iter Unix.close toclose; - Unix.execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |] - | id -> Hashtbl.add popen_processes proc id - -let open_process_in cmd = - let (in_read, in_write) = pipe_in () in - let inchan = Unix.in_channel_of_descr in_read in - open_proc cmd (Process_in inchan) Unix.stdin in_write [in_read]; - Unix.close in_write; - Lwt.return inchan - -let open_process_out cmd = - let (out_read, out_write) = pipe_out () in - let outchan = Unix.out_channel_of_descr out_write in - open_proc cmd (Process_out outchan) out_read Unix.stdout [out_write]; - Unix.close out_read; - Lwt.return outchan - -let open_process cmd = - let (in_read, in_write) = pipe_in () in - let (out_read, out_write) = pipe_out () in - let inchan = Unix.in_channel_of_descr in_read in - let outchan = Unix.out_channel_of_descr out_write in - open_proc cmd (Process(inchan, outchan)) out_read in_write - [in_read; out_write]; - Unix.close out_read; - Unix.close in_write; - Lwt.return (inchan, outchan) - -(* FIX: Subprocesses that use /dev/tty to print things on the terminal - will NOT have this output captured and returned to the caller of this - function. There's an argument that this is correct, but if we are - running from a GUI the user may not be looking at any terminal and it - will appear that the process is just hanging. This can be fixed, in - principle, by writing a little C code that opens /dev/tty and then uses - the TIOCNOTTY ioctl control to detach the terminal. *) - -let open_proc_full cmd env proc input output error toclose = - match Unix.fork () with - 0 -> Unix.dup2 input Unix.stdin; Unix.close input; - Unix.dup2 output Unix.stdout; Unix.close output; - Unix.dup2 error Unix.stderr; Unix.close error; - List.iter Unix.close toclose; - Unix.execve "/bin/sh" [| "/bin/sh"; "-c"; cmd |] env - | id -> Hashtbl.add popen_processes proc id - -let open_process_full cmd env = - let (in_read, in_write) = pipe_in () in - let (out_read, out_write) = pipe_out () in - let (err_read, err_write) = pipe_in () in - let inchan = Unix.in_channel_of_descr in_read in - let outchan = Unix.out_channel_of_descr out_write in - let errchan = Unix.in_channel_of_descr err_read in - open_proc_full cmd env (Process_full(inchan, outchan, errchan)) - out_read in_write err_write [in_write; out_read; err_read]; - Unix.close out_read; - Unix.close in_write; - Unix.close err_write; - Lwt.return (inchan, outchan, errchan) - -let find_proc_id fun_name proc = - try - let pid = Hashtbl.find popen_processes proc in - Hashtbl.remove popen_processes proc; - pid - with Not_found -> - raise (Unix.Unix_error (Unix.EBADF, fun_name, "")) - -let close_process_in inchan = - let pid = find_proc_id "close_process_in" (Process_in inchan) in - close_in inchan; - Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) - -let close_process_out outchan = - let pid = find_proc_id "close_process_out" (Process_out outchan) in - close_out outchan; - Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) - -let close_process (inchan, outchan) = - let pid = find_proc_id "close_process" (Process(inchan, outchan)) in - close_in inchan; close_out outchan; - Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) - -let close_process_full (outchan, inchan, errchan) = - let pid = - find_proc_id "close_process_full" - (Process_full(outchan, inchan, errchan)) in - close_out inchan; close_in outchan; close_in errchan; - Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) Added: trunk/src/lwt/lwt_unix.ml =================================================================== --- trunk/src/lwt/lwt_unix.ml (rev 0) +++ trunk/src/lwt/lwt_unix.ml 2010-01-22 09:52:57 UTC (rev 406) @@ -0,0 +1 @@ +include Lwt_unix_impl Modified: trunk/src/lwt/lwt_unix.mli =================================================================== --- trunk/src/lwt/lwt_unix.mli 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/lwt/lwt_unix.mli 2010-01-22 09:52:57 UTC (rev 406) @@ -42,9 +42,6 @@ val pipe_out : unit -> Unix.file_descr * file_descr val socket : Unix.socket_domain -> Unix.socket_type -> int -> file_descr -val socketpair : - Unix.socket_domain -> Unix.socket_type -> int -> - (file_descr * file_descr) Lwt.t val bind : file_descr -> Unix.sockaddr -> unit val setsockopt : file_descr -> Unix.socket_bool_option -> bool -> unit val accept : file_descr -> (file_descr * Unix.sockaddr) Lwt.t @@ -53,32 +50,7 @@ val close : file_descr -> unit val set_close_on_exec : file_descr -> unit -val wait : unit -> (int * Unix.process_status) Lwt.t -val waitpid : Unix.wait_flag list -> int -> (int * Unix.process_status) Lwt.t - -val system : string -> Unix.process_status Lwt.t - type lwt_in_channel -type lwt_out_channel val intern_in_channel : in_channel -> lwt_in_channel -val intern_out_channel : out_channel -> lwt_out_channel - -val input_char : lwt_in_channel -> char Lwt.t val input_line : lwt_in_channel -> string Lwt.t -val input : lwt_in_channel -> string -> int -> int -> int Lwt.t -val really_input : lwt_in_channel -> string -> int -> int -> unit Lwt.t - -val open_process_in: string -> lwt_in_channel Lwt.t -val open_process_out: string -> lwt_out_channel Lwt.t -val open_process: string -> (lwt_in_channel * lwt_out_channel) Lwt.t -val open_process_full: - string -> string array -> - (lwt_in_channel * lwt_out_channel * lwt_in_channel) Lwt.t -val close_process_in: lwt_in_channel -> Unix.process_status Lwt.t -val close_process_out: lwt_out_channel -> Unix.process_status Lwt.t -val close_process: - lwt_in_channel * lwt_out_channel -> Unix.process_status Lwt.t -val close_process_full: - lwt_in_channel * lwt_out_channel * lwt_in_channel -> - Unix.process_status Lwt.t Added: trunk/src/lwt/lwt_unix_stubs.c =================================================================== --- trunk/src/lwt/lwt_unix_stubs.c (rev 0) +++ trunk/src/lwt/lwt_unix_stubs.c 2010-01-22 09:52:57 UTC (rev 406) @@ -0,0 +1,600 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//#define D(x) x +#define D(x) while(0){} + +#define UNIX_BUFFER_SIZE 16384 +#define Nothing ((value) 0) + +typedef struct +{ + OVERLAPPED overlapped; + long id; + long action; +} completionData; + +struct filedescr { + union { + HANDLE handle; + SOCKET socket; + } fd; + enum { KIND_HANDLE, KIND_SOCKET } kind; + int crt_fd; +}; +#define Handle_val(v) (((struct filedescr *) Data_custom_val(v))->fd.handle) +#define Socket_val(v) (((struct filedescr *) Data_custom_val(v))->fd.socket) + +extern void win32_maperr (DWORD errcode); +extern void uerror (char * cmdname, value arg); +extern value unix_error_of_code (int errcode); +extern value win_alloc_handle (HANDLE h); +extern value win_alloc_socket(SOCKET); +extern void get_sockaddr (value mladdr, + struct sockaddr * addr /*out*/, + int * addr_len /*out*/); + +#define Array_data(a, i) (((char *) a->data) + Long_val(i)) + +CAMLprim value ml_blit_string_to_buffer +(value s, value i, value a, value j, value l) +{ + char *src = String_val(s) + Int_val(i); + char *dest = Array_data(Bigarray_val(a), j); + memcpy(dest, src, Long_val(l)); + return Val_unit; +} + +CAMLprim value ml_blit_buffer_to_string +(value a, value i, value s, value j, value l) +{ + char *src = Array_data(Bigarray_val(a), i); + char *dest = String_val(s) + Long_val(j); + memcpy(dest, src, Long_val(l)); + return Val_unit; +} + +/****/ + +#define READ 0 +#define WRITE 1 +#define READ_OVERLAPPED 2 +#define WRITE_OVERLAPPED 3 + +static char * action_name[4] = { + "read", "write", "read(overlapped)", "write(overlapped)" +}; + +static value completionCallback; + +static void invoke_completion_callback +(long id, long len, long errCode, long action) { + CAMLlocal2 (err, name); + value args[4]; + err = Val_long(0); + if (errCode != NO_ERROR) { + len = -1; + win32_maperr (errCode); + err = unix_error_of_code(errno); + } + name = copy_string (action_name[action]); + D(printf("Action %s completed: id %ld -> len %ld / err %d (errCode %ld)\n", + action_name[action], id, len, errno, errCode)); + args[0] = Val_long(id); + args[1] = Val_long(len); + args[2] = err; + args[3] = name; + caml_callbackN(completionCallback, 4, args); + D(printf("Callback performed\n")); +} + +typedef struct { + long id; + long len; + long errCode; + long action; } completionInfo; + +int compN = 0; +int complQueueSize = 0; +completionInfo * complQueue = NULL; + +static void completion (long id, long len, long errCode, long action) { + D(printf("Queueing action %s: id %ld -> len %ld / err %d (errCode %ld)\n", + action_name[action], id, len, errno, errCode)); + if (compN + 1 > complQueueSize) { + int n = complQueueSize * 2 + 1; + D(printf("Resizing queue to %d\n", n)); + completionInfo * queue = + (completionInfo *) GlobalAlloc(GPTR, n * sizeof(completionInfo)); + if (complQueue != NULL) + CopyMemory (queue, complQueue, complQueueSize * sizeof(completionInfo)); + complQueue = queue; + complQueueSize = n; + } + complQueue[compN].id = id; + complQueue[compN].len = len; + complQueue[compN].errCode = errCode; + complQueue[compN].action = action; + compN++; +} + +CAMLprim value get_queue (value unit) { + CAMLparam1 (unit); + int i; + for (i = 0; i < compN; i++) + invoke_completion_callback + (complQueue[i].id, complQueue[i].len, + complQueue[i].errCode, complQueue[i].action); + compN = 0; + CAMLreturn (Val_unit); +} + +/****/ + +static HANDLE main_thread; + +static DWORD CALLBACK helper_thread (void * param) { + D(printf("Helper thread created\n")); + while (1) SleepEx(INFINITE, TRUE); +} + +static VOID CALLBACK exit_thread(ULONG_PTR param) { + D(printf("Helper thread exiting\n")); + ExitThread(0); +} + +static HANDLE get_helper_thread (value threads, int kind) { + HANDLE h = (HANDLE) Field(threads, kind); + + if (h != INVALID_HANDLE_VALUE) return h; + + h = CreateThread (NULL, 0, helper_thread, NULL, 0, NULL); + if (h == NULL) { + win32_maperr (GetLastError ()); + uerror("createHelperThread", Nothing); + } + Field(threads, kind) = (value) h; + return h; +} + +static void kill_thread (HANDLE *h) { + D(printf("Killing thread\n")); + QueueUserAPC(exit_thread, *h, 0); + CloseHandle(*h); + *h = INVALID_HANDLE_VALUE; +} + +CAMLprim value win_kill_threads (value fd) { + CAMLparam1(fd); + if (Field(fd, 1) != Val_long(0)) { + kill_thread((HANDLE *) &Field(Field(fd, 1), READ)); + kill_thread((HANDLE *) &Field(Field(fd, 1), WRITE)); + } + CAMLreturn(Val_unit); +} + +CAMLprim value win_wrap_fd (value fd) { + CAMLparam1(fd); + CAMLlocal2(th, res); + D(printf("Wrapping file descriptor (sync)\n")); + res = caml_alloc_tuple(2); + Store_field(res, 0, fd); + th = caml_alloc(2, Abstract_tag); + Field(th, READ) = (value) INVALID_HANDLE_VALUE; + Field(th, WRITE) = (value) INVALID_HANDLE_VALUE; + Store_field(res, 1, th); + CAMLreturn(res); +} + +/****/ + +typedef struct { + long action; + long id; + HANDLE fd; + char * buffer; + long len; + long error; +} ioInfo; + + +static VOID CALLBACK thread_completion(ULONG_PTR param) { + ioInfo * info = (ioInfo *) param; + completion (info->id, info->len, info->error, info->action); + GlobalFree (info); +} + +static VOID CALLBACK perform_io_on_thread(ULONG_PTR param) { + ioInfo * info = (ioInfo *) param; + DWORD l; + BOOL res; + + D(printf("Starting %s: id %ld, len %ld\n", + action_name[info->action], info->id, info->len)); + + res = + (info->action == READ)? + ReadFile(info->fd, info->buffer,info->len, &l, NULL): + WriteFile(info->fd, info->buffer,info->len, &l, NULL); + if (!res) { + info->len = -1; + info->error = GetLastError (); + } else { + info->len = l; + info->error = NO_ERROR; + } + D(printf("Action %s done: id %ld -> len %ld / err %d (errCode %ld)\n", + action_name[info->action], + info->id, info->len, errno, info->error)); + QueueUserAPC(thread_completion, main_thread, param); +} + +static void thread_io +(long action, long id, value threads, HANDLE h, char * buf, long len) { + struct caml_bigarray *buf_arr = Bigarray_val(buf); + ioInfo * info = GlobalAlloc(GPTR, sizeof(ioInfo)); + if (info == NULL) { + errno = ENOMEM; + uerror(action_name[action], Nothing); + } + + info->action = action; + info->id = id; + info->fd = h; + info->buffer = buf; + info->len = len; + + h = get_helper_thread(threads, action); + QueueUserAPC(perform_io_on_thread, h, (ULONG_PTR) info); +} + +/****/ + +static void CALLBACK overlapped_completion +(DWORD errCode, DWORD len, LPOVERLAPPED overlapped) { + completionData * d = (completionData * )overlapped; + completion (d->id, len, errCode, d->action); + GlobalFree (d); +} + +static void overlapped_action(long action, long id, + HANDLE fd, char *buf, long len) { + BOOL res; + long err; + completionData * d = GlobalAlloc(GPTR, sizeof(completionData)); + if (d == NULL) { + errno = ENOMEM; + uerror(action_name[action], Nothing); + } + d->id = id; + d->action = action; + + D(printf("Starting %s: id %ld, len %ld\n", action_name[action], id, len)); + res = + (action == READ_OVERLAPPED)? + ReadFileEx(fd, buf, len, &(d->overlapped), overlapped_completion): + WriteFileEx(fd, buf, len, &(d->overlapped), overlapped_completion); + + if (!res) { + err = GetLastError (); + if (err != ERROR_IO_PENDING) { + win32_maperr (err); + D(printf("Action %s failed: id %ld -> err %d (errCode %ld)\n", + action_name[action], id, errno, err)); + uerror("ReadFileEx", Nothing); + } + } +} + +CAMLprim value win_wrap_overlapped (value fd) { + CAMLparam1(fd); + CAMLlocal1(res); + D(printf("Wrapping file descriptor (async)\n")); + res = caml_alloc_tuple(2); + Store_field(res, 0, fd); + Store_field(res, 1, Val_long(0)); + CAMLreturn(res); +} + +/****/ + +#define Handle(fd) Handle_val(Field(fd, 0)) + +CAMLprim value win_read +(value fd, value buf, value ofs, value len, value id) { + CAMLparam4(fd, buf, ofs, len); + struct caml_bigarray *buf_arr = Bigarray_val(buf); + + if (Field(fd, 1) == Val_long(0)) + overlapped_action (READ_OVERLAPPED, Long_val(id), Handle(fd), + Array_data (buf_arr, ofs), Long_val(len)); + else + thread_io (READ, Long_val(id), Field(fd, 1), Handle(fd), + Array_data (buf_arr, ofs), Long_val(len)); + CAMLreturn (Val_unit); +} + +CAMLprim value win_write +(value fd, value buf, value ofs, value len, value id) { + CAMLparam4(fd, buf, ofs, len); + struct caml_bigarray *buf_arr = Bigarray_val(buf); + + if (Field(fd, 1) == Val_long(0)) + overlapped_action (WRITE_OVERLAPPED, Long_val(id), Handle(fd), + Array_data (buf_arr, ofs), Long_val(len)); + else + thread_io (WRITE, Long_val(id), Field(fd, 1), Handle(fd), + Array_data (buf_arr, ofs), Long_val(len)); + CAMLreturn (Val_unit); +} + +/* +#ifndef SO_UPDATE_CONNECT_CONTEXT +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif + +static void after_connect (SOCKET s) { + if (!setsockopt(s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0)) { + win32_maperr (GetLastError ()); + uerror("after_connect", Nothing); + } +} +*/ + +static HANDLE events[MAXIMUM_WAIT_OBJECTS]; +//static OVERLAPPED oData[MAXIMUM_WAIT_OBJECTS]; + +CAMLprim value win_register_wait (value socket, value kind, value idx) { + CAMLparam3(socket, kind, idx); + long i = Long_val(idx); + long mask; + + D(printf("Register: i %ld, kind %ld\n", Long_val(i), Long_val(kind))); + events[i] = CreateEvent(NULL, TRUE, FALSE, NULL); + mask = (Long_val(kind) == 0) ? FD_CONNECT : FD_ACCEPT; + if (WSAEventSelect(Socket_val(socket), events[i], mask) == SOCKET_ERROR) { + win32_maperr(WSAGetLastError ()); + uerror("WSAEventSelect", Nothing); + } + + CAMLreturn (Val_unit); +} + +CAMLprim value win_check_connection (value socket, value kind, value idx) { + CAMLparam3 (socket, kind, idx); + WSANETWORKEVENTS evs; + int res, err, i = Long_val(idx); + + D(printf("Check connection... %d\n", i)); + if (WSAEnumNetworkEvents(Socket_val(socket), NULL, &evs)) { + win32_maperr(WSAGetLastError ()); + uerror("WSAEnumNetworkEvents", Nothing); + } + if (WSAEventSelect(Socket_val(socket), NULL, 0) == SOCKET_ERROR) { + win32_maperr(WSAGetLastError ()); + uerror("WSAEventSelect", Nothing); + } + if (!CloseHandle(events[i])) { + win32_maperr(GetLastError ()); + uerror("CloseHandle", Nothing); + } + err = + evs.iErrorCode[(Long_val(kind) == 0) ? FD_CONNECT_BIT : FD_ACCEPT_BIT]; + D(printf("Check connection: %ld, err %d\n", evs.lNetworkEvents, err)); + if (err != 0) { + win32_maperr(err); + uerror("check_connection", Nothing); + } + CAMLreturn (Val_unit); +} + +static HANDLE dummyEvent; + +CAMLprim value init_lwt (value callback) { + CAMLparam1 (callback); + // GUID GuidConnectEx = WSAID_CONNECTEX; + // SOCKET s; + // DWORD l; + int i; + + D(printf("Init...\n")); + register_global_root (&completionCallback); + completionCallback = callback; + + dummyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // Dummy event + + DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), + GetCurrentProcess (), &main_thread, + 0, FALSE, DUPLICATE_SAME_ACCESS); + + /* + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) return Val_unit; + WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, + &GuidConnectEx, sizeof(GuidConnectEx), + &ConnectEx, sizeof(ConnectExPtr), + &l, NULL, NULL); + closesocket(s); + */ + + D(printf("Init done\n")); + CAMLreturn (Val_long (MAXIMUM_WAIT_OBJECTS)); +} + +CAMLprim value win_wait (value timeout, value event_count) { + CAMLparam2(timeout, event_count); + DWORD t, t2; + DWORD res; + long ret, n = Long_val(event_count); + t = Long_val(timeout); + if (t < 0) t = INFINITE; + t2 = (compN > 0) ? 0 : t; + D(printf("Waiting: %ld events, timeout %ldms -> %ldms\n", n, t, t2)); + res = + (n > 0) ? + WaitForMultipleObjectsEx(n, events, FALSE, t, TRUE) : + WaitForMultipleObjectsEx(1, &dummyEvent, FALSE, t, TRUE); + D(printf("Done waiting\n")); + if ((t != t2) && (res == WAIT_TIMEOUT)) res = WAIT_IO_COMPLETION; + switch (res) { + case WAIT_TIMEOUT: + D(printf("Timeout\n")); + ret = -1; + break; + case WAIT_IO_COMPLETION: + D(printf("I/O completion\n")); + ret = -2; + break; + case WAIT_FAILED: + D(printf("Wait failed\n")); + ret = 0; + win32_maperr (GetLastError ()); + uerror("WaitForMultipleObjectsEx", Nothing); + break; + default: + ret = res; + D(printf("Event: %ld\n", res)); + break; + } + get_queue (Val_unit); + CAMLreturn (Val_long(ret)); +} + +static long pipeSerial; + +value win_pipe(long readMode, long writeMode) { + CAMLparam0(); + SECURITY_ATTRIBUTES attr; + HANDLE readh, writeh; + CHAR name[MAX_PATH]; + CAMLlocal3(readfd, writefd, res); + + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = TRUE; + + sprintf(name, "\\\\.\\Pipe\\UnisonAnonPipe.%08lx.%08lx", + GetCurrentProcessId(), pipeSerial++); + + readh = + CreateNamedPipeA + (name, PIPE_ACCESS_INBOUND | readMode, PIPE_TYPE_BYTE | PIPE_WAIT, + 1, UNIX_BUFFER_SIZE, UNIX_BUFFER_SIZE, 0, &attr); + + if (readh == INVALID_HANDLE_VALUE) { + win32_maperr(GetLastError()); + uerror("CreateNamedPipe", Nothing); + return FALSE; + } + + writeh = + CreateFileA + (name, GENERIC_WRITE, 0, &attr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | writeMode, NULL); + + if (writeh == INVALID_HANDLE_VALUE) { + win32_maperr(GetLastError()); + CloseHandle(readh); + uerror("CreateFile", Nothing); + return FALSE; + } + + readfd = win_alloc_handle(readh); + writefd = win_alloc_handle(writeh); + res = alloc_small(2, 0); + Store_field(res, 0, readfd); + Store_field(res, 1, writefd); + CAMLreturn (res); +} + +CAMLprim value win_pipe_in (value unit) { + CAMLparam0(); + CAMLreturn (win_pipe (FILE_FLAG_OVERLAPPED, 0)); +} + +CAMLprim value win_pipe_out (value unit) { + CAMLparam0(); + CAMLreturn (win_pipe (0, FILE_FLAG_OVERLAPPED)); +} + +static int socket_domain_table[] = { + PF_UNIX, PF_INET +}; + +static int socket_type_table[] = { + SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET +}; + +CAMLprim value win_socket (value domain, value type, value proto) { + CAMLparam3(domain, type, proto); + SOCKET s; + + s = WSASocket(socket_domain_table[Int_val(domain)], + socket_type_table[Int_val(type)], + Int_val(proto), + NULL, 0, WSA_FLAG_OVERLAPPED); + D(printf("Created socket %lx\n", (long)s)); + if (s == INVALID_SOCKET) { + win32_maperr(WSAGetLastError ()); + uerror("WSASocket", Nothing); + } + CAMLreturn(win_alloc_socket(s)); +} + +/* +#ifndef WSAID_CONNECTEX +#define WSAID_CONNECTEX \ + {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} +#endif + +typedef BOOL (WINAPI *ConnectExPtr)(SOCKET, const struct sockaddr *, int, PVOID, DWORD, LPDWORD, LPOVERLAPPED); + +static ConnectExPtr ConnectEx = NULL; + +CAMLprim value win_connect (value socket, value address, value id) { + CAMLparam3(socket, address, id); + SOCKET s = Socket_val (socket); + struct sockaddr addr; + int addr_len; + DWORD err; + int i; + + if (ConnectEx == NULL) { + errno = ENOSYS; + uerror("ConnectEx", Nothing); + } + if (eventCount == MAXIMUM_WAIT_OBJECTS) { + errno = EAGAIN; + uerror("ConnectEx", Nothing); + } + i = free_list[eventCount]; + eventCount++; + + ZeroMemory(&(oData[i]), sizeof(OVERLAPPED)); + oData[i].hEvent = events[i]; + ids[i] = Long_val(id); + sockets[i] = s; + + get_sockaddr(address, &addr, &addr_len); + if (!ConnectEx(s, &addr, addr_len, NULL, 0, 0, &(oData[i]))) { + err = WSAGetLastError (); + if (err != ERROR_IO_PENDING) { + win32_maperr(err); + uerror("ConnectEx", Nothing); + } + } else + after_connect(s); + CAMLreturn (Val_unit); +} +*/ Property changes on: trunk/src/lwt/win ___________________________________________________________________ Added: svn:ignore + *.cmx *.cmi *.cmo Added: trunk/src/lwt/win/lwt_unix_impl.ml =================================================================== --- trunk/src/lwt/win/lwt_unix_impl.ml (rev 0) +++ trunk/src/lwt/win/lwt_unix_impl.ml 2010-01-22 09:52:57 UTC (rev 406) @@ -0,0 +1,645 @@ +(* +- should check all events before looping again for avoiding race + conditions... + (we have the first, scan the subsequent ones) +*) + +let no_overlapped_io = false +let d = ref false + +(****) + +type buffer = + (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t + +let buffer_create l = Bigarray.Array1.create Bigarray.char Bigarray.c_layout l + +external unsafe_blit_string_to_buffer : + string -> int -> buffer -> int -> int -> unit = "ml_blit_string_to_buffer" +external unsafe_blit_buffer_to_string : + buffer -> int -> string -> int -> int -> unit = "ml_blit_buffer_to_string" + +let buffer_length = Bigarray.Array1.dim + +let blit_string_to_buffer s i a j l = + if l < 0 || i < 0 || i > String.length s - l + || j < 0 || j > buffer_length a - l + then invalid_arg "Lwt_unix.blit_string_to_buffer" + else unsafe_blit_string_to_buffer s i a j l + +let blit_buffer_to_string a i s j l = + if l < 0 || i < 0 || i > buffer_length a - l + || j < 0 || j > String.length s - l + then invalid_arg "Lwt_unix.blit_buffer_to_string" + else unsafe_blit_buffer_to_string a i s j l + +let buffer_size = 16384 + +let avail_buffers = ref [] + +let acquire_buffer () = + match !avail_buffers with + [] -> buffer_create buffer_size + | b :: r -> avail_buffers := r; b + +let release_buffer b = avail_buffers := b :: !avail_buffers + +(****) + +let last_id = ref 0 +let free_list = ref (Array.init 1 (fun i -> i)) + +let acquire_id () = + let len = Array.length !free_list in + if !last_id = len then begin + let a = Array.init (len * 2) (fun i -> i) in + Array.blit !free_list 0 a 0 len; + free_list := a + end; + let i = !free_list.(!last_id) in + incr last_id; + i + +let release_id i = + decr last_id; + !free_list.(!last_id) <- i + +(****) + +let completionEvents = ref [] + +let actionCompleted id len errno name = + completionEvents := (id, len, errno, name) :: !completionEvents + +external init_lwt : + (int -> int -> Unix.error -> string -> unit) -> int = "init_lwt" + +let max_event_count = init_lwt actionCompleted + +let event_count = ref 0 +let free_list = Array.init max_event_count (fun i -> i) + +let acquire_event nm = + if !event_count = max_event_count then + raise (Unix.Unix_error (Unix.EAGAIN, nm, "")); + let i = free_list.(!event_count) in + incr event_count; + i + +let release_event i = + decr event_count; + free_list.(!event_count) <- i + +(****) + +type helpers +type file_descr = { fd : Unix.file_descr; helpers : helpers } + +external of_unix_file_descr : Unix.file_descr -> file_descr = "win_wrap_fd" + +external win_wrap_async : Unix.file_descr -> file_descr = "win_wrap_overlapped" + +let wrap_async = + if no_overlapped_io then of_unix_file_descr else win_wrap_async + +(****) + +module SleepQueue = + Pqueue.Make (struct + type t = float * int * unit Lwt.t + let compare (t, i, _) (t', i', _) = + let c = compare t t' in + if c = 0 then i - i' else c + end) +let sleep_queue = ref SleepQueue.empty + +let event_counter = ref 0 + +let sleep d = + let res = Lwt.wait () in + incr event_counter; + let t = if d <= 0. then 0. else Unix.gettimeofday () +. d in + sleep_queue := + SleepQueue.add (t, !event_counter, res) !sleep_queue; + res + +let yield () = sleep 0. + +let get_time t = + if !t = -1. then t := Unix.gettimeofday (); + !t + +let in_the_past now t = + t = 0. || t <= get_time now + +let rec restart_threads imax now = + match + try Some (SleepQueue.find_min !sleep_queue) with Not_found -> None + with + Some (time, i, thr) when in_the_past now time && i - imax <= 0 -> + sleep_queue := SleepQueue.remove_min !sleep_queue; +if !d then Format.eprintf "RESTART at ."; + Lwt.wakeup thr (); +if !d then Format.eprintf "RESTART...DONE at ."; + restart_threads imax now + | _ -> + () + +module IntTbl = + Hashtbl.Make + (struct type t = int let equal (x : int) y = x = y let hash x = x end) + +let ioInFlight = IntTbl.create 17 +let connInFlight = IntTbl.create 17 + +let handleCompletionEvent (id, len, errno, name) = +if !d then Format.eprintf "Handling event %d (len %d)@." id len; + let (action, buf, res) = + try IntTbl.find ioInFlight id with Not_found -> assert false + in + begin match action with + `Write -> () + | `Read (s, pos) -> if len > 0 then blit_buffer_to_string buf 0 s pos len + end; + IntTbl.remove ioInFlight id; + release_id id; + release_buffer buf; + if len = -1 then + Lwt.wakeup_exn res (Unix.Unix_error (errno, name, "")) + else + Lwt.wakeup res len + +type kind = CONNECT | ACCEPT + +external win_wait : int -> int -> int = "win_wait" + +external win_register_wait : + Unix.file_descr -> kind -> int -> unit = "win_register_wait" + +external win_check_connection : + Unix.file_descr -> kind -> int -> unit = "win_check_connection" + +let handle_wait_event i ch kind cont action = +if !d then prerr_endline "MMM"; + let res = + try + Some (action ()) + with + Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK | Unix.EINTR), _, _) -> +if !d then prerr_endline "NNN"; + win_register_wait ch.fd kind i; + None + | e -> +if !d then prerr_endline "OOO"; + release_event i; + IntTbl.remove connInFlight i; + Lwt.wakeup_exn cont e; + None + in + match res with + Some v -> +if !d then prerr_endline "PPP"; + release_event i; + IntTbl.remove connInFlight i; + Lwt.wakeup cont v + | None -> + () + +let rec run thread = +if !d then Format.eprintf "Main loop at ."; + match Lwt.poll thread with + Some v -> +if !d then Format.eprintf "DONE!@."; + v + | None -> + let next_event = + try + let (time, _, _) = SleepQueue.find_min !sleep_queue in Some time + with Not_found -> + None + in + let now = ref (-1.) in + let delay = + match next_event with + None -> -1. + | Some 0. -> 0. + | Some time -> max 0. (time -. get_time now) + in +if !d then Format.eprintf "vvv at ."; + let i = + try + win_wait (truncate (ceil (delay *. 1000.))) !event_count + with e -> assert false + in +if !d then Format.eprintf "^^^@."; + if i = -1 then now := !now +. delay; + restart_threads !event_counter now; +if !d then Format.eprintf "threads restarted at ."; + let ev = !completionEvents in + completionEvents := []; + List.iter handleCompletionEvent (List.rev ev); + if i >= 0 then begin + let (kind, ch) = + try IntTbl.find connInFlight i with Not_found -> assert false in + match kind with + `CheckSocket res -> +if !d then prerr_endline "CHECK CONN"; + handle_wait_event i ch CONNECT res + (fun () -> win_check_connection ch.fd CONNECT i) + | `Accept res -> +if !d then prerr_endline "ACCEPT"; + handle_wait_event i ch ACCEPT res + (fun () -> + win_check_connection ch.fd ACCEPT i; + let (v, info) = Unix.accept ch.fd in + (wrap_async v, info)) + end; +(* + let infds = List.map fst !inputs in + let outfds = List.map fst !outputs in + let (readers, writers, _) = + if windows_hack && not recent_ocaml then + let writers = outfds in + let readers = + if delay = 0. || writers <> [] then [] else infds in + (readers, writers, []) + else if infds = [] && outfds = [] && delay = 0. then + ([], [], []) + else + try + let res = Unix.select infds outfds [] delay in + if delay > 0. && !now <> -1. then now := !now +. delay; + res + with + Unix.Unix_error (Unix.EINTR, _, _) -> + ([], [], []) + | Unix.Unix_error (Unix.EBADF, _, _) -> + (List.filter bad_fd infds, List.filter bad_fd outfds, []) + | Unix.Unix_error (Unix.EPIPE, _, _) + when windows_hack && recent_ocaml -> + (* Workaround for a bug in Ocaml 3.11: select fails with an + EPIPE error when the file descriptor is remotely closed *) + (infds, [], []) + in + restart_threads !event_counter now; + List.iter + (fun fd -> + try + match List.assoc fd !inputs with + `Read (buf, pos, len, res) -> + wrap_syscall inputs fd res + (fun () -> Unix.read fd buf pos len) + | `Accept res -> + wrap_syscall inputs fd res + (fun () -> + let (s, i) = Unix.accept fd.fd in + if not windows_hack then Unix.set_nonblock s; + (wrap_async s, i)) + | `Wait res -> + wrap_syscall inputs fd res (fun () -> ()) + with Not_found -> + ()) + readers; + List.iter + (fun fd -> + try + match List.assoc fd !outputs with + `Write (buf, pos, len, res) -> + wrap_syscall outputs fd res + (fun () -> Unix.write fd buf pos len) + | `Wait res -> + wrap_syscall inputs fd res (fun () -> ()) + with Not_found -> + ()) + writers; + if !child_exited then begin + child_exited := false; + List.iter + (fun (id, (res, flags, pid)) -> + wrap_syscall wait_children id res + (fun () -> + let (pid', _) as v = Unix.waitpid flags pid in + if pid' = 0 then raise Exit; + v)) + !wait_children + end; +*) + run thread + +(****) + +let wait_read ch = assert false + +let wait_write ch = assert false + +external start_read : + file_descr -> buffer -> int -> int -> int -> unit = "win_read" +external start_write : + file_descr -> buffer -> int -> int -> int -> unit = "win_write" + +let read ch s pos len = +if !d then Format.eprintf "Start reading at ."; + let id = acquire_id () in + let buf = acquire_buffer () in + let len = if len > buffer_size then buffer_size else len in + let res = Lwt.wait () in + IntTbl.add ioInFlight id (`Read (s, pos), buf, res); + start_read ch buf 0 len id; +if !d then Format.eprintf "Reading started at ."; + res + +let write ch s pos len = +if !d then Format.eprintf "Start writing at ."; + let id = acquire_id () in + let buf = acquire_buffer () in + let len = if len > buffer_size then buffer_size else len in + blit_string_to_buffer s pos buf 0 len; + let res = Lwt.wait () in + IntTbl.add ioInFlight id (`Write, buf, res); + start_write ch buf 0 len id; +if !d then Format.eprintf "Writing started at ."; + res + +external win_pipe_in : + unit -> Unix.file_descr * Unix.file_descr = "win_pipe_in" +external win_pipe_out : + unit -> Unix.file_descr * Unix.file_descr = "win_pipe_out" + +let pipe_in () = + let (i, o) = if no_overlapped_io then Unix.pipe () else win_pipe_in () in + (wrap_async i, o) +let pipe_out () = + let (i, o) = if no_overlapped_io then Unix.pipe () else win_pipe_out () in + (i, wrap_async o) + +external win_socket : + Unix.socket_domain -> Unix.socket_type -> int -> Unix.file_descr = + "win_socket" + +let socket d t p = + let s = if no_overlapped_io then Unix.socket d t p else win_socket d t p in + Unix.set_nonblock s; + wrap_async s + +let bind ch addr = Unix.bind ch.fd addr +let setsockopt ch opt v = Unix.setsockopt ch.fd opt v +let listen ch n = Unix.listen ch.fd n +let set_close_on_exec ch = Unix.set_close_on_exec ch.fd + +external kill_threads : file_descr -> unit = "win_kill_threads" + +let close ch = Unix.close ch.fd; kill_threads ch + +let accept ch = + let res = Lwt.wait () in + let i = acquire_event "accept" in + IntTbl.add connInFlight i (`Accept res, ch); + win_register_wait ch.fd ACCEPT i; + res + +let check_socket ch = + let res = Lwt.wait () in + let i = acquire_event "connect" in + IntTbl.add connInFlight i (`CheckSocket res, ch); + win_register_wait ch.fd CONNECT i; + res + +let connect s addr = + try + Unix.connect s.fd addr; +if !d then prerr_endline "AAA"; + Lwt.return () + with + Unix.Unix_error + ((Unix.EINPROGRESS | Unix.EWOULDBLOCK | Unix.EAGAIN), _, _) -> +if !d then prerr_endline "BBB"; + check_socket s + | e -> +if !d then prerr_endline "CCC"; + Lwt.fail e + +(* +let ids = ref 0 +let new_id () = incr ids; !ids + +let _waitpid flags pid = + try + Lwt.return (Unix.waitpid flags pid) + with e -> + Lwt.fail e + +let waitpid flags pid = + if List.mem Unix.WNOHANG flags || windows_hack then + _waitpid flags pid + else + let flags = Unix.WNOHANG :: flags in + Lwt.bind (_waitpid flags pid) (fun ((pid', _) as res) -> + if pid' <> 0 then + Lwt.return res + else + let res = Lwt.wait () in + wait_children := (new_id (), (res, flags, pid)) :: !wait_children; + res) + +let wait () = waitpid [] (-1) + +let system cmd = + match Unix.fork () with + 0 -> Unix.execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |] + | id -> Lwt.bind (waitpid [] id) (fun (pid, status) -> Lwt.return status) +*) + +(****) +(* +type lwt_in_channel = in_channel +type lwt_out_channel = out_channel + +let intern_in_channel ch = + Unix.set_nonblock (Unix.descr_of_in_channel ch); ch +let intern_out_channel ch = + Unix.set_nonblock (Unix.descr_of_out_channel ch); ch + + +let wait_inchan ic = wait_read (Unix.descr_of_in_channel ic) +let wait_outchan oc = wait_write (Unix.descr_of_out_channel oc) + +let rec input_char ic = + try + Lwt.return (Pervasives.input_char ic) + with + Sys_blocked_io -> + Lwt.bind (wait_inchan ic) (fun () -> input_char ic) + | e -> + Lwt.fail e + +let rec input ic s ofs len = + try + Lwt.return (Pervasives.input ic s ofs len) + with + Sys_blocked_io -> + Lwt.bind (wait_inchan ic) (fun () -> input ic s ofs len) + | e -> + Lwt.fail e + +let rec unsafe_really_input ic s ofs len = + if len <= 0 then + Lwt.return () + else begin + Lwt.bind (input ic s ofs len) (fun r -> + if r = 0 + then Lwt.fail End_of_file + else unsafe_really_input ic s (ofs+r) (len-r)) + end + +let really_input ic s ofs len = + if ofs < 0 || len < 0 || ofs > String.length s - len + then Lwt.fail (Invalid_argument "really_input") + else unsafe_really_input ic s ofs len + +let input_line ic = + let buf = ref (String.create 128) in + let pos = ref 0 in + let rec loop () = + if !pos = String.length !buf then begin + let newbuf = String.create (2 * !pos) in + String.blit !buf 0 newbuf 0 !pos; + buf := newbuf + end; + Lwt.bind (input_char ic) (fun c -> + if c = '\n' then + Lwt.return () + else begin + !buf.[!pos] <- c; + incr pos; + loop () + end) + in + Lwt.bind + (Lwt.catch loop + (fun e -> + match e with + End_of_file when !pos <> 0 -> + Lwt.return () + | _ -> + Lwt.fail e)) + (fun () -> + let res = String.create !pos in + String.blit !buf 0 res 0 !pos; + Lwt.return res) +*) +(****) + +(* +type popen_process = + Process of in_channel * out_channel + | Process_in of in_channel + | Process_out of out_channel + | Process_full of in_channel * out_channel * in_channel + +let popen_processes = (Hashtbl.create 7 : (popen_process, int) Hashtbl.t) + +let open_proc cmd proc input output toclose = + match Unix.fork () with + 0 -> if input <> Unix.stdin then begin + Unix.dup2 input Unix.stdin; + Unix.close input + end; + if output <> Unix.stdout then begin + Unix.dup2 output Unix.stdout; + Unix.close output + end; + List.iter Unix.close toclose; + Unix.execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |] + | id -> Hashtbl.add popen_processes proc id + +let open_process_in cmd = + let (in_read, in_write) = pipe_in () in + let inchan = Unix.in_channel_of_descr in_read in + open_proc cmd (Process_in inchan) Unix.stdin in_write [in_read]; + Unix.close in_write; + Lwt.return inchan + +let open_process_out cmd = + let (out_read, out_write) = pipe_out () in + let outchan = Unix.out_channel_of_descr out_write in + open_proc cmd (Process_out outchan) out_read Unix.stdout [out_write]; + Unix.close out_read; + Lwt.return outchan + +let open_process cmd = + let (in_read, in_write) = pipe_in () in + let (out_read, out_write) = pipe_out () in + let inchan = Unix.in_channel_of_descr in_read in + let outchan = Unix.out_channel_of_descr out_write in + open_proc cmd (Process(inchan, outchan)) out_read in_write + [in_read; out_write]; + Unix.close out_read; + Unix.close in_write; + Lwt.return (inchan, outchan) + +(* FIX: Subprocesses that use /dev/tty to print things on the terminal + will NOT have this output captured and returned to the caller of this + function. There's an argument that this is correct, but if we are + running from a GUI the user may not be looking at any terminal and it + will appear that the process is just hanging. This can be fixed, in + principle, by writing a little C code that opens /dev/tty and then uses + the TIOCNOTTY ioctl control to detach the terminal. *) + +let open_proc_full cmd env proc input output error toclose = + match Unix.fork () with + 0 -> Unix.dup2 input Unix.stdin; Unix.close input; + Unix.dup2 output Unix.stdout; Unix.close output; + Unix.dup2 error Unix.stderr; Unix.close error; + List.iter Unix.close toclose; + Unix.execve "/bin/sh" [| "/bin/sh"; "-c"; cmd |] env + | id -> Hashtbl.add popen_processes proc id + +let open_process_full cmd env = + let (in_read, in_write) = pipe_in () in + let (out_read, out_write) = pipe_out () in + let (err_read, err_write) = pipe_in () in + let inchan = Unix.in_channel_of_descr in_read in + let outchan = Unix.out_channel_of_descr out_write in + let errchan = Unix.in_channel_of_descr err_read in + open_proc_full cmd env (Process_full(inchan, outchan, errchan)) + out_read in_write err_write [in_write; out_read; err_read]; + Unix.close out_read; + Unix.close in_write; + Unix.close err_write; + Lwt.return (inchan, outchan, errchan) + +let find_proc_id fun_name proc = + try + let pid = Hashtbl.find popen_processes proc in + Hashtbl.remove popen_processes proc; + pid + with Not_found -> + raise (Unix.Unix_error (Unix.EBADF, fun_name, "")) +*) +(* +let close_process_in inchan = + let pid = find_proc_id "close_process_in" (Process_in inchan) in + close_in inchan; + Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) + +let close_process_out outchan = + let pid = find_proc_id "close_process_out" (Process_out outchan) in + close_out outchan; + Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) + +let close_process (inchan, outchan) = + let pid = find_proc_id "close_process" (Process(inchan, outchan)) in + close_in inchan; close_out outchan; + Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) + +let close_process_full (outchan, inchan, errchan) = + let pid = + find_proc_id "close_process_full" + (Process_full(outchan, inchan, errchan)) in + close_out inchan; close_in outchan; close_in errchan; + Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status) +*) + +type lwt_in_channel +let input_line _ = assert false (*XXXXX*) +let intern_in_channel _ = assert false (*XXXXX*) Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/mkProjectInfo.ml 2010-01-22 09:52:57 UTC (rev 406) @@ -98,3 +98,4 @@ Printf.printf "NAME=%s\n" projectName;; + Modified: trunk/src/osx.ml =================================================================== --- trunk/src/osx.ml 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/osx.ml 2010-01-22 09:52:57 UTC (rev 406) @@ -514,6 +514,7 @@ output_string outch "\000\000\014\176"; (* length *) output_string outch "\000\000\000\002"; (* Resource fork *) output_string outch "\000\000\014\226"; (* offset *) +(* FIX: should check for overflow! *) output_string outch (setInt4 (Uutil.Filesize.toInt64 length)); (* length *) output_string outch (emptyFinderInfo ()); Modified: trunk/src/osxsupport.c =================================================================== --- trunk/src/osxsupport.c 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/osxsupport.c 2010-01-22 09:52:57 UTC (rev 406) @@ -33,12 +33,12 @@ CAMLlocal3(res, fInfo, length); int retcode; struct attrlist attrList; - unsigned long options = 0; + unsigned long options = FSOPT_REPORT_FULLSIZE; struct { - unsigned long length; - char finderInfo [32]; - off_t rsrcLength; - } attrBuf; + u_int32_t length; + char finderInfo [32]; + off_t rsrcLength; + } __attribute__ ((packed)) attrBuf; attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.reserved = 0; @@ -58,10 +58,10 @@ if (Bool_val (need_size)) { if (attrBuf.length != sizeof attrBuf) - unix_error (EOPNOTSUPP, "getattrlist", path); + unix_error (EINVAL, "getattrlist", path); } else { - if (attrBuf.length < sizeof (unsigned long) + 32) - unix_error (EOPNOTSUPP, "getattrlist", path); + if (attrBuf.length != sizeof (u_int32_t) + 32) + unix_error (EINVAL, "getattrlist", path); } fInfo = alloc_string (32); @@ -92,9 +92,9 @@ struct attrlist attrList; unsigned long options = 0; struct { - unsigned long length; - char finderInfo [32]; - } attrBuf; + u_int32_t length; + char finderInfo [32]; + } __attribute__ ((packed)) attrBuf; attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.reserved = 0; Modified: trunk/src/remote.ml =================================================================== --- trunk/src/remote.ml 2010-01-20 16:11:27 UTC (rev 405) +++ trunk/src/remote.ml 2010-01-22 09:52:57 UTC (rev 406) @@ -28,10 +28,9 @@ But that resulted in huge amounts of output from '-debug all'. *) -let windowsHack = Sys.os_type <> "Unix" -let recent_ocaml = - Scanf.sscanf Sys.ocaml_version "%d.%d" - (fun maj min -> (maj = 3 && min >= 11) || maj > 3) +let _ = + if Sys.os_type = "Unix" then + ignore(Sys.set_signal Sys.sigpipe Sys.Signal_ignore) let _ = if Sys.os_type = "Unix" then @@ -53,8 +52,6 @@ But then, there is the risk that the two sides exchange spurious messages. *) -let needFlowControl = windowsHack -let readOrWrite = needFlowControl && not recent_ocaml (****) @@ -307,10 +304,9 @@ type connection = { inputBuffer : ioBuffer; outputBuffer : ioBuffer; - outputQueue : outputQueue; - receiver : (unit -> unit Lwt.t) option ref } + outputQueue : outputQueue } -let maybeFlush receiver pendingFlush q buf = +let maybeFlush pendingFlush q buf = (* We return immediately if a flush is already scheduled, or if the output buffer is already empty. *) (* If we are doing flow control and we can write, we need to send @@ -335,25 +331,19 @@ flushBuffer buf end else flushBuffer buf) >>= fun () -> - assert (not (q.flowControl && q.canWrite)); - (* Restart the reader thread if needed *) - match !receiver with - None -> Lwt.return () - | Some f -> f () + Lwt.return () end else Lwt.return () end let makeConnection isServer inCh outCh = let pendingFlush = ref false in - let receiver = ref None in let outputBuffer = makeBuffer outCh in - { inputBuffer = makeBuffer inCh; - outputBuffer = outputBuffer; - outputQueue = - makeOutputQueue isServer - (fun q -> maybeFlush receiver pendingFlush q outputBuffer); - receiver = receiver } + { inputBuffer = makeBuffer inCh; + outputBuffer = outputBuffer; + outputQueue = + makeOutputQueue isServer + (fun q -> maybeFlush pendingFlush q outputBuffer) } (* Send message [l] *) let dump conn l = @@ -694,9 +684,7 @@ (* Receiving thread: read a message and dispatch it to the right thread or create a new thread to process requests. *) let rec receive conn = - if readOrWrite && conn.outputQueue.canWrite then begin - conn.receiver := Some (fun () -> receive conn); Lwt.return () - end else begin + begin debugE (fun () -> Util.msg "Waiting for next message\n"); (* Get the message ID *) let id = Bytearray.create intSize in @@ -966,15 +954,15 @@ in a deadlock." let negociateFlowControlLocal conn () = - if not needFlowControl then disableFlowControl conn.outputQueue; - Lwt.return needFlowControl + disableFlowControl conn.outputQueue; + Lwt.return false let negociateFlowControlRemote = registerServerCmd "negociateFlowControl" negociateFlowControlLocal let negociateFlowControl conn = (* Flow control negociation can be done asynchronously. *) - if not (needFlowControl || Prefs.read halfduplex) then + if not (Prefs.read halfduplex) then Lwt.ignore_result (negociateFlowControlRemote conn () >>= fun needed -> if not needed then Property changes on: trunk/src/system/win ___________________________________________________________________ Added: svn:ignore + *.cmx *.cmi *.cmo From vouillon at seas.upenn.edu Fri Jan 22 05:26:14 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Fri, 22 Jan 2010 05:26:14 -0500 Subject: [Unison-hackers] [unison-svn] r407 - branches/2.32/src Message-ID: <201001221026.o0MAQEMM020048@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-22 05:26:14 -0500 (Fri, 22 Jan 2010) New Revision: 407 Modified: branches/2.32/src/RECENTNEWS branches/2.32/src/copy.ml branches/2.32/src/mkProjectInfo.ml branches/2.32/src/osxsupport.c branches/2.32/src/props.ml branches/2.32/src/remote.ml Log: Fixes to the stable release: * Fixed bug which made Unison ignore finder information and resource fork when compiled to 64bit on Mac OSX. * IPV6, socket mode: properly deal with Unix errors, so that Unison correctly falls back to IPV4 if the kernel does not support IPV6 * copyprog: use better rsync options Modified: branches/2.32/src/RECENTNEWS =================================================================== --- branches/2.32/src/RECENTNEWS 2010-01-22 09:52:57 UTC (rev 406) +++ branches/2.32/src/RECENTNEWS 2010-01-22 10:26:14 UTC (rev 407) @@ -1,3 +1,13 @@ +CHANGES FROM VERSION 2.32.66 + +Fixes to the stable release: +* Fixed bug which made Unison ignore finder information and resource + fork when compiled to 64bit on Mac OSX. +* IPV6, socket mode: properly deal with Unix errors, so that Unison + correctly falls back to IPV4 if the kernel does not support IPV6 +* copyprog: use better rsync options + +------------------------------- CHANGES FROM VERSION 2.32.52 * GTK UI: Unison now take into account the arguments given (including Modified: branches/2.32/src/copy.ml =================================================================== --- branches/2.32/src/copy.ml 2010-01-22 09:52:57 UTC (rev 406) +++ branches/2.32/src/copy.ml 2010-01-22 10:26:14 UTC (rev 407) @@ -506,7 +506,7 @@ (****) let copyprog = - Prefs.createString "copyprog" "rsync --inplace --compress" + Prefs.createString "copyprog" "rsync --partial --inplace --compress" "!external program for copying large files" ("A string giving the name of an " ^ "external program that can be used to copy large files efficiently " @@ -515,7 +515,7 @@ ^ "options---most users should not need to change it.") let copyprogrest = - Prefs.createString "copyprogrest" "rsync --partial --inplace --compress" + Prefs.createString "copyprogrest" "rsync --partial --append-verify --compress" "!variant of copyprog for resuming partial transfers" ("A variant of {\\tt copyprog} that names an external program " ^ "that should be used to continue the transfer of a large file " Modified: branches/2.32/src/mkProjectInfo.ml =================================================================== --- branches/2.32/src/mkProjectInfo.ml 2010-01-22 09:52:57 UTC (rev 406) +++ branches/2.32/src/mkProjectInfo.ml 2010-01-22 10:26:14 UTC (rev 407) @@ -120,3 +120,4 @@ + Modified: branches/2.32/src/osxsupport.c =================================================================== --- branches/2.32/src/osxsupport.c 2010-01-22 09:52:57 UTC (rev 406) +++ branches/2.32/src/osxsupport.c 2010-01-22 10:26:14 UTC (rev 407) @@ -33,12 +33,12 @@ CAMLlocal3(res, fInfo, length); int retcode; struct attrlist attrList; - unsigned long options = 0; + unsigned long options = FSOPT_REPORT_FULLSIZE; struct { - unsigned long length; - char finderInfo [32]; - off_t rsrcLength; - } attrBuf; + u_int32_t length; + char finderInfo [32]; + off_t rsrcLength; + } __attribute__ ((packed)) attrBuf; attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.reserved = 0; @@ -58,10 +58,10 @@ if (Bool_val (need_size)) { if (attrBuf.length != sizeof attrBuf) - unix_error (EOPNOTSUPP, "getattrlist", path); + unix_error (EINVAL, "getattrlist", path); } else { - if (attrBuf.length < sizeof (unsigned long) + 32) - unix_error (EOPNOTSUPP, "getattrlist", path); + if (attrBuf.length != sizeof (u_int32_t) + 32) + unix_error (EINVAL, "getattrlist", path); } fInfo = alloc_string (32); @@ -92,9 +92,9 @@ struct attrlist attrList; unsigned long options = 0; struct { - unsigned long length; - char finderInfo [32]; - } attrBuf; + u_int32_t length; + char finderInfo [32]; + } __attribute__ ((packed)) attrBuf; attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.reserved = 0; Modified: branches/2.32/src/props.ml =================================================================== --- branches/2.32/src/props.ml 2010-01-22 09:52:57 UTC (rev 406) +++ branches/2.32/src/props.ml 2010-01-22 10:26:14 UTC (rev 407) @@ -485,7 +485,7 @@ let toString t = Util.time2string (extract t) let syncedPartsToString t = match t with - Synced _ -> toString t + Synced _ -> Format.sprintf "%s (%f)" (toString t) (extract t) | NotSynced _ -> "" let iCanWrite p = Modified: branches/2.32/src/remote.ml =================================================================== --- branches/2.32/src/remote.ml 2010-01-22 09:52:57 UTC (rev 406) +++ branches/2.32/src/remote.ml 2010-01-22 10:26:14 UTC (rev 407) @@ -845,29 +845,96 @@ let targetHostEntry = Unix.gethostbyname host in targetHostEntry.Unix.h_addr_list.(0) +let printAddr host addr = + match addr with + Unix.ADDR_UNIX s -> + assert false + | Unix.ADDR_INET (s, p) -> + Format.sprintf "%s[%s]:%d" host (Unix.string_of_inet_addr s) p + +let rec findFirst f l = + match l with + [] -> None + | x :: r -> match f x with + None -> findFirst f r + | Some _ as v -> v + +let buildSocket host port kind = + let attemptCreation ai = + try + let socket = + Unix.socket ai.Unix.ai_family ai.Unix.ai_socktype ai.Unix.ai_protocol + in + try + begin match kind with + `Connect -> + (* Connect to the remote host *) + Unix.connect socket ai.Unix.ai_addr + | `Bind -> + (* Allow reuse of local addresses for bind *) + Unix.setsockopt socket Unix.SO_REUSEADDR true; + (* Bind the socket to portnum on the local host *) + Unix.bind socket ai.Unix.ai_addr; + (* Start listening, allow up to 1 pending request *) + Unix.listen socket 1 + end; + Some socket + with Unix.Unix_error _ as e -> + Unix.close socket; + raise e + with Unix.Unix_error (error, _, _) -> + begin match error with + Unix.EAFNOSUPPORT | Unix.EPROTONOSUPPORT | Unix.EINVAL -> + () + | _ -> + let msg = + match kind with + `Connect -> + Printf.sprintf "Can't connect to server %s: %s\n" + (printAddr host ai.Unix.ai_addr) + (Unix.error_message error) + | `Bind -> + Printf.sprintf + "Can't bind socket to port %s at address [%s]: %s\n" + port + (match ai.Unix.ai_addr with + Unix.ADDR_INET (addr, _) -> Unix.string_of_inet_addr addr + | _ -> assert false) + (Unix.error_message error) + in + Util.warn msg + end; + None + in + let options = + match kind with + `Connect -> [ Unix.AI_SOCKTYPE Unix.SOCK_STREAM ] + | `Bind -> [ Unix.AI_SOCKTYPE Unix.SOCK_STREAM ; Unix.AI_PASSIVE ] + in + match + findFirst attemptCreation (Unix.getaddrinfo host port options) + with + Some socket -> + socket + | None -> + let msg = + match kind with + `Connect -> + Printf.sprintf + "Failed to connect to the server on host %s:%s" host port + | `Bind -> + if host = "" then + Printf.sprintf "Can't bind socket to port %s" port + else + Printf.sprintf "Can't bind socket to port %s on host %s" + port host + in + raise (Util.Fatal msg) + let buildSocketConnection host port = Util.convertUnixErrorsToFatal "canonizeRoot" (fun () -> - let rec loop = function - [] -> - raise (Util.Fatal - (Printf.sprintf - "Can't find the IP address of the server (%s:%s)" host - port)) - | ai::r -> - (* create a socket to talk to the remote host *) - let socket = Unix.socket ai.Unix.ai_family ai.Unix.ai_socktype ai.Unix.ai_protocol in - begin try - Unix.connect socket ai.Unix.ai_addr; - initConnection socket socket - with - Unix.Unix_error (error, _, reason) -> - (if error != Unix.EAFNOSUPPORT then - Util.warn - (Printf.sprintf - "Can't connect to server (%s:%s): %s" host port reason); - loop r) - end - in loop (Unix.getaddrinfo host port [ Unix.AI_SOCKTYPE Unix.SOCK_STREAM ])) + let socket = buildSocket host port `Connect in + initConnection socket socket) let buildShellConnection shell host userOpt portOpt rootName termInteract = let remoteCmd = @@ -1159,38 +1226,7 @@ let host = match hostOpt with Some host -> host | None -> "" in - let rec loop = function - [] -> raise (Util.Fatal - (if host = "" then - Printf.sprintf "Can't bind socket to port %s" port - else - Printf.sprintf "Can't bind socket to port %s on host %s" port host)) - | ai::r -> - (* Open a socket to listen for queries *) - let socket = Unix.socket ai.Unix.ai_family ai.Unix.ai_socktype - ai.Unix.ai_protocol in - begin try - (* Allow reuse of local addresses for bind *) - Unix.setsockopt socket Unix.SO_REUSEADDR true; - (* Bind the socket to portnum on the local host *) - Unix.bind socket ai.Unix.ai_addr; - (* Start listening, allow up to 1 pending request *) - Unix.listen socket 1; - socket - with - Unix.Unix_error (error, _, reason) -> - (if error != Unix.EAFNOSUPPORT then - Util.msg - "Can't bind socket to port %s at address [%s]: %s\n" - port - (match ai.Unix.ai_addr with - Unix.ADDR_INET (addr, _) -> Unix.string_of_inet_addr addr - | _ -> assert false) - (Unix.error_message error); - loop r) - end in - let listening = loop (Unix.getaddrinfo host port [ Unix.AI_SOCKTYPE - Unix.SOCK_STREAM ; Unix.AI_PASSIVE ]) in + let listening = buildSocket host port `Bind in Util.msg "server started\n"; while (* Accept a connection *) From vouillon at seas.upenn.edu Fri Jan 22 07:50:07 2010 From: vouillon at seas.upenn.edu (vouillon@seas.upenn.edu) Date: Fri, 22 Jan 2010 07:50:07 -0500 Subject: [Unison-hackers] [unison-svn] r408 - trunk/src Message-ID: <201001221250.o0MCo7EC023991@yaws.seas.upenn.edu> Author: vouillon Date: 2010-01-22 07:50:07 -0500 (Fri, 22 Jan 2010) New Revision: 408 Modified: trunk/src/RECENTNEWS trunk/src/fingerprint.ml trunk/src/mkProjectInfo.ml Log: * Fixed "assertion failed" error introduced in last commit... Modified: trunk/src/RECENTNEWS =================================================================== --- trunk/src/RECENTNEWS 2010-01-22 10:26:14 UTC (rev 407) +++ trunk/src/RECENTNEWS 2010-01-22 12:50:07 UTC (rev 408) @@ -1,5 +1,10 @@ CHANGES FROM VERSION 2.39.6 +* Fixed "assertion failed" error introduced in last commit... + +------------------------------- +CHANGES FROM VERSION 2.39.6 + * Fixed bug which made Unison ignore finder information and resource fork when compiled to 64bit on Mac OSX. * Use asynchronous I/O under Windows Modified: trunk/src/fingerprint.ml =================================================================== --- trunk/src/fingerprint.ml 2010-01-22 10:26:14 UTC (rev 407) +++ trunk/src/fingerprint.ml 2010-01-22 12:50:07 UTC (rev 408) @@ -81,10 +81,11 @@ let dummy = "" let hash d = - if d == dummy then + let l = String.length d in + if l = 0 then 1234577 else begin - assert (String.length d >= 3); + assert (l >= 3); Char.code (String.unsafe_get d 0) + (Char.code (String.unsafe_get d 1) lsl 8) + (Char.code (String.unsafe_get d 2) lsl 16) Modified: trunk/src/mkProjectInfo.ml =================================================================== --- trunk/src/mkProjectInfo.ml 2010-01-22 10:26:14 UTC (rev 407) +++ trunk/src/mkProjectInfo.ml 2010-01-22 12:50:07 UTC (rev 408) @@ -99,3 +99,4 @@ + From jiaoyingtian at nrchpc.ac.cn Wed Jan 27 02:58:24 2010 From: jiaoyingtian at nrchpc.ac.cn (jiaoyingtian) Date: Wed, 27 Jan 2010 15:58:24 +0800 Subject: [Unison-hackers] Does unison collect the memory automatically? Message-ID: <201001271558.24646.jiaoyingtian@nrchpc.ac.cn> Hello, there is a problem i have. There are two machine in my environment,and they sync the file by unison. When the environment run at the beinging time the memory which the unison client use is 8m,but after it run two days ,the memory has been up to 68m. So i want to know whether the unison has the memory collection method? Why the memory has increased so much? From sylvain at le-gall.net Wed Jan 27 10:19:28 2010 From: sylvain at le-gall.net (Sylvain Le Gall) Date: Wed, 27 Jan 2010 15:19:28 +0000 (UTC) Subject: [Unison-hackers] Does unison collect the memory automatically? References: <201001271558.24646.jiaoyingtian@nrchpc.ac.cn> Message-ID: On 27-01-2010, jiaoyingtian wrote: > Hello, there is a problem i have. > There are two machine in my environment,and they sync the file by unison. > When the environment run at the beinging time the memory which the unison > client use is 8m,but after it run two days ,the memory has been up to 68m. > So i want to know whether the unison has the memory collection method? Why > the memory has increased so much? Talking about OCaml, the language used in Unison, there is a garbage collector. So memory should not leak. However, looking at your figures, you probably encounter a "memory fragmentation" problem. OCaml doesn't compact the memory very often and the memory usage seems to rise. After a while, OCaml might trigger a compactation and you memory consumption will be back to normal. Other people can answer more precisely on Unison and possible memory leaks. Regards, Sylvain Le Gall