[Unison-hackers] [unison-svn] r487 - in branches: . 2.45/doc 2.45/src 2.45/src/uimacnew09

bcpierce at seas.upenn.edu bcpierce at seas.upenn.edu
Mon Apr 2 11:54:47 EDT 2012


Author: bcpierce
Date: 2012-04-02 11:54:47 -0400 (Mon, 02 Apr 2012)
New Revision: 487

Added:
   branches/2.45/
   branches/2.45/doc/changes.tex
   branches/2.45/src/INSTALL.win32-msvc
   branches/2.45/src/RECENTNEWS
   branches/2.45/src/copy.mli
   branches/2.45/src/globals.ml
   branches/2.45/src/mkProjectInfo.ml
   branches/2.45/src/uicommon.ml
   branches/2.45/src/uicommon.mli
   branches/2.45/src/uimacnew09/MyController.m
Removed:
   branches/2.45/doc/changes.tex
   branches/2.45/src/INSTALL.win32-msvc
   branches/2.45/src/RECENTNEWS
   branches/2.45/src/copy.mli
   branches/2.45/src/globals.ml
   branches/2.45/src/mkProjectInfo.ml
   branches/2.45/src/uicommon.ml
   branches/2.45/src/uicommon.mli
   branches/2.45/src/uimacnew09/MyController.m
Log:
New release branch

Deleted: branches/2.45/doc/changes.tex
===================================================================
--- trunk/doc/changes.tex	2012-03-10 16:12:51 UTC (rev 485)
+++ branches/2.45/doc/changes.tex	2012-04-02 15:54:47 UTC (rev 487)
@@ -1,1840 +0,0 @@
-\begin{changesfromversion}{2.40.1}
-\item Added "BelowPath" patterns, that match a path as well as all paths below
-  (convenient to use with no{deletion,update,creation}partial preferences)
-\item Added a "fat" preference that makes Unison use the right options
-  when one of the replica is on a FAT filesystem.
-\item Allow "prefer/force=newer" even when not synchronizing modification
-  times.  (The reconciler will not be aware of the modification time
-  of unchanged files, so the synchronization choices of Unison can be
-  different from when "times=true", but the behavior remains sane:
-  changed files with the most recent modification time will be
-  propagated.)
-\item Minor fixes and improvements:
-\begin{itemize}
-\item Compare filenames up to decomposition in case sensitive mode when
-  one host is running MacOSX and the unicode preference is set to
-  true.
-\item Rsync: somewhat faster compressor
-\item Make Unicode the default on all architectures (it was only the
-  default when a Mac OS X or Windows machine was involved).
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.32}
-\item Major enhancement: Unicode support.  
-\begin{itemize}
-\item Unison should now handle unicode filenames correctly on all platforms.
-\item This functionality is controlled by a new preference {\tt unicode}.
-\item 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.
-\end{itemize}
-\item Partial transfer of directories.  If an error occurs while
-  transferring a directory, the part transferred so far is copied into
-  place (and the archives are updated accordingly).
-  The "maxerrors" preference controls how many transfer error Unison
-  will accept before stopping the transfer of a directory (by default,
-  only one).  This makes it possible to transfer most of a directory
-  even if there are some errors.  Currently, only the first error is
-  reported by the GUIs.
-
-  Also, allow partial transfer of a directory when there was an error deep
-  inside this directory during update detection.  At the moment, this
-  is only activated with the text and GTK UIs, which have been
-  modified so that they show that the transfer is going to be partial
-  and so that they can display all errors.
-\item Improvement to the code for resuming directory transfers:
-\begin{itemize}
-\item 
-   if a file was not correctly transferred (or the source has been
-    modified since, with unchanged size), Unison performs a new
-    transfer rather than failing
-  \item spurious files are deleted (this can happen if a file is deleted
-    on the source replica before resuming the transfer; not deleting
-    the file would result in it reappearing on the target replica)
-\end{itemize}
-\item Experimental streaming protocol for transferring file contents (can
-  be disabled by setting the directive "stream" to false): file
-  contents is transfered asynchronously (without waiting for a response
-  from the destination after each chunk sent) rather than using the
-  synchronous RPC mechanism.  As a consequence:
-  \begin{itemize}
-  \item 
-   Unison now transfers the contents of a single file at a time
-    (Unison used to transfer several contents simultaneously in order
-    to hide the connection latency.)
-  \item the transfer of large files uses the full available bandwidth
-    and is not slowed done due to the connection latency anymore
-  \item we get performance improvement for small files as well by
-    scheduling many files simultaneously (as scheduling a file for
-    transfer consume little ressource: it does not mean allocating a
-    large buffer anymore)
-  \end{itemize}
-\item Changes to the internal implementation of the rsync algorithm:
-\begin{itemize}
-\item 
-  use longer blocks for large files (the size of a block is the
-    square root of the size of the file for large files);
-  \item transmit less checksum information per block (we still have less
-    than one chance in a hundred million of transferring a file
-    incorrectly, and Unison will catch any transfer error when
-    fingerprinting the whole file)
-  \item avoid transfer overhead (which was 4 bytes per block)
-\end{itemize}
-  For a 1G file, the first optimization saves a factor 50 on the
-  amount of data transferred from the target to the source (blocks
-  are 32768 bytes rather than just 700 bytes).  The two other
-  optimizations save another factor of 2 (from 24 bytes per block
-  down to 10).
-\item 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.
-  \begin{itemize}
-  \item 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.
-  \end{itemize}
-\item New preferences "noupdate=root", "nodeletion=root", "nocreation=root"
-  that prevent Unison from performing files updates, deletions or
-  creations on the given root.  Also 'partial' versions of 'noupdate',
-  'nodeletion' and 'nocreation' 
-\item Limit the number of simultaneous external copy program
-  ("copymax" preference)
-\item New "links" preference.  When set to false, Unison will report an
-  error on symlinks during update detection.  (This is the default
-  when one host is running Windows but not Cygwin.)  This is better
-  than failing during propagation.
-\item Added a preference "halfduplex" to force half-duplex communication
-  with the server.  This may be useful on unreliable links (as a more
-  efficient alternative to "maxthreads = 1").
-\item Renamed preference "pretendwin" to "ignoreinodenumbers" (an alias is
-  kept for backwards compatibility).
-\item Ignore one-second differences when synchronizing modification time.
-  (Technically, this is an incompatible archive format change, but it
-   is backward compatible.  To trigger a problem, a user would have to
-   synchronize modification times on a filesystem with a two-second
-   granularity and then downgrade to a previous version of Unison,
-   which does not work well in such a case.  Thus, it does not
-   seem worthwhile to increment the archive format number, which would
-   impact all users.)
-\item Do not keep many files simultaneously opened anymore when the rsync
-  algorithm is in use.
-\item Add ``ignorearchives'' preference to ignore existing archives (to
-  avoid forcing users to delete them manually, in situations where one
-  archive has gotten   deleted or corrupted).
-\item Mac OS
-\begin{itemize}
-\item fixed rsync bug which could result in an "index out of bounds"
-  error when transferring resource forks.
-\item Fixed bug which made Unison ignore finder information and resource
-  fork when compiled to 64bit on Mac OSX.
-\item should now be 64 bit clean (the Growl framework is not up to date,
-    though)
-\item 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)
-\item use darker grey arrows (patch contributed by Eric Y. Kow)
-\end{itemize}
-\item GTK user interface
-\begin{itemize}
-\item  assistant for creating profiles
-\item profile editor
-\item pop up a summary window when the replicas are not fully
-    synchronized after transport
-\item display estimated remaining time and transfer rate on the
-  progress bar
-\item allow simultaneous selection of several items
-\item Do not reload the preference file before a new update
-  detection if it is unchanged
-\item disabled scrolling to the first unfinished item during transport.
-  It goes way too fast when lot of small files are synchronized, and it
-  makes it impossible to browse the file list during transport.
-\item take into account the "height" preference again
-\item the internal list of selected reconciler item was not always in
-    sync with what was displayed (GTK bug?); workaround implemented
-\item Do not display "Looking for change" messages during propagation
-  (when checking the targe is unchanged) but only during update detection
-\item Apply patch to fix some crashes in the OSX GUI, thanks to Onne Gorter.
-\end{itemize}
-\item Text UI
-\begin{itemize}
-\item During update detection, display status by updating a single line
-rather than generating a new line of output every so often.  Should be less
-confusing.
-\end{itemize}
-\item Windows 
-\begin{itemize}
-\item 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.
-\item Only use long UNC path for accessing replicas (as '..' is
-  not handled with this format of paths, but can be useful)
-\item Windows text UI: now put the console into UTF-8 output mode.  This
-  is the right thing to do when in Unicode mode, and is no worse than
-  what we had previously otherwise (the console use some esoteric
-  encoding by default).  This only works when using a Unicode font
-  instead of the default raster font.
-\item Don't get the home directory from environment variable HOME under
-  Windows (except for Cygwin binaries): we don't want the behavior of
-  Unison to depends on whether it is run from a Cygwin shell (where
-  HOME is set) or in any other way (where HOME is usually not set).
-\end{itemize}
-\item Miscellaneous fixes and improvements
-\begin{itemize}
-\item Made a server waiting on a socket more resilient to unexpected
-  lost connections from the client.
-\item Small patch to property setting code suggested by Ulrich Gernkow.
-\item Several fixes to the change transfer functions (both the internal ones
-  and external transfers using rsync).  In particular, limit the number of
-  simultaneous transfer using an rsync
-  (as the rsync algorithm can use a large amount of memory when
-   processing huge files)
-\item Keep track of which file contents are being transferred, and delay
-  the transfer of a file when another file with the same contents is
-  currently being transferred.  This way, the second transfer can be
-  skipped and replaced by a local copy.
-\item Experimental update detection optimization:
-  do not read the contents of unchanged directories
-\item When a file transfer fails, turn off fastcheck for this file on the
-  next sync.
-\item Fixed bug with case insensitive mode on a case sensitive filesystem:
-\begin{itemize}
-\item 
-   if file "a/a" is created on one replica and directory "A" is
-    created on the other, the file failed to be synchronized the first
-    time Unison is run afterwards, as Unison uses the wrong path "a/a"
-    (if Unison is run again, the directories are in the archive, so
-     the right path is used);
-  \item if file "a" appears on one replica and file "A" appears on the
-    other with different contents, Unison was unable to synchronize
-    them.
-\end{itemize}
-\item Improved error reporting when the destination is updated during
-  synchronization: Unison now tells which file has been updated, and how.
-\item Limit the length of temporary file names
-\item Case sensitivity information put in the archive (in a backward
-  compatible way) and checked when the archive is loaded
-\item Got rid of the 16mb marshalling limit by marshalling to a bigarray.
-\item Resume copy of partially transferred files.
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.31}
-\item Small user interface changes
-\begin{itemize}
-\item Small change to text UI "scanning..." messages, to print just
-  directories (hopefully making it clearer that individual files are
-  not necessarily being fingerprinted).  
-\end{itemize}
-\item Minor fixes and improvements:
-\begin{itemize}
-\item Ignore one hour differences when deciding whether a file may have
-  been updated.  This avoids slow update detection after daylight
-  saving time changes under Windows.  This makes Unison slightly more
-  likely to miss an update, but it should be safe enough.
-\item Fix a small bug that was affecting mainly windows users.  We need to
-  commit the archives at the end of the sync even if there are no
-  updates to propagate because some files (in fact, if we've just
-  switched to DST on windows, a LOT of files) might have new modtimes
-  in the archive.  (Changed the text UI only.  It's less clear where
-  to change the GUI.)
-\item Don't delete the temp file when a transfer fails due to a
-  fingerprint mismatch (so that we can have a look and see why!)  We've also
-  added more debugging code togive more informative error messages when we
-  encounter the dreaded and longstanding "assert failed during file
-  transfer" bug
-\item Incorrect paths ("path" directive) now result in an error update
-  item rather than a fatal error.
-\item Create parent directories (with correct permissions) during
-  transport for paths which point to non-existent locations in the
-  destination replica.
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.27}
-\item If Unison is interrupted during a directory transfer, it will now
-leave the partially transferred directory intact in a temporary
-location. (This maintains the invariant that new files/directories are
-transferred either completely or not at all.)  The next time Unison is run,
-it will continue filling in this temporary directory, skipping transferring
-files that it finds are already there.
-\item We've added experimental support for invoking an external file
-transfer tool for whole-file copies instead of Unison's built-in transfer
-protocol.  Three new preferences have been added:
-\begin{itemize}
-\item {\tt copyprog} is a string giving the name (and command-line
-switches, if needed) of an external program that can be used to copy large
-files efficiently.  By default, rsync is invoked, but other tools such as
-scp can be used instead by changing the value of this preference.  (Although
-this is not its primary purpose, rsync is actually a pretty fast way of
-copying files that don't already exist on the receiving host.)  For files
-that do already exist on (but that have been changed in one replica), Unison
-will always use its built-in implementation of the rsync algorithm.
-\item Added a "copyprogrest" preference, so that we can give different
-command lines for invoking the external copy utility depending on whether a
-partially transferred file already exists or not.  (Rsync doesn't seem to
-care about this, but other utilities may.)
-\item {\tt copythreshold} is an integer (-1 by default), indicating above what
-filesize (in megabytes) Unison should use the external copying utility
-specified by copyprog.  Specifying 0 will cause ALL copies to use the
-external program; a negative number will prevent any files from using it.
-(Default is -1.)
-\end{itemize}
-Thanks to Alan Schmitt for a huge amount of hacking and to an anonymous
-sponsor for suggesting and underwriting this extension.
-\item Small improvements:
-\begin{itemize}
-\item Added a new preference, {\tt dontchmod}.  By default, Unison uses the
-{\tt 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 {\tt chmod}.
-\item Don't ignore files that look like backup files if the {\tt
-  backuplocation} preference is set to {\tt central}
-\item Shortened the names of several preferences.  The old names are also
-still supported, for backwards compatibility, but they do not appear in the
-documentation.
-\item Lots of little documentation tidying.  (In particular, preferences are
-separated into Basic and Advanced!  This should hopefully make Unison a
-little more approachable for new users.
-\item Unison can sometimes fail to transfer a file, giving the unhelpful
-message "Destination updated during synchronization" even though the file
-has not been changed.  This can be caused by programs that change either the
-file's contents \emph{or} the file's extended attributes without changing
-its modification time.  It's not clear what is the best fix for this -- it
-is not Unison's fault, but it makes Unison's behavior puzzling -- but at
-least Unison can be more helpful about suggesting a workaround (running once
-with {\tt fastcheck} set to false).  The failure message has been changed to
-give this advice.
-\item Further improvements to the OS X GUI (thanks to Alan Schmitt and Craig
-Federighi).
-\end{itemize}
-\item Very preliminary support for triggering Unison from an external 
-  filesystem-watching utility.  The current implementation is very
-  simple, not efficient, and almost completely untested---not ready 
-  for real users.  But if someone wants to help improve it (e.g.,
-  by writing a filesystem watcher for your favorite OS), please make
-  yourself known!
-
-  On the Unison side, the new behavior is very simple:
-  \begin{itemize}
-  \item use the text UI 
-    \item start Unison with the command-line flag "-repeat FOO", 
-      where FOO is name of a file where Unison should look 
-      for notifications of changes
-    \item  when it starts up, Unison will read the whole contents 
-      of this file (on both hosts), which should be a 
-      newline-separated list of paths (relative to the root 
-      of the synchronization) and synchronize just these paths, 
-      as if it had been started with the "-path=xxx" option for 
-      each one of them
-    \item when it finishes, it will sleep for a few seconds and then
-      examine the watchfile again; if anything has been added, it
-      will read the new paths, synchronize them, and go back to 
-      sleep
-    \item that's it!
-  \end{itemize}
-  To use this to drive Unison "incrementally," just start it in 
-  this mode and start up a tool (on each host) to watch for
-  new changes to the filesystem and append the appropriate paths
-  to the watchfile.  Hopefully such tools should not be too hard
-  to write.
-\item Bug fixes:
-\begin{itemize}
-\item Fixed a bug that was causing new files to be created with
-  permissions 0x600 instead of using a reasonable default (like
-  0x644), if the 'perms' flag was set to 0.  (Bug reported by Ben
-  Crowell.)
-\item Follow maxthreads preference when transferring directories.
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.17}
-\item Major rewrite and cleanup of the whole Mac OS X graphical user
-interface by Craig Federighi.  Thanks, Craig!!!
-\item Small fix to ctime (non-)handling in update detection under windows
-  with fastcheck.  
-\item Several small fixes to the GTK2 UI to make it work better under
-Windows [thanks to Karl M for these].
-\item The backup functionality has been completely rewritten.  The external
-interface has not changed, but numerous bugs, irregular behaviors, and
-cross-platform inconsistencies have been corrected.
-\item The Unison project now accepts donations via PayPal.  If you'd like to
-donate, you can find a link to the donation page on the
-\URL{http://www.cis.upenn.edu/~bcpierce/unison/lists.html}{Unison home
-  page}.
-\item Some important safety improvements:
-\begin{itemize}
-\item Added a new \verb|mountpoint| preference, which can be used to specify
-a path that must exist in both replicas at the end of update detection
-(otherwise Unison aborts).  This can be used to avoid potentially dangerous
-situations when Unison is used with removable media such as external hard
-drives and compact flash cards.  
-\item The confirmation of ``big deletes'' is now controlled by a boolean preference
-  \verb|confirmbigdeletes|.  Default is true, which gives the same behavior as
-  previously.  (This functionality is at least partly superceded by the
-  \verb|mountpoint| preference, but it has been left in place in case it is
-  useful to some people.)
-  \item If Unison is asked to ``follow'' a symbolic link but there is
-  nothing at the other end of the link, it will now flag this path as an
-  error, rather than treating the symlink itself as missing or deleted.
-  This avoids a potentially dangerous situation where a followed symlink
-  points to an external filesystem that might be offline when Unison is run
-  (whereupon Unison would cheerfully delete the corresponding files in the
-  other replica!).
-\end{itemize}
-
-\item Smaller changes:
-\begin{itemize}
-\item Added \verb|forcepartial| and \verb|preferpartial| preferences, which
-behave like \verb|force| and \verb|prefer| but can be specified on a
-per-path basis. [Thanks to Alan Schmitt for this.]
-\item A bare-bones self test feature was added, which runs unison through
-  some of its paces and checks that the results are as expected.  The
-  coverage of the tests is still very limited, but the facility has already
-  been very useful in debugging the new backup functionality (especially in
-  exposing some subtle cross-platform issues).
-\item Refined debugging code so that the verbosity of individual modules
-  can be controlled separately.  Instead of just putting '-debug
-  verbose' on the command line, you can put '-debug update+', which
-  causes all the extra messages in the Update module, but not other
-  modules, to be printed.  Putting '-debug verbose' causes all modules
-  to print with maximum verbosity.
-\item Removed \verb|mergebatch| preference.  (It never seemed very useful, and
-  its semantics were confusing.)
-\item Rewrote some of the merging functionality, for better cooperation
-  with external Harmony instances.
-\item Changed the temp file prefix from \verb|.#| to \verb|.unison|.
-\item Compressed the output from the text user interface (particularly
-  when run with the \verb|-terse| flag) to make it easier to interpret the
-  results when Unison is run several times in succession from a script.
-\item Diff and merge functions now work under Windows.
-\item Changed the order of arguments to the default diff command (so that
-  the + and - annotations in diff's output are reversed).
-\item Added \verb|.mpp| files to the ``never fastcheck'' list (like
-\verb|.xls| files). 
-\end{itemize}
-
-\item Many small bugfixes, including:
-\begin{itemize}
-\item Fixed a longstanding bug regarding fastcheck and daylight saving time
-  under Windows when Unison is set up to synchronize modification times.
-  (Modification times cannot be updated in the archive in this case,
-  so we have to ignore one hour differences.)
-\item Fixed a bug that would occasionally cause the archives to be left in
-  non-identical states on the two hosts after synchronization.
-\item Fixed a bug that prevented Unison from communicating correctly between
-  32- and 64-bit architectures.
-\item On windows, file creation times are no longer used as a proxy for
-  inode numbers.  (This is unfortunate, as it makes fastcheck a little less
-  safe.  But it turns out that file creation times are not reliable 
-  under Windows: if a file is removed and a new file is created in its
-  place, the new one will sometimes be given the same creation date as the
-  old one!)
-\item Set read-only file to R/W on OSX before attempting to change other attributes.
-\item Fixed bug resulting in spurious "Aborted" errors during transport
-(thanks to Jerome Vouillon) 
-\item Enable diff if file contents have changed in one replica, but
-only properties in the other.
-\item Removed misleading documentation for 'repeat' preference.
-\item Fixed a bug in merging code where Unison could sometimes deadlock
-  with the external merge program, if the latter produced large
-  amounts of output.
-\item Workaround for a bug compiling gtk2 user interface against current versions
-  of gtk2+ libraries.  
-\item Added a better error message for "ambiguous paths".
-\item Squashed a longstanding bug that would cause file transfer to fail
-  with the message ``Failed: Error in readWrite: Is a directory.''
-\item Replaced symlinks with copies of their targets in the Growl framework in src/uimac.
-  This should make the sources easier to check out from the svn repository on WinXP
-  systems.
-\item Added a workaround (suggested by Karl M.) for the problem discussed
-  on the unison users mailing list where, on the Windows platform, the
-  server would hang when transferring files.  I conjecture that
-  the problem has to do with the RPC mechanism, which was used to
-  make a call {\em back} from the server to the client (inside the Trace.log 
-  function) so that the log message would be appended to the log file on 
-  the client.  The workaround is to dump these messages (about when
-  xferbycopying shortcuts are applied and whether they succeed) just to the
-  standard output of the Unison process, not to the log file.
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.13.0}
-\item The features for performing backups and for invoking external merge
-programs have been completely rewritten by Stephane Lescuyer (thanks,
-Stephane!).  The user-visible functionality should not change, but the
-internals have been rationalized and there are a number of new features.
-See the manual (in particular, the description of the \verb|backupXXX|
-preferences) for details.
-\item Incorporated patches for ipv6 support, contributed by Samuel Thibault.
-(Note that, due to a bug in the released OCaml 3.08.3 compiler, this code
-will not actually work with ipv6 unless compiled with the CVS version of the
-OCaml compiler, where the bug has been fixed; however, ipv4 should continue
-to work normally.)
-\item OSX interface:
-\begin{itemize}
-\item Incorporated Ben Willmore's cool new icon for the Mac UI.
-\end{itemize}
-\item Small fixes:
-\begin{itemize}
-\item Fixed off by one error in month numbers (in printed dates) reported 
-  by Bob Burger
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.12.0}
-\item New convention for release numbering: Releases will continue to be
-given numbers of the form \verb|X.Y.Z|, but, 
-from now on, just the major version number (\verb|X.Y|) will be considered
-significant when checking compatibility between client and server versions.
-The third component of the version number will be used only to identify
-``patch levels'' of releases.
-
-This change goes hand in hand with a change to the procedure for making new
-releases.  Candidate releases will initially be given ``beta release''
-status when they are announced for public consumption.  Any bugs that are
-discovered will be fixed in a separate branch of the source repository
-(without changing the major version number) and new tarballs re-released as
-needed.  When this process converges, the patched beta version will be
-dubbed stable.
-\item Warning (failure in batch mode) when one path is completely emptied.
-  This prevents Unison from deleting everything on one replica when
-  the other disappear.
-\item Fix diff bug (where no difference is shown the first time the diff
-  command is given).
-\item User interface changes:
-\begin{itemize}
-\item Improved workaround for button focus problem (GTK2 UI)
-\item Put leading zeroes in date fields
-\item More robust handling of character encodings in GTK2 UI
-\item Changed format of modification time displays, from \verb|modified at hh:mm:ss on dd MMM, yyyy|
-to \verb|modified on yyyy-mm-dd hh:mm:ss|
-\item Changed time display to include seconds (so that people on FAT
-  filesystems will not be confused when Unison tries to update a file
-  time to an odd number of seconds and the filesystem truncates it to
-  an even number!)
-\item Use the diff "-u" option by default when showing differences between files
-  (the output is more readable)
-\item In text mode, pipe the diff output to a pager if the environment
-  variable PAGER is set
-\item Bug fixes and cleanups in ssh password prompting.  Now works with
-  the GTK2 UI under Linux.  (Hopefully the Mac OS X one is not broken!)
-\item Include profile name in the GTK2 window name
-\item Added bindings ',' (same as '<') and '.' (same as '>') in the GTK2 UI
-\end{itemize}
-\item Mac GUI:
-\begin{itemize}
-\item actions like < and > scroll to the next item as necessary.
-\item Restart has a menu item and keyboard shortcut (command-R).
-\item 
-    Added a command-line tool for Mac OS X.  It can be installed from
-    the Unison menu.
-\item New icon.
-\item   Handle the "help" command-line argument properly.
-\item   Handle profiles given on the command line properly.
-\item  When a profile has been selected, the profile dialog is replaced by a
-    "connecting" message while the connection is being made.  This
-    gives better feedback.
-\item   Size of left and right columns is now large enough so that
-    "PropsChanged" is not cut off.
-\end{itemize}
-\item Minor changes:
-\begin{itemize}
-\item Disable multi-threading when both roots are local
-\item Improved error handling code.  In particular, make sure all files
-  are closed in case of a transient failure
-\item Under Windows, use \verb|$UNISON| for home directory as a last resort
-  (it was wrongly moved before \verb|$HOME| and \verb|$USERPROFILE| in
-  Unison 2.12.0)
-\item Reopen the logfile if its name changes (profile change)
-\item Double-check that permissions and modification times have been
-  properly set: there are some combination of OS and filesystem on
-  which setting them can fail in a silent way.
-\item Check for bad Windows filenames for pure Windows synchronization
-  also (not just cross architecture synchronization).
-  This way, filenames containing backslashes, which are not correctly
-  handled by unison, are rejected right away.
-\item Attempt to resolve issues with synchronizing modification times
-  of read-only files under Windows
-\item Ignore chmod failures when deleting files
-\item Ignore trailing dots in filenames in case insensitive mode
-\item Proper quoting of paths, files and extensions ignored using the UI
-\item The strings CURRENT1 and CURRENT2 are now correctly substitued when
-  they occur in the diff preference
-\item Improvements to syncing resource forks between Macs via a non-Mac system.
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.10.2}
-\item \incompatible{} Archive format has changed.  
-\item Source code availability: The Unison sources are now managed using
-  Subversion.  One nice side-effect is that anonymous checkout is now
-  possible, like this:
-\begin{verbatim}
-        svn co https://cvs.cis.upenn.edu:3690/svnroot/unison/
-\end{verbatim}
-We will also continue to export a ``developer tarball'' of the current
-(modulo one day) sources in the web export directory.  To receive commit logs
-for changes to the sources, subscribe to the \verb|unison-hackers| list
-(\ONEURL{http://www.cis.upenn.edu/~bcpierce/unison/lists.html}). 
-\item Text user interface:
-\begin{itemize}
-\item Substantial reworking of the internal logic of the text UI to make it
-a bit easier to modify.
-\item The {\tt dumbtty} flag in the text UI is automatically set to true if
-the client is running on a Unix system and the {\tt EMACS} environment
-variable is set to anything other than the empty string.
-\end{itemize}
-\item Native OS X gui:
-\begin{itemize}
-\item Added a synchronize menu item with keyboard shortcut
-\item Added a merge menu item, still needs to be debugged
-\item Fixes to compile for Panther
-\item Miscellaneous improvements and bugfixes
-\end{itemize}
-\item Small changes:
-\begin{itemize}
-\item Changed the filename checking code to apply to Windows only, instead
-  of OS X as well.
-\item Finder flags now synchronized
-\item Fallback in copy.ml for filesystem that do not support \verb|O_EXCL|
-\item  Changed buffer size for local file copy (was highly inefficient with
-  synchronous writes)
-\item Ignore chmod failure when deleting a directory
-\item  Fixed assertion failure when resolving a conflict content change /
-  permission changes in favor of the content change.
-\item Workaround for transferring large files using rsync.
-\item Use buffered I/O for files (this is the only way to open files in binary
-  mode under Cygwin).
-\item On non-Cygwin Windows systems, the UNISON environment variable is now checked first to determine 
-  where to look for Unison's archive and preference files, followed by \verb|HOME| and 
-  \verb|USERPROFILE| in that order.  On Unix and Cygwin systems, \verb|HOME| is used.
-\item Generalized \verb|diff| preference so that it can be given either as just 
-  the command name to be used for calculating diffs or else a whole command
-  line, containing the strings \verb|CURRENT1| and \verb|CURRENT2|, which will be replaced
-  by the names of the files to be diff'ed before the command is called.
-\item Recognize password prompts in some newer versions of ssh.
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.9.20}
-\item \incompatible{} Archive format has changed.  
-\item Major functionality changes:
-\begin{itemize}
-\item Major tidying and enhancement of 'merge' functionality.  The main
-  user-visible change is that the external merge program may either write
-  the merged output to a single new file, as before, or it may modify one or
-  both of its input files, or it may write {\em two} new files.  In the
-  latter cases, its modifications will be copied back into place on both the
-  local and the remote host, and (if the two files are now equal) the
-  archive will be updated appropriately.  More information can be found in
-  the user manual.  Thanks to Malo Denielou and Alan Schmitt for these
-  improvements.
-
-  Warning: the new merging functionality is not completely compatible with
-  old versions!  Check the manual for details.
-\item Files larger than 2Gb are now supported.
-\item Added preliminary (and still somewhat experimental) support for the
-  Apple OS X operating system.   
-\begin{itemize}
-\item Resource forks should be transferred correctly.  (See the manual for
-details of how this works when synchronizing HFS with non-HFS volumes.)
-Synchronization of file type and creator information is also supported.
-\item On OSX systems, the name of the directory for storing Unison's
-archives, preference files, etc., is now determined as follows:
-\begin{itemize}
-    \item if \verb+~/.unison+ exists, use it
-     \item otherwise, use \verb|~/Library/Application Support/Unison|, 
-         creating it if necessary.
-\end{itemize}
-\item A preliminary native-Cocoa user interface is under construction.  This
-still needs some work, and some users experience unpredictable crashes, so
-it is only for hackers for now.  Run make with {\tt UISTYLE=mac} to build
-this interface.
-\end{itemize}
-\end{itemize}
-
-\item Minor functionality changes:
-\begin{itemize}
-\item Added an {\tt ignorelocks} preference, which forces Unison to override left-over
-  archive locks.  (Setting this preference is dangerous!  Use it only if you
-  are positive you know what you are doing.) 
-% BCP: removed later
-% \item Running with the {\tt -timers} flag set to true will now show the total time taken
-%   to check for updates on each directory.  (This can be helpful for tidying directories to improve
-%   update detection times.)  
-\item Added a new preference {\tt assumeContentsAreImmutable}.  If a directory
-  matches one of the patterns set in this preference, then update detection
-  is skipped for files in this directory.  (The 
-  purpose is to speed update detection for cases like Mail folders, which
-  contain lots and lots of immutable files.)  Also a preference
-  {\tt assumeContentsAreImmutableNot}, which overrides the first, similarly
-  to {\tt ignorenot}.  (Later amendment: these preferences are now called
-  {\tt immutable} and {\tt immutablenot}.)
-\item The {\tt ignorecase} flag has been changed from a boolean to a three-valued
-  preference.  The default setting, called {\tt default}, checks the operating systems
-  running on the client and server and ignores filename case if either of them is
-  OSX or Windows.  Setting ignorecase to {\tt true} or {\tt false} overrides
-  this behavior.  If you have been setting {\tt ignorecase} on the command
-  line using {\tt -ignorecase=true} or {\tt -ignorecase=false}, you will
-  need to change to {\tt -ignorecase true} or {\tt -ignorecase false}.
-\item a new preference, 'repeat', for the text user interface (only).  If 'repeat' is set to
-  a number, then, after it finishes synchronizing, Unison will wait for that many seconds and
-  then start over, continuing this way until it is killed from outside.  Setting repeat to true
-  will automatically set the batch preference to true.  
-\item Excel files are now handled specially, so that the {\tt fastcheck}
-  optimization is skipped even if the {\tt fastcheck} flag is set.  (Excel
-  does some naughty things with modtimes, making this optimization
-  unreliable and leading to failures during change propagation.)
-\item The ignorecase flag has been changed from a boolean to a three-valued
-  preference.  The default setting, called 'default', checks the operating systems
-  running on the client and server and ignores filename case if either of them is
-  OSX or Windows.  Setting ignorecase to 'true' or 'false' overrides this behavior.
-\item Added a new preference, 'repeat', for the text user interface (only,
-  at the moment).  If 'repeat' is set to a number, then, after it finishes
-  synchronizing, Unison will wait for that many seconds and then start over,
-  continuing this way until it is killed from outside.  Setting repeat to
-  true will automatically set the batch preference to true.
-\item The 'rshargs' preference has been split into 'rshargs' and 'sshargs' 
-  (mainly to make the documentation clearer).  In fact, 'rshargs' is no longer
-  mentioned in the documentation at all, since pretty much everybody uses
-  ssh now anyway.
-\end{itemize}
-\item Documentation
-\begin{itemize}
-\item The web pages have been completely redesigned and reorganized.
-  (Thanks to Alan Schmitt for help with this.)
-\end{itemize}
-\item User interface improvements
-\begin{itemize}
-\item Added a GTK2 user interface, capable (among other things) of displaying filenames
-  in any locale encoding.  Kudos to Stephen Tse for contributing this code!  
-\item The text UI now prints a list of failed and skipped transfers at the end of
-  synchronization. 
-\item Restarting update detection from the graphical UI will reload the current
-  profile (which in particular will reset the -path preference, in case
-  it has been narrowed by using the ``Recheck unsynchronized items''
-  command).
-\item Several small improvements to the text user interface, including a
-  progress display.
-\end{itemize}
-\item Bug fixes (too numerous to count, actually, but here are some):
-\begin{itemize}
-\item The {\tt maxthreads} preference works now.
-\item Fixed bug where warning message about uname returning an unrecognized
-  result was preventing connection to server.  (The warning is no longer
-  printed, and all systems where 'uname' returns anything other than 'Darwin' 
-  are assumed not to be running OS X.)
-\item Fixed a problem on OS X that caused some valid file names (e.g.,
-  those including colons) to be considered invalid.
-\item Patched Path.followLink to follow links under cygwin in addition to Unix
-  (suggested by Matt Swift).
-\item Small change to the storeRootsName function, suggested by bliviero at 
-  ichips.intel.com, to fix a problem in unison with the `rootalias'
-  option, which allows you to tell unison that two roots contain the same 
-  files.  Rootalias was being applied after the hosts were 
-  sorted, so it wouldn't work properly in all cases.
-\item Incorporated a fix by Dmitry Bely for setting utimes of read-only files
-  on Win32 systems.   
-\end{itemize}
-\item Installation / portability:
-\begin{itemize}
-\item Unison now compiles with OCaml version 3.07 and later out of the box.
-\item Makefile.OCaml fixed to compile out of the box under OpenBSD.
-\item a few additional ports (e.g. OpenBSD, Zaurus/IPAQ) are now mentioned in 
-  the documentation 
-\item Unison can now be installed easily on OSX systems using the Fink
-  package manager
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.9.1}
-\item Added a preference {\tt maxthreads} that can be used to limit the
-number of simultaneous file transfers.
-\item Added a {\tt backupdir} preference, which controls where backup
-files are stored.
-\item Basic support added for OSX.  In particular, Unison now recognizes
-when one of the hosts being synchronized is running OSX and switches to
-a case-insensitive treatment of filenames (i.e., 'foo' and 'FOO' are
-considered to be the same file).
-  (OSX is not yet fully working,
-  however: in particular, files with resource forks will not be
-  synchronized correctly.)
-\item The same hash used to form the archive name is now also added to
-the names of the temp files created during file transfer.  The reason for
-this is that, during update detection, we are going to silently delete
-any old temp files that we find along the way, and we want to prevent
-ourselves from deleting temp files belonging to other instances of Unison
-that may be running in parallel, e.g. synchronizing with a different
-host.  Thanks to Ruslan Ermilov for this suggestion.
-\item Several small user interface improvements
-\item Documentation
-\begin{itemize}
-\item FAQ and bug reporting instructions have been split out as separate
-      HTML pages, accessible directly from the unison web page.
-\item Additions to FAQ, in particular suggestions about performance
-tuning. 
-\end{itemize}
-\item Makefile
-\begin{itemize}
-\item Makefile.OCaml now sets UISTYLE=text or UISTYLE=gtk automatically,
-  depending on whether it finds lablgtk installed
-\item Unison should now compile ``out of the box'' under OSX
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.8.1}
-\item Changing profile works again under Windows
-\item File movement optimization: Unison now tries to use local copy instead of
-  transfer for moved or copied files.  It is controled by a boolean option
-  ``xferbycopying''.
-\item Network statistics window (transfer rate, amount of data transferred).
-      [NB: not available in Windows-Cygwin version.]
-\item symlinks work under the cygwin version (which is dynamically linked).
-\item Fixed potential deadlock when synchronizing between Windows and
-Unix 
-\item Small improvements:
-  \begin{itemize} 
-  \item If neither the {\tt USERPROFILE} nor the {\tt HOME} environment
-    variables are set, then Unison will put its temporary commit log
-    (called {\tt DANGER.README}) into the directory named by the 
-    {\tt UNISON} environment variable, if any; otherwise it will use
-    {\tt C:}.
-  \item alternative set of values for fastcheck: yes = true; no = false;
-  default = auto.
-  \item -silent implies -contactquietly
-  \end{itemize}
-\item Source code:
-  \begin{itemize}
-  \item Code reorganization and tidying.  (Started breaking up some of the
-    basic utility modules so that the non-unison-specific stuff can be
-    made available for other projects.)
-  \item several Makefile and docs changes (for release);
-  \item further comments in ``update.ml'';
-  \item connection information is not stored in global variables anymore.
-  \end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.7.78}
-\item Small bugfix to textual user interface under Unix (to avoid leaving
-  the terminal in a bad state where it would not echo inputs after Unison
-  exited).
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.7.39}
-\item Improvements to the main web page (stable and beta version docs are
-  now both accessible).
-\item User manual revised.
-\item Added some new preferences:
-\begin{itemize}
-\item ``sshcmd'' and ``rshcmd'' for specifying paths to ssh and rsh programs.
-\item ``contactquietly'' for suppressing the ``contacting server'' message
-during Unison startup (under the graphical UI).
-\end{itemize}
-\item Bug fixes:
-\begin{itemize}
-\item Fixed small bug in UI that neglected to change the displayed column 
-  headers if loading a new profile caused the roots to change.
-\item Fixed a bug that would put the text UI into an infinite loop if it
-  encountered a conflict when run in batch mode.
-\item Added some code to try to fix the display of non-Ascii characters in 
-  filenames on Windows systems in the GTK UI.  (This code is currently 
-  untested---if you're one of the people that had reported problems with
-  display of non-ascii filenames, we'd appreciate knowing if this actually 
-  fixes things.)
-\item `\verb|-prefer/-force newer|' works properly now.  
-        (The bug was reported by Sebastian Urbaniak and Sean Fulton.)
-\end{itemize}
-\item User interface and Unison behavior:
-\begin{itemize}
-\item Renamed `Proceed' to `Go' in the graphical UI.
-\item Added exit status for the textual user interface.
-\item Paths that are not synchronized because of conflicts or errors during 
-  update detection are now noted in the log file.
-\item \verb|[END]| messages in log now use a briefer format
-\item Changed the text UI startup sequence so that
-  {\tt ./unison -ui text} will use the default profile instead of failing.
-\item Made some improvements to the error messages.
-\item Added some debugging messages to remote.ml.
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.7.7}
-\item Incorporated, once again, a multi-threaded transport sub-system.
-  It transfers several files at the same time, thereby making much
-  more effective use of available network bandwidth.  Unlike the
-  earlier attempt, this time we do not rely on the native thread
-  library of OCaml.  Instead, we implement a light-weight,
-  non-preemptive multi-thread library in OCaml directly.  This version
-  appears stable.  
-
-  Some adjustments to unison are made to accommodate the multi-threaded
-  version.  These include, in particular, changes to the
-  user interface and logging, for example:
-  \begin{itemize}
-  \item Two log entries for each transferring task, one for the
-    beginning, one for the end.
-  \item Suppressed warning messages against removing temp files left
-    by a previous unison run, because warning does not work nicely
-    under multi-threading.  The temp file names are made less likely
-    to coincide with the name of a file created by the user.  They
-    take the form \\ \verb|.#<filename>.<serial>.unison.tmp|.
-    [N.b. This was later changed to \verb|.unison.<filename>.<serial>.unison.tmp|.]
-  \end{itemize}
-\item Added a new command to the GTK user interface: pressing 'f' causes
-  Unison to start a new update detection phase, using as paths {\em just}
-  those paths that have been detected as changed and not yet marked as
-  successfully completed.  Use this command to quickly restart Unison on
-  just the set of paths still needing attention after a previous run.
-\item Made the {\tt ignorecase} preference user-visible, and changed the
-  initialization code so that it can be manually set to true, even if
-  neither host is running Windows.  (This may be useful, e.g., when using 
-  Unison running on a Unix system with a FAT volume mounted.)
-\item Small improvements and bug fixes:
-  \begin{itemize}
-  \item Errors in preference files now generate fatal errors rather than
-    warnings at startup time.  (I.e., you can't go on from them.)  Also,
-    we fixed a bug that was preventing these warnings from appearing in the
-    text UI, so some users who have been running (unsuspectingly) with 
-    garbage in their prefs files may now get error reports.
-  \item Error reporting for preference files now provides file name and
-    line number.
-  \item More intelligible message in the case of identical change to the same 
-    files: ``Nothing to do: replicas have been changed only in identical 
-    ways since last sync.''
-  \item Files with prefix '.\#' excluded when scanning for preference
-    files.
-  \item Rsync instructions are send directly instead of first
-    marshaled.
-  \item Won't try forever to get the fingerprint of a continuously changing file:
-    unison will give up after certain number of retries.
-  \item Other bug fixes, including the one reported by Peter Selinger
-    (\verb|force=older preference| not working).
-  \end{itemize}
-\item Compilation:
-  \begin{itemize}
-  \item Upgraded to the new OCaml 3.04 compiler, with the LablGtk
-    1.2.3 library (patched version used for compiling under Windows).
-  \item Added the option to compile unison on the Windows platform with
-    Cygwin GNU C compiler.  This option only supports building
-    dynamically linked unison executables.
-  \end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.7.4}
-\item Fixed a silly (but debilitating) bug in the client startup sequence.
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.7.1}
-\item Added \verb|addprefsto| preference, which (when set) controls which
-preference file new preferences (e.g. new ignore patterns) are added to.
-\item Bug fix: read the initial connection header one byte at a time, so
-that we don't block if the header is shorter than expected.  (This bug
-did not affect normal operation --- it just made it hard to tell when you
-were trying to use Unison incorrectly with an old version of the server,
-since it would hang instead of giving an error message.)
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.6.59}
-\item Changed \verb|fastcheck| from a boolean to a string preference.  Its 
-  legal values are \verb|yes| (for a fast check), \verb|no| (for a safe 
-  check), or \verb|default| (for a fast check---which also happens to be 
-  safe---when running on Unix and a safe check when on Windows).  The default 
-  is \verb|default|.
-  \item Several preferences have been renamed for consistency.  All
-  preference names are now spelled out in lowercase.  For backward
-  compatibility, the old names still work, but they are not mentioned in
-  the manual any more.
-\item The temp files created by the 'diff' and 'merge' commands are now
-   named by {\em pre}pending a new prefix to the file name, rather than
-   appending a suffix.  This should avoid confusing diff/merge programs
-   that depend on the suffix  to guess the type of the file contents.
-\item We now set the keepalive option on the server socket, to make sure
-  that the server times out if the communication link is unexpectedly broken. 
-\item Bug fixes:
-\begin{itemize}
-\item When updating small files, Unison now closes the destination file.
-\item File permissions are properly updated when the file is behind a
-  followed link.
-\item Several other small fixes.
-\end{itemize}
-\end{changesfromversion}
-
-
-\begin{changesfromversion}{2.6.38}
-\item Major Windows performance improvement!  
-
-We've added a preference \verb|fastcheck| that makes Unison look only at
-a file's creation time and last-modified time to check whether it has
-changed.  This should result in a huge speedup when checking for updates
-in large replicas.
-
-  When this switch is set, Unison will use file creation times as 
-  'pseudo inode numbers' when scanning Windows replicas for updates, 
-  instead of reading the full contents of every file.  This may cause 
-  Unison to miss propagating an update if the create time, 
-  modification time, and length of the file are all unchanged by 
-  the update (this is not easy to achieve, but it can be done).  
-  However, Unison will never {\em overwrite} such an update with
-  a change from the other replica, since it 
-  always does a safe check for updates just before propagating a 
-  change.  Thus, it is reasonable to use this switch most of the time 
-  and occasionally run Unison once with {\tt fastcheck} set to false, 
-  if you are worried that Unison may have overlooked an update.
-
-  Warning: This change is has not yet been thoroughly field-tested.  If you 
-  set the \verb|fastcheck| preference, pay careful attention to what
-  Unison is doing.
-
-\item New functionality: centralized backups and merging 
-\begin{itemize}
-\item This version incorporates two pieces of major new functionality,
-   implemented by Sylvain Roy during a summer internship at Penn: a
-   {\em centralized backup} facility that keeps a full backup of
-   (selected files 
-   in) each replica, and a {\em merging} feature that allows Unison to
-   invoke an external file-merging tool to resolve conflicting changes to
-   individual files.
- 
-\item Centralized backups:
-\begin{itemize}
-  \item Unison now maintains full backups of the last-synchronized versions
-      of (some of) the files in each replica; these function both as
-      backups in the usual sense
-      and as the ``common version'' when invoking external
-      merge programs.
-  \item The backed up files are stored in a directory ~/.unison/backup on each
-      host.  (The name of this directory can be changed by setting
-      the environment variable \verb|UNISONBACKUPDIR|.)
-  \item The predicate \verb|backup| controls which files are actually
-     backed up:
-      giving the preference '\verb|backup = Path *|' causes backing up
-      of all files.
-  \item Files are added to the backup directory whenever unison updates
-      its archive.  This means that
-      \begin{itemize}
-       \item When unison reconstructs its archive from scratch (e.g., 
-           because of an upgrade, or because the archive files have
-           been manually deleted), all files will be backed up.
-       \item Otherwise, each file will be backed up the first time unison
-           propagates an update for it.
-      \end{itemize}
-  \item The preference \verb|backupversions| controls how many previous
-      versions of each file are kept.  The default is 2 (i.e., the last 
-      synchronized version plus one backup).
-  \item For backward compatibility, the \verb|backups| preference is also
-      still supported, but \verb|backup| is now preferred.
-  \item It is OK to manually delete files from the backup directory (or to throw
-      away the directory itself).  Before unison uses any of these files for 
-      anything important, it checks that its fingerprint matches the one 
-      that it expects. 
-\end{itemize}
-
-\item Merging:
-\begin{itemize}
-  \item Both user interfaces offer a new 'merge' command, invoked by pressing
-      'm' (with a changed file selected).  
-  \item The actual merging is performed by an external program.  
-      The preferences \verb|merge| and \verb|merge2| control how this
-      program is invoked.  If a backup exists for this file (see the
-      \verb|backup| preference), then the \verb|merge| preference is used for 
-      this purpose; otherwise \verb|merge2| is used.  In both cases, the 
-      value of the preference should be a string representing the command 
-      that should be passed to a shell to invoke the 
-      merge program.  Within this string, the special substrings
-      \verb|CURRENT1|, \verb|CURRENT2|, \verb|NEW|,  and \verb|OLD| may appear
-      at any point.  Unison will substitute these as follows before invoking
-      the command:
-        \begin{itemize}
-        \item \relax\verb|CURRENT1| is replaced by the name of the local 
-        copy of the file;
-        \item \relax\verb|CURRENT2| is replaced by the name of a temporary
-        file, into which the contents of the remote copy of the file have
-        been transferred by Unison prior to performing the merge;
-        \item \relax\verb|NEW| is replaced by the name of a temporary
-        file that Unison expects to be written by the merge program when
-        it finishes, giving the desired new contents of the file; and
-        \item \relax\verb|OLD| is replaced by the name of the backed up
-        copy of the original version of the file (i.e., its state at the 
-        end of the last successful run of Unison), if one exists 
-        (applies only to \verb|merge|, not \verb|merge2|).
-        \end{itemize}
-      For example, on Unix systems setting the \verb|merge| preference to
-\begin{verbatim}
-   merge = diff3 -m CURRENT1 OLD CURRENT2 > NEW
-\end{verbatim}
-      will tell Unison to use the external \verb|diff3| program for merging.  
-
-      A large number of external merging programs are available.  For 
-      example, \verb|emacs| users may find the following convenient:
-\begin{verbatim}
-    merge2 = emacs -q --eval '(ediff-merge-files "CURRENT1" "CURRENT2" 
-               nil "NEW")' 
-    merge = emacs -q --eval '(ediff-merge-files-with-ancestor 
-               "CURRENT1" "CURRENT2" "OLD" nil "NEW")' 
-\end{verbatim}
-(These commands are displayed here on two lines to avoid running off the
-edge of the page.  In your preference file, each should be written on a
-single line.) 
-
-  \item If the external program exits without leaving any file at the
-  path \verb|NEW|, 
-      Unison considers the merge to have failed.  If the merge program writes
-      a file called \verb|NEW| but exits with a non-zero status code,
-      then Unison 
-      considers the merge to have succeeded but to have generated conflicts.
-      In this case, it attempts to invoke an external editor so that the
-      user can resolve the conflicts.  The value of the \verb|editor| 
-      preference controls what editor is invoked by Unison.  The default
-      is \verb|emacs|.
-
-  \item Please send us suggestions for other useful values of the
-       \verb|merge2| and \verb|merge| preferences -- we'd like to give several 
-       examples in the manual.
-\end{itemize}
-\end{itemize}
-
-\item Smaller changes:
-\begin{itemize}
-\item When one preference file includes another, unison no longer adds the
-  suffix '\verb|.prf|' to the included file by default.  If a file with 
-  precisely the given name exists in the .unison directory, it will be used; 
-  otherwise Unison will 
-  add \verb|.prf|, as it did before.  (This change means that included 
-  preference files can be named \verb|blah.include| instead of 
-  \verb|blah.prf|, so that unison will not offer them in its 'choose 
-  a preference file' dialog.)
-\item For Linux systems, we now offer both a statically linked and a dynamically
-  linked executable.  The static one is larger, but will probably run on more
-  systems, since it doesn't depend on the same versions of dynamically
-  linked library modules being available.
-\item Fixed the \verb|force| and \verb|prefer| preferences, which were
-  getting the propagation direction exactly backwards.
-\item Fixed a bug in the startup code that would cause unison to crash
-  when the default profile (\verb|~/.unison/default.prf|) does not exist.
-\item Fixed a bug where, on the run when a profile is first created, 
-  Unison would confusingly display the roots in reverse order in the user
-  interface.
-\end{itemize}
-
-\item For developers:
-\begin{itemize}
-\item We've added a module dependency diagram to the source distribution, in
-   \verb|src/DEPENDENCIES.ps|, to help new prospective developers with
-   navigating the code. 
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.6.11}
-\item \incompatible{} Archive format has changed.  
-
-\item \incompatible{} The startup sequence has been completely rewritten
-and greatly simplified.  The main user-visible change is that the
-\verb|defaultpath| preference has been removed.  Its effect can be
-approximated by using multiple profiles, with \verb|include| directives
-to incorporate common settings.  All uses of \verb|defaultpath| in
-existing profiles should be changed to \verb|path|.
-
-Another change in startup behavior that will affect some users is that it
-is no longer possible to specify roots {\em both} in the profile {\em
-  and} on the command line.
-
-You can achieve a similar effect, though, by breaking your profile into
-two:
-\begin{verbatim}
-  
-  default.prf = 
-      root = blah
-      root = foo
-      include common
-
-  common.prf = 
-      <everything else>
-\end{verbatim}
-Now do
-\begin{verbatim}
-  unison common root1 root2
-\end{verbatim}
-when you want to specify roots explicitly.
-
-\item The \verb|-prefer| and \verb|-force| options have been extended to
-allow users to specify that files with more recent modtimes should be
-propagated, writing either \verb|-prefer newer| or \verb|-force newer|.
-(For symmetry, Unison will also accept \verb|-prefer older| or
-\verb|-force older|.)  The \verb|-force older/newer| options can only be
-used when \verb|-times| is also set.
-
-The graphical user interface provides access to these facilities on a
-one-off basis via the \verb|Actions| menu.
-
-\item Names of roots can now be ``aliased'' to allow replicas to be
-relocated without changing the name of the archive file where Unison
-stores information between runs.  (This feature is for experts only.  See
-the ``Archive Files'' section of the manual for more information.)
-
-\item Graphical user-interface:
-\begin{itemize}
-\item A new command is provided in the Synchronization menu for
-  switching to a new profile without restarting Unison from scratch.
-\item The GUI also supports one-key shortcuts for commonly
-used profiles.  If a profile contains a preference of the form 
-%
-'\verb|key = n|', where \verb|n| is a single digit, then pressing this
-key will cause Unison to immediately switch to this profile and begin
-synchronization again from scratch.  (Any actions that may have been
-selected for a set of changes currently being displayed will be
-discarded.) 
-
-\item Each profile may include a preference '\verb|label = <string>|' giving a
-  descriptive string that described the options selected in this profile.
-  The string is listed along with the profile name in the profile selection
-  dialog, and displayed in the top-right corner of the main Unison window.
-\end{itemize}
-
-\item Minor:
-\begin{itemize}
-\item Fixed a bug that would sometimes cause the 'diff' display to order
-  the files backwards relative to the main user interface.  (Thanks
-  to Pascal Brisset for this fix.)
-\item On Unix systems, the graphical version of Unison will check the
-  \verb|DISPLAY| variable and, if it is not set, automatically fall back
-  to the textual user interface.
-\item Synchronization paths (\verb|path| preferences) are now matched
-  against the ignore preferences.  So if a path is both specified in a
-  \verb|path| preference and ignored, it will be skipped.
-\item Numerous other bugfixes and small improvements.
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.6.1}
-\item The synchronization of modification times has been disabled for
-  directories.
-
-\item Preference files may now include lines of the form
-  \verb+include <name>+, which will cause \verb+name.prf+ to be read
-  at that point.
-
-\item The synchronization of permission between Windows and Unix now
-  works properly.
-
-\item A binding \verb|CYGWIN=binmode| in now added to the environment
-  so that the Cygwin port of OpenSSH works properly in a non-Cygwin
-  context.
-
-\item The \verb|servercmd| and \verb|addversionno| preferences can now
-  be used together: \verb|-addversionno| appends an appropriate
-  \verb+-NNN+ to the server command, which is found by using the value
-  of the \verb|-servercmd| preference if there is one, or else just
-  \verb|unison|.
-
-\item Both \verb|'-pref=val'| and \verb|'-pref val'| are now allowed for
-  boolean values.  (The former can be used to set a preference to false.)
-
-\item Lot of small bugs fixed.
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.5.31}
-\item The \verb|log| preference is now set to \verb|true| by default,
-  since the log file seems useful for most users.  
-\item Several miscellaneous bugfixes (most involving symlinks).
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.5.25}
-\item \incompatible{} Archive format has changed (again).  
-
-\item Several significant bugs introduced in 2.5.25 have been fixed.  
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.5.1}
-\item \incompatible{} Archive format has changed.  Make sure you
-synchronize your replicas before upgrading, to avoid spurious
-conflicts.  The first sync after upgrading will be slow.
-
-\item New functionality:
-\begin{itemize}
-\item Unison now synchronizes file modtimes, user-ids, and group-ids.  
-
-These new features are controlled by a set of new preferences, all of
-which are currently \verb|false| by default.  
-
-\begin{itemize}
-\item When the \verb|times| preference is set to \verb|true|, file
-modification times are propaged.  (Because the representations of time
-may not have the same granularity on both replicas, Unison may not always
-be able to make the modtimes precisely equal, but it will get them as
-close as the operating systems involved allow.)
-\item When the \verb|owner| preference is set to \verb|true|, file
-ownership information is synchronized.
-\item When the \verb|group| preference is set to \verb|true|, group 
-information is synchronized.
-\item When the \verb|numericIds| preference is set to \verb|true|, owner
-and group information is synchronized numerically.  By default, owner and
-group numbers are converted to names on each replica and these names are
-synchronized.  (The special user id 0 and the special group 0 are never
-mapped via user/group names even if this preference is not set.)
-\end{itemize}
-
-\item Added an integer-valued preference \verb|perms| that can be used to
-control the propagation of permission bits.  The value of this preference
-is a mask indicating which permission bits should be synchronized.  It is
-set by default to $0o1777$: all bits but the set-uid and set-gid bits are
-synchronised (synchronizing theses latter bits can be a security hazard).
-If you want to synchronize all bits, you can set the value of this
-preference to $-1$.
-
-\item Added a \verb|log| preference (default \verb|false|), which makes
-Unison keep a complete record of the changes it makes to the replicas.
-By default, this record is written to a file called \verb|unison.log| in
-the user's home directory (the value of the \verb|HOME| environment
-variable).  If you want it someplace else, set the \verb|logfile|
-preference to the full pathname you want Unison to use.
-
-\item Added an \verb|ignorenot| preference that maintains a set of patterns 
-  for paths that should definitely {\em not} be ignored, whether or not
-  they match an \verb|ignore| pattern.  (That is, a path will now be ignored
-  iff it matches an ignore pattern and does not match any ignorenot patterns.)
-\end{itemize}
-  
-\item User-interface improvements:
-\begin{itemize}
-\item Roots are now displayed in the user interface in the same order
-as they were given on the command line or in the preferences file.
-\item When the \verb|batch| preference is set, the graphical user interface no 
-  longer waits for user confirmation when it displays a warning message: it
-  simply pops up an advisory window with a Dismiss button at the bottom and
-  keeps on going.
-\item Added a new preference for controlling how many status messages are
-  printed during update detection: \verb|statusdepth| controls the maximum
-  depth for paths on the local machine (longer paths are not displayed, nor
-  are non-directory paths).  The value should be an integer; default is 1.  
-\item Removed the \verb|trace| and \verb|silent| preferences.  They did
-not seem very useful, and there were too many preferences for controlling
-output in various ways.
-\item The text UI now displays just the default command (the one that
-will be used if the user just types \verb|<return>|) instead of all
-available commands.  Typing \verb|?| will print the full list of
-possibilities.
-\item The function that finds the canonical hostname of the local host
-(which is used, for example, in calculating the name of the archive file
-used to remember which files have been synchronized) normally uses the
-\verb|gethostname| operating system call.  However, if the environment
-variable \verb|UNISONLOCALHOSTNAME| is set, its value will now be used
-instead.  This makes it easier to use Unison in situations where a
-machine's name changes frequently (e.g., because it is a laptop and gets
-moved around a lot).
-\item File owner and group are now displayed in the ``detail window'' at
-the bottom of the screen, when unison is configured to synchronize them.
-\end{itemize}
-
-\item For hackers:
-\begin{itemize}
-\item Updated to Jacques Garrigue's new version of \verb|lablgtk|, which
-  means we can throw away our local patched version.  
-
-  If you're compiling the GTK version of unison from sources, you'll need
-  to update your copy of lablgtk to the developers release.
-  (Warning: installing lablgtk under Windows is currently a bit
-  challenging.) 
-
-\item The TODO.txt file (in the source distribution) has been cleaned up
-and reorganized.  The list of pending tasks should be much easier to
-make sense of, for people that may want to contribute their programming
-energies.  There is also a separate file BUGS.txt for open bugs.
-\item The Tk user interface has been removed (it was not being maintained
-and no longer compiles).
-\item The \verb|debug| preference now prints quite a bit of additional
-information that should be useful for identifying sources of problems.
-\item The version number of the remote server is now checked right away 
-  during the connection setup handshake, rather than later.  (Somebody
-  sent a bug report of a server crash that turned out to come from using
-  inconsistent versions: better to check this earlier and in a way that
-  can't crash either client or server.)
-\item Unison now runs correctly on 64-bit architectures (e.g. Alpha
-linux).  We will not be distributing binaries for these architectures
-ourselves (at least for a while) but if someone would like to make them
-available, we'll be glad to provide a link to them.
-\end{itemize}
-
-\item Bug fixes:
-\begin{itemize}
-\item Pattern matching (e.g. for \verb|ignore|) is now case-insensitive
-  when Unison is in case-insensitive mode (i.e., when one of the replicas
-  is on a windows machine).
-\item Some people had trouble with mysterious failures during
-  propagation of updates, where files would be falsely reported as having
-  changed during synchronization.  This should be fixed.
-\item Numerous smaller fixes.
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.4.1}
-\item Added a number of 'sorting modes' for the user interface.  By
-default, conflicting changes are displayed at the top, and the rest of
-the entries are sorted in alphabetical order.  This behavior can be
-changed in the following ways:
-\begin{itemize}
-\item Setting  the \verb|sortnewfirst| preference to \verb|true| causes
-newly created files to be displayed before changed files.
-\item Setting \verb|sortbysize| causes files to be displayed in
-increasing order of size.
-\item Giving the preference \verb|sortfirst=<pattern>| (where
-\verb|<pattern>| is a path descriptor in the same format as 'ignore' and 'follow'
-patterns, causes paths matching this pattern to be displayed first.
-\item Similarly, giving the preference \verb|sortlast=<pattern>| 
-causes paths matching this pattern to be displayed last.
-\end{itemize}
-The sorting preferences are described in more detail in the user manual.
-The \verb|sortnewfirst| and \verb|sortbysize| flags can also be accessed
-from the 'Sort' menu in the grpahical user interface.
-
-\item Added two new preferences that can be used to change unison's
-fundamental behavior to make it more like a mirroring tool instead of
-a synchronizer.
-\begin{itemize}
-\item Giving the preference \verb|prefer| with argument \verb|<root>|
-(by adding \verb|-prefer <root>| to the command line or \verb|prefer=<root>|)
-to your profile) means that, if there is a conflict, the contents of
-\verb|<root>| 
-should be propagated to the other replica (with no questions asked).
-Non-conflicting changes are treated as usual.
-\item Giving the preference \verb|force| with argument \verb|<root>|
-will make unison resolve {\em all} differences in favor of the given
-root, even if it was the other replica that was changed.
-\end{itemize}
-These options should be used with care!  (More information is available in
-the manual.)
-
-\item Small changes:
-\begin{itemize}
-\item 
-Changed default answer to 'Yes' in all two-button dialogs in the 
-  graphical interface (this seems more intuitive).
-
-\item The \verb|rsync| preference has been removed (it was used to
-activate rsync compression for file transfers, but rsync compression is
-now enabled by default). 
-\item  In the text user interface, the arrows indicating which direction
-changes are being 
-  propagated are printed differently when the user has overridded Unison's
-  default recommendation (\verb|====>| instead of \verb|---->|).  This
-  matches the behavior of the graphical interface, which displays such
-  arrows in a different color.
-\item Carriage returns (Control-M's) are ignored at the ends of lines in
-  profiles, for Windows compatibility.
-\item All preferences are now fully documented in the user manual. 
-\end{itemize}
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.3.12}
-\item \incompatible{} Archive format has changed.  Make sure you
-synchronize your replicas before upgrading, to avoid spurious
-conflicts.  The first sync after upgrading will be slow.
-
-\item New/improved functionality:
-\begin{itemize}
-\item  A new preference -sortbysize controls the order in which changes
-  are displayed to the user: when it is set to true, the smallest
-  changed files are displayed first.  (The default setting is false.) 
-\item A new preference -sortnewfirst causes newly created files to be 
-  listed before other updates in the user interface.
-\item We now allow the ssh protocol to specify a port.  
-\item Incompatible change: The unison: protocol is deprecated, and we added
-  file: and socket:.  You may have to modify your profiles in the
-  .unison directory.
-  If a replica is specified without an explicit protocol, we now
-  assume it refers to a file.  (Previously "//saul/foo" meant to use
-  SSH to connect to saul, then access the foo directory.  Now it means
-  to access saul via a remote file mechanism such as samba; the old
-  effect is now achieved by writing {\tt ssh://saul/foo}.)
-\item Changed the startup sequence for the case where roots are given but
-  no profile is given on the command line.  The new behavior is to
-  use the default profile (creating it if it does not exist), and
-  temporarily override its roots.  The manual claimed that this case
-  would work by reading no profile at all, but AFAIK this was never
-  true.
-\item In all user interfaces, files with conflicts are always listed first
-\item A new preference 'sshversion' can be used to control which version
-  of ssh should be used to connect to the server.  Legal values are 1 and 2.
-  (Default is empty, which will make unison use whatever version of ssh
-  is installed as the default 'ssh' command.)
-\item The situation when the permissions of a file was updated the same on
-  both side is now handled correctly (we used to report a spurious conflict)
-
-\end{itemize}
-
-\item Improvements for the Windows version:
-\begin{itemize}
-\item The fact that filenames are treated case-insensitively under
-Windows should now be handled correctly.  The exact behavior is described
-in the cross-platform section of the manual.
-\item It should be possible to synchronize with Windows shares, e.g.,
-  //host/drive/path.
-\item Workarounds to the bug in syncing root directories in Windows.
-The most difficult thing to fix is an ocaml bug: Unix.opendir fails on
-c: in some versions of Windows.
-\end{itemize}
-
-\item Improvements to the GTK user interface (the Tk interface is no
-longer being maintained): 
-\begin{itemize}
-\item The UI now displays actions differently (in blue) when they have been
-  explicitly changed by the user from Unison's default recommendation.
-\item More colorful appearance.
-\item The initial profile selection window works better.
-\item If any transfers failed, a message to this effect is displayed along with
-  'Synchronization complete' at the end of the transfer phase (in case they
-  may have scrolled off the top).
-\item Added a global progress meter, displaying the percentage of {\em total}
-  bytes that have been transferred so far.
-\end{itemize}
-
-\item Improvements to the text user interface:
-\begin{itemize}
-\item The file details will be displayed automatically when a
-  conflict is been detected.
-\item when a warning is generated (e.g. for a temporary
-  file left over from a previous run of unison) Unison will no longer
-  wait for a response if it is running in -batch mode.
-\item The UI now displays a short list of possible inputs each time it waits
-  for user interaction.  
-\item The UI now quits immediately (rather than looping back and starting
-  the interaction again) if the user presses 'q' when asked whether to 
-  propagate changes.
-\item Pressing 'g' in the text user interface will proceed immediately
-  with propagating updates, without asking any more questions.
-\end{itemize}
-
-\item Documentation and installation changes:
-\begin{itemize}
-\item The manual now includes a FAQ, plus sections on common problems and
-on tricks contributed by users.
-\item Both the download page and the download directory explicitly say
-what are the current stable and beta-test version numbers.
-\item The OCaml sources for the up-to-the-minute developers' version (not
-guaranteed to be stable, or even to compile, at any given time!) are now
-available from the download page.
-\item Added a subsection to the manual describing cross-platform
-  issues (case conflicts, illegal filenames)
-\end{itemize}
-
-\item Many small bug fixes and random improvements.
-
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.3.1}
-\item Several bug fixes.  The most important is a bug in the rsync
-module that would occasionally cause change propagation to fail with a
-'rename' error.
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.2}
-\item The multi-threaded transport system is now disabled by default.
-(It is not stable enough yet.)
-\item Various bug fixes.
-\item A new experimental feature: 
-
-  The final component of a -path argument may now be the wildcard 
-  specifier \verb|*|.  When Unison sees such a path, it expands this path on 
-  the client into into the corresponding list of paths by listing the
-  contents of that directory.  
-
-  Note that if you use wildcard paths from the command line, you will
-  probably need to use quotes or a backslash to prevent the * from
-  being interpreted by your shell.
-
-  If both roots are local, the contents of the first one will be used
-  for expanding wildcard paths.  (Nb: this is the first one {\em after} the
-  canonization step -- i.e., the one that is listed first in the user 
-  interface -- not the one listed first on the command line or in the
-  preferences file.)
-\end{changesfromversion}
-
-\begin{changesfromversion}{2.1}
-\item The transport subsystem now includes an implementation by
-Sylvain Gommier and Norman Ramsey of Tridgell and Mackerras's
-\verb|rsync| protocol.  This protocol achieves much faster 
-transfers when only a small part of a large file has been changed by
-sending just diffs.  This feature is mainly helpful for transfers over
-slow links---on fast local area networks it can actually degrade
-performance---so we have left it off by default.  Start unison with
-the \verb|-rsync| option (or put \verb|rsync=true| in your preferences
-file) to turn it on.
-
-\item ``Progress bars'' are now diplayed during remote file transfers,
-showing what percentage of each file has been transferred so far.
-
-\item The version numbering scheme has changed.  New releases will now
-      be have numbers like 2.2.30, where the second component is
-      incremented on every significant public release and the third
-      component is the ``patch level.''
-
-\item Miscellaneous improvements to the GTK-based user interface.
-\item The manual  is now available in PDF format.
-
-\item We are experimenting with using a multi-threaded transport
-subsystem to transfer several files at the same time, making
-much more effective use of available network bandwidth.  This feature
-is not completely stable yet, so by default it is disabled in the
-release version of Unison.
-
-If you want to play with the multi-threaded version, you'll need to
-recompile Unison from sources (as described in the documentation),
-setting the THREADS flag in Makefile.OCaml to true.  Make sure that
-your OCaml compiler has been installed with the \verb|-with-pthreads|
-configuration option.  (You can verify this by checking whether the
-file \verb|threads/threads.cma| in the OCaml standard library
-directory contains the string \verb|-lpthread| near the end.)
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.292}
-\item Reduced memory footprint (this is especially important during
-the first run of unison, where it has to gather information about all
-the files in both repositories). 
-\item Fixed a bug that would cause the socket server under NT to fail
-  after the client exits. 
-\item Added a SHIFT modifier to the Ignore menu shortcut keys in GTK
-  interface (to avoid hitting them accidentally).  
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.231}
-\item Tunneling over ssh is now supported in the Windows version.  See
-the installation section of the manual for detailed instructions.
-
-\item The transport subsystem now includes an implementation of the
-\verb|rsync| protocol, built by Sylvain Gommier and Norman Ramsey.
-This protocol achieves much faster transfers when only a small part of
-a large file has been changed by sending just diffs.  The rsync
-feature is off by default in the current version.  Use the
-\verb|-rsync| switch to turn it on.  (Nb.  We still have a lot of
-tuning to do: you may not notice much speedup yet.)
-
-\item We're experimenting with a multi-threaded transport subsystem,
-written by Jerome Vouillon.  The downloadable binaries are still
-single-threaded: if you want to try the multi-threaded version, you'll
-need to recompile from sources.  (Say \verb|make THREADS=true|.)
-Native thread support from the compiler is required.  Use the option
-\verb|-threads N| to select the maximal number of concurrent 
-threads (default is 5).  Multi-threaded
-and single-threaded clients/servers can interoperate.  
-
-\item A new GTK-based user interface is now available, thanks to
-Jacques Garrigue.  The Tk user interface still works, but we'll be
-shifting development effort to the GTK interface from now on.
-\item OCaml 3.00 is now required for compiling Unison from sources.
-The modules \verb|uitk| and \verb|myfileselect| have been changed to
-use labltk instead of camltk.  To compile the Tk interface in Windows,
-you must have ocaml-3.00 and tk8.3.  When installing tk8.3, put it in
-\verb|c:\Tcl| rather than the suggested \verb|c:\Program Files\Tcl|, 
-and be sure to install the headers and libraries (which are not 
-installed by default).
-
-\item Added a new \verb|-addversionno| switch, which causes unison to
-use \verb|unison-<currentversionnumber>| instead of just \verb|unison|
-as the remote server command.  This allows multiple versions of unison
-to coexist conveniently on the same server: whichever version is run
-on the client, the same version will be selected on the server.
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.219}
-\item \incompatible{} Archive format has changed.  Make sure you
-synchronize your replicas before upgrading, to avoid spurious
-conflicts.  The first sync after upgrading will be slow.
-
-\item This version fixes several annoying bugs, including:
-\begin{itemize}
-\item Some cases where propagation of file permissions was not
-working.
-\item umask is now ignored when creating directories
-\item directories are create writable, so that a read-only directory and
-    its contents can be propagated.
-\item Handling of warnings generated by the server.
-\item Synchronizing a path whose parent is not a directory on both sides is
-now flagged as erroneous.  
-\item Fixed some bugs related to symnbolic links and nonexistant roots.
-\begin{itemize}
-\item 
-   When a change (deletion or new contents) is propagated onto a 
-     'follow'ed symlink, the file pointed to by the link is now changed.
-     (We used to change the link itself, which doesn't fit our assertion
-     that 'follow' means the link is completely invisible)
-   \item When one root did not exist, propagating the other root on top of it
-     used to fail, becuase unison could not calculate the working directory
-     into which to write changes.  This should be fixed.
-\end{itemize}
-\end{itemize}
-
-\item A human-readable timestamp has been added to Unison's archive files.
-
-\item The semantics of Path and Name regular expressions now
-correspond better. 
-
-\item Some minor improvements to the text UI (e.g. a command for going
-back to previous items)
-
-\item The organization of the export directory has changed --- should
-be easier to find / download things now.
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.200}
-\item \incompatible{} Archive format has changed.  Make sure you
-synchronize your replicas before upgrading, to avoid spurious
-conflicts.  The first sync after upgrading will be slow.
-
-\item This version has not been tested extensively on Windows.
-
-\item Major internal changes designed to make unison safer to run
-at the same time as the replicas are being changed by the user.
-
-\item Internal performance improvements.  
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.190}
-\item \incompatible{} Archive format has changed.  Make sure you
-synchronize your replicas before upgrading, to avoid spurious
-conflicts.  The first sync after upgrading will be slow.
-
-\item A number of internal functions have been changed to reduce the
-amount of memory allocation, especially during the first
-synchronization.  This should help power users with very big replicas.
-
-\item Reimplementation of low-level remote procedure call stuff, in
-preparation for adding rsync-like smart file transfer in a later
-release.   
-
-\item Miscellaneous bug fixes.
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.180}
-\item \incompatible{} Archive format has changed.  Make sure you
-synchronize your replicas before upgrading, to avoid spurious
-conflicts.  The first sync after upgrading will be slow.
-
-\item Fixed some small bugs in the interpretation of ignore patterns. 
-
-\item Fixed some problems that were preventing the Windows version
-from working correctly when click-started.
-
-\item Fixes to treatment of file permissions under Windows, which were
-causing spurious reports of different permissions when synchronizing
-between windows and unix systems.
-
-\item Fixed one more non-tail-recursive list processing function,
-which was causing stack overflows when synchronizing very large
-replicas. 
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.169}
-\item The text user interface now provides commands for ignoring
-  files. 
-\item We found and fixed some {\em more} non-tail-recursive list
-  processing functions.  Some power users have reported success with
-  very large replicas.
-\item \incompatible 
-Files ending in \verb|.tmp| are no longer ignored automatically.  If you want
-to ignore such files, put an appropriate ignore pattern in your profile.
-
-\item \incompatible{} The syntax of {\tt ignore} and {\tt follow}
-patterns has changed. Instead of putting a line of the form
-\begin{verbatim}
-                 ignore = <regexp>
-\end{verbatim}
-  in your profile ({\tt .unison/default.prf}), you should put:
-\begin{verbatim}
-                 ignore = Regex <regexp>
-\end{verbatim}
-Moreover, two other styles of pattern are also recognized:
-\begin{verbatim}
-                 ignore = Name <name>
-\end{verbatim}
-matches any path in which one component matches \verb|<name>|, while
-\begin{verbatim}
-                 ignore = Path <path>
-\end{verbatim}
-matches exactly the path \verb|<path>|.
-
-Standard ``globbing'' conventions can be used in \verb|<name>| and
-\verb|<path>|:  
-\begin{itemize}
-\item a \verb|?| matches any single character except \verb|/|
-\item a \verb|*| matches any sequence of characters not including \verb|/|
-\item \verb|[xyz]| matches any character from the set $\{{\tt x},
-  {\tt y}, {\tt z} \}$
-\item \verb|{a,bb,ccc}| matches any one of \verb|a|, \verb|bb|, or
-  \verb|ccc|. 
-\end{itemize}
-
-See the user manual for some examples.
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.146}
-\item Some users were reporting stack overflows when synchronizing
-  huge directories.  We found and fixed some non-tail-recursive list
-  processing functions, which we hope will solve the problem.  Please 
-  give it a try and let us know.
-\item Major additions to the documentation.  
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.142}
-\item Major internal tidying and many small bugfixes.
-\item Major additions to the user manual.
-\item Unison can now be started with no arguments -- it will prompt
-automatically for the name of a profile file containing the roots to
-be synchronized.  This makes it possible to start the graphical UI
-from a desktop icon.
-\item Fixed a small bug where the text UI on NT was raising a 'no such
-  signal' exception.
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.139}
-\item The precompiled windows binary in the last release was compiled
-with an old OCaml compiler, causing propagation of permissions not to
-work (and perhaps leading to some other strange behaviors we've heard
-reports about).  This has been corrected.  If you're using precompiled
-binaries on Windows, please upgrade.
-\item Added a \verb|-debug| command line flag, which controls debugging
-of various modules.  Say \verb|-debug XXX| to enable debug tracing for
-module \verb|XXX|, or \verb|-debug all| to turn on absolutely everything.
-\item Fixed a small bug where the text UI on NT was raising a 'no such signal'
-exception.
-\end{changesfromversion}
-
-\begin{changesfromversion}{1.111}
-\item \incompatible{} The names and formats of the preference files in
-the .unison directory have changed.  In particular:
-\begin{itemize}
-\item the file ``prefs'' should be renamed to default.prf
-\item the contents of the file ``ignore'' should be merged into
-  default.prf.  Each line of the form \verb|REGEXP| in ignore should
-  become a line of the form \verb|ignore = REGEXP| in default.prf.
-\end{itemize}
-\item Unison now handles permission bits and  symbolic links.  See the
-manual for details.
-
-\item You can now have different preference files in your .unison
-directory.  If you start unison like this
-\begin{verbatim}
-             unison profilename
-\end{verbatim}
-(i.e. with just one ``anonymous'' command-line argument), then the
-file \verb|~/.unison/profilename.prf| will be loaded instead of
-\verb|default.prf|. 
-
-\item Some improvements to terminal handling in the text user interface
-
-\item Added a switch -killServer that terminates the remote server process
-when the unison client is shutting down, even when using sockets for 
-communication.  (By default, a remote server created using ssh/rsh is 
-terminated automatically, while a socket server is left running.)
-\item When started in 'socket server' mode, unison prints 'server started' on
-  stderr when it is ready to accept connections.  
-  (This may be useful for scripts that want to tell when a socket-mode server 
-  has finished initalization.)
-\item We now make a nightly mirror of our current internal development
-  tree, in case anyone wants an up-to-the-minute version to hack
-  around with.
-\item Added a file CONTRIB with some suggestions for how to help us
-make Unison better.
-\end{changesfromversion}
-

Copied: branches/2.45/doc/changes.tex (from rev 486, trunk/doc/changes.tex)
===================================================================
--- branches/2.45/doc/changes.tex	                        (rev 0)
+++ branches/2.45/doc/changes.tex	2012-04-02 15:54:47 UTC (rev 487)
@@ -0,0 +1,1901 @@
+\begin{changesfromversion}{2.40.63}
+\item New preference {\tt fastercheckUNSAFE}, which can be used (with care!)
+to achieve {\em much} faster update detection when all the common files in
+the two replicas are known to be identical.  See the manual for more
+information.
+
+This feature should still be considered experimental, but it's ready for
+other people to try out.
+\item Added option {\tt clientHostName}. If specified, it will be used to as
+the client host name, overriding {\tt UNISONLOCALHOSTNAME} and the actual
+host name.
+\item OS X GUI: 
+\begin{itemize}
+\item fix crash under Lion, because of problems with the toolbar, using the
+fix suggested in {\tt http://blitzbasic.com/Community/posts.php?topic=95778}.
+
+\item uimacnew09 is now the standard graphical interface on OSX
+\item A small improvement to the uimacnew09 interface from Alan Schmitt
+  and Steve Kalkwarf: when Unison is run with the -batch flag, the
+  interface will now automatically propagate changes and terminate,
+  without waiting for user interaction.
+\item Show a modal warning window if there is no archive for the hosts.  The
+user can then choose to exit or proceed (proceed is the default). The window
+is not shown if the {\tt batch} preference is true.
+\item file details panel selectable
+\end{itemize}
+\item GTK GUI:
+\begin{itemize}
+\item New version of uigtk2.ml from Matt Zagrabelny that reorganizes the
+icons in a slightly more intuitive way.
+\end{itemize}
+\item Minor fixes:
+\begin{itemize}
+\item Setting the {\tt prefer} preference to {\tt older} or {\tt newer} now
+propagates deletions when there is no conflict.
+\item Correctly quote the path when running merge commands.
+\item Add quotes to paths when calling external file watcher utility.
+\item Incorporate a patch to fsmonitor.py (the external filewatcher
+  utility) from Tomasz Zernicki to make it work better under Windows.
+\item Incorporated new version of fsmonitor.py from Christophe Gohle
+\item Fixed incompatibility with OpenSSH 5.6.
+\item Fixed fingerprint cache: do not cache file properties
+\item Some spelling corrections in documentation and comments from Stephane
+Glondu 
+\item Fixed O_APPEND mode for open under Windows (the previous attempt in
+  revision 422 was incomplete)
+\item Fixed String.sub invalid argument error when an AppleDouble file does
+  not contain a finder information field
+\item Trim duplicate paths when using "-repeat watch"
+\item Unison now passes path arguments and --follow directives to
+  fsmonitor.py.  This seems to work except for one small issue with
+  how fsmonitor.py treats {\tt -follow} directives for directories that
+  don't exist (or maybe this is an issue with how it treats any kind
+  of monitoring when the thing being monitored doesn't exist?).  If we
+  create a symlink to a nonexistant directory, give Unison (hence
+  fsmonitor.py) a 'follow' directive for the symlink, start unison, and
+  {\em then} create the directory, fsmonitor.py misses the change.
+\item Lines added in profile files by unison always start at a new line
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.40.1}
+\item Added "BelowPath" patterns, that match a path as well as all paths below
+  (convenient to use with no{deletion,update,creation}partial preferences)
+\item Added a "fat" preference that makes Unison use the right options
+  when one of the replica is on a FAT filesystem.
+\item Allow "prefer/force=newer" even when not synchronizing modification
+  times.  (The reconciler will not be aware of the modification time
+  of unchanged files, so the synchronization choices of Unison can be
+  different from when "times=true", but the behavior remains sane:
+  changed files with the most recent modification time will be
+  propagated.)
+\item Minor fixes and improvements:
+\begin{itemize}
+\item Compare filenames up to decomposition in case sensitive mode when
+  one host is running MacOSX and the unicode preference is set to
+  true.
+\item Rsync: somewhat faster compressor
+\item Make Unicode the default on all architectures (it was only the
+  default when a Mac OS X or Windows machine was involved).
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.32}
+\item Major enhancement: Unicode support.  
+\begin{itemize}
+\item Unison should now handle unicode filenames correctly on all platforms.
+\item This functionality is controlled by a new preference {\tt unicode}.
+\item 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.
+\end{itemize}
+\item Partial transfer of directories.  If an error occurs while
+  transferring a directory, the part transferred so far is copied into
+  place (and the archives are updated accordingly).
+  The "maxerrors" preference controls how many transfer error Unison
+  will accept before stopping the transfer of a directory (by default,
+  only one).  This makes it possible to transfer most of a directory
+  even if there are some errors.  Currently, only the first error is
+  reported by the GUIs.
+
+  Also, allow partial transfer of a directory when there was an error deep
+  inside this directory during update detection.  At the moment, this
+  is only activated with the text and GTK UIs, which have been
+  modified so that they show that the transfer is going to be partial
+  and so that they can display all errors.
+\item Improvement to the code for resuming directory transfers:
+\begin{itemize}
+\item 
+   if a file was not correctly transferred (or the source has been
+    modified since, with unchanged size), Unison performs a new
+    transfer rather than failing
+  \item spurious files are deleted (this can happen if a file is deleted
+    on the source replica before resuming the transfer; not deleting
+    the file would result in it reappearing on the target replica)
+\end{itemize}
+\item Experimental streaming protocol for transferring file contents (can
+  be disabled by setting the directive "stream" to false): file
+  contents is transfered asynchronously (without waiting for a response
+  from the destination after each chunk sent) rather than using the
+  synchronous RPC mechanism.  As a consequence:
+  \begin{itemize}
+  \item 
+   Unison now transfers the contents of a single file at a time
+    (Unison used to transfer several contents simultaneously in order
+    to hide the connection latency.)
+  \item the transfer of large files uses the full available bandwidth
+    and is not slowed done due to the connection latency anymore
+  \item we get performance improvement for small files as well by
+    scheduling many files simultaneously (as scheduling a file for
+    transfer consume little ressource: it does not mean allocating a
+    large buffer anymore)
+  \end{itemize}
+\item Changes to the internal implementation of the rsync algorithm:
+\begin{itemize}
+\item 
+  use longer blocks for large files (the size of a block is the
+    square root of the size of the file for large files);
+  \item transmit less checksum information per block (we still have less
+    than one chance in a hundred million of transferring a file
+    incorrectly, and Unison will catch any transfer error when
+    fingerprinting the whole file)
+  \item avoid transfer overhead (which was 4 bytes per block)
+\end{itemize}
+  For a 1G file, the first optimization saves a factor 50 on the
+  amount of data transferred from the target to the source (blocks
+  are 32768 bytes rather than just 700 bytes).  The two other
+  optimizations save another factor of 2 (from 24 bytes per block
+  down to 10).
+\item 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.
+  \begin{itemize}
+  \item 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.
+  \end{itemize}
+\item New preferences "noupdate=root", "nodeletion=root", "nocreation=root"
+  that prevent Unison from performing files updates, deletions or
+  creations on the given root.  Also 'partial' versions of 'noupdate',
+  'nodeletion' and 'nocreation' 
+\item Limit the number of simultaneous external copy program
+  ("copymax" preference)
+\item New "links" preference.  When set to false, Unison will report an
+  error on symlinks during update detection.  (This is the default
+  when one host is running Windows but not Cygwin.)  This is better
+  than failing during propagation.
+\item Added a preference "halfduplex" to force half-duplex communication
+  with the server.  This may be useful on unreliable links (as a more
+  efficient alternative to "maxthreads = 1").
+\item Renamed preference "pretendwin" to "ignoreinodenumbers" (an alias is
+  kept for backwards compatibility).
+\item Ignore one-second differences when synchronizing modification time.
+  (Technically, this is an incompatible archive format change, but it
+   is backward compatible.  To trigger a problem, a user would have to
+   synchronize modification times on a filesystem with a two-second
+   granularity and then downgrade to a previous version of Unison,
+   which does not work well in such a case.  Thus, it does not
+   seem worthwhile to increment the archive format number, which would
+   impact all users.)
+\item Do not keep many files simultaneously opened anymore when the rsync
+  algorithm is in use.
+\item Add ``ignorearchives'' preference to ignore existing archives (to
+  avoid forcing users to delete them manually, in situations where one
+  archive has gotten   deleted or corrupted).
+\item Mac OS
+\begin{itemize}
+\item fixed rsync bug which could result in an "index out of bounds"
+  error when transferring resource forks.
+\item Fixed bug which made Unison ignore finder information and resource
+  fork when compiled to 64bit on Mac OSX.
+\item should now be 64 bit clean (the Growl framework is not up to date,
+    though)
+\item 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)
+\item use darker grey arrows (patch contributed by Eric Y. Kow)
+\end{itemize}
+\item GTK user interface
+\begin{itemize}
+\item  assistant for creating profiles
+\item profile editor
+\item pop up a summary window when the replicas are not fully
+    synchronized after transport
+\item display estimated remaining time and transfer rate on the
+  progress bar
+\item allow simultaneous selection of several items
+\item Do not reload the preference file before a new update
+  detection if it is unchanged
+\item disabled scrolling to the first unfinished item during transport.
+  It goes way too fast when lot of small files are synchronized, and it
+  makes it impossible to browse the file list during transport.
+\item take into account the "height" preference again
+\item the internal list of selected reconciler item was not always in
+    sync with what was displayed (GTK bug?); workaround implemented
+\item Do not display "Looking for change" messages during propagation
+  (when checking the targe is unchanged) but only during update detection
+\item Apply patch to fix some crashes in the OSX GUI, thanks to Onne Gorter.
+\end{itemize}
+\item Text UI
+\begin{itemize}
+\item During update detection, display status by updating a single line
+rather than generating a new line of output every so often.  Should be less
+confusing.
+\end{itemize}
+\item Windows 
+\begin{itemize}
+\item 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.
+\item Only use long UNC path for accessing replicas (as '..' is
+  not handled with this format of paths, but can be useful)
+\item Windows text UI: now put the console into UTF-8 output mode.  This
+  is the right thing to do when in Unicode mode, and is no worse than
+  what we had previously otherwise (the console use some esoteric
+  encoding by default).  This only works when using a Unicode font
+  instead of the default raster font.
+\item Don't get the home directory from environment variable HOME under
+  Windows (except for Cygwin binaries): we don't want the behavior of
+  Unison to depends on whether it is run from a Cygwin shell (where
+  HOME is set) or in any other way (where HOME is usually not set).
+\end{itemize}
+\item Miscellaneous fixes and improvements
+\begin{itemize}
+\item Made a server waiting on a socket more resilient to unexpected
+  lost connections from the client.
+\item Small patch to property setting code suggested by Ulrich Gernkow.
+\item Several fixes to the change transfer functions (both the internal ones
+  and external transfers using rsync).  In particular, limit the number of
+  simultaneous transfer using an rsync
+  (as the rsync algorithm can use a large amount of memory when
+   processing huge files)
+\item Keep track of which file contents are being transferred, and delay
+  the transfer of a file when another file with the same contents is
+  currently being transferred.  This way, the second transfer can be
+  skipped and replaced by a local copy.
+\item Experimental update detection optimization:
+  do not read the contents of unchanged directories
+\item When a file transfer fails, turn off fastcheck for this file on the
+  next sync.
+\item Fixed bug with case insensitive mode on a case sensitive filesystem:
+\begin{itemize}
+\item 
+   if file "a/a" is created on one replica and directory "A" is
+    created on the other, the file failed to be synchronized the first
+    time Unison is run afterwards, as Unison uses the wrong path "a/a"
+    (if Unison is run again, the directories are in the archive, so
+     the right path is used);
+  \item if file "a" appears on one replica and file "A" appears on the
+    other with different contents, Unison was unable to synchronize
+    them.
+\end{itemize}
+\item Improved error reporting when the destination is updated during
+  synchronization: Unison now tells which file has been updated, and how.
+\item Limit the length of temporary file names
+\item Case sensitivity information put in the archive (in a backward
+  compatible way) and checked when the archive is loaded
+\item Got rid of the 16mb marshalling limit by marshalling to a bigarray.
+\item Resume copy of partially transferred files.
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.31}
+\item Small user interface changes
+\begin{itemize}
+\item Small change to text UI "scanning..." messages, to print just
+  directories (hopefully making it clearer that individual files are
+  not necessarily being fingerprinted).  
+\end{itemize}
+\item Minor fixes and improvements:
+\begin{itemize}
+\item Ignore one hour differences when deciding whether a file may have
+  been updated.  This avoids slow update detection after daylight
+  saving time changes under Windows.  This makes Unison slightly more
+  likely to miss an update, but it should be safe enough.
+\item Fix a small bug that was affecting mainly windows users.  We need to
+  commit the archives at the end of the sync even if there are no
+  updates to propagate because some files (in fact, if we've just
+  switched to DST on windows, a LOT of files) might have new modtimes
+  in the archive.  (Changed the text UI only.  It's less clear where
+  to change the GUI.)
+\item Don't delete the temp file when a transfer fails due to a
+  fingerprint mismatch (so that we can have a look and see why!)  We've also
+  added more debugging code togive more informative error messages when we
+  encounter the dreaded and longstanding "assert failed during file
+  transfer" bug
+\item Incorrect paths ("path" directive) now result in an error update
+  item rather than a fatal error.
+\item Create parent directories (with correct permissions) during
+  transport for paths which point to non-existent locations in the
+  destination replica.
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.27}
+\item If Unison is interrupted during a directory transfer, it will now
+leave the partially transferred directory intact in a temporary
+location. (This maintains the invariant that new files/directories are
+transferred either completely or not at all.)  The next time Unison is run,
+it will continue filling in this temporary directory, skipping transferring
+files that it finds are already there.
+\item We've added experimental support for invoking an external file
+transfer tool for whole-file copies instead of Unison's built-in transfer
+protocol.  Three new preferences have been added:
+\begin{itemize}
+\item {\tt copyprog} is a string giving the name (and command-line
+switches, if needed) of an external program that can be used to copy large
+files efficiently.  By default, rsync is invoked, but other tools such as
+scp can be used instead by changing the value of this preference.  (Although
+this is not its primary purpose, rsync is actually a pretty fast way of
+copying files that don't already exist on the receiving host.)  For files
+that do already exist on (but that have been changed in one replica), Unison
+will always use its built-in implementation of the rsync algorithm.
+\item Added a "copyprogrest" preference, so that we can give different
+command lines for invoking the external copy utility depending on whether a
+partially transferred file already exists or not.  (Rsync doesn't seem to
+care about this, but other utilities may.)
+\item {\tt copythreshold} is an integer (-1 by default), indicating above what
+filesize (in megabytes) Unison should use the external copying utility
+specified by copyprog.  Specifying 0 will cause ALL copies to use the
+external program; a negative number will prevent any files from using it.
+(Default is -1.)
+\end{itemize}
+Thanks to Alan Schmitt for a huge amount of hacking and to an anonymous
+sponsor for suggesting and underwriting this extension.
+\item Small improvements:
+\begin{itemize}
+\item Added a new preference, {\tt dontchmod}.  By default, Unison uses the
+{\tt 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 {\tt chmod}.
+\item Don't ignore files that look like backup files if the {\tt
+  backuplocation} preference is set to {\tt central}
+\item Shortened the names of several preferences.  The old names are also
+still supported, for backwards compatibility, but they do not appear in the
+documentation.
+\item Lots of little documentation tidying.  (In particular, preferences are
+separated into Basic and Advanced!  This should hopefully make Unison a
+little more approachable for new users.
+\item Unison can sometimes fail to transfer a file, giving the unhelpful
+message "Destination updated during synchronization" even though the file
+has not been changed.  This can be caused by programs that change either the
+file's contents \emph{or} the file's extended attributes without changing
+its modification time.  It's not clear what is the best fix for this -- it
+is not Unison's fault, but it makes Unison's behavior puzzling -- but at
+least Unison can be more helpful about suggesting a workaround (running once
+with {\tt fastcheck} set to false).  The failure message has been changed to
+give this advice.
+\item Further improvements to the OS X GUI (thanks to Alan Schmitt and Craig
+Federighi).
+\end{itemize}
+\item Very preliminary support for triggering Unison from an external 
+  filesystem-watching utility.  The current implementation is very
+  simple, not efficient, and almost completely untested---not ready 
+  for real users.  But if someone wants to help improve it (e.g.,
+  by writing a filesystem watcher for your favorite OS), please make
+  yourself known!
+
+  On the Unison side, the new behavior is very simple:
+  \begin{itemize}
+  \item use the text UI 
+    \item start Unison with the command-line flag "-repeat FOO", 
+      where FOO is name of a file where Unison should look 
+      for notifications of changes
+    \item  when it starts up, Unison will read the whole contents 
+      of this file (on both hosts), which should be a 
+      newline-separated list of paths (relative to the root 
+      of the synchronization) and synchronize just these paths, 
+      as if it had been started with the "-path=xxx" option for 
+      each one of them
+    \item when it finishes, it will sleep for a few seconds and then
+      examine the watchfile again; if anything has been added, it
+      will read the new paths, synchronize them, and go back to 
+      sleep
+    \item that's it!
+  \end{itemize}
+  To use this to drive Unison "incrementally," just start it in 
+  this mode and start up a tool (on each host) to watch for
+  new changes to the filesystem and append the appropriate paths
+  to the watchfile.  Hopefully such tools should not be too hard
+  to write.
+\item Bug fixes:
+\begin{itemize}
+\item Fixed a bug that was causing new files to be created with
+  permissions 0x600 instead of using a reasonable default (like
+  0x644), if the 'perms' flag was set to 0.  (Bug reported by Ben
+  Crowell.)
+\item Follow maxthreads preference when transferring directories.
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.17}
+\item Major rewrite and cleanup of the whole Mac OS X graphical user
+interface by Craig Federighi.  Thanks, Craig!!!
+\item Small fix to ctime (non-)handling in update detection under windows
+  with fastcheck.  
+\item Several small fixes to the GTK2 UI to make it work better under
+Windows [thanks to Karl M for these].
+\item The backup functionality has been completely rewritten.  The external
+interface has not changed, but numerous bugs, irregular behaviors, and
+cross-platform inconsistencies have been corrected.
+\item The Unison project now accepts donations via PayPal.  If you'd like to
+donate, you can find a link to the donation page on the
+\URL{http://www.cis.upenn.edu/~bcpierce/unison/lists.html}{Unison home
+  page}.
+\item Some important safety improvements:
+\begin{itemize}
+\item Added a new \verb|mountpoint| preference, which can be used to specify
+a path that must exist in both replicas at the end of update detection
+(otherwise Unison aborts).  This can be used to avoid potentially dangerous
+situations when Unison is used with removable media such as external hard
+drives and compact flash cards.  
+\item The confirmation of ``big deletes'' is now controlled by a boolean preference
+  \verb|confirmbigdeletes|.  Default is true, which gives the same behavior as
+  previously.  (This functionality is at least partly superceded by the
+  \verb|mountpoint| preference, but it has been left in place in case it is
+  useful to some people.)
+  \item If Unison is asked to ``follow'' a symbolic link but there is
+  nothing at the other end of the link, it will now flag this path as an
+  error, rather than treating the symlink itself as missing or deleted.
+  This avoids a potentially dangerous situation where a followed symlink
+  points to an external filesystem that might be offline when Unison is run
+  (whereupon Unison would cheerfully delete the corresponding files in the
+  other replica!).
+\end{itemize}
+
+\item Smaller changes:
+\begin{itemize}
+\item Added \verb|forcepartial| and \verb|preferpartial| preferences, which
+behave like \verb|force| and \verb|prefer| but can be specified on a
+per-path basis. [Thanks to Alan Schmitt for this.]
+\item A bare-bones self test feature was added, which runs unison through
+  some of its paces and checks that the results are as expected.  The
+  coverage of the tests is still very limited, but the facility has already
+  been very useful in debugging the new backup functionality (especially in
+  exposing some subtle cross-platform issues).
+\item Refined debugging code so that the verbosity of individual modules
+  can be controlled separately.  Instead of just putting '-debug
+  verbose' on the command line, you can put '-debug update+', which
+  causes all the extra messages in the Update module, but not other
+  modules, to be printed.  Putting '-debug verbose' causes all modules
+  to print with maximum verbosity.
+\item Removed \verb|mergebatch| preference.  (It never seemed very useful, and
+  its semantics were confusing.)
+\item Rewrote some of the merging functionality, for better cooperation
+  with external Harmony instances.
+\item Changed the temp file prefix from \verb|.#| to \verb|.unison|.
+\item Compressed the output from the text user interface (particularly
+  when run with the \verb|-terse| flag) to make it easier to interpret the
+  results when Unison is run several times in succession from a script.
+\item Diff and merge functions now work under Windows.
+\item Changed the order of arguments to the default diff command (so that
+  the + and - annotations in diff's output are reversed).
+\item Added \verb|.mpp| files to the ``never fastcheck'' list (like
+\verb|.xls| files). 
+\end{itemize}
+
+\item Many small bugfixes, including:
+\begin{itemize}
+\item Fixed a longstanding bug regarding fastcheck and daylight saving time
+  under Windows when Unison is set up to synchronize modification times.
+  (Modification times cannot be updated in the archive in this case,
+  so we have to ignore one hour differences.)
+\item Fixed a bug that would occasionally cause the archives to be left in
+  non-identical states on the two hosts after synchronization.
+\item Fixed a bug that prevented Unison from communicating correctly between
+  32- and 64-bit architectures.
+\item On windows, file creation times are no longer used as a proxy for
+  inode numbers.  (This is unfortunate, as it makes fastcheck a little less
+  safe.  But it turns out that file creation times are not reliable 
+  under Windows: if a file is removed and a new file is created in its
+  place, the new one will sometimes be given the same creation date as the
+  old one!)
+\item Set read-only file to R/W on OSX before attempting to change other attributes.
+\item Fixed bug resulting in spurious "Aborted" errors during transport
+(thanks to Jerome Vouillon) 
+\item Enable diff if file contents have changed in one replica, but
+only properties in the other.
+\item Removed misleading documentation for 'repeat' preference.
+\item Fixed a bug in merging code where Unison could sometimes deadlock
+  with the external merge program, if the latter produced large
+  amounts of output.
+\item Workaround for a bug compiling gtk2 user interface against current versions
+  of gtk2+ libraries.  
+\item Added a better error message for "ambiguous paths".
+\item Squashed a longstanding bug that would cause file transfer to fail
+  with the message ``Failed: Error in readWrite: Is a directory.''
+\item Replaced symlinks with copies of their targets in the Growl framework in src/uimac.
+  This should make the sources easier to check out from the svn repository on WinXP
+  systems.
+\item Added a workaround (suggested by Karl M.) for the problem discussed
+  on the unison users mailing list where, on the Windows platform, the
+  server would hang when transferring files.  I conjecture that
+  the problem has to do with the RPC mechanism, which was used to
+  make a call {\em back} from the server to the client (inside the Trace.log 
+  function) so that the log message would be appended to the log file on 
+  the client.  The workaround is to dump these messages (about when
+  xferbycopying shortcuts are applied and whether they succeed) just to the
+  standard output of the Unison process, not to the log file.
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.13.0}
+\item The features for performing backups and for invoking external merge
+programs have been completely rewritten by Stephane Lescuyer (thanks,
+Stephane!).  The user-visible functionality should not change, but the
+internals have been rationalized and there are a number of new features.
+See the manual (in particular, the description of the \verb|backupXXX|
+preferences) for details.
+\item Incorporated patches for ipv6 support, contributed by Samuel Thibault.
+(Note that, due to a bug in the released OCaml 3.08.3 compiler, this code
+will not actually work with ipv6 unless compiled with the CVS version of the
+OCaml compiler, where the bug has been fixed; however, ipv4 should continue
+to work normally.)
+\item OSX interface:
+\begin{itemize}
+\item Incorporated Ben Willmore's cool new icon for the Mac UI.
+\end{itemize}
+\item Small fixes:
+\begin{itemize}
+\item Fixed off by one error in month numbers (in printed dates) reported 
+  by Bob Burger
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.12.0}
+\item New convention for release numbering: Releases will continue to be
+given numbers of the form \verb|X.Y.Z|, but, 
+from now on, just the major version number (\verb|X.Y|) will be considered
+significant when checking compatibility between client and server versions.
+The third component of the version number will be used only to identify
+``patch levels'' of releases.
+
+This change goes hand in hand with a change to the procedure for making new
+releases.  Candidate releases will initially be given ``beta release''
+status when they are announced for public consumption.  Any bugs that are
+discovered will be fixed in a separate branch of the source repository
+(without changing the major version number) and new tarballs re-released as
+needed.  When this process converges, the patched beta version will be
+dubbed stable.
+\item Warning (failure in batch mode) when one path is completely emptied.
+  This prevents Unison from deleting everything on one replica when
+  the other disappear.
+\item Fix diff bug (where no difference is shown the first time the diff
+  command is given).
+\item User interface changes:
+\begin{itemize}
+\item Improved workaround for button focus problem (GTK2 UI)
+\item Put leading zeroes in date fields
+\item More robust handling of character encodings in GTK2 UI
+\item Changed format of modification time displays, from \verb|modified at hh:mm:ss on dd MMM, yyyy|
+to \verb|modified on yyyy-mm-dd hh:mm:ss|
+\item Changed time display to include seconds (so that people on FAT
+  filesystems will not be confused when Unison tries to update a file
+  time to an odd number of seconds and the filesystem truncates it to
+  an even number!)
+\item Use the diff "-u" option by default when showing differences between files
+  (the output is more readable)
+\item In text mode, pipe the diff output to a pager if the environment
+  variable PAGER is set
+\item Bug fixes and cleanups in ssh password prompting.  Now works with
+  the GTK2 UI under Linux.  (Hopefully the Mac OS X one is not broken!)
+\item Include profile name in the GTK2 window name
+\item Added bindings ',' (same as '<') and '.' (same as '>') in the GTK2 UI
+\end{itemize}
+\item Mac GUI:
+\begin{itemize}
+\item actions like < and > scroll to the next item as necessary.
+\item Restart has a menu item and keyboard shortcut (command-R).
+\item 
+    Added a command-line tool for Mac OS X.  It can be installed from
+    the Unison menu.
+\item New icon.
+\item   Handle the "help" command-line argument properly.
+\item   Handle profiles given on the command line properly.
+\item  When a profile has been selected, the profile dialog is replaced by a
+    "connecting" message while the connection is being made.  This
+    gives better feedback.
+\item   Size of left and right columns is now large enough so that
+    "PropsChanged" is not cut off.
+\end{itemize}
+\item Minor changes:
+\begin{itemize}
+\item Disable multi-threading when both roots are local
+\item Improved error handling code.  In particular, make sure all files
+  are closed in case of a transient failure
+\item Under Windows, use \verb|$UNISON| for home directory as a last resort
+  (it was wrongly moved before \verb|$HOME| and \verb|$USERPROFILE| in
+  Unison 2.12.0)
+\item Reopen the logfile if its name changes (profile change)
+\item Double-check that permissions and modification times have been
+  properly set: there are some combination of OS and filesystem on
+  which setting them can fail in a silent way.
+\item Check for bad Windows filenames for pure Windows synchronization
+  also (not just cross architecture synchronization).
+  This way, filenames containing backslashes, which are not correctly
+  handled by unison, are rejected right away.
+\item Attempt to resolve issues with synchronizing modification times
+  of read-only files under Windows
+\item Ignore chmod failures when deleting files
+\item Ignore trailing dots in filenames in case insensitive mode
+\item Proper quoting of paths, files and extensions ignored using the UI
+\item The strings CURRENT1 and CURRENT2 are now correctly substitued when
+  they occur in the diff preference
+\item Improvements to syncing resource forks between Macs via a non-Mac system.
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.10.2}
+\item \incompatible{} Archive format has changed.  
+\item Source code availability: The Unison sources are now managed using
+  Subversion.  One nice side-effect is that anonymous checkout is now
+  possible, like this:
+\begin{verbatim}
+        svn co https://cvs.cis.upenn.edu:3690/svnroot/unison/
+\end{verbatim}
+We will also continue to export a ``developer tarball'' of the current
+(modulo one day) sources in the web export directory.  To receive commit logs
+for changes to the sources, subscribe to the \verb|unison-hackers| list
+(\ONEURL{http://www.cis.upenn.edu/~bcpierce/unison/lists.html}). 
+\item Text user interface:
+\begin{itemize}
+\item Substantial reworking of the internal logic of the text UI to make it
+a bit easier to modify.
+\item The {\tt dumbtty} flag in the text UI is automatically set to true if
+the client is running on a Unix system and the {\tt EMACS} environment
+variable is set to anything other than the empty string.
+\end{itemize}
+\item Native OS X gui:
+\begin{itemize}
+\item Added a synchronize menu item with keyboard shortcut
+\item Added a merge menu item, still needs to be debugged
+\item Fixes to compile for Panther
+\item Miscellaneous improvements and bugfixes
+\end{itemize}
+\item Small changes:
+\begin{itemize}
+\item Changed the filename checking code to apply to Windows only, instead
+  of OS X as well.
+\item Finder flags now synchronized
+\item Fallback in copy.ml for filesystem that do not support \verb|O_EXCL|
+\item  Changed buffer size for local file copy (was highly inefficient with
+  synchronous writes)
+\item Ignore chmod failure when deleting a directory
+\item  Fixed assertion failure when resolving a conflict content change /
+  permission changes in favor of the content change.
+\item Workaround for transferring large files using rsync.
+\item Use buffered I/O for files (this is the only way to open files in binary
+  mode under Cygwin).
+\item On non-Cygwin Windows systems, the UNISON environment variable is now checked first to determine 
+  where to look for Unison's archive and preference files, followed by \verb|HOME| and 
+  \verb|USERPROFILE| in that order.  On Unix and Cygwin systems, \verb|HOME| is used.
+\item Generalized \verb|diff| preference so that it can be given either as just 
+  the command name to be used for calculating diffs or else a whole command
+  line, containing the strings \verb|CURRENT1| and \verb|CURRENT2|, which will be replaced
+  by the names of the files to be diff'ed before the command is called.
+\item Recognize password prompts in some newer versions of ssh.
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.9.20}
+\item \incompatible{} Archive format has changed.  
+\item Major functionality changes:
+\begin{itemize}
+\item Major tidying and enhancement of 'merge' functionality.  The main
+  user-visible change is that the external merge program may either write
+  the merged output to a single new file, as before, or it may modify one or
+  both of its input files, or it may write {\em two} new files.  In the
+  latter cases, its modifications will be copied back into place on both the
+  local and the remote host, and (if the two files are now equal) the
+  archive will be updated appropriately.  More information can be found in
+  the user manual.  Thanks to Malo Denielou and Alan Schmitt for these
+  improvements.
+
+  Warning: the new merging functionality is not completely compatible with
+  old versions!  Check the manual for details.
+\item Files larger than 2Gb are now supported.
+\item Added preliminary (and still somewhat experimental) support for the
+  Apple OS X operating system.   
+\begin{itemize}
+\item Resource forks should be transferred correctly.  (See the manual for
+details of how this works when synchronizing HFS with non-HFS volumes.)
+Synchronization of file type and creator information is also supported.
+\item On OSX systems, the name of the directory for storing Unison's
+archives, preference files, etc., is now determined as follows:
+\begin{itemize}
+    \item if \verb+~/.unison+ exists, use it
+     \item otherwise, use \verb|~/Library/Application Support/Unison|, 
+         creating it if necessary.
+\end{itemize}
+\item A preliminary native-Cocoa user interface is under construction.  This
+still needs some work, and some users experience unpredictable crashes, so
+it is only for hackers for now.  Run make with {\tt UISTYLE=mac} to build
+this interface.
+\end{itemize}
+\end{itemize}
+
+\item Minor functionality changes:
+\begin{itemize}
+\item Added an {\tt ignorelocks} preference, which forces Unison to override left-over
+  archive locks.  (Setting this preference is dangerous!  Use it only if you
+  are positive you know what you are doing.) 
+% BCP: removed later
+% \item Running with the {\tt -timers} flag set to true will now show the total time taken
+%   to check for updates on each directory.  (This can be helpful for tidying directories to improve
+%   update detection times.)  
+\item Added a new preference {\tt assumeContentsAreImmutable}.  If a directory
+  matches one of the patterns set in this preference, then update detection
+  is skipped for files in this directory.  (The 
+  purpose is to speed update detection for cases like Mail folders, which
+  contain lots and lots of immutable files.)  Also a preference
+  {\tt assumeContentsAreImmutableNot}, which overrides the first, similarly
+  to {\tt ignorenot}.  (Later amendment: these preferences are now called
+  {\tt immutable} and {\tt immutablenot}.)
+\item The {\tt ignorecase} flag has been changed from a boolean to a three-valued
+  preference.  The default setting, called {\tt default}, checks the operating systems
+  running on the client and server and ignores filename case if either of them is
+  OSX or Windows.  Setting ignorecase to {\tt true} or {\tt false} overrides
+  this behavior.  If you have been setting {\tt ignorecase} on the command
+  line using {\tt -ignorecase=true} or {\tt -ignorecase=false}, you will
+  need to change to {\tt -ignorecase true} or {\tt -ignorecase false}.
+\item a new preference, 'repeat', for the text user interface (only).  If 'repeat' is set to
+  a number, then, after it finishes synchronizing, Unison will wait for that many seconds and
+  then start over, continuing this way until it is killed from outside.  Setting repeat to true
+  will automatically set the batch preference to true.  
+\item Excel files are now handled specially, so that the {\tt fastcheck}
+  optimization is skipped even if the {\tt fastcheck} flag is set.  (Excel
+  does some naughty things with modtimes, making this optimization
+  unreliable and leading to failures during change propagation.)
+\item The ignorecase flag has been changed from a boolean to a three-valued
+  preference.  The default setting, called 'default', checks the operating systems
+  running on the client and server and ignores filename case if either of them is
+  OSX or Windows.  Setting ignorecase to 'true' or 'false' overrides this behavior.
+\item Added a new preference, 'repeat', for the text user interface (only,
+  at the moment).  If 'repeat' is set to a number, then, after it finishes
+  synchronizing, Unison will wait for that many seconds and then start over,
+  continuing this way until it is killed from outside.  Setting repeat to
+  true will automatically set the batch preference to true.
+\item The 'rshargs' preference has been split into 'rshargs' and 'sshargs' 
+  (mainly to make the documentation clearer).  In fact, 'rshargs' is no longer
+  mentioned in the documentation at all, since pretty much everybody uses
+  ssh now anyway.
+\end{itemize}
+\item Documentation
+\begin{itemize}
+\item The web pages have been completely redesigned and reorganized.
+  (Thanks to Alan Schmitt for help with this.)
+\end{itemize}
+\item User interface improvements
+\begin{itemize}
+\item Added a GTK2 user interface, capable (among other things) of displaying filenames
+  in any locale encoding.  Kudos to Stephen Tse for contributing this code!  
+\item The text UI now prints a list of failed and skipped transfers at the end of
+  synchronization. 
+\item Restarting update detection from the graphical UI will reload the current
+  profile (which in particular will reset the -path preference, in case
+  it has been narrowed by using the ``Recheck unsynchronized items''
+  command).
+\item Several small improvements to the text user interface, including a
+  progress display.
+\end{itemize}
+\item Bug fixes (too numerous to count, actually, but here are some):
+\begin{itemize}
+\item The {\tt maxthreads} preference works now.
+\item Fixed bug where warning message about uname returning an unrecognized
+  result was preventing connection to server.  (The warning is no longer
+  printed, and all systems where 'uname' returns anything other than 'Darwin' 
+  are assumed not to be running OS X.)
+\item Fixed a problem on OS X that caused some valid file names (e.g.,
+  those including colons) to be considered invalid.
+\item Patched Path.followLink to follow links under cygwin in addition to Unix
+  (suggested by Matt Swift).
+\item Small change to the storeRootsName function, suggested by bliviero at 
+  ichips.intel.com, to fix a problem in unison with the `rootalias'
+  option, which allows you to tell unison that two roots contain the same 
+  files.  Rootalias was being applied after the hosts were 
+  sorted, so it wouldn't work properly in all cases.
+\item Incorporated a fix by Dmitry Bely for setting utimes of read-only files
+  on Win32 systems.   
+\end{itemize}
+\item Installation / portability:
+\begin{itemize}
+\item Unison now compiles with OCaml version 3.07 and later out of the box.
+\item Makefile.OCaml fixed to compile out of the box under OpenBSD.
+\item a few additional ports (e.g. OpenBSD, Zaurus/IPAQ) are now mentioned in 
+  the documentation 
+\item Unison can now be installed easily on OSX systems using the Fink
+  package manager
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.9.1}
+\item Added a preference {\tt maxthreads} that can be used to limit the
+number of simultaneous file transfers.
+\item Added a {\tt backupdir} preference, which controls where backup
+files are stored.
+\item Basic support added for OSX.  In particular, Unison now recognizes
+when one of the hosts being synchronized is running OSX and switches to
+a case-insensitive treatment of filenames (i.e., 'foo' and 'FOO' are
+considered to be the same file).
+  (OSX is not yet fully working,
+  however: in particular, files with resource forks will not be
+  synchronized correctly.)
+\item The same hash used to form the archive name is now also added to
+the names of the temp files created during file transfer.  The reason for
+this is that, during update detection, we are going to silently delete
+any old temp files that we find along the way, and we want to prevent
+ourselves from deleting temp files belonging to other instances of Unison
+that may be running in parallel, e.g. synchronizing with a different
+host.  Thanks to Ruslan Ermilov for this suggestion.
+\item Several small user interface improvements
+\item Documentation
+\begin{itemize}
+\item FAQ and bug reporting instructions have been split out as separate
+      HTML pages, accessible directly from the unison web page.
+\item Additions to FAQ, in particular suggestions about performance
+tuning. 
+\end{itemize}
+\item Makefile
+\begin{itemize}
+\item Makefile.OCaml now sets UISTYLE=text or UISTYLE=gtk automatically,
+  depending on whether it finds lablgtk installed
+\item Unison should now compile ``out of the box'' under OSX
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.8.1}
+\item Changing profile works again under Windows
+\item File movement optimization: Unison now tries to use local copy instead of
+  transfer for moved or copied files.  It is controled by a boolean option
+  ``xferbycopying''.
+\item Network statistics window (transfer rate, amount of data transferred).
+      [NB: not available in Windows-Cygwin version.]
+\item symlinks work under the cygwin version (which is dynamically linked).
+\item Fixed potential deadlock when synchronizing between Windows and
+Unix 
+\item Small improvements:
+  \begin{itemize} 
+  \item If neither the {\tt USERPROFILE} nor the {\tt HOME} environment
+    variables are set, then Unison will put its temporary commit log
+    (called {\tt DANGER.README}) into the directory named by the 
+    {\tt UNISON} environment variable, if any; otherwise it will use
+    {\tt C:}.
+  \item alternative set of values for fastcheck: yes = true; no = false;
+  default = auto.
+  \item -silent implies -contactquietly
+  \end{itemize}
+\item Source code:
+  \begin{itemize}
+  \item Code reorganization and tidying.  (Started breaking up some of the
+    basic utility modules so that the non-unison-specific stuff can be
+    made available for other projects.)
+  \item several Makefile and docs changes (for release);
+  \item further comments in ``update.ml'';
+  \item connection information is not stored in global variables anymore.
+  \end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.7.78}
+\item Small bugfix to textual user interface under Unix (to avoid leaving
+  the terminal in a bad state where it would not echo inputs after Unison
+  exited).
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.7.39}
+\item Improvements to the main web page (stable and beta version docs are
+  now both accessible).
+\item User manual revised.
+\item Added some new preferences:
+\begin{itemize}
+\item ``sshcmd'' and ``rshcmd'' for specifying paths to ssh and rsh programs.
+\item ``contactquietly'' for suppressing the ``contacting server'' message
+during Unison startup (under the graphical UI).
+\end{itemize}
+\item Bug fixes:
+\begin{itemize}
+\item Fixed small bug in UI that neglected to change the displayed column 
+  headers if loading a new profile caused the roots to change.
+\item Fixed a bug that would put the text UI into an infinite loop if it
+  encountered a conflict when run in batch mode.
+\item Added some code to try to fix the display of non-Ascii characters in 
+  filenames on Windows systems in the GTK UI.  (This code is currently 
+  untested---if you're one of the people that had reported problems with
+  display of non-ascii filenames, we'd appreciate knowing if this actually 
+  fixes things.)
+\item `\verb|-prefer/-force newer|' works properly now.  
+        (The bug was reported by Sebastian Urbaniak and Sean Fulton.)
+\end{itemize}
+\item User interface and Unison behavior:
+\begin{itemize}
+\item Renamed `Proceed' to `Go' in the graphical UI.
+\item Added exit status for the textual user interface.
+\item Paths that are not synchronized because of conflicts or errors during 
+  update detection are now noted in the log file.
+\item \verb|[END]| messages in log now use a briefer format
+\item Changed the text UI startup sequence so that
+  {\tt ./unison -ui text} will use the default profile instead of failing.
+\item Made some improvements to the error messages.
+\item Added some debugging messages to remote.ml.
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.7.7}
+\item Incorporated, once again, a multi-threaded transport sub-system.
+  It transfers several files at the same time, thereby making much
+  more effective use of available network bandwidth.  Unlike the
+  earlier attempt, this time we do not rely on the native thread
+  library of OCaml.  Instead, we implement a light-weight,
+  non-preemptive multi-thread library in OCaml directly.  This version
+  appears stable.  
+
+  Some adjustments to unison are made to accommodate the multi-threaded
+  version.  These include, in particular, changes to the
+  user interface and logging, for example:
+  \begin{itemize}
+  \item Two log entries for each transferring task, one for the
+    beginning, one for the end.
+  \item Suppressed warning messages against removing temp files left
+    by a previous unison run, because warning does not work nicely
+    under multi-threading.  The temp file names are made less likely
+    to coincide with the name of a file created by the user.  They
+    take the form \\ \verb|.#<filename>.<serial>.unison.tmp|.
+    [N.b. This was later changed to \verb|.unison.<filename>.<serial>.unison.tmp|.]
+  \end{itemize}
+\item Added a new command to the GTK user interface: pressing 'f' causes
+  Unison to start a new update detection phase, using as paths {\em just}
+  those paths that have been detected as changed and not yet marked as
+  successfully completed.  Use this command to quickly restart Unison on
+  just the set of paths still needing attention after a previous run.
+\item Made the {\tt ignorecase} preference user-visible, and changed the
+  initialization code so that it can be manually set to true, even if
+  neither host is running Windows.  (This may be useful, e.g., when using 
+  Unison running on a Unix system with a FAT volume mounted.)
+\item Small improvements and bug fixes:
+  \begin{itemize}
+  \item Errors in preference files now generate fatal errors rather than
+    warnings at startup time.  (I.e., you can't go on from them.)  Also,
+    we fixed a bug that was preventing these warnings from appearing in the
+    text UI, so some users who have been running (unsuspectingly) with 
+    garbage in their prefs files may now get error reports.
+  \item Error reporting for preference files now provides file name and
+    line number.
+  \item More intelligible message in the case of identical change to the same 
+    files: ``Nothing to do: replicas have been changed only in identical 
+    ways since last sync.''
+  \item Files with prefix '.\#' excluded when scanning for preference
+    files.
+  \item Rsync instructions are send directly instead of first
+    marshaled.
+  \item Won't try forever to get the fingerprint of a continuously changing file:
+    unison will give up after certain number of retries.
+  \item Other bug fixes, including the one reported by Peter Selinger
+    (\verb|force=older preference| not working).
+  \end{itemize}
+\item Compilation:
+  \begin{itemize}
+  \item Upgraded to the new OCaml 3.04 compiler, with the LablGtk
+    1.2.3 library (patched version used for compiling under Windows).
+  \item Added the option to compile unison on the Windows platform with
+    Cygwin GNU C compiler.  This option only supports building
+    dynamically linked unison executables.
+  \end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.7.4}
+\item Fixed a silly (but debilitating) bug in the client startup sequence.
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.7.1}
+\item Added \verb|addprefsto| preference, which (when set) controls which
+preference file new preferences (e.g. new ignore patterns) are added to.
+\item Bug fix: read the initial connection header one byte at a time, so
+that we don't block if the header is shorter than expected.  (This bug
+did not affect normal operation --- it just made it hard to tell when you
+were trying to use Unison incorrectly with an old version of the server,
+since it would hang instead of giving an error message.)
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.6.59}
+\item Changed \verb|fastcheck| from a boolean to a string preference.  Its 
+  legal values are \verb|yes| (for a fast check), \verb|no| (for a safe 
+  check), or \verb|default| (for a fast check---which also happens to be 
+  safe---when running on Unix and a safe check when on Windows).  The default 
+  is \verb|default|.
+  \item Several preferences have been renamed for consistency.  All
+  preference names are now spelled out in lowercase.  For backward
+  compatibility, the old names still work, but they are not mentioned in
+  the manual any more.
+\item The temp files created by the 'diff' and 'merge' commands are now
+   named by {\em pre}pending a new prefix to the file name, rather than
+   appending a suffix.  This should avoid confusing diff/merge programs
+   that depend on the suffix  to guess the type of the file contents.
+\item We now set the keepalive option on the server socket, to make sure
+  that the server times out if the communication link is unexpectedly broken. 
+\item Bug fixes:
+\begin{itemize}
+\item When updating small files, Unison now closes the destination file.
+\item File permissions are properly updated when the file is behind a
+  followed link.
+\item Several other small fixes.
+\end{itemize}
+\end{changesfromversion}
+
+
+\begin{changesfromversion}{2.6.38}
+\item Major Windows performance improvement!  
+
+We've added a preference \verb|fastcheck| that makes Unison look only at
+a file's creation time and last-modified time to check whether it has
+changed.  This should result in a huge speedup when checking for updates
+in large replicas.
+
+  When this switch is set, Unison will use file creation times as 
+  'pseudo inode numbers' when scanning Windows replicas for updates, 
+  instead of reading the full contents of every file.  This may cause 
+  Unison to miss propagating an update if the create time, 
+  modification time, and length of the file are all unchanged by 
+  the update (this is not easy to achieve, but it can be done).  
+  However, Unison will never {\em overwrite} such an update with
+  a change from the other replica, since it 
+  always does a safe check for updates just before propagating a 
+  change.  Thus, it is reasonable to use this switch most of the time 
+  and occasionally run Unison once with {\tt fastcheck} set to false, 
+  if you are worried that Unison may have overlooked an update.
+
+  Warning: This change is has not yet been thoroughly field-tested.  If you 
+  set the \verb|fastcheck| preference, pay careful attention to what
+  Unison is doing.
+
+\item New functionality: centralized backups and merging 
+\begin{itemize}
+\item This version incorporates two pieces of major new functionality,
+   implemented by Sylvain Roy during a summer internship at Penn: a
+   {\em centralized backup} facility that keeps a full backup of
+   (selected files 
+   in) each replica, and a {\em merging} feature that allows Unison to
+   invoke an external file-merging tool to resolve conflicting changes to
+   individual files.
+ 
+\item Centralized backups:
+\begin{itemize}
+  \item Unison now maintains full backups of the last-synchronized versions
+      of (some of) the files in each replica; these function both as
+      backups in the usual sense
+      and as the ``common version'' when invoking external
+      merge programs.
+  \item The backed up files are stored in a directory ~/.unison/backup on each
+      host.  (The name of this directory can be changed by setting
+      the environment variable \verb|UNISONBACKUPDIR|.)
+  \item The predicate \verb|backup| controls which files are actually
+     backed up:
+      giving the preference '\verb|backup = Path *|' causes backing up
+      of all files.
+  \item Files are added to the backup directory whenever unison updates
+      its archive.  This means that
+      \begin{itemize}
+       \item When unison reconstructs its archive from scratch (e.g., 
+           because of an upgrade, or because the archive files have
+           been manually deleted), all files will be backed up.
+       \item Otherwise, each file will be backed up the first time unison
+           propagates an update for it.
+      \end{itemize}
+  \item The preference \verb|backupversions| controls how many previous
+      versions of each file are kept.  The default is 2 (i.e., the last 
+      synchronized version plus one backup).
+  \item For backward compatibility, the \verb|backups| preference is also
+      still supported, but \verb|backup| is now preferred.
+  \item It is OK to manually delete files from the backup directory (or to throw
+      away the directory itself).  Before unison uses any of these files for 
+      anything important, it checks that its fingerprint matches the one 
+      that it expects. 
+\end{itemize}
+
+\item Merging:
+\begin{itemize}
+  \item Both user interfaces offer a new 'merge' command, invoked by pressing
+      'm' (with a changed file selected).  
+  \item The actual merging is performed by an external program.  
+      The preferences \verb|merge| and \verb|merge2| control how this
+      program is invoked.  If a backup exists for this file (see the
+      \verb|backup| preference), then the \verb|merge| preference is used for 
+      this purpose; otherwise \verb|merge2| is used.  In both cases, the 
+      value of the preference should be a string representing the command 
+      that should be passed to a shell to invoke the 
+      merge program.  Within this string, the special substrings
+      \verb|CURRENT1|, \verb|CURRENT2|, \verb|NEW|,  and \verb|OLD| may appear
+      at any point.  Unison will substitute these as follows before invoking
+      the command:
+        \begin{itemize}
+        \item \relax\verb|CURRENT1| is replaced by the name of the local 
+        copy of the file;
+        \item \relax\verb|CURRENT2| is replaced by the name of a temporary
+        file, into which the contents of the remote copy of the file have
+        been transferred by Unison prior to performing the merge;
+        \item \relax\verb|NEW| is replaced by the name of a temporary
+        file that Unison expects to be written by the merge program when
+        it finishes, giving the desired new contents of the file; and
+        \item \relax\verb|OLD| is replaced by the name of the backed up
+        copy of the original version of the file (i.e., its state at the 
+        end of the last successful run of Unison), if one exists 
+        (applies only to \verb|merge|, not \verb|merge2|).
+        \end{itemize}
+      For example, on Unix systems setting the \verb|merge| preference to
+\begin{verbatim}
+   merge = diff3 -m CURRENT1 OLD CURRENT2 > NEW
+\end{verbatim}
+      will tell Unison to use the external \verb|diff3| program for merging.  
+
+      A large number of external merging programs are available.  For 
+      example, \verb|emacs| users may find the following convenient:
+\begin{verbatim}
+    merge2 = emacs -q --eval '(ediff-merge-files "CURRENT1" "CURRENT2" 
+               nil "NEW")' 
+    merge = emacs -q --eval '(ediff-merge-files-with-ancestor 
+               "CURRENT1" "CURRENT2" "OLD" nil "NEW")' 
+\end{verbatim}
+(These commands are displayed here on two lines to avoid running off the
+edge of the page.  In your preference file, each should be written on a
+single line.) 
+
+  \item If the external program exits without leaving any file at the
+  path \verb|NEW|, 
+      Unison considers the merge to have failed.  If the merge program writes
+      a file called \verb|NEW| but exits with a non-zero status code,
+      then Unison 
+      considers the merge to have succeeded but to have generated conflicts.
+      In this case, it attempts to invoke an external editor so that the
+      user can resolve the conflicts.  The value of the \verb|editor| 
+      preference controls what editor is invoked by Unison.  The default
+      is \verb|emacs|.
+
+  \item Please send us suggestions for other useful values of the
+       \verb|merge2| and \verb|merge| preferences -- we'd like to give several 
+       examples in the manual.
+\end{itemize}
+\end{itemize}
+
+\item Smaller changes:
+\begin{itemize}
+\item When one preference file includes another, unison no longer adds the
+  suffix '\verb|.prf|' to the included file by default.  If a file with 
+  precisely the given name exists in the .unison directory, it will be used; 
+  otherwise Unison will 
+  add \verb|.prf|, as it did before.  (This change means that included 
+  preference files can be named \verb|blah.include| instead of 
+  \verb|blah.prf|, so that unison will not offer them in its 'choose 
+  a preference file' dialog.)
+\item For Linux systems, we now offer both a statically linked and a dynamically
+  linked executable.  The static one is larger, but will probably run on more
+  systems, since it doesn't depend on the same versions of dynamically
+  linked library modules being available.
+\item Fixed the \verb|force| and \verb|prefer| preferences, which were
+  getting the propagation direction exactly backwards.
+\item Fixed a bug in the startup code that would cause unison to crash
+  when the default profile (\verb|~/.unison/default.prf|) does not exist.
+\item Fixed a bug where, on the run when a profile is first created, 
+  Unison would confusingly display the roots in reverse order in the user
+  interface.
+\end{itemize}
+
+\item For developers:
+\begin{itemize}
+\item We've added a module dependency diagram to the source distribution, in
+   \verb|src/DEPENDENCIES.ps|, to help new prospective developers with
+   navigating the code. 
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.6.11}
+\item \incompatible{} Archive format has changed.  
+
+\item \incompatible{} The startup sequence has been completely rewritten
+and greatly simplified.  The main user-visible change is that the
+\verb|defaultpath| preference has been removed.  Its effect can be
+approximated by using multiple profiles, with \verb|include| directives
+to incorporate common settings.  All uses of \verb|defaultpath| in
+existing profiles should be changed to \verb|path|.
+
+Another change in startup behavior that will affect some users is that it
+is no longer possible to specify roots {\em both} in the profile {\em
+  and} on the command line.
+
+You can achieve a similar effect, though, by breaking your profile into
+two:
+\begin{verbatim}
+  
+  default.prf = 
+      root = blah
+      root = foo
+      include common
+
+  common.prf = 
+      <everything else>
+\end{verbatim}
+Now do
+\begin{verbatim}
+  unison common root1 root2
+\end{verbatim}
+when you want to specify roots explicitly.
+
+\item The \verb|-prefer| and \verb|-force| options have been extended to
+allow users to specify that files with more recent modtimes should be
+propagated, writing either \verb|-prefer newer| or \verb|-force newer|.
+(For symmetry, Unison will also accept \verb|-prefer older| or
+\verb|-force older|.)  The \verb|-force older/newer| options can only be
+used when \verb|-times| is also set.
+
+The graphical user interface provides access to these facilities on a
+one-off basis via the \verb|Actions| menu.
+
+\item Names of roots can now be ``aliased'' to allow replicas to be
+relocated without changing the name of the archive file where Unison
+stores information between runs.  (This feature is for experts only.  See
+the ``Archive Files'' section of the manual for more information.)
+
+\item Graphical user-interface:
+\begin{itemize}
+\item A new command is provided in the Synchronization menu for
+  switching to a new profile without restarting Unison from scratch.
+\item The GUI also supports one-key shortcuts for commonly
+used profiles.  If a profile contains a preference of the form 
+%
+'\verb|key = n|', where \verb|n| is a single digit, then pressing this
+key will cause Unison to immediately switch to this profile and begin
+synchronization again from scratch.  (Any actions that may have been
+selected for a set of changes currently being displayed will be
+discarded.) 
+
+\item Each profile may include a preference '\verb|label = <string>|' giving a
+  descriptive string that described the options selected in this profile.
+  The string is listed along with the profile name in the profile selection
+  dialog, and displayed in the top-right corner of the main Unison window.
+\end{itemize}
+
+\item Minor:
+\begin{itemize}
+\item Fixed a bug that would sometimes cause the 'diff' display to order
+  the files backwards relative to the main user interface.  (Thanks
+  to Pascal Brisset for this fix.)
+\item On Unix systems, the graphical version of Unison will check the
+  \verb|DISPLAY| variable and, if it is not set, automatically fall back
+  to the textual user interface.
+\item Synchronization paths (\verb|path| preferences) are now matched
+  against the ignore preferences.  So if a path is both specified in a
+  \verb|path| preference and ignored, it will be skipped.
+\item Numerous other bugfixes and small improvements.
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.6.1}
+\item The synchronization of modification times has been disabled for
+  directories.
+
+\item Preference files may now include lines of the form
+  \verb+include <name>+, which will cause \verb+name.prf+ to be read
+  at that point.
+
+\item The synchronization of permission between Windows and Unix now
+  works properly.
+
+\item A binding \verb|CYGWIN=binmode| in now added to the environment
+  so that the Cygwin port of OpenSSH works properly in a non-Cygwin
+  context.
+
+\item The \verb|servercmd| and \verb|addversionno| preferences can now
+  be used together: \verb|-addversionno| appends an appropriate
+  \verb+-NNN+ to the server command, which is found by using the value
+  of the \verb|-servercmd| preference if there is one, or else just
+  \verb|unison|.
+
+\item Both \verb|'-pref=val'| and \verb|'-pref val'| are now allowed for
+  boolean values.  (The former can be used to set a preference to false.)
+
+\item Lot of small bugs fixed.
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.5.31}
+\item The \verb|log| preference is now set to \verb|true| by default,
+  since the log file seems useful for most users.  
+\item Several miscellaneous bugfixes (most involving symlinks).
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.5.25}
+\item \incompatible{} Archive format has changed (again).  
+
+\item Several significant bugs introduced in 2.5.25 have been fixed.  
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.5.1}
+\item \incompatible{} Archive format has changed.  Make sure you
+synchronize your replicas before upgrading, to avoid spurious
+conflicts.  The first sync after upgrading will be slow.
+
+\item New functionality:
+\begin{itemize}
+\item Unison now synchronizes file modtimes, user-ids, and group-ids.  
+
+These new features are controlled by a set of new preferences, all of
+which are currently \verb|false| by default.  
+
+\begin{itemize}
+\item When the \verb|times| preference is set to \verb|true|, file
+modification times are propaged.  (Because the representations of time
+may not have the same granularity on both replicas, Unison may not always
+be able to make the modtimes precisely equal, but it will get them as
+close as the operating systems involved allow.)
+\item When the \verb|owner| preference is set to \verb|true|, file
+ownership information is synchronized.
+\item When the \verb|group| preference is set to \verb|true|, group 
+information is synchronized.
+\item When the \verb|numericIds| preference is set to \verb|true|, owner
+and group information is synchronized numerically.  By default, owner and
+group numbers are converted to names on each replica and these names are
+synchronized.  (The special user id 0 and the special group 0 are never
+mapped via user/group names even if this preference is not set.)
+\end{itemize}
+
+\item Added an integer-valued preference \verb|perms| that can be used to
+control the propagation of permission bits.  The value of this preference
+is a mask indicating which permission bits should be synchronized.  It is
+set by default to $0o1777$: all bits but the set-uid and set-gid bits are
+synchronised (synchronizing theses latter bits can be a security hazard).
+If you want to synchronize all bits, you can set the value of this
+preference to $-1$.
+
+\item Added a \verb|log| preference (default \verb|false|), which makes
+Unison keep a complete record of the changes it makes to the replicas.
+By default, this record is written to a file called \verb|unison.log| in
+the user's home directory (the value of the \verb|HOME| environment
+variable).  If you want it someplace else, set the \verb|logfile|
+preference to the full pathname you want Unison to use.
+
+\item Added an \verb|ignorenot| preference that maintains a set of patterns 
+  for paths that should definitely {\em not} be ignored, whether or not
+  they match an \verb|ignore| pattern.  (That is, a path will now be ignored
+  iff it matches an ignore pattern and does not match any ignorenot patterns.)
+\end{itemize}
+  
+\item User-interface improvements:
+\begin{itemize}
+\item Roots are now displayed in the user interface in the same order
+as they were given on the command line or in the preferences file.
+\item When the \verb|batch| preference is set, the graphical user interface no 
+  longer waits for user confirmation when it displays a warning message: it
+  simply pops up an advisory window with a Dismiss button at the bottom and
+  keeps on going.
+\item Added a new preference for controlling how many status messages are
+  printed during update detection: \verb|statusdepth| controls the maximum
+  depth for paths on the local machine (longer paths are not displayed, nor
+  are non-directory paths).  The value should be an integer; default is 1.  
+\item Removed the \verb|trace| and \verb|silent| preferences.  They did
+not seem very useful, and there were too many preferences for controlling
+output in various ways.
+\item The text UI now displays just the default command (the one that
+will be used if the user just types \verb|<return>|) instead of all
+available commands.  Typing \verb|?| will print the full list of
+possibilities.
+\item The function that finds the canonical hostname of the local host
+(which is used, for example, in calculating the name of the archive file
+used to remember which files have been synchronized) normally uses the
+\verb|gethostname| operating system call.  However, if the environment
+variable \verb|UNISONLOCALHOSTNAME| is set, its value will now be used
+instead.  This makes it easier to use Unison in situations where a
+machine's name changes frequently (e.g., because it is a laptop and gets
+moved around a lot).
+\item File owner and group are now displayed in the ``detail window'' at
+the bottom of the screen, when unison is configured to synchronize them.
+\end{itemize}
+
+\item For hackers:
+\begin{itemize}
+\item Updated to Jacques Garrigue's new version of \verb|lablgtk|, which
+  means we can throw away our local patched version.  
+
+  If you're compiling the GTK version of unison from sources, you'll need
+  to update your copy of lablgtk to the developers release.
+  (Warning: installing lablgtk under Windows is currently a bit
+  challenging.) 
+
+\item The TODO.txt file (in the source distribution) has been cleaned up
+and reorganized.  The list of pending tasks should be much easier to
+make sense of, for people that may want to contribute their programming
+energies.  There is also a separate file BUGS.txt for open bugs.
+\item The Tk user interface has been removed (it was not being maintained
+and no longer compiles).
+\item The \verb|debug| preference now prints quite a bit of additional
+information that should be useful for identifying sources of problems.
+\item The version number of the remote server is now checked right away 
+  during the connection setup handshake, rather than later.  (Somebody
+  sent a bug report of a server crash that turned out to come from using
+  inconsistent versions: better to check this earlier and in a way that
+  can't crash either client or server.)
+\item Unison now runs correctly on 64-bit architectures (e.g. Alpha
+linux).  We will not be distributing binaries for these architectures
+ourselves (at least for a while) but if someone would like to make them
+available, we'll be glad to provide a link to them.
+\end{itemize}
+
+\item Bug fixes:
+\begin{itemize}
+\item Pattern matching (e.g. for \verb|ignore|) is now case-insensitive
+  when Unison is in case-insensitive mode (i.e., when one of the replicas
+  is on a windows machine).
+\item Some people had trouble with mysterious failures during
+  propagation of updates, where files would be falsely reported as having
+  changed during synchronization.  This should be fixed.
+\item Numerous smaller fixes.
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.4.1}
+\item Added a number of 'sorting modes' for the user interface.  By
+default, conflicting changes are displayed at the top, and the rest of
+the entries are sorted in alphabetical order.  This behavior can be
+changed in the following ways:
+\begin{itemize}
+\item Setting  the \verb|sortnewfirst| preference to \verb|true| causes
+newly created files to be displayed before changed files.
+\item Setting \verb|sortbysize| causes files to be displayed in
+increasing order of size.
+\item Giving the preference \verb|sortfirst=<pattern>| (where
+\verb|<pattern>| is a path descriptor in the same format as 'ignore' and 'follow'
+patterns, causes paths matching this pattern to be displayed first.
+\item Similarly, giving the preference \verb|sortlast=<pattern>| 
+causes paths matching this pattern to be displayed last.
+\end{itemize}
+The sorting preferences are described in more detail in the user manual.
+The \verb|sortnewfirst| and \verb|sortbysize| flags can also be accessed
+from the 'Sort' menu in the grpahical user interface.
+
+\item Added two new preferences that can be used to change unison's
+fundamental behavior to make it more like a mirroring tool instead of
+a synchronizer.
+\begin{itemize}
+\item Giving the preference \verb|prefer| with argument \verb|<root>|
+(by adding \verb|-prefer <root>| to the command line or \verb|prefer=<root>|)
+to your profile) means that, if there is a conflict, the contents of
+\verb|<root>| 
+should be propagated to the other replica (with no questions asked).
+Non-conflicting changes are treated as usual.
+\item Giving the preference \verb|force| with argument \verb|<root>|
+will make unison resolve {\em all} differences in favor of the given
+root, even if it was the other replica that was changed.
+\end{itemize}
+These options should be used with care!  (More information is available in
+the manual.)
+
+\item Small changes:
+\begin{itemize}
+\item 
+Changed default answer to 'Yes' in all two-button dialogs in the 
+  graphical interface (this seems more intuitive).
+
+\item The \verb|rsync| preference has been removed (it was used to
+activate rsync compression for file transfers, but rsync compression is
+now enabled by default). 
+\item  In the text user interface, the arrows indicating which direction
+changes are being 
+  propagated are printed differently when the user has overridded Unison's
+  default recommendation (\verb|====>| instead of \verb|---->|).  This
+  matches the behavior of the graphical interface, which displays such
+  arrows in a different color.
+\item Carriage returns (Control-M's) are ignored at the ends of lines in
+  profiles, for Windows compatibility.
+\item All preferences are now fully documented in the user manual. 
+\end{itemize}
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.3.12}
+\item \incompatible{} Archive format has changed.  Make sure you
+synchronize your replicas before upgrading, to avoid spurious
+conflicts.  The first sync after upgrading will be slow.
+
+\item New/improved functionality:
+\begin{itemize}
+\item  A new preference -sortbysize controls the order in which changes
+  are displayed to the user: when it is set to true, the smallest
+  changed files are displayed first.  (The default setting is false.) 
+\item A new preference -sortnewfirst causes newly created files to be 
+  listed before other updates in the user interface.
+\item We now allow the ssh protocol to specify a port.  
+\item Incompatible change: The unison: protocol is deprecated, and we added
+  file: and socket:.  You may have to modify your profiles in the
+  .unison directory.
+  If a replica is specified without an explicit protocol, we now
+  assume it refers to a file.  (Previously "//saul/foo" meant to use
+  SSH to connect to saul, then access the foo directory.  Now it means
+  to access saul via a remote file mechanism such as samba; the old
+  effect is now achieved by writing {\tt ssh://saul/foo}.)
+\item Changed the startup sequence for the case where roots are given but
+  no profile is given on the command line.  The new behavior is to
+  use the default profile (creating it if it does not exist), and
+  temporarily override its roots.  The manual claimed that this case
+  would work by reading no profile at all, but AFAIK this was never
+  true.
+\item In all user interfaces, files with conflicts are always listed first
+\item A new preference 'sshversion' can be used to control which version
+  of ssh should be used to connect to the server.  Legal values are 1 and 2.
+  (Default is empty, which will make unison use whatever version of ssh
+  is installed as the default 'ssh' command.)
+\item The situation when the permissions of a file was updated the same on
+  both side is now handled correctly (we used to report a spurious conflict)
+
+\end{itemize}
+
+\item Improvements for the Windows version:
+\begin{itemize}
+\item The fact that filenames are treated case-insensitively under
+Windows should now be handled correctly.  The exact behavior is described
+in the cross-platform section of the manual.
+\item It should be possible to synchronize with Windows shares, e.g.,
+  //host/drive/path.
+\item Workarounds to the bug in syncing root directories in Windows.
+The most difficult thing to fix is an ocaml bug: Unix.opendir fails on
+c: in some versions of Windows.
+\end{itemize}
+
+\item Improvements to the GTK user interface (the Tk interface is no
+longer being maintained): 
+\begin{itemize}
+\item The UI now displays actions differently (in blue) when they have been
+  explicitly changed by the user from Unison's default recommendation.
+\item More colorful appearance.
+\item The initial profile selection window works better.
+\item If any transfers failed, a message to this effect is displayed along with
+  'Synchronization complete' at the end of the transfer phase (in case they
+  may have scrolled off the top).
+\item Added a global progress meter, displaying the percentage of {\em total}
+  bytes that have been transferred so far.
+\end{itemize}
+
+\item Improvements to the text user interface:
+\begin{itemize}
+\item The file details will be displayed automatically when a
+  conflict is been detected.
+\item when a warning is generated (e.g. for a temporary
+  file left over from a previous run of unison) Unison will no longer
+  wait for a response if it is running in -batch mode.
+\item The UI now displays a short list of possible inputs each time it waits
+  for user interaction.  
+\item The UI now quits immediately (rather than looping back and starting
+  the interaction again) if the user presses 'q' when asked whether to 
+  propagate changes.
+\item Pressing 'g' in the text user interface will proceed immediately
+  with propagating updates, without asking any more questions.
+\end{itemize}
+
+\item Documentation and installation changes:
+\begin{itemize}
+\item The manual now includes a FAQ, plus sections on common problems and
+on tricks contributed by users.
+\item Both the download page and the download directory explicitly say
+what are the current stable and beta-test version numbers.
+\item The OCaml sources for the up-to-the-minute developers' version (not
+guaranteed to be stable, or even to compile, at any given time!) are now
+available from the download page.
+\item Added a subsection to the manual describing cross-platform
+  issues (case conflicts, illegal filenames)
+\end{itemize}
+
+\item Many small bug fixes and random improvements.
+
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.3.1}
+\item Several bug fixes.  The most important is a bug in the rsync
+module that would occasionally cause change propagation to fail with a
+'rename' error.
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.2}
+\item The multi-threaded transport system is now disabled by default.
+(It is not stable enough yet.)
+\item Various bug fixes.
+\item A new experimental feature: 
+
+  The final component of a -path argument may now be the wildcard 
+  specifier \verb|*|.  When Unison sees such a path, it expands this path on 
+  the client into into the corresponding list of paths by listing the
+  contents of that directory.  
+
+  Note that if you use wildcard paths from the command line, you will
+  probably need to use quotes or a backslash to prevent the * from
+  being interpreted by your shell.
+
+  If both roots are local, the contents of the first one will be used
+  for expanding wildcard paths.  (Nb: this is the first one {\em after} the
+  canonization step -- i.e., the one that is listed first in the user 
+  interface -- not the one listed first on the command line or in the
+  preferences file.)
+\end{changesfromversion}
+
+\begin{changesfromversion}{2.1}
+\item The transport subsystem now includes an implementation by
+Sylvain Gommier and Norman Ramsey of Tridgell and Mackerras's
+\verb|rsync| protocol.  This protocol achieves much faster 
+transfers when only a small part of a large file has been changed by
+sending just diffs.  This feature is mainly helpful for transfers over
+slow links---on fast local area networks it can actually degrade
+performance---so we have left it off by default.  Start unison with
+the \verb|-rsync| option (or put \verb|rsync=true| in your preferences
+file) to turn it on.
+
+\item ``Progress bars'' are now diplayed during remote file transfers,
+showing what percentage of each file has been transferred so far.
+
+\item The version numbering scheme has changed.  New releases will now
+      be have numbers like 2.2.30, where the second component is
+      incremented on every significant public release and the third
+      component is the ``patch level.''
+
+\item Miscellaneous improvements to the GTK-based user interface.
+\item The manual  is now available in PDF format.
+
+\item We are experimenting with using a multi-threaded transport
+subsystem to transfer several files at the same time, making
+much more effective use of available network bandwidth.  This feature
+is not completely stable yet, so by default it is disabled in the
+release version of Unison.
+
+If you want to play with the multi-threaded version, you'll need to
+recompile Unison from sources (as described in the documentation),
+setting the THREADS flag in Makefile.OCaml to true.  Make sure that
+your OCaml compiler has been installed with the \verb|-with-pthreads|
+configuration option.  (You can verify this by checking whether the
+file \verb|threads/threads.cma| in the OCaml standard library
+directory contains the string \verb|-lpthread| near the end.)
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.292}
+\item Reduced memory footprint (this is especially important during
+the first run of unison, where it has to gather information about all
+the files in both repositories). 
+\item Fixed a bug that would cause the socket server under NT to fail
+  after the client exits. 
+\item Added a SHIFT modifier to the Ignore menu shortcut keys in GTK
+  interface (to avoid hitting them accidentally).  
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.231}
+\item Tunneling over ssh is now supported in the Windows version.  See
+the installation section of the manual for detailed instructions.
+
+\item The transport subsystem now includes an implementation of the
+\verb|rsync| protocol, built by Sylvain Gommier and Norman Ramsey.
+This protocol achieves much faster transfers when only a small part of
+a large file has been changed by sending just diffs.  The rsync
+feature is off by default in the current version.  Use the
+\verb|-rsync| switch to turn it on.  (Nb.  We still have a lot of
+tuning to do: you may not notice much speedup yet.)
+
+\item We're experimenting with a multi-threaded transport subsystem,
+written by Jerome Vouillon.  The downloadable binaries are still
+single-threaded: if you want to try the multi-threaded version, you'll
+need to recompile from sources.  (Say \verb|make THREADS=true|.)
+Native thread support from the compiler is required.  Use the option
+\verb|-threads N| to select the maximal number of concurrent 
+threads (default is 5).  Multi-threaded
+and single-threaded clients/servers can interoperate.  
+
+\item A new GTK-based user interface is now available, thanks to
+Jacques Garrigue.  The Tk user interface still works, but we'll be
+shifting development effort to the GTK interface from now on.
+\item OCaml 3.00 is now required for compiling Unison from sources.
+The modules \verb|uitk| and \verb|myfileselect| have been changed to
+use labltk instead of camltk.  To compile the Tk interface in Windows,
+you must have ocaml-3.00 and tk8.3.  When installing tk8.3, put it in
+\verb|c:\Tcl| rather than the suggested \verb|c:\Program Files\Tcl|, 
+and be sure to install the headers and libraries (which are not 
+installed by default).
+
+\item Added a new \verb|-addversionno| switch, which causes unison to
+use \verb|unison-<currentversionnumber>| instead of just \verb|unison|
+as the remote server command.  This allows multiple versions of unison
+to coexist conveniently on the same server: whichever version is run
+on the client, the same version will be selected on the server.
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.219}
+\item \incompatible{} Archive format has changed.  Make sure you
+synchronize your replicas before upgrading, to avoid spurious
+conflicts.  The first sync after upgrading will be slow.
+
+\item This version fixes several annoying bugs, including:
+\begin{itemize}
+\item Some cases where propagation of file permissions was not
+working.
+\item umask is now ignored when creating directories
+\item directories are create writable, so that a read-only directory and
+    its contents can be propagated.
+\item Handling of warnings generated by the server.
+\item Synchronizing a path whose parent is not a directory on both sides is
+now flagged as erroneous.  
+\item Fixed some bugs related to symnbolic links and nonexistant roots.
+\begin{itemize}
+\item 
+   When a change (deletion or new contents) is propagated onto a 
+     'follow'ed symlink, the file pointed to by the link is now changed.
+     (We used to change the link itself, which doesn't fit our assertion
+     that 'follow' means the link is completely invisible)
+   \item When one root did not exist, propagating the other root on top of it
+     used to fail, becuase unison could not calculate the working directory
+     into which to write changes.  This should be fixed.
+\end{itemize}
+\end{itemize}
+
+\item A human-readable timestamp has been added to Unison's archive files.
+
+\item The semantics of Path and Name regular expressions now
+correspond better. 
+
+\item Some minor improvements to the text UI (e.g. a command for going
+back to previous items)
+
+\item The organization of the export directory has changed --- should
+be easier to find / download things now.
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.200}
+\item \incompatible{} Archive format has changed.  Make sure you
+synchronize your replicas before upgrading, to avoid spurious
+conflicts.  The first sync after upgrading will be slow.
+
+\item This version has not been tested extensively on Windows.
+
+\item Major internal changes designed to make unison safer to run
+at the same time as the replicas are being changed by the user.
+
+\item Internal performance improvements.  
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.190}
+\item \incompatible{} Archive format has changed.  Make sure you
+synchronize your replicas before upgrading, to avoid spurious
+conflicts.  The first sync after upgrading will be slow.
+
+\item A number of internal functions have been changed to reduce the
+amount of memory allocation, especially during the first
+synchronization.  This should help power users with very big replicas.
+
+\item Reimplementation of low-level remote procedure call stuff, in
+preparation for adding rsync-like smart file transfer in a later
+release.   
+
+\item Miscellaneous bug fixes.
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.180}
+\item \incompatible{} Archive format has changed.  Make sure you
+synchronize your replicas before upgrading, to avoid spurious
+conflicts.  The first sync after upgrading will be slow.
+
+\item Fixed some small bugs in the interpretation of ignore patterns. 
+
+\item Fixed some problems that were preventing the Windows version
+from working correctly when click-started.
+
+\item Fixes to treatment of file permissions under Windows, which were
+causing spurious reports of different permissions when synchronizing
+between windows and unix systems.
+
+\item Fixed one more non-tail-recursive list processing function,
+which was causing stack overflows when synchronizing very large
+replicas. 
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.169}
+\item The text user interface now provides commands for ignoring
+  files. 
+\item We found and fixed some {\em more} non-tail-recursive list
+  processing functions.  Some power users have reported success with
+  very large replicas.
+\item \incompatible 
+Files ending in \verb|.tmp| are no longer ignored automatically.  If you want
+to ignore such files, put an appropriate ignore pattern in your profile.
+
+\item \incompatible{} The syntax of {\tt ignore} and {\tt follow}
+patterns has changed. Instead of putting a line of the form
+\begin{verbatim}
+                 ignore = <regexp>
+\end{verbatim}
+  in your profile ({\tt .unison/default.prf}), you should put:
+\begin{verbatim}
+                 ignore = Regex <regexp>
+\end{verbatim}
+Moreover, two other styles of pattern are also recognized:
+\begin{verbatim}
+                 ignore = Name <name>
+\end{verbatim}
+matches any path in which one component matches \verb|<name>|, while
+\begin{verbatim}
+                 ignore = Path <path>
+\end{verbatim}
+matches exactly the path \verb|<path>|.
+
+Standard ``globbing'' conventions can be used in \verb|<name>| and
+\verb|<path>|:  
+\begin{itemize}
+\item a \verb|?| matches any single character except \verb|/|
+\item a \verb|*| matches any sequence of characters not including \verb|/|
+\item \verb|[xyz]| matches any character from the set $\{{\tt x},
+  {\tt y}, {\tt z} \}$
+\item \verb|{a,bb,ccc}| matches any one of \verb|a|, \verb|bb|, or
+  \verb|ccc|. 
+\end{itemize}
+
+See the user manual for some examples.
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.146}
+\item Some users were reporting stack overflows when synchronizing
+  huge directories.  We found and fixed some non-tail-recursive list
+  processing functions, which we hope will solve the problem.  Please 
+  give it a try and let us know.
+\item Major additions to the documentation.  
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.142}
+\item Major internal tidying and many small bugfixes.
+\item Major additions to the user manual.
+\item Unison can now be started with no arguments -- it will prompt
+automatically for the name of a profile file containing the roots to
+be synchronized.  This makes it possible to start the graphical UI
+from a desktop icon.
+\item Fixed a small bug where the text UI on NT was raising a 'no such
+  signal' exception.
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.139}
+\item The precompiled windows binary in the last release was compiled
+with an old OCaml compiler, causing propagation of permissions not to
+work (and perhaps leading to some other strange behaviors we've heard
+reports about).  This has been corrected.  If you're using precompiled
+binaries on Windows, please upgrade.
+\item Added a \verb|-debug| command line flag, which controls debugging
+of various modules.  Say \verb|-debug XXX| to enable debug tracing for
+module \verb|XXX|, or \verb|-debug all| to turn on absolutely everything.
+\item Fixed a small bug where the text UI on NT was raising a 'no such signal'
+exception.
+\end{changesfromversion}
+
+\begin{changesfromversion}{1.111}
+\item \incompatible{} The names and formats of the preference files in
+the .unison directory have changed.  In particular:
+\begin{itemize}
+\item the file ``prefs'' should be renamed to default.prf
+\item the contents of the file ``ignore'' should be merged into
+  default.prf.  Each line of the form \verb|REGEXP| in ignore should
+  become a line of the form \verb|ignore = REGEXP| in default.prf.
+\end{itemize}
+\item Unison now handles permission bits and  symbolic links.  See the
+manual for details.
+
+\item You can now have different preference files in your .unison
+directory.  If you start unison like this
+\begin{verbatim}
+             unison profilename
+\end{verbatim}
+(i.e. with just one ``anonymous'' command-line argument), then the
+file \verb|~/.unison/profilename.prf| will be loaded instead of
+\verb|default.prf|. 
+
+\item Some improvements to terminal handling in the text user interface
+
+\item Added a switch -killServer that terminates the remote server process
+when the unison client is shutting down, even when using sockets for 
+communication.  (By default, a remote server created using ssh/rsh is 
+terminated automatically, while a socket server is left running.)
+\item When started in 'socket server' mode, unison prints 'server started' on
+  stderr when it is ready to accept connections.  
+  (This may be useful for scripts that want to tell when a socket-mode server 
+  has finished initalization.)
+\item We now make a nightly mirror of our current internal development
+  tree, in case anyone wants an up-to-the-minute version to hack
+  around with.
+\item Added a file CONTRIB with some suggestions for how to help us
+make Unison better.
+\end{changesfromversion}
+

Deleted: branches/2.45/src/INSTALL.win32-msvc
===================================================================
--- trunk/src/INSTALL.win32-msvc	2012-03-10 16:12:51 UTC (rev 485)
+++ branches/2.45/src/INSTALL.win32-msvc	2012-04-02 15:54:47 UTC (rev 487)
@@ -1,512 +0,0 @@
-Installation notes to build Unison on Windows systems, with Visual C++
-
-[The following instructions were tested for Unison 2.9.1 on a Windows
-2000 machine running OCaml 3.04 -- that was a long time ago, so there
-may be some discrepancies with current versions of things.  If you
-notice any, please send a correction to unison-users at yahoogroups.com.]
-
-Contents
-
-1.Setting up the Windows system
-  1.1 General requirements
-  1.2 A Unix-like layer: CygWin
-  1.3 Visual C++
-  1.4 The OCaml compiler
-2.Compiling Unison
-  2.1 Text user interface
-  2.2 Tk user interface
-  2.3 Gtk user interface
-3.Using new public versions of Tk/Gtk/LablGtk
-  3.1 Using a new version of Tcl/Tk
-  3.2 Patching a new version of Gtk
-  3.3 Patching a new version of LablGtk
-  3.4 Making patches from the public sources
-Appendix
-  A.Windows text format
-  B.'.bashrc'
-  C.Windows files and directories names
-  D.Windows icons
-
-
-
-Section 1 - Setting up the Windows system
-
-
-1.1 General requirements
-
-  We will assume your are logged in as a regular user. We will mention cases 
-when you need to be granted administrator permissions.
-
-  We will work in your home directory.
-
-  For a complete installation from scratch, you will need about 300 Mb.
-
-  CygWin, a Unix-like layer, is needed to be able to use GNU tools
-like 'bash', 'make', 'sed', 'patch', etc.
-
-  The native Win32 port of OCaml distribution version 3.04 is required.
-It itself requires Visual C++ 6.0.
-
-1.2 A Unix-like layer: CygWin
-
-  Download CygWin from 'http://www.cygwin.com/': 
-* click "install cygwin now" and follow the instruction to set up cygwin.
-  install the essential packages such as "make", "fileutil", "openssh", etc.
-  set the root directory (e.g. 'd:\cygwin')
-
-  Setup 'bash':
-* click on 'bash'.
-* enter 'export HOME=/home/<username>', make the directory, then 'cd'.
-* create a '.bashrc' in CygWin's root directory to suit your
-  needs (see Appendix B for an example).
-* check the environment variable OSTYPE with 'echo $OSTYPE'.  If the result is
-  not 'cygwin' or 'cygwin20', then add 'export OSTYPE=cygwin' to the
-  '.bashrc' file.  This variable helps the unison Makefile (project
-  file) to understand that we are compiling under Windows platform.
-
-  Remember you can access the whole Windows filesystem with a Unix
-path through '/cygdrive/<drive letter>/<path>' (e.g. '/cygdrive/c/winnt' 
-stands for 'C:\WinNT')
-
-
-1.3 Visual C++
-
-  Run the installation program from the CD with Administrator
-permissions. We only need Visual C++ and MsDN is not required.
-
-  To check out your installation, use 'bash' to enter 'cl /?'.
-
-  If something goes wrong :
-* your path must contain the Visual C++ 'bin' directory; you may have to 
-  enter something like 
-  'export PATH=$PATH:/cygdrive/progra~1/micros~1/vc98/bin'.
-* your path must contain the Visual Studio '.dll' files' directory; you may
-  have to enter something like 
-  'export PATH=$PATH:/cygdrive/progra~1/micros~1/common/msdev98/bin'.
-* the Visual C++ compiler must be able to access the headers; you may have to
-  enter something like 'export INCLUDE='C:\progra~1\micros~1\vc98\include'' 
-  (path between single quotes).
-* the Visual C++ linker must be able to access the libraries; you may have to
-  enter something like 'export LIB='C:\progra~1\micros~1\vc98\lib'' (path 
-  between single quotes).
-
-
-1.4 The OCaml compiler
-
-  Download the Native Win32 port of OCaml 3.04 from 
-'http://caml.inria.fr/ocaml/distrib.html'.  It's a self-extracting binary.
-
-  Run it with Administrator permissions (only use 8 characters-long
-names in the installation directory).
-
-  To check out your installation, use 'bash' to enter 'ocamlc -v'.
-
-  If something goes wrong :
-* your path must contain the OCaml 'bin' directory;  you may have to enter 
-  something like 'export PATH=$PATH:/cygdrive/c/ocaml/bin'.
-* 'ocamlc -v' must report the OCaml 'lib' directory; you may have to enter
-  something like "export CAMLLIB='C:\ocaml\lib'" (path between single 
-  quotes).
-
-1.5  Microsoft Macro Assembler (MASM32)
-
-Download MASM32 from http://www.masm32.com/masmdl.htm, unzip and install
-it. Add the MASM32 bin directory (e.g. C:\masm32\bin) to your Path. Test
-the assembler with
-
-  ml
-
-Your shell should answer with
-
-  Microsoft (R) Macro Assembler Version 6.14.8444
-  Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.
-
-  usage: ML [ options ] filelist [ /link linkoptions]
-  Run "ML /help" or "ML /?" for more info
-
-
-Section 2 - Compiling Unison
- 
-
-2.1 Text user interface
-
-  Unpack the Unison sources.
-
-  Using 'bash', enter 'make clean', then 'make UISTYLE=text' to compile.
-
-  If something goes wrong :
-* if 'make' reports 'missing separator', be sure the makefiles are in
-  Unix text format (see Appendix A).
-* if .depend is not provided, create one using 
-  'ocamldep *.mli *.ml > .depend'; you will have to convert this file
-  to Unix text format (see Appendix A).
-* the minor 'etags' error is reported when 'emacs' is missing; you may
-  want to install it.
-
-2.2 Gtk user interface
-
-  You need the Gtk libraries (already installed if you got the Tcl/Tk
-libraries). Get the 'guilib.tar.gz' tarball from the 'resources'
-directory of the Unison web site and unpack it in your Ocaml 'lib'
-directory. This will create a 'guilib' directory containing the
-libraries.
-
-  Now you need the LablGtk extension to OCaml. 
-  First, the Gtk development package is required. Get the
-'wingtk.patched.tar.gz' tarball from the 'resources' directory of the
-Unison web site and unpack it. This will create a 'wingtk' directory.
-
-  Now, get the 'lablgtk-1.2.3-msvc-static.tar.gz' tarball from the
-'resources' directory of the Unison web site and unpack it somewhere
-(a building location, just for the compilation).  This will create a
-'lablgtk-1.2.3-static' directory. Edit the 'config.make.nt' file to
-set up the access path to your OCaml 'lib' directory and to the
-'wingtk' directory you created in the previous step.  In
-'lablgtk-1.2.3-static/src', run 'nmake -f Makefile.nt'. If you can use
-the OCaml native-code compiler, run 'nmake -f Makefile.nt opt' too. If
-you can't, you probably need the MASM assembler, also available in the
-'resources' directory of the Unison web site.  If everything goes
-well, run 'nmake -f Makefile.nt install' to install the software.  You
-may want to remove the compilation directory 'lablgtk-1.2.3-static'.
-
-  Using 'bash' in the Unison sources directory, enter 'make clean'
-then 'make UISTYLE=gtk'.
-
-  Run 'unison.exe' with the Gtk .dll's in your search path (they can
-be found in the 'guilib' directory), unless you built with the
-NATIVE=true option.
-
-  "unison.exe" built with NATIVE=true option is statically linked.
-This means that the executable doesn't refer to Cygwin and Gtk DLLs,
-and can therefore be distributed as a standalone application.
-
-
-Section 3 - Using new public versions of Tk/Gtk/LablGtk
-
-3.1 Patching a new version of Gtk
-
-  Download the 'wingtk.patch.tar.gz' tarball from the 'resources'
-directory of the Unison web site and unpack it. Follow the
-instructions in the 'README.patch' file to download the Gtk sources,
-to patch them and to build the new static and dynamic libraries.
-
-  Important: if a patch fails for any reason, try to apply the patches on a
-             Unix system.
-
-  Copy those new libraries to your 'ocaml/lib/guilib' directory, along
-with the .dll's (dynamic version).
-
-  Using the new version of 'wingtk', recompile LablGtk (see section 2.3).
-  
-
-3.2 Patching a new version of LablGtk
-
-   Download lablgtk-1.2.3.tar.gz from the LablGtk homepage
-<http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/lablgtk.html>.  Unpack
-it.
-
-  Download the 'lablgtk-1.2.3-msvc-static.patch.gz' from the 'resources'
-directory of the Unison web site. Apply the patch by typing: 
-  'patch < lablgtk.patch'
-above the 'lablgtk' directory.
-
-  Important: if a patch fails for any reason, try to apply the patches on a
-             Unix system.
-
-
-3.3 Making patches from the public sources
-
-  The way from public Gtk/LablGtk sources to the provided Gtk/LablGtk
-dynamic/static extension has been somehow perilous. We strongly
-recommand using the provided sources and patches as a base for your
-further enhancements.
-
-  To be exhaustive, here are the steps followed to create the provided
-sources (hoping it would help when trying to adapt a new version):  
-
-  WinGtk: 
-
-* Download the Gtk win32 sources from
-  'http://www.gimp.org/~tml/gimp/win32//downloads.html'. We need
-  'glib-src-yyyymmdd' and 'gtk+-src-yyyymmdd' where 'yyyymmdd' is the
-  release date. Version 2000/04/16 of these files was used.  
-
-* We will make new Windows Makefiles from the old ones. Here is how to
-  convert a Makefile:
-  - change all '/MD' to '/MT' to use the same windows system 
-    libraries than ocaml
-    (e.g. 'OPTIMIZE = -Ox -MD' becomes 'OPTIMIZE = -Ox -MT')
-  - turns all '.dll' targets to '.lib' ones using
-    'MKLIB = lib /nologo /out:'
-    you must remove all references to '.def' files
-    you must remove all references to other '.lib' and '.res' but 
-    you will have to provide them when linking an executable later
-    (e.g. 
-     glib-$(GLIB_VER).dll : $(glib_OBJECTS) glib.def
-	$(CC) $(CFLAGS) -LD -Feglib-$(GLIB_VER).dll $(glib_OBJECTS) \
-        user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:glib.def
-     becomes:
-     glib-$(GLIB_VER).lib : $(glib_OBJECTS)
-	$(MKLIB)glib-$(GLIB_VER).lib $(glib_OBJECTS)
-     )
-  - remove all '-GD' compilation flags
-    (e.g.
-     .c.obj :
-	$(CC) $(CFLAGS) -GD -c -DGLIB_COMPILATION \
-        -DG_LOG_DOMAIN=g_log_domain_glib $<
-     becomes:
-     .c.obj :
-	$(CC) $(CFLAGS)     -c -DGLIB_COMPILATION \
-        -DG_LOG_DOMAIN=g_log_domain_glib $<
-     )
-  - provides the right libraries when linking executables
-    (e.g.
-     testgtk.exe : gtk-$(GTK_VER).dll testgtk.obj
-	$(CC) $(CFLAGS) testgtk.obj gtk-$(GTK_VER).lib \
-        ..\gdk\gdk-$(GTK_VER).lib $(GLIB)\glib-$(GLIB_VER).lib \
-        $(LDFLAGS)
-     becomes: 
-     testgtk.exe : gtk-$(GTK_VER).lib testgtk.obj
-	$(CC) $(CFLAGS) testgtk.obj gtk-$(GTK_VER).lib \
-        ..\gdk\gdk-$(GTK_VER).lib ..\gdk\win32\gdk-win32.lib \
-        $(GLIB)\glib-$(GLIB_VER).lib $(GLIB)\gmodule-$(GLIB_VER).lib \
-        user32.lib advapi32.lib wsock32.lib gdi32.lib imm32.lib \
-        shell32.lib ole32.lib ../gdk/win32/gdk.res $(LDFLAGS)
-     )
-
-* Convert 'glib/makefile.msc' and remove all references to the
-  'gthread' and 'pthread' directories and libraries from it (but keep
-  'gthread.obj').
-
-* Erase the 'gthread' directory.
-
-* Comment out the '#include <psapi.h>' line in
-  'glib/gmodule/gmodule-win32.c'.
-
-* You should now be able to compile the 'glib' and 'gmodule' libraries
-  by typing 'nmake -f <new makefile>'. You can test it with 'testglib'
-  and the other test programs. Remember to provide those two libraries
-  when linking programs.
-
-* In 'gtk+/config.h.win32', undefine the following variables by
-  commenting out their definition lines:
-  HAVE_WINTAB, ENABLE_NLS, HAVE_GETTEXT, HAVE_LIBINTL
-
-* Convert 'gtk+/gdk/win32/makefile.msc' and remove all references to
-  'WTKIT', 'wntab32x', 'INTL' and 'gnu-intl' from it.
-
-* In 'gtk+/gdk/win32/rc/gdk.rc', comment out ',BUILDNUMBER'.
-
-* In 'gtk+/gdk/win32/gdkcursor-win32.c', replace 'gdk_DLLInstance' by
-  'gdk_ProgInstance'.
-
-* You should now be able to compile 'gdk-win32.lib'.
-
-* Convert 'gtk+/gdk/makefile.msc' and remove all references to
-  'WTKIT', 'wntab32x', 'INTL' and 'gnu-intl' from it. Include
-  'gdk-win32.lib' as an object for the 'gdk' library.
- 
-* You should now be able to compile the 'gdk' library. Remember to
-  provide 'win32/gdk.res' as well as the 'gdk' library when linking
-  programs.
-
-* Convert 'gtk+/gtk/makefile.msc' and remove all references to
-  'WTKIT', 'wntab32x', 'INTL', 'gnu-intl' and 'PTHREAD' from it.
-
-* Be sure to include all needed libraries in the '.exe' files' compilation
-  command lines.
-  In most case you need the following:
-  gtk-$(GTK_VER).lib ..\gdk\gdk-$(GTK_VER).lib \
-  $(GLIB)\glib-$(GLIB_VER).lib $(GLIB)\gmodule-$(GLIB_VER).lib \
-  user32.lib advapi32.lib wsock32.lib gdi32.lib imm32.lib shell32.lib \
-  ole32.lib \
-  ../gdk/win32/gdk.res
-
-* You should now be able to compile the 'gtk' library. You can test it
-  with 'testglib' and the other test programs.
-
-* With some cleaning of the Makefiles, it is also possible to build a
-  dynamic version of the libraries, along with the .dll's, so that we
-  finally obtain static/dynamic sources.
-
-* Make a patch with 'diff -Nr -C 5 <old dir> <new dir>' (you have to
-  use the GNU diffutils' 'diff'). You will apply the patch with 
-  'patch -p1 < <patch file>'.
-
-  LablGtk:
-
-* Download LablGtk from 'http://www.gtk.org' or
-  'ftp://ftp.inria.fr/lang/caml-light/bazar-ocaml/'.
-
-* You can remove all subdirectories.
-
-* Edit 'config.make.nt' to include the right Gtk libraries.
-
-* Comment out all references to 'gutter' to be found in the sources
-  with 'grep gutter *.h *.c *.mli *.ml'.
-
-* Compile with 'nmake -f Makefile.nt'. If you can use the OCaml
-  native-code compiler, run 'nmake -f Makefile.nt opt' too. If you
-  can't, you probably need the MASM assembler. It was downloaded from
-  'http://www.cs.uu.nl/wais/html/na-dir/assembly-language/x86/microsoft.html'.
-
-* Make a patch as for WinGtk.
-
-
-
-Appendix A - Windows text format
-
-Windows and Unix use different text file formats. This section
-explains how to convert a file from a format to another.
-
-A.1 Text format conversion
-  
-  In order to convert a dos text file to a unix text file, we have to
-remove all extra characters that are : 
-* carriage return or CR  or ^M (ctrl-M) or \x0d or \o13 or \r 
-* dos end-of-file or SUB or ^Z (ctrl-Z) or \x1a or \o26
-
-A.2 Conversion tools 
-
-  On a Unix-like top level (e.g any unix system or cygwin), you can use:
-
-* dos -> unix
-  - tr -d '\15\32' < dosfile.txt > unixfile.txt
-  - awk '{ sub("\r$", ""); print }' dosfile.txt > unixfile.txt
-  - perl -p -e 's/\r$//' < dosfile.txt > unixfile.txt
-
-* unix -> dos
-  - awk 'sub("$", "\r")' unixfile.txt > dosfile.txt
-  - perl -p -e 's/$/\r/' < unixfile.txt > dosfile.txt
-
-  You may want to use a short script like the following to convert
-more than one file at a time (doesn't work recursively; use at your
-own risk):
-
-  #!/bin/sh
-  echo dos2unix
-  for F in "$@"
-  do
-    echo converting "$F"
-    tr -d '\15\32' < $F > $F.tmp
-    mv -f $F.tmp $F
-  done
-
-A.3 Transmission issues
-
-  If you transfer files using 'ftp' between a Unix system and a
-Windows system, be sure to run it in binary mode to disable any
-automatic conversion. To switch to binary mode, enter 'binary' (or
-simply 'bin').
-
-
-
-Appendix B - '.bashrc'.
-
-  Copy the following '.bashrc' as a base to your own one. Be sure this
-file is in Unix text format.
-
-# .bashrc
-# gommier at saul.cis.upenn.edu
-
-export HOME=/
-export PS1="[\u@\h \w]$ "
-cd
-
-# Set up Path
-# $PATH currently contains the Windows Path converted to Unix path,
-export PATH=./:/bin:$PATH
-echo "Current path is :"
-echo $PATH
-echo " "
-
-# end
-
-
-
-Appendix C - Windows files and directories names
-
-  Here are some general rules for applications creating names for
-directories and files or processing names supplied by the user:
-
-* Use any character in the current code page for a name, but do not
-  use a path separator, a character in the range 0 through 31, or any
-  character explicitly disallowed by the file system. A name can
-  contain characters in the extended character set (128-255).
-
-* Use the backslash (\), the forward slash (/), or both to separate
-  components in a path.  No other character is acceptable as a path
-  separator. Note that UNC names must adhere to the following format:
-  \\server\share.
-
-* Use a period (.) as a directory component in a path to represent the
-  current directory.
-
-* Use two consecutive periods (..) as a directory component in a path
-  to represent the parent of the current directory.
-
-* Use a period (.) to separate the base file name from the extension
-  in a directory name or file name.
-
-* Do not use the following characters in directory names or file
-  names, because they are reserved: < > : " / \ |
-
-* Do not use device names, such as aux, con, lpt1, and prn, as file
-  names or directory names.
-
-* Process a path as a null-terminated string. The maximum length for a
-  path, including a trailing backslash, is given by MAX_PATH.
-
-* The Unicode versions of several functions permit paths that exceed
-  the MAX_PATH length if the path has the "\\?\" prefix. The "\\?\"
-  tells the function to turn off path parsing. However, each component
-  in the path cannot be more than MAX_PATH characters long. Use the
-  "\\?\" prefix with paths for local storage devices and the
-  "\\?\UNC\" prefix with paths having the Universal Naming Convention
-  (UNC) format. The "\\?\" is ignored as part of the path. For
-  example, "\\?\C:\myworld\private" is seen as "C:\myworld\private",
-  and "\\?\UNC\bill_g_1\hotstuff\coolapps" is seen as
-  "\\bill_g_1\hotstuff\coolapps".
-
-* Do not assume case sensitivity. Consider names such as OSCAR, Oscar,
-  and oscar to be the same.
-
-
-
-Appendix D - Windows icons
-
-  Here are some general informations on how to make your Windows
-program have a nice icon.
-
-* What we mean by icon is a set of bitmaps that are displayed by
-  Windows to represent your program on the desktop, on the top left
-  corner of each window, etc. For your program's binary to include an
-  icon, you will have to draw each bitmap and to store them in .bmp
-  files, then to archive them in a .ico file, then to archive that
-  icon file in a .res file along with other resources, and finally to
-  link your program with that very .res file.
-
-* Current graphic formats for icons are 16 x 16, 32 x 32 and 48 x 48
-  pixels with 16 or 256 colors. One format must always exist for
-  compatibility with all Windows versions: the 32 x 32 x 16 format.
-  Furthermore, the colors refer to the standard palette (sometimes
-  called www palette), which means you mustn't use optimized palette
-  when turning RGB colors to indexed colors. If you need subtle nuances,
-  remember you can interleave pixels of two different colors to create
-  the impression of a third, average one.
-
-* Once your bitmaps are ready, you can use the Visual C++ IDE to create
-  your .ico file. Use the resource tool bar to create a 'new icon'.
-  Open your .bmp files and simply cut and paste them into the icon
-  window. You have to select the proper device (or format) for each
-  bitmap before achieving the copy. When your icon (i.e. set of bitmaps)
-  is ready, right-click on the icon name in the resource list window to
-  export it.
-  Note: you should never trust that IDE when dealing with colors, since
-        it seems to get quickly lost between 16 or 256 colors.
-
-* To include your icon into a resource file, add a line for it into the
-  .rc script file and compile with rc to create the .res file.
-
-* Just add the .res file to the link command line to have your binary
-  include the icon.

Copied: branches/2.45/src/INSTALL.win32-msvc (from rev 486, trunk/src/INSTALL.win32-msvc)
===================================================================
--- branches/2.45/src/INSTALL.win32-msvc	                        (rev 0)
+++ branches/2.45/src/INSTALL.win32-msvc	2012-04-02 15:54:47 UTC (rev 487)
@@ -0,0 +1,512 @@
+Installation notes to build Unison on Windows systems, with Visual C++
+
+[The following instructions were tested for Unison 2.9.1 on a Windows
+2000 machine running OCaml 3.04 -- that was a long time ago, so there
+may be some discrepancies with current versions of things.  If you
+notice any, please send a correction to unison-users at yahoogroups.com.]
+
+Contents
+
+1.Setting up the Windows system
+  1.1 General requirements
+  1.2 A Unix-like layer: CygWin
+  1.3 Visual C++
+  1.4 The OCaml compiler
+2.Compiling Unison
+  2.1 Text user interface
+  2.2 Tk user interface
+  2.3 Gtk user interface
+3.Using new public versions of Tk/Gtk/LablGtk
+  3.1 Using a new version of Tcl/Tk
+  3.2 Patching a new version of Gtk
+  3.3 Patching a new version of LablGtk
+  3.4 Making patches from the public sources
+Appendix
+  A.Windows text format
+  B.'.bashrc'
+  C.Windows files and directories names
+  D.Windows icons
+
+
+
+Section 1 - Setting up the Windows system
+
+
+1.1 General requirements
+
+  We will assume your are logged in as a regular user. We will mention cases 
+when you need to be granted administrator permissions.
+
+  We will work in your home directory.
+
+  For a complete installation from scratch, you will need about 300 Mb.
+
+  CygWin, a Unix-like layer, is needed to be able to use GNU tools
+like 'bash', 'make', 'sed', 'patch', etc.
+
+  The native Win32 port of OCaml distribution version 3.04 is required.
+It itself requires Visual C++ 6.0.
+
+1.2 A Unix-like layer: CygWin
+
+  Download CygWin from 'http://www.cygwin.com/': 
+* click "install cygwin now" and follow the instruction to set up cygwin.
+  install the essential packages such as "make", "fileutil", "openssh", etc.
+  set the root directory (e.g. 'd:\cygwin')
+
+  Setup 'bash':
+* click on 'bash'.
+* enter 'export HOME=/home/<username>', make the directory, then 'cd'.
+* create a '.bashrc' in CygWin's root directory to suit your
+  needs (see Appendix B for an example).
+* check the environment variable OSTYPE with 'echo $OSTYPE'.  If the result is
+  not 'cygwin' or 'cygwin20', then add 'export OSTYPE=cygwin' to the
+  '.bashrc' file.  This variable helps the unison Makefile (project
+  file) to understand that we are compiling under Windows platform.
+
+  Remember you can access the whole Windows filesystem with a Unix
+path through '/cygdrive/<drive letter>/<path>' (e.g. '/cygdrive/c/winnt' 
+stands for 'C:\WinNT')
+
+
+1.3 Visual C++
+
+  Run the installation program from the CD with Administrator
+permissions. We only need Visual C++ and MsDN is not required.
+
+  To check out your installation, use 'bash' to enter 'cl /?'.
+
+  If something goes wrong :
+* your path must contain the Visual C++ 'bin' directory; you may have to 
+  enter something like 
+  'export PATH=$PATH:/cygdrive/progra~1/micros~1/vc98/bin'.
+* your path must contain the Visual Studio '.dll' files' directory; you may
+  have to enter something like 
+  'export PATH=$PATH:/cygdrive/progra~1/micros~1/common/msdev98/bin'.
+* the Visual C++ compiler must be able to access the headers; you may have to
+  enter something like 'export INCLUDE='C:\progra~1\micros~1\vc98\include'' 
+  (path between single quotes).
+* the Visual C++ linker must be able to access the libraries; you may have to
+  enter something like 'export LIB='C:\progra~1\micros~1\vc98\lib'' (path 
+  between single quotes).
+
+
+1.4 The OCaml compiler
+
+  Download the Native Win32 port of OCaml 3.04 from 
+'http://caml.inria.fr/ocaml/distrib.html'.  It's a self-extracting binary.
+
+  Run it with Administrator permissions (only use 8 characters-long
+names in the installation directory).
+
+  To check out your installation, use 'bash' to enter 'ocamlc -v'.
+
+  If something goes wrong :
+* your path must contain the OCaml 'bin' directory;  you may have to enter 
+  something like 'export PATH=$PATH:/cygdrive/c/ocaml/bin'.
+* 'ocamlc -v' must report the OCaml 'lib' directory; you may have to enter
+  something like "export CAMLLIB='C:\ocaml\lib'" (path between single 
+  quotes).
+
+1.5  Microsoft Macro Assembler (MASM32)
+
+Download MASM32 from http://www.masm32.com/masmdl.htm, unzip and install
+it. Add the MASM32 bin directory (e.g. C:\masm32\bin) to your Path. Test
+the assembler with
+
+  ml
+
+Your shell should answer with
+
+  Microsoft (R) Macro Assembler Version 6.14.8444
+  Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.
+
+  usage: ML [ options ] filelist [ /link linkoptions]
+  Run "ML /help" or "ML /?" for more info
+
+
+Section 2 - Compiling Unison
+ 
+
+2.1 Text user interface
+
+  Unpack the Unison sources.
+
+  Using 'bash', enter 'make clean', then 'make UISTYLE=text' to compile.
+
+  If something goes wrong :
+* if 'make' reports 'missing separator', be sure the makefiles are in
+  Unix text format (see Appendix A).
+* if .depend is not provided, create one using 
+  'ocamldep *.mli *.ml > .depend'; you will have to convert this file
+  to Unix text format (see Appendix A).
+* the minor 'etags' error is reported when 'emacs' is missing; you may
+  want to install it.
+
+2.2 Gtk user interface
+
+  You need the Gtk libraries (already installed if you got the Tcl/Tk
+libraries). Get the 'guilib.tar.gz' tarball from the 'resources'
+directory of the Unison web site and unpack it in your Ocaml 'lib'
+directory. This will create a 'guilib' directory containing the
+libraries.
+
+  Now you need the LablGtk extension to OCaml. 
+  First, the Gtk development package is required. Get the
+'wingtk.patched.tar.gz' tarball from the 'resources' directory of the
+Unison web site and unpack it. This will create a 'wingtk' directory.
+
+  Now, get the 'lablgtk-1.2.3-msvc-static.tar.gz' tarball from the
+'resources' directory of the Unison web site and unpack it somewhere
+(a building location, just for the compilation).  This will create a
+'lablgtk-1.2.3-static' directory. Edit the 'config.make.nt' file to
+set up the access path to your OCaml 'lib' directory and to the
+'wingtk' directory you created in the previous step.  In
+'lablgtk-1.2.3-static/src', run 'nmake -f Makefile.nt'. If you can use
+the OCaml native-code compiler, run 'nmake -f Makefile.nt opt' too. If
+you can't, you probably need the MASM assembler, also available in the
+'resources' directory of the Unison web site.  If everything goes
+well, run 'nmake -f Makefile.nt install' to install the software.  You
+may want to remove the compilation directory 'lablgtk-1.2.3-static'.
+
+  Using 'bash' in the Unison sources directory, enter 'make clean'
+then 'make UISTYLE=gtk'.
+
+  Run 'unison.exe' with the Gtk .dll's in your search path (they can
+be found in the 'guilib' directory), unless you built with the
+NATIVE=true option.
+
+  "unison.exe" built with NATIVE=true option is statically linked.
+This means that the executable doesn't refer to Cygwin and Gtk DLLs,
+and can therefore be distributed as a standalone application.
+
+
+Section 3 - Using new public versions of Tk/Gtk/LablGtk
+
+3.1 Patching a new version of Gtk
+
+  Download the 'wingtk.patch.tar.gz' tarball from the 'resources'
+directory of the Unison web site and unpack it. Follow the
+instructions in the 'README.patch' file to download the Gtk sources,
+to patch them and to build the new static and dynamic libraries.
+
+  Important: if a patch fails for any reason, try to apply the patches on a
+             Unix system.
+
+  Copy those new libraries to your 'ocaml/lib/guilib' directory, along
+with the .dll's (dynamic version).
+
+  Using the new version of 'wingtk', recompile LablGtk (see section 2.3).
+  
+
+3.2 Patching a new version of LablGtk
+
+   Download lablgtk-1.2.3.tar.gz from the LablGtk homepage
+<http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/lablgtk.html>.  Unpack
+it.
+
+  Download the 'lablgtk-1.2.3-msvc-static.patch.gz' from the 'resources'
+directory of the Unison web site. Apply the patch by typing: 
+  'patch < lablgtk.patch'
+above the 'lablgtk' directory.
+
+  Important: if a patch fails for any reason, try to apply the patches on a
+             Unix system.
+
+
+3.3 Making patches from the public sources
+
+  The way from public Gtk/LablGtk sources to the provided Gtk/LablGtk
+dynamic/static extension has been somehow perilous. We strongly
+recommand using the provided sources and patches as a base for your
+further enhancements.
+
+  To be exhaustive, here are the steps followed to create the provided
+sources (hoping it would help when trying to adapt a new version):  
+
+  WinGtk: 
+
+* Download the Gtk win32 sources from
+  'http://www.gimp.org/~tml/gimp/win32//downloads.html'. We need
+  'glib-src-yyyymmdd' and 'gtk+-src-yyyymmdd' where 'yyyymmdd' is the
+  release date. Version 2000/04/16 of these files was used.  
+
+* We will make new Windows Makefiles from the old ones. Here is how to
+  convert a Makefile:
+  - change all '/MD' to '/MT' to use the same windows system 
+    libraries than ocaml
+    (e.g. 'OPTIMIZE = -Ox -MD' becomes 'OPTIMIZE = -Ox -MT')
+  - turns all '.dll' targets to '.lib' ones using
+    'MKLIB = lib /nologo /out:'
+    you must remove all references to '.def' files
+    you must remove all references to other '.lib' and '.res' but 
+    you will have to provide them when linking an executable later
+    (e.g. 
+     glib-$(GLIB_VER).dll : $(glib_OBJECTS) glib.def
+	$(CC) $(CFLAGS) -LD -Feglib-$(GLIB_VER).dll $(glib_OBJECTS) \
+        user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:glib.def
+     becomes:
+     glib-$(GLIB_VER).lib : $(glib_OBJECTS)
+	$(MKLIB)glib-$(GLIB_VER).lib $(glib_OBJECTS)
+     )
+  - remove all '-GD' compilation flags
+    (e.g.
+     .c.obj :
+	$(CC) $(CFLAGS) -GD -c -DGLIB_COMPILATION \
+        -DG_LOG_DOMAIN=g_log_domain_glib $<
+     becomes:
+     .c.obj :
+	$(CC) $(CFLAGS)     -c -DGLIB_COMPILATION \
+        -DG_LOG_DOMAIN=g_log_domain_glib $<
+     )
+  - provides the right libraries when linking executables
+    (e.g.
+     testgtk.exe : gtk-$(GTK_VER).dll testgtk.obj
+	$(CC) $(CFLAGS) testgtk.obj gtk-$(GTK_VER).lib \
+        ..\gdk\gdk-$(GTK_VER).lib $(GLIB)\glib-$(GLIB_VER).lib \
+        $(LDFLAGS)
+     becomes: 
+     testgtk.exe : gtk-$(GTK_VER).lib testgtk.obj
+	$(CC) $(CFLAGS) testgtk.obj gtk-$(GTK_VER).lib \
+        ..\gdk\gdk-$(GTK_VER).lib ..\gdk\win32\gdk-win32.lib \
+        $(GLIB)\glib-$(GLIB_VER).lib $(GLIB)\gmodule-$(GLIB_VER).lib \
+        user32.lib advapi32.lib wsock32.lib gdi32.lib imm32.lib \
+        shell32.lib ole32.lib ../gdk/win32/gdk.res $(LDFLAGS)
+     )
+
+* Convert 'glib/makefile.msc' and remove all references to the
+  'gthread' and 'pthread' directories and libraries from it (but keep
+  'gthread.obj').
+
+* Erase the 'gthread' directory.
+
+* Comment out the '#include <psapi.h>' line in
+  'glib/gmodule/gmodule-win32.c'.
+
+* You should now be able to compile the 'glib' and 'gmodule' libraries
+  by typing 'nmake -f <new makefile>'. You can test it with 'testglib'
+  and the other test programs. Remember to provide those two libraries
+  when linking programs.
+
+* In 'gtk+/config.h.win32', undefine the following variables by
+  commenting out their definition lines:
+  HAVE_WINTAB, ENABLE_NLS, HAVE_GETTEXT, HAVE_LIBINTL
+
+* Convert 'gtk+/gdk/win32/makefile.msc' and remove all references to
+  'WTKIT', 'wntab32x', 'INTL' and 'gnu-intl' from it.
+
+* In 'gtk+/gdk/win32/rc/gdk.rc', comment out ',BUILDNUMBER'.
+
+* In 'gtk+/gdk/win32/gdkcursor-win32.c', replace 'gdk_DLLInstance' by
+  'gdk_ProgInstance'.
+
+* You should now be able to compile 'gdk-win32.lib'.
+
+* Convert 'gtk+/gdk/makefile.msc' and remove all references to
+  'WTKIT', 'wntab32x', 'INTL' and 'gnu-intl' from it. Include
+  'gdk-win32.lib' as an object for the 'gdk' library.
+ 
+* You should now be able to compile the 'gdk' library. Remember to
+  provide 'win32/gdk.res' as well as the 'gdk' library when linking
+  programs.
+
+* Convert 'gtk+/gtk/makefile.msc' and remove all references to
+  'WTKIT', 'wntab32x', 'INTL', 'gnu-intl' and 'PTHREAD' from it.
+
+* Be sure to include all needed libraries in the '.exe' files' compilation
+  command lines.
+  In most case you need the following:
+  gtk-$(GTK_VER).lib ..\gdk\gdk-$(GTK_VER).lib \
+  $(GLIB)\glib-$(GLIB_VER).lib $(GLIB)\gmodule-$(GLIB_VER).lib \
+  user32.lib advapi32.lib wsock32.lib gdi32.lib imm32.lib shell32.lib \
+  ole32.lib \
+  ../gdk/win32/gdk.res
+
+* You should now be able to compile the 'gtk' library. You can test it
+  with 'testglib' and the other test programs.
+
+* With some cleaning of the Makefiles, it is also possible to build a
+  dynamic version of the libraries, along with the .dll's, so that we
+  finally obtain static/dynamic sources.
+
+* Make a patch with 'diff -Nr -C 5 <old dir> <new dir>' (you have to
+  use the GNU diffutils' 'diff'). You will apply the patch with 
+  'patch -p1 < <patch file>'.
+
+  LablGtk:
+
+* Download LablGtk from 'http://www.gtk.org' or
+  'ftp://ftp.inria.fr/lang/caml-light/bazar-ocaml/'.
+
+* You can remove all subdirectories.
+
+* Edit 'config.make.nt' to include the right Gtk libraries.
+
+* Comment out all references to 'gutter' to be found in the sources
+  with 'grep gutter *.h *.c *.mli *.ml'.
+
+* Compile with 'nmake -f Makefile.nt'. If you can use the OCaml
+  native-code compiler, run 'nmake -f Makefile.nt opt' too. If you
+  can't, you probably need the MASM assembler. It was downloaded from
+  'http://www.cs.uu.nl/wais/html/na-dir/assembly-language/x86/microsoft.html'.
+
+* Make a patch as for WinGtk.
+
+
+
+Appendix A - Windows text format
+
+Windows and Unix use different text file formats. This section
+explains how to convert a file from a format to another.
+
+A.1 Text format conversion
+  
+  In order to convert a dos text file to a unix text file, we have to
+remove all extra characters that are : 
+* carriage return or CR  or ^M (ctrl-M) or \x0d or \o13 or \r 
+* dos end-of-file or SUB or ^Z (ctrl-Z) or \x1a or \o26
+
+A.2 Conversion tools 
+
+  On a Unix-like top level (e.g any unix system or cygwin), you can use:
+
+* dos -> unix
+  - tr -d '\15\32' < dosfile.txt > unixfile.txt
+  - awk '{ sub("\r$", ""); print }' dosfile.txt > unixfile.txt
+  - perl -p -e 's/\r$//' < dosfile.txt > unixfile.txt
+
+* unix -> dos
+  - awk 'sub("$", "\r")' unixfile.txt > dosfile.txt
+  - perl -p -e 's/$/\r/' < unixfile.txt > dosfile.txt
+
+  You may want to use a short script like the following to convert
+more than one file at a time (doesn't work recursively; use at your
+own risk):
+
+  #!/bin/sh
+  echo dos2unix
+  for F in "$@"
+  do
+    echo converting "$F"
+    tr -d '\15\32' < $F > $F.tmp
+    mv -f $F.tmp $F
+  done
+
+A.3 Transmission issues
+
+  If you transfer files using 'ftp' between a Unix system and a
+Windows system, be sure to run it in binary mode to disable any
+automatic conversion. To switch to binary mode, enter 'binary' (or
+simply 'bin').
+
+
+
+Appendix B - '.bashrc'.
+
+  Copy the following '.bashrc' as a base to your own one. Be sure this
+file is in Unix text format.
+
+# .bashrc
+# gommier at saul.cis.upenn.edu
+
+export HOME=/
+export PS1="[\u@\h \w]$ "
+cd
+
+# Set up Path
+# $PATH currently contains the Windows Path converted to Unix path,
+export PATH=./:/bin:$PATH
+echo "Current path is :"
+echo $PATH
+echo " "
+
+# end
+
+
+
+Appendix C - Windows files and directories names
+
+  Here are some general rules for applications creating names for
+directories and files or processing names supplied by the user:
+
+* Use any character in the current code page for a name, but do not
+  use a path separator, a character in the range 0 through 31, or any
+  character explicitly disallowed by the file system. A name can
+  contain characters in the extended character set (128-255).
+
+* Use the backslash (\), the forward slash (/), or both to separate
+  components in a path.  No other character is acceptable as a path
+  separator. Note that UNC names must adhere to the following format:
+  \\server\share.
+
+* Use a period (.) as a directory component in a path to represent the
+  current directory.
+
+* Use two consecutive periods (..) as a directory component in a path
+  to represent the parent of the current directory.
+
+* Use a period (.) to separate the base file name from the extension
+  in a directory name or file name.
+
+* Do not use the following characters in directory names or file
+  names, because they are reserved: < > : " / \ |
+
+* Do not use device names, such as aux, con, lpt1, and prn, as file
+  names or directory names.
+
+* Process a path as a null-terminated string. The maximum length for a
+  path, including a trailing backslash, is given by MAX_PATH.
+
+* The Unicode versions of several functions permit paths that exceed
+  the MAX_PATH length if the path has the "\\?\" prefix. The "\\?\"
+  tells the function to turn off path parsing. However, each component
+  in the path cannot be more than MAX_PATH characters long. Use the
+  "\\?\" prefix with paths for local storage devices and the
+  "\\?\UNC\" prefix with paths having the Universal Naming Convention
+  (UNC) format. The "\\?\" is ignored as part of the path. For
+  example, "\\?\C:\myworld\private" is seen as "C:\myworld\private",
+  and "\\?\UNC\bill_g_1\hotstuff\coolapps" is seen as
+  "\\bill_g_1\hotstuff\coolapps".
+
+* Do not assume case sensitivity. Consider names such as OSCAR, Oscar,
+  and oscar to be the same.
+
+
+
+Appendix D - Windows icons
+
+  Here is some general information on how to make your Windows
+program have a nice icon.
+
+* What we mean by icon is a set of bitmaps that are displayed by
+  Windows to represent your program on the desktop, on the top left
+  corner of each window, etc. For your program's binary to include an
+  icon, you will have to draw each bitmap and to store them in .bmp
+  files, then to archive them in a .ico file, then to archive that
+  icon file in a .res file along with other resources, and finally to
+  link your program with that very .res file.
+
+* Current graphic formats for icons are 16 x 16, 32 x 32 and 48 x 48
+  pixels with 16 or 256 colors. One format must always exist for
+  compatibility with all Windows versions: the 32 x 32 x 16 format.
+  Furthermore, the colors refer to the standard palette (sometimes
+  called www palette), which means you mustn't use optimized palette
+  when turning RGB colors to indexed colors. If you need subtle nuances,
+  remember you can interleave pixels of two different colors to create
+  the impression of a third, average one.
+
+* Once your bitmaps are ready, you can use the Visual C++ IDE to create
+  your .ico file. Use the resource tool bar to create a 'new icon'.
+  Open your .bmp files and simply cut and paste them into the icon
+  window. You have to select the proper device (or format) for each
+  bitmap before achieving the copy. When your icon (i.e. set of bitmaps)
+  is ready, right-click on the icon name in the resource list window to
+  export it.
+  Note: you should never trust that IDE when dealing with colors, since
+        it seems to get quickly lost between 16 or 256 colors.
+
+* To include your icon into a resource file, add a line for it into the
+  .rc script file and compile with rc to create the .res file.
+
+* Just add the .res file to the link command line to have your binary
+  include the icon.

Deleted: branches/2.45/src/RECENTNEWS
===================================================================
--- trunk/src/RECENTNEWS	2012-03-10 16:12:51 UTC (rev 485)
+++ branches/2.45/src/RECENTNEWS	2012-04-02 15:54:47 UTC (rev 487)
@@ -1,305 +0,0 @@
-CHANGES FROM VERSION 2.44.12
-
-* Update copyright dates
-
-* Add quotes to paths when calling external file watcher utility
-
--------------------------------
-CHANGES FROM VERSION 2.44.11
-
-* Correctly quote the path when running merge commands
-
--------------------------------
-CHANGES FROM VERSION 2.44.10
-
-* Added option clientHostName. If specified, it will be used to as the client
-  host name, overriding UNISONLOCALHOSTNAME and the actual host name.
-
--------------------------------
-CHANGES FROM VERSION 2.44.9
-
-- OS X GUI: fix crash under Lion, because of problems with the toolbar, using
-  the fix suggested in http://blitzbasic.com/Community/posts.php?topic=95778.
-
-- OS X GUI: show a modal warning window if there is no archive for the hosts.
-  The user can then choose to exit or proceed (proceed is the default). The
-  window is not shown if "batch" is true.
-
--------------------------------
-CHANGES FROM VERSION 2.44.8
-
-* Change uimacnew09 icon to classic "U with arrows"
-
-* Incorporate a patch to fsmonitor.py (the external filewatcher
-  utility) from Tomasz Zernicki to make it work better under Windows.
--------------------------------
-CHANGES FROM VERSION 2.44.6
-
-* A small improvement to the uimacnew09 interface from Alan Schmitt
-  and Steve Kalkwarf: when Unison is run with the -batch flag, the
-  interface will now automatically propagate changes and terminate,
-  without waiting for user interaction.
-
-* uimacnew09 is now the standard graphical interface on OSX
--------------------------------
-CHANGES FROM VERSION 2.44.5
-
-- uimacnew09: file details panel selectable
-- uimacnew09: automatically quit when done synchronizing if called from the command line with "-batch"
-
--------------------------------
-CHANGES FROM VERSION 2.44.4
-
-- fixed a bug when calling the Unison macnew09 GUI from the command line with a profile specified
-- now possible to use the "-batch" option when calling the macnew09 GUI on the command line
-
--------------------------------
-CHANGES FROM VERSION 2.44.2
-
-* Some spelling corrections in documentation and comments from Stephane Glondu
-
--------------------------------
-CHANGES FROM VERSION 2.44.0
-
-* Small patch from Stephane Glondu to make Unison compile with Ocaml 3.12.
-
-* New version of uigtk2.ml from Matt Zagrabelny that reorganizes the
-  icons in a slightly more intuitive way.
-
-* Finished implementing the "fastercheckUNSAFE" option, which can be
-  used (with care!) to achieve *much* faster update detection when all
-  the common files in the two replicas are known to be identical.  See
-  the documentation for more information.
-
-  This feature should still be considered experimental, but it's ready
-  for other people to try out.
-
--------------------------------
-CHANGES FROM VERSION 2.43.12
-
-* Small patch from Stephane Glondu to make Unison compile with Ocaml 3.12.
-
-* New version of uigtk2.ml from Matt Zagrabelny that reorganizes the
-  icons in a slightly more intuitive way.
-
-* Incorporated new version of fsmonitor.py from Christophe Gohle
-
--------------------------------
-CHANGES FROM VERSION 2.43.10
-
-* Fixed incompatibility with OpenSSH 5.6.
-
--------------------------------
-CHANGES FROM VERSION 2.43.7
-
-* Fixed fingerprint cache: do not cache file properties
-* Fixed O_APPEND mode for open under Windows (the previous attempt in
-  revision 422 was incomplete)
-* Fixed String.sub invalid argument error when an AppleDouble file does
-  not contain a finder information field
-
--------------------------------
-CHANGES FROM VERSION 2.43.6
-
-* A small fix suggested by Jerome.  Still thinking about what needs to
-  change to get newly created files to transfer without failing.
-
--------------------------------
-CHANGES FROM VERSION 2.43.0
-
-* See if we can get revisionString to update automatically now...
-
--------------------------------
-CHANGES FROM VERSION 2.43.0
-
-* Bump revisionString -- not sure why this isn't happening automatically.
-
--------------------------------
-CHANGES FROM VERSION 2.43.-30
-
-* Experimental implementation of a new "faster check" mode for update
-  detection.  When this mode is enabled (by running with
-  'fastercheckUNSAFE=true'), Unison will skip calculating fingerprints
-  of the contents of files that it has not seen before -- it just uses
-  the file's size as a pseudo-fingerprint, allowing the archives to be
-  built very quickly.  
-
-  This feature has not been extensively tested -- if you use it on
-  live replicas, please pay careful attention to what Unison is doing.
-  Also, note that the cost of faster update detection is that it is
-  possible Unison will miss a conflict; this flag should be used only
-  when the replicas are known to be identical.
-
-  Here's the full documentation.  
-
-let fastercheckUNSAFE =
-  Prefs.createBool "fastercheckUNSAFE"
-    false "!skip computing fingerprints for new files (experts only!)"
-    (  "THIS FEATURE IS STILL EXPERIMENTAL AND SHOULD BE USED WITH EXTREME CAUTION.  "
-       ^ "\n\n"
-       ^ "When this flag is set to {\\tt true}, Unison will compute a 'pseudo-" 
-       ^ "fingerprint' the first time it sees a file (either because the file is "
-       ^ "new or because Unison is running for the first time).  This enormously "
-       ^ "speeds update detection, but it must be used with care, as it can cause "
-       ^ "Unison to miss conflicts: If "
-       ^ "a given path in the filesystem contains files on {\\em both} sides that "
-       ^ "Unison has not yet seen, and if those files have the same length but different "
-       ^ "contents, then Unison will not notice the presence of a conflict.  If, later, one "
-       ^ "of the files is changed, the changed file will be propagated, overwriting  "
-       ^ "the other.  "
-       ^ "\n\n"
-       ^ "Moreover, even when the files are initially identical, setting this flag can lead "
-       ^ "to potentially confusing behavior: "
-       ^ "if a newly created file is later touched without being modified, Unison will "
-       ^ "treat this "
-       ^ "conservatively as a potential change (since it has no record of the earlier "
-       ^ "contents) and show it as needing to be propagated to the other replica. "
-       ^ "\n\n"
-       ^ "Most users should leave this flag off -- the small time savings of not "
-       ^ "fingerprinting new files is not worth the cost in terms of safety.  However, "
-       ^ "it can be very useful for power users with huge replicas that are known to "
-       ^ "be already synchronized (e.g., because one replica is a newly created duplicate "
-       ^ "of the other, or because they have previously been synchronized with Unison but "
-       ^ "Unison's archives need to be rebuilt).  In such situations, it is recommended "
-       ^ "that this flag be set only for the initial run of Unison, so that new archives "
-       ^ "can be created quickly, and then turned off for normal use.")
-
--------------------------------
-CHANGES FROM VERSION 2.43.-29
-
-* Added support for "pseudo-fingerprints", as a first step to
-  implementing super-fast initial scans, following recent discussion
-  on the unison-hackers list
-
--------------------------------
-CHANGES FROM VERSION 2.42.-29
-
-* Small patch from Stephane Glondu to make Unison compile with Ocaml 3.12.
-
-
--------------------------------
-CHANGES FROM VERSION 2.41.-27
-
-* Try again to fix version number
-
-
-
-
-
--------------------------------
-CHANGES FROM VERSION 2.41.-27
-
-* Fix version number
-
-
-
-
-
--------------------------------
-CHANGES FROM VERSION 2.41.-28
-
-* Trim duplicate paths when using "-repeat watch"
-
-* Bump version number, since protocol has changed (should have done
-  this a few commits ago)
-
-
-
-
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-* Correct a bug in the watcher startup code for remote sync
-
-
-
-
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-* Unison now passes path arguments and --follow directives to
-  fsmonitor.py.  This seems to work except for one small issue with
-  how fsmonitor.py treats --follow directives for directories that
-  don't exist (or maybe this is an issue with how it treats any kind
-  of monitoring when the thing being monitored doesn't exist?).  If I
-  create a symlink to a nonexistant directory, give Unison (hence
-  fsmonitor.py) a 'follow' directive for the symlink, start unison, and
-  *then* create the directory, fsmonitor.py misses the change.
-
-
-
-
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-* More progress on file watching
-* Add external fsmonitor.py script to svn repo
-
-* If you want to play with the filewatching functionality, here's 
-  what you do: 
-    - add fsmonitor.py to your search path (or make a symlink to 
-      it from somewhere on your search path) on both machines that 
-      you're going to synchronize
-    - recompile the text ui ('make text') on both machines
-    - start unison with "-repeat watch" on the command line
-
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-* Progress on filesystem watching (see uitext.ml)
-
-* Update copyright dates, while I'm thinking about it :-)
-
-
-
-
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-* Progress on filesystem watching (see uitext.ml)
-
-* Update copyright dates, while I'm thinking about it :-)
-
-
-
-
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-* One more fix to Unicode case sensitive mode
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-Lines added in profile files by unison always start at a new line
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-* Fix to Unicode case sensitive mode (call the right function for
-  comparing file names).
-* "prefer = older/newer" now propagates deletions when there is no
-  conflict
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-* Fixed Unicode decomposition tables
-
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-Move old 2.40 branch aside to make room for a new beta version with
-same major number
--------------------------------
-CHANGES FROM VERSION 2.40.16
-
-Update documentation
-
--------------------------------

Copied: branches/2.45/src/RECENTNEWS (from rev 486, trunk/src/RECENTNEWS)
===================================================================
Deleted: branches/2.45/src/copy.mli
===================================================================
--- trunk/src/copy.mli	2012-03-10 16:12:51 UTC (rev 485)
+++ branches/2.45/src/copy.mli	2012-04-02 15:54:47 UTC (rev 487)
@@ -1,28 +0,0 @@
-
-(* Transfer a file from one replica to the other *)
-val file :
-    Common.root         (* root of source *)
- -> Path.local          (* path of source *)
- -> Common.root         (* root of target *)
- -> Fspath.t            (* fspath of target *)
- -> Path.local          (* path of target (temp location) *)
- -> Path.local          (* path of "real" (original) target *)
- -> [`Update of (Uutil.Filesize.t * Uutil.Filesize.t) | `Copy]
- -> Props.t             (* permissions for new file *)
- -> Os.fullfingerprint  (* fingerprint of file *)
- -> Fileinfo.stamp option (* source file stamp, if available *)
- -> Osx.ressStamp       (* ressource info of file *)
- -> Uutil.File.t        (* file's index in UI (for progress bars) *)
- -> Fileinfo.t Lwt.t    (* information regarding the transferred file *)
-
-val localFile :
-    Fspath.t             (* fspath of source *)
- -> Path.local           (* path of source *)
- -> Fspath.t             (* fspath of target *)
- -> Path.local           (* path of target *)
- -> Path.local           (* path of "real" [original] target *)
- -> [`Update of (Uutil.Filesize.t * Uutil.Filesize.t) | `Copy]
- -> Props.t              (* permissions for new file *)
- -> Uutil.Filesize.t     (* fork length *)
- -> Uutil.File.t option  (* file's index in UI (for progress bars), if appropriate *)
- -> unit

Copied: branches/2.45/src/copy.mli (from rev 486, trunk/src/copy.mli)
===================================================================
--- branches/2.45/src/copy.mli	                        (rev 0)
+++ branches/2.45/src/copy.mli	2012-04-02 15:54:47 UTC (rev 487)
@@ -0,0 +1,28 @@
+
+(* Transfer a file from one replica to the other *)
+val file :
+    Common.root         (* root of source *)
+ -> Path.local          (* path of source *)
+ -> Common.root         (* root of target *)
+ -> Fspath.t            (* fspath of target *)
+ -> Path.local          (* path of target (temp location) *)
+ -> Path.local          (* path of "real" (original) target *)
+ -> [`Update of (Uutil.Filesize.t * Uutil.Filesize.t) | `Copy]
+ -> Props.t             (* permissions for new file *)
+ -> Os.fullfingerprint  (* fingerprint of file *)
+ -> Fileinfo.stamp option (* source file stamp, if available *)
+ -> Osx.ressStamp       (* resource info of file *)
+ -> Uutil.File.t        (* file's index in UI (for progress bars) *)
+ -> Fileinfo.t Lwt.t    (* information regarding the transferred file *)
+
+val localFile :
+    Fspath.t             (* fspath of source *)
+ -> Path.local           (* path of source *)
+ -> Fspath.t             (* fspath of target *)
+ -> Path.local           (* path of target *)
+ -> Path.local           (* path of "real" [original] target *)
+ -> [`Update of (Uutil.Filesize.t * Uutil.Filesize.t) | `Copy]
+ -> Props.t              (* permissions for new file *)
+ -> Uutil.Filesize.t     (* fork length *)
+ -> Uutil.File.t option  (* file's index in UI (for progress bars), if appropriate *)
+ -> unit

Deleted: branches/2.45/src/globals.ml
===================================================================
--- trunk/src/globals.ml	2012-03-10 16:12:51 UTC (rev 485)
+++ branches/2.45/src/globals.ml	2012-04-02 15:54:47 UTC (rev 487)
@@ -1,306 +0,0 @@
-(* Unison file synchronizer: src/globals.ml *)
-(* Copyright 1999-2012, 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 <http://www.gnu.org/licenses/>.
-*)
-
-
-open Common
-
-let debug = Trace.debug "globals"
-
-(*****************************************************************************)
-(*                          ROOTS and PATHS                                  *)
-(*****************************************************************************)
-
-let rawroots =
-  Prefs.createStringList "root"
-    "root of a replica (should be used exactly twice)"
-    ("Each use of this preference names the root of one of the replicas "
-     ^ "for Unison to synchronize.  Exactly two roots are needed, so normal "
-     ^ "modes of usage are either to give two values for \\verb|root| in the "
-     ^ "profile, or to give no values in the profile and provide two "
-     ^ "on the command line.  "
-     ^ "Details of the syntax of roots can be found in "
-     ^ "\\sectionref{roots}{Roots}.\n\n"
-     ^ "The two roots can be given in either order; Unison will sort them "
-     ^ "into a canonical order before doing anything else.  It also tries to "
-     ^ "`canonize' the machine names and paths that appear in the roots, so "
-     ^ "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 (Safelist.rev l)
-
-let rawRoots () = Safelist.rev (Prefs.read rawroots)
-
-let rawRootPair () =
-  match rawRoots () with
-    [r1; r2] -> (r1, r2)
-  | _        -> assert false
-
-let theroots = ref []
-
-open Lwt
-let installRoots termInteract =
-  let roots = rawRoots () in
-  if Safelist.length roots <> 2 then
-    raise (Util.Fatal (Printf.sprintf
-      "Wrong number of roots: 2 expected, but %d provided (%s)\n(Maybe you specified roots both on the command line and in the profile?)"
-      (Safelist.length roots)
-      (String.concat ", " roots) ));
-  Safelist.fold_right
-    (fun r cont ->
-       Remote.canonizeRoot r (Clroot.parseRoot r) termInteract
-       >>= (fun r' ->
-       cont >>= (fun l ->
-       return (r' :: l))))
-    roots (return []) >>= (fun roots' ->
-  theroots := roots';
-  return ())
-
-(* Alternate interface, should replace old interface eventually *)
-let installRoots2 () =
-  debug (fun () -> Util.msg "Installing roots...");
-  let roots = rawRoots () in
-  theroots :=
-    Safelist.map Remote.canonize ((Safelist.map Clroot.parseRoot) roots);
-  theroots := !theroots
-
-let roots () =
-  match !theroots with
-    [root1;root2] -> (root1,root2)
-  | _ -> assert false
-
-let rootsList() = !theroots
-
-let rootsInCanonicalOrder() = Common.sortRoots (!theroots)
-
-let localRoot () = List.hd (rootsInCanonicalOrder ())
-
-let reorderCanonicalListToUsersOrder l =
-  if rootsList() = rootsInCanonicalOrder() then l
-  else Safelist.rev l
-
-let rec nice_rec i
-  : unit Lwt.t =
-  if i <= 0 then
-    Lwt.return ()
-  else
-    Lwt_unix.yield() >>= (fun () -> nice_rec (i - 1))
-
-(* [nice r] yields 5 times on local roots [r] to give processes
-   corresponding to remote roots a chance to run *)
-let nice r =
-  if List.exists (fun r -> fst r <> Local) (rootsList ()) && fst r = Local then
-    nice_rec 5
-  else
-    Lwt.return ()
-
-let allRootsIter f =
-  Lwt_util.iter
-    (fun r -> nice r >>= (fun () -> f r)) (rootsInCanonicalOrder ())
-
-let allRootsIter2 f l =
-  let l = Safelist.combine (rootsList ()) l in
-  Lwt_util.iter (fun (r, v) -> nice r >>= (fun () -> f r v))
-    (Safelist.sort (fun (r, _) (r', _) -> Common.compareRoots r r') l)
-
-let allRootsMap f =
-  Lwt_util.map
-    (fun r -> nice r >>= (fun () -> f r >>= (fun v -> return (r, v))))
-    (rootsInCanonicalOrder ()) >>= (fun l ->
-      return (Safelist.map snd (reorderCanonicalListToUsersOrder l)))
-
-let allRootsMapWithWaitingAction f wa =
-  Lwt_util.map_with_waiting_action
-    (fun r -> nice r >>= (fun () -> f r >>= (fun v -> return (r, v))))
-    (fun r -> wa r)
-    (rootsInCanonicalOrder ()) >>= (fun l ->
-      return (Safelist.map snd (reorderCanonicalListToUsersOrder l)))
-
-let replicaHostnames () =
-  Safelist.map
-    (function (Local, _) -> ""
-            | (Remote h,_) -> h)
-    (rootsList())
-
-let allHostsIter f =
-  let rec iter l =
-    match l with
-      [] ->
-        return ()
-    | root :: rem ->
-        f root >>= (fun () ->
-        iter rem)
-  in
-  iter (replicaHostnames ())
-
-let allHostsMap f = Safelist.map f (replicaHostnames())
-
-let paths =
-  Prefs.create "path" []
-    "path to synchronize"
-    ("When no \\verb|path| preference is given, Unison will simply synchronize "
-     ^ "the two entire replicas, beginning from the given pair of roots.  "
-     ^ "If one or more \\verb|path| preferences are given, then Unison will "
-     ^ "synchronize only these paths and their children.  (This is useful "
-     ^ "for doing a fast sync of just one directory, for example.)  "
-     ^ "Note that {\\tt path} preferences are intepreted literally---they "
-     ^ "are not regular expressions.")
-    (fun oldpaths string -> Safelist.append oldpaths [Path.fromString string])
-    (fun l -> Safelist.map Path.toString l)
-
-(* FIX: this does weird things in case-insensitive mode... *)
-let globPath lr p =
-  let p = Path.forceLocal p in
-  debug (fun() ->
-    Util.msg "Checking path '%s' for expansions\n"
-      (Path.toDebugString p) );
-  match Path.deconstructRev p with
-    Some(n,parent) when (Name.toString n = "*") -> begin
-      debug (fun() -> Util.msg "Expanding path %s\n" (Path.toString p));
-      match lr with
-        None -> raise (Util.Fatal (Printf.sprintf
-                  "Path %s ends with *, %s"
-                  (Path.toString p)
-                  "but first root (after canonizing) is non-local"))
-      | Some lrfspath -> 
-          Safelist.map (fun c -> Path.makeGlobal (Path.child parent c))
-            (Os.childrenOf lrfspath parent)
-      end 
-  | _ -> [Path.makeGlobal p]
-
-let expandWildcardPaths() =
-  let lr =
-    match rootsInCanonicalOrder() with
-      [(Local, fspath); _] -> Some fspath
-    | _ -> None in
-  Prefs.set paths 
-    (Safelist.flatten_map (globPath lr) (Prefs.read paths))
-
-(*****************************************************************************)
-(*                         PROPAGATION OF PREFERENCES                        *)
-(*****************************************************************************)
-
-let propagatePrefsTo =
-  Remote.registerHostCmd
-    "installPrefs"
-    (fun prefs -> return (Prefs.load prefs))
-    
-let propagatePrefs () =
-  let prefs = Prefs.dump() in
-  let toHost root =
-    match root with
-      (Local, _) -> return ()
-    | (Remote host,_) ->
-        propagatePrefsTo host prefs
-  in
-  allRootsIter toHost
-
-(*****************************************************************************)
-(*                      PREFERENCES AND PREDICATES                           *)
-(*****************************************************************************)
-
-let batch =
-  Prefs.createBool "batch" false "batch mode: ask no questions at all"
-    ("When this is set to {\\tt true}, the user "
-     ^ "interface will ask no questions at all.  Non-conflicting changes "
-     ^ "will be propagated; conflicts will be skipped.")
-
-let confirmBigDeletes =
-  Prefs.createBool "confirmbigdel" true
-    "!ask about whole-replica (or path) deletes"
-    ("When this is set to {\\tt true}, Unison will request an extra confirmation if it appears "
-     ^ "that the entire replica has been deleted, before propagating the change.  If the {\\tt batch} "
-     ^ "flag is also set, synchronization will be aborted.  When the {\\tt path} preference is used, "
-     ^ "the same confirmation will be requested for top-level paths.  (At the moment, this flag only "
-     ^ "affects the text user interface.)  See also the {\\tt mountpoint} preference.")
-
-let () = Prefs.alias confirmBigDeletes "confirmbigdeletes"
-
-let ignorePred =
-  Pred.create "ignore"
-    ("Including the preference \\texttt{-ignore \\ARG{pathspec}} causes Unison to "
-     ^ "completely ignore paths that match \\ARG{pathspec} (as well as their "
-     ^ "children).  This is useful for avoiding synchronizing temporary "
-     ^ "files, object files, etc. The syntax of \\ARG{pathspec} is "
-     ^ "described in \\sectionref{pathspec}{Path Specification}, and further "
-     ^ "details on ignoring paths is found in"
-     ^ " \\sectionref{ignore}{Ignoring Paths}.")
-    
-let ignorenotPred =
-  Pred.create "ignorenot"
-    ("This preference overrides the preference \\texttt{ignore}. 
-      It gives a list of patterns 
-     (in the same format as 
-     \\verb|ignore|) for paths that should definitely {\\em not} be ignored, 
-     whether or not they happen to match one of the \\verb|ignore| patterns.
-     \\par Note that the semantics of {\\tt ignore} and {\\tt ignorenot} is a
-     little counter-intuitive.  When detecting updates, Unison examines
-     paths in depth-first order, starting from the roots of the replicas
-     and working downwards.  Before examining each path, it checks whether
-     it matches {\\tt ignore} and does not match {\\tt ignorenot}; in this case
-     it skips this path {\\em and all its descendants}.  This means that,
-     if some parent of a given path matches an {\\tt ignore} pattern, then 
-     it will be skipped even if the path itself matches an {\\tt ignorenot}
-     pattern.  In particular, putting {\\tt ignore = Path *} in your profile
-     and then using {\\tt ignorenot} to select particular paths to be 
-     synchronized will not work.  Instead, you should use the {\\tt path}
-     preference to choose particular paths to synchronize.")
-    
-let shouldIgnore p =
-  let p = Path.toString p in
-  (Pred.test ignorePred p) && not (Pred.test ignorenotPred p) 
-
-let addRegexpToIgnore re =
-  let oldRE = Pred.extern ignorePred in
-  let newRE = re::oldRE in
-  Pred.intern ignorePred newRE
-
-let merge = 
-  Pred.create "merge" ~advanced:true
-    ("This preference can be used to run a merge program which will create "
-     ^ "a new version for each of the files and the backup, "
-     ^ "with the last backup and the both replicas.  Setting the {\\tt merge} "
-     ^ "preference for a path will also cause this path to be backed up, "
-     ^ "just like {\tt backup}.  "
-     ^ "The syntax of \\ARG{pathspec>cmd} is "
-     ^ "described in \\sectionref{pathspec}{Path Specification}, and further "
-     ^ "details on Merging functions are present in "
-     ^ "\\sectionref{merge}{Merging files}.")
-        
-let shouldMerge p = Pred.test merge (Path.toString p)
-
-let mergeCmdForPath p = Pred.assoc merge (Path.toString p)
-
-let someHostIsRunningWindows =
-  Prefs.createBool "someHostIsRunningWindows" false "*" ""
-
-let allHostsAreRunningWindows =
-  Prefs.createBool "allHostsAreRunningWindows" false "*" ""
-
-let fatFilesystem =
-  Prefs.createBool "fat" ~local:true false
-    "use appropriate options for FAT filesystems"
-    ("When this is set to {\\tt true}, Unison will use appropriate options \
-      to synchronize efficiently and without error a replica located on a \
-      FAT filesystem on a non-Windows machine: \
-      do not synchronize permissions ({\\tt perms = 0}); \
-      never use chmod ({\tt dontchmod = true}); \
-      treat filenames as case insensitive ({\\tt ignorecase = true}); \
-      do not attempt to synchronize symbolic links ({\\tt links = false}); \
-      ignore inode number changes when detecting updates \
-      ({\\tt ignoreinodenumbers = true}).  \
-      Any of these change can be overridden by explicitely setting \
-      the corresponding preference in the profile.")

Copied: branches/2.45/src/globals.ml (from rev 486, trunk/src/globals.ml)
===================================================================
--- branches/2.45/src/globals.ml	                        (rev 0)
+++ branches/2.45/src/globals.ml	2012-04-02 15:54:47 UTC (rev 487)
@@ -0,0 +1,306 @@
+(* Unison file synchronizer: src/globals.ml *)
+(* Copyright 1999-2012, 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 <http://www.gnu.org/licenses/>.
+*)
+
+
+open Common
+
+let debug = Trace.debug "globals"
+
+(*****************************************************************************)
+(*                          ROOTS and PATHS                                  *)
+(*****************************************************************************)
+
+let rawroots =
+  Prefs.createStringList "root"
+    "root of a replica (should be used exactly twice)"
+    ("Each use of this preference names the root of one of the replicas "
+     ^ "for Unison to synchronize.  Exactly two roots are needed, so normal "
+     ^ "modes of usage are either to give two values for \\verb|root| in the "
+     ^ "profile, or to give no values in the profile and provide two "
+     ^ "on the command line.  "
+     ^ "Details of the syntax of roots can be found in "
+     ^ "\\sectionref{roots}{Roots}.\n\n"
+     ^ "The two roots can be given in either order; Unison will sort them "
+     ^ "into a canonical order before doing anything else.  It also tries to "
+     ^ "`canonize' the machine names and paths that appear in the roots, so "
+     ^ "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 (Safelist.rev l)
+
+let rawRoots () = Safelist.rev (Prefs.read rawroots)
+
+let rawRootPair () =
+  match rawRoots () with
+    [r1; r2] -> (r1, r2)
+  | _        -> assert false
+
+let theroots = ref []
+
+open Lwt
+let installRoots termInteract =
+  let roots = rawRoots () in
+  if Safelist.length roots <> 2 then
+    raise (Util.Fatal (Printf.sprintf
+      "Wrong number of roots: 2 expected, but %d provided (%s)\n(Maybe you specified roots both on the command line and in the profile?)"
+      (Safelist.length roots)
+      (String.concat ", " roots) ));
+  Safelist.fold_right
+    (fun r cont ->
+       Remote.canonizeRoot r (Clroot.parseRoot r) termInteract
+       >>= (fun r' ->
+       cont >>= (fun l ->
+       return (r' :: l))))
+    roots (return []) >>= (fun roots' ->
+  theroots := roots';
+  return ())
+
+(* Alternate interface, should replace old interface eventually *)
+let installRoots2 () =
+  debug (fun () -> Util.msg "Installing roots...");
+  let roots = rawRoots () in
+  theroots :=
+    Safelist.map Remote.canonize ((Safelist.map Clroot.parseRoot) roots);
+  theroots := !theroots
+
+let roots () =
+  match !theroots with
+    [root1;root2] -> (root1,root2)
+  | _ -> assert false
+
+let rootsList() = !theroots
+
+let rootsInCanonicalOrder() = Common.sortRoots (!theroots)
+
+let localRoot () = List.hd (rootsInCanonicalOrder ())
+
+let reorderCanonicalListToUsersOrder l =
+  if rootsList() = rootsInCanonicalOrder() then l
+  else Safelist.rev l
+
+let rec nice_rec i
+  : unit Lwt.t =
+  if i <= 0 then
+    Lwt.return ()
+  else
+    Lwt_unix.yield() >>= (fun () -> nice_rec (i - 1))
+
+(* [nice r] yields 5 times on local roots [r] to give processes
+   corresponding to remote roots a chance to run *)
+let nice r =
+  if List.exists (fun r -> fst r <> Local) (rootsList ()) && fst r = Local then
+    nice_rec 5
+  else
+    Lwt.return ()
+
+let allRootsIter f =
+  Lwt_util.iter
+    (fun r -> nice r >>= (fun () -> f r)) (rootsInCanonicalOrder ())
+
+let allRootsIter2 f l =
+  let l = Safelist.combine (rootsList ()) l in
+  Lwt_util.iter (fun (r, v) -> nice r >>= (fun () -> f r v))
+    (Safelist.sort (fun (r, _) (r', _) -> Common.compareRoots r r') l)
+
+let allRootsMap f =
+  Lwt_util.map
+    (fun r -> nice r >>= (fun () -> f r >>= (fun v -> return (r, v))))
+    (rootsInCanonicalOrder ()) >>= (fun l ->
+      return (Safelist.map snd (reorderCanonicalListToUsersOrder l)))
+
+let allRootsMapWithWaitingAction f wa =
+  Lwt_util.map_with_waiting_action
+    (fun r -> nice r >>= (fun () -> f r >>= (fun v -> return (r, v))))
+    (fun r -> wa r)
+    (rootsInCanonicalOrder ()) >>= (fun l ->
+      return (Safelist.map snd (reorderCanonicalListToUsersOrder l)))
+
+let replicaHostnames () =
+  Safelist.map
+    (function (Local, _) -> ""
+            | (Remote h,_) -> h)
+    (rootsList())
+
+let allHostsIter f =
+  let rec iter l =
+    match l with
+      [] ->
+        return ()
+    | root :: rem ->
+        f root >>= (fun () ->
+        iter rem)
+  in
+  iter (replicaHostnames ())
+
+let allHostsMap f = Safelist.map f (replicaHostnames())
+
+let paths =
+  Prefs.create "path" []
+    "path to synchronize"
+    ("When no \\verb|path| preference is given, Unison will simply synchronize "
+     ^ "the two entire replicas, beginning from the given pair of roots.  "
+     ^ "If one or more \\verb|path| preferences are given, then Unison will "
+     ^ "synchronize only these paths and their children.  (This is useful "
+     ^ "for doing a fast sync of just one directory, for example.)  "
+     ^ "Note that {\\tt path} preferences are intepreted literally---they "
+     ^ "are not regular expressions.")
+    (fun oldpaths string -> Safelist.append oldpaths [Path.fromString string])
+    (fun l -> Safelist.map Path.toString l)
+
+(* FIX: this does weird things in case-insensitive mode... *)
+let globPath lr p =
+  let p = Path.forceLocal p in
+  debug (fun() ->
+    Util.msg "Checking path '%s' for expansions\n"
+      (Path.toDebugString p) );
+  match Path.deconstructRev p with
+    Some(n,parent) when (Name.toString n = "*") -> begin
+      debug (fun() -> Util.msg "Expanding path %s\n" (Path.toString p));
+      match lr with
+        None -> raise (Util.Fatal (Printf.sprintf
+                  "Path %s ends with *, %s"
+                  (Path.toString p)
+                  "but first root (after canonizing) is non-local"))
+      | Some lrfspath -> 
+          Safelist.map (fun c -> Path.makeGlobal (Path.child parent c))
+            (Os.childrenOf lrfspath parent)
+      end 
+  | _ -> [Path.makeGlobal p]
+
+let expandWildcardPaths() =
+  let lr =
+    match rootsInCanonicalOrder() with
+      [(Local, fspath); _] -> Some fspath
+    | _ -> None in
+  Prefs.set paths 
+    (Safelist.flatten_map (globPath lr) (Prefs.read paths))
+
+(*****************************************************************************)
+(*                         PROPAGATION OF PREFERENCES                        *)
+(*****************************************************************************)
+
+let propagatePrefsTo =
+  Remote.registerHostCmd
+    "installPrefs"
+    (fun prefs -> return (Prefs.load prefs))
+    
+let propagatePrefs () =
+  let prefs = Prefs.dump() in
+  let toHost root =
+    match root with
+      (Local, _) -> return ()
+    | (Remote host,_) ->
+        propagatePrefsTo host prefs
+  in
+  allRootsIter toHost
+
+(*****************************************************************************)
+(*                      PREFERENCES AND PREDICATES                           *)
+(*****************************************************************************)
+
+let batch =
+  Prefs.createBool "batch" false "batch mode: ask no questions at all"
+    ("When this is set to {\\tt true}, the user "
+     ^ "interface will ask no questions at all.  Non-conflicting changes "
+     ^ "will be propagated; conflicts will be skipped.")
+
+let confirmBigDeletes =
+  Prefs.createBool "confirmbigdel" true
+    "!ask about whole-replica (or path) deletes"
+    ("When this is set to {\\tt true}, Unison will request an extra confirmation if it appears "
+     ^ "that the entire replica has been deleted, before propagating the change.  If the {\\tt batch} "
+     ^ "flag is also set, synchronization will be aborted.  When the {\\tt path} preference is used, "
+     ^ "the same confirmation will be requested for top-level paths.  (At the moment, this flag only "
+     ^ "affects the text user interface.)  See also the {\\tt mountpoint} preference.")
+
+let () = Prefs.alias confirmBigDeletes "confirmbigdeletes"
+
+let ignorePred =
+  Pred.create "ignore"
+    ("Including the preference \\texttt{-ignore \\ARG{pathspec}} causes Unison to "
+     ^ "completely ignore paths that match \\ARG{pathspec} (as well as their "
+     ^ "children).  This is useful for avoiding synchronizing temporary "
+     ^ "files, object files, etc. The syntax of \\ARG{pathspec} is "
+     ^ "described in \\sectionref{pathspec}{Path Specification}, and further "
+     ^ "details on ignoring paths is found in"
+     ^ " \\sectionref{ignore}{Ignoring Paths}.")
+    
+let ignorenotPred =
+  Pred.create "ignorenot"
+    ("This preference overrides the preference \\texttt{ignore}. 
+      It gives a list of patterns 
+     (in the same format as 
+     \\verb|ignore|) for paths that should definitely {\\em not} be ignored, 
+     whether or not they happen to match one of the \\verb|ignore| patterns.
+     \\par Note that the semantics of {\\tt ignore} and {\\tt ignorenot} is a
+     little counter-intuitive.  When detecting updates, Unison examines
+     paths in depth-first order, starting from the roots of the replicas
+     and working downwards.  Before examining each path, it checks whether
+     it matches {\\tt ignore} and does not match {\\tt ignorenot}; in this case
+     it skips this path {\\em and all its descendants}.  This means that,
+     if some parent of a given path matches an {\\tt ignore} pattern, then 
+     it will be skipped even if the path itself matches an {\\tt ignorenot}
+     pattern.  In particular, putting {\\tt ignore = Path *} in your profile
+     and then using {\\tt ignorenot} to select particular paths to be 
+     synchronized will not work.  Instead, you should use the {\\tt path}
+     preference to choose particular paths to synchronize.")
+    
+let shouldIgnore p =
+  let p = Path.toString p in
+  (Pred.test ignorePred p) && not (Pred.test ignorenotPred p) 
+
+let addRegexpToIgnore re =
+  let oldRE = Pred.extern ignorePred in
+  let newRE = re::oldRE in
+  Pred.intern ignorePred newRE
+
+let merge = 
+  Pred.create "merge" ~advanced:true
+    ("This preference can be used to run a merge program which will create "
+     ^ "a new version for each of the files and the backup, "
+     ^ "with the last backup and the both replicas.  Setting the {\\tt merge} "
+     ^ "preference for a path will also cause this path to be backed up, "
+     ^ "just like {\tt backup}.  "
+     ^ "The syntax of \\ARG{pathspec>cmd} is "
+     ^ "described in \\sectionref{pathspec}{Path Specification}, and further "
+     ^ "details on Merging functions are present in "
+     ^ "\\sectionref{merge}{Merging files}.")
+        
+let shouldMerge p = Pred.test merge (Path.toString p)
+
+let mergeCmdForPath p = Pred.assoc merge (Path.toString p)
+
+let someHostIsRunningWindows =
+  Prefs.createBool "someHostIsRunningWindows" false "*" ""
+
+let allHostsAreRunningWindows =
+  Prefs.createBool "allHostsAreRunningWindows" false "*" ""
+
+let fatFilesystem =
+  Prefs.createBool "fat" ~local:true false
+    "use appropriate options for FAT filesystems"
+    ("When this is set to {\\tt true}, Unison will use appropriate options \
+      to synchronize efficiently and without error a replica located on a \
+      FAT filesystem on a non-Windows machine: \
+      do not synchronize permissions ({\\tt perms = 0}); \
+      never use chmod ({\tt dontchmod = true}); \
+      treat filenames as case insensitive ({\\tt ignorecase = true}); \
+      do not attempt to synchronize symbolic links ({\\tt links = false}); \
+      ignore inode number changes when detecting updates \
+      ({\\tt ignoreinodenumbers = true}).  \
+      Any of these change can be overridden by explicitly setting \
+      the corresponding preference in the profile.")

Deleted: branches/2.45/src/mkProjectInfo.ml
===================================================================
--- trunk/src/mkProjectInfo.ml	2012-03-10 16:12:51 UTC (rev 485)
+++ branches/2.45/src/mkProjectInfo.ml	2012-04-02 15:54:47 UTC (rev 487)
@@ -1,67 +0,0 @@
-(* Program for printing project info into a Makefile.  Documentation below. *)
-
-(* FIX: When the time comes for the next alpha-release, remember to
-   increment the archive version number first. See update.ml. *)
-
-let projectName = "unison"
-let majorVersion = 2
-let minorVersion = 44
-let pointVersionOrigin = 471 (* Revision that corresponds to point version 0 *)
-
-(* Documentation:
-   This is a program to construct a version of the form Major.Minor.Point,
-   e.g., 2.10.4.
-   The Point release number is calculated from the Subversion revision number,
-   so it will be automatically incremented on svn commit.
-   The Major and Minor numbers are hard coded, as is the revision number
-   corresponding to the 0 point release.
-
-   If you want to increment the Major or Minor number, you will have to do a
-   little thinking to get the Point number back to 0.  Suppose the current svn
-   revision number is 27, and we have below
-
-        let majorVersion = 2
-        let minorVersion = 11
-        let pointVersionOrigin = 3
-
-   This means that the current Unison version is 2.11.24, since 27-3 = 24.
-   If we want to change the release to 3.0.0 we need to change things to
-
-        let majorVersion = 3
-        let minorVersion = 0
-        let pointVersionOrigin = 28
-
-   and then do a svn commit.
-
-   The first two lines are obvious.  The last line says that Subversion
-   revision 28 corresponds to a 0 point release.  Since we were at revision
-   27 and we're going to do a commit before making a release, we
-   will be at 28 after the commit and this will be Unison version 3.0.0.
-*)
-
-(* ---------------------------------------------------------------------- *)
-(* You shouldn't need to edit below. *)
-
-let revisionString = "$Rev$";;
-
-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;;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

Copied: branches/2.45/src/mkProjectInfo.ml (from rev 486, trunk/src/mkProjectInfo.ml)
===================================================================
--- branches/2.45/src/mkProjectInfo.ml	                        (rev 0)
+++ branches/2.45/src/mkProjectInfo.ml	2012-04-02 15:54:47 UTC (rev 487)
@@ -0,0 +1,68 @@
+(* Program for printing project info into a Makefile.  Documentation below. *)
+
+(* FIX: When the time comes for the next alpha-release, remember to
+   increment the archive version number first. See update.ml. *)
+
+let projectName = "unison"
+let majorVersion = 2
+let minorVersion = 45
+let pointVersionOrigin = 485 (* Revision that corresponds to point version 0 *)
+
+(* Documentation:
+   This is a program to construct a version of the form Major.Minor.Point,
+   e.g., 2.10.4.
+   The Point release number is calculated from the Subversion revision number,
+   so it will be automatically incremented on svn commit.
+   The Major and Minor numbers are hard coded, as is the revision number
+   corresponding to the 0 point release.
+
+   If you want to increment the Major or Minor number, you will have to do a
+   little thinking to get the Point number back to 0.  Suppose the current svn
+   revision number is 27, and we have below
+
+        let majorVersion = 2
+        let minorVersion = 11
+        let pointVersionOrigin = 3
+
+   This means that the current Unison version is 2.11.24, since 27-3 = 24.
+   If we want to change the release to 3.0.0 we need to change things to
+
+        let majorVersion = 3
+        let minorVersion = 0
+        let pointVersionOrigin = 28
+
+   and then do a svn commit.
+
+   The first two lines are obvious.  The last line says that Subversion
+   revision 28 corresponds to a 0 point release.  Since we were at revision
+   27 and we're going to do a commit before making a release, we
+   will be at 28 after the commit and this will be Unison version 3.0.0.
+*)
+
+(* ---------------------------------------------------------------------- *)
+(* You shouldn't need to edit below. *)
+
+let revisionString = "$Rev$";;
+
+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;;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Deleted: branches/2.45/src/uicommon.ml
===================================================================
--- trunk/src/uicommon.ml	2012-03-10 16:12:51 UTC (rev 485)
+++ branches/2.45/src/uicommon.ml	2012-04-02 15:54:47 UTC (rev 487)
@@ -1,759 +0,0 @@
-(* Unison file synchronizer: src/uicommon.ml *)
-(* Copyright 1999-2012, 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 <http://www.gnu.org/licenses/>.
-*)
-
-
-open Common
-open Lwt
-
-(**********************************************************************
-                             UI selection
- **********************************************************************)
-
-type interface =
-   Text
- | Graphic
-
-module type UI =
-sig
- val start : interface -> unit
- val defaultUi : interface
-end
-
-
-(**********************************************************************
-                             Preferences
- **********************************************************************)
-
-let auto =
-  Prefs.createBool "auto" false "automatically accept default (nonconflicting) actions"
-    ("When set to {\\tt true}, this flag causes the user "
-     ^ "interface to skip asking for confirmations on "
-     ^ "non-conflicting changes.  (More precisely, when the user interface "
-     ^ "is done setting the propagation direction for one entry and is about "
-     ^ "to move to the next, it will skip over all non-conflicting entries "
-     ^ "and go directly to the next conflict.)" )
-
-(* 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" 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.")
-
-let expert =
-  Prefs.createBool "expert" false
-    "*Enable some developers-only functionality in the UI" ""
-
-let profileLabel =
-  Prefs.createString "label" ""
-    "!provide a descriptive string label for this profile"
-    ("Used in a profile to provide a descriptive string documenting its "
-     ^ "settings.  (This is useful for users that switch between several "
-     ^ "profiles, especially using the `fast switch' feature of the "
-     ^ "graphical user interface.)")
-
-let profileKey =
-  Prefs.createString "key" ""
-    "!define a keyboard shortcut for this profile (in some UIs)"
-    ("Used in a profile to define a numeric key (0-9) that can be used in "
-     ^ "the graphical user interface to switch immediately to this profile.")
-(* This preference is not actually referred to in the code anywhere, since
-   the keyboard shortcuts are constructed by a separate scan of the preference
-   file in uigtk.ml, but it must be present to prevent the preferences module
-   from complaining about 'key = n' lines in profiles. *)
-
-let contactquietly =
-  Prefs.createBool "contactquietly" false
-    "!suppress the 'contacting server' message during startup"
-    ("If this flag is set, Unison will skip displaying the "
-     ^ "`Contacting server' message (which some users find annoying) "
-     ^ "during startup.")
-
-let contactingServerMsg () =
-  Printf.sprintf "Contacting server..." 
-
-let repeat =
-  Prefs.createString "repeat" ""
-    "!synchronize repeatedly (text interface only)"
-    ("Setting this preference causes the text-mode interface to synchronize "
-     ^ "repeatedly, rather than doing it just once and stopping.  If the "
-     ^ "argument is a number, Unison will pause for that many seconds before "
-     ^ "beginning again.")
-
-(*   ^ "If the argument is a path, Unison will wait for the "
-     ^ "file at this path---called a {\\em changelog}---to "
-     ^ "be modified (on either the client or the server "
-     ^ "machine), read the contents of the changelog (which should be a newline-"
-     ^ "separated list of paths) on both client and server, "
-     ^ "combine the results, "
-     ^ "and start again, using the list of paths read from the changelogs as the "
-     ^ " '-path' preference for the new run.  The idea is that an external "
-     ^ "process will watch the filesystem and, when it thinks something may have "
-     ^ "changed, write the changed pathname to its local changelog where Unison "
-     ^ "will find it the next time it looks.  If the changelogs have not been "
-     ^ "modified, Unison will wait, checking them again every few seconds."
-*)
-
-let retry =
-  Prefs.createInt "retry" 0
-    "!re-try failed synchronizations N times (text ui only)"
-    ("Setting this preference causes the text-mode interface to try again "
-     ^ "to synchronize "
-     ^ "updated paths where synchronization fails.  Each such path will be "
-     ^ "tried N times."
-    )
-
-let confirmmerge =
-  Prefs.createBool "confirmmerge" false
-    "!ask for confirmation before commiting results of a merge"
-    ("Setting this preference causes both the text and graphical interfaces"
-     ^ " to ask the user if the results of a merge command may be commited "
-     ^ " to the replica or not. Since the merge command works on temporary files,"
-     ^ " the user can then cancel all the effects of applying the merge if it"
-     ^ " turns out that the result is not satisfactory.  In "
-     ^ " batch-mode, this preference has no effect.  Default is false.")
-    
-let runTestsPrefName = "selftest"
-let runtests =
-  Prefs.createBool runTestsPrefName false
-    "!run internal tests and exit"
-   ("Run internal tests and exit.  This option is mostly for developers and must be used "
-  ^ "carefully: in particular, "
-  ^ "it will delete the contents of both roots, so that it can install its own files "
-  ^ "for testing.  This flag only makes sense on the command line.  When it is "
-  ^ "provided, no preference file is read: all preferences must be specified on the"
-  ^ "command line.  Also, since the self-test procedure involves overwriting the roots "
-  ^ "and backup directory, the names of the roots and of the backupdir preference "
-  ^ "must include the string "
-  ^ "\"test\" or else the tests will be aborted.  (If these are not given "
-  ^ "on the command line, dummy "
-  ^ "subdirectories in the current directory will be created automatically.)")
-
-(* This ref is set to Test.test during initialization, avoiding a circular
-   dependency *)
-let testFunction = ref (fun () -> assert false)
-
-(**********************************************************************
-                         Formatting functions
- **********************************************************************)
-
-(* When no archives were found, we omit 'new' in status descriptions, since
-   *all* files would be marked new and this won't make sense to the user. *)
-let choose s1 s2 = if !Update.foundArchives then s1 else s2
-
-let showprev =
-  Prefs.createBool "showprev" false
-    "*Show previous properties, if they differ from current"
-    ""
-
-(* The next function produces nothing unless the "showprev" 
-   preference is set.  This is because it tends to make the 
-   output trace too long and annoying. *)
-let prevProps newprops ui =
-  if not (Prefs.read showprev) then ""
-  else match ui with
-    NoUpdates | Error _
-      -> ""
-  | Updates (_, New) ->
-      " (new)"
-  | Updates (_, Previous(_,oldprops,_,_)) ->
-      (* || Props.similar newprops oldprops *)
-      " (was: "^(Props.toString oldprops)^")"
-
-let replicaContentDesc rc =
-  Props.toString (Props.setLength rc.desc (snd rc.size))
-
-let replicaContent2string rc sep =
-  let d s = s ^ sep ^ replicaContentDesc rc ^ prevProps rc.desc rc.ui in
-  match rc.typ, rc.status with
-    `ABSENT, `Unchanged ->
-      "absent"
-  | _, `Unchanged ->
-      "unchanged "
-     ^(Util.truncateString (Fileinfo.type2string rc.typ) 7)
-     ^ sep
-     ^ replicaContentDesc rc
-  | `ABSENT, `Deleted -> "deleted"
-  | `FILE, `Created ->
-     d (choose "new file         " "file             ")
-  | `FILE, `Modified ->
-     d "changed file     " 
-  | `FILE, `PropsChanged ->
-     d "changed props    " 
-  | `SYMLINK, `Created ->
-     d (choose "new symlink      " "symlink          ")
-  | `SYMLINK, `Modified ->
-     d "changed symlink  " 
-  | `DIRECTORY, `Created ->
-     d (choose "new dir          " "dir              ")
-  | `DIRECTORY, `Modified ->
-     d "changed dir      "
-  | `DIRECTORY, `PropsChanged ->
-     d "dir props changed" 
-
-  (* Some cases that can't happen... *)
-  | `ABSENT, (`Created | `Modified | `PropsChanged)
-  | `SYMLINK, `PropsChanged
-  | (`FILE|`SYMLINK|`DIRECTORY), `Deleted ->
-      assert false
-  
-let replicaContent2shortString rc =
-  match rc.typ, rc.status with
-    _, `Unchanged             -> "        "
-  | `ABSENT, `Deleted         -> "deleted "
-  | `FILE, `Created           -> choose "new file" "file    "
-  | `FILE, `Modified          -> "changed "
-  | `FILE, `PropsChanged      -> "props   "
-  | `SYMLINK, `Created        -> choose "new link" "link    "
-  | `SYMLINK, `Modified       -> "chgd lnk"
-  | `DIRECTORY, `Created      -> choose "new dir " "dir     "
-  | `DIRECTORY, `Modified     -> "chgd dir"
-  | `DIRECTORY, `PropsChanged -> "props   "
-  (* Cases that can't happen... *)
-  | `ABSENT, (`Created | `Modified | `PropsChanged)
-  | `SYMLINK, `PropsChanged
-  | (`FILE|`SYMLINK|`DIRECTORY), `Deleted
-                              -> assert false
-
-let roots2niceStrings length = function
-   (Local,fspath1), (Local,fspath2) ->
-    let name1, name2 = Fspath.differentSuffix fspath1 fspath2 in
-    (Util.truncateString name1 length, Util.truncateString name2 length)
- | (Local,fspath1), (Remote host, fspath2) ->
-    (Util.truncateString "local" length, Util.truncateString host length)
- | (Remote host, fspath1), (Local,fspath2) ->
-    (Util.truncateString host length, Util.truncateString "local" length)
- | _ -> assert false  (* BOGUS? *)
-
-let details2string theRi sep =
-  match theRi.replicas with
-    Problem s ->
-      Printf.sprintf "Error: %s\n" s
-  | Different {rc1 = rc1; rc2 = rc2} ->
-      let root1str, root2str =
-        roots2niceStrings 12 (Globals.roots()) in
-      Printf.sprintf "%s : %s\n%s : %s"
-        root1str (replicaContent2string rc1 sep)
-        root2str (replicaContent2string rc2 sep)
-
-let displayPath previousPath path =
-  let previousNames = Path.toNames previousPath in
-  let names = Path.toNames path in
-  if names = [] then "/" else
-  (* Strip the greatest common prefix of previousNames and names
-     from names.  level is the number of names in the greatest
-     common prefix. *)
-  let rec loop level names1 names2 =
-    match (names1,names2) with
-      (hd1::tl1,hd2::tl2) ->
-        if Name.compare hd1 hd2 = 0
-        then loop (level+1) tl1 tl2
-        else (level,names2)
-    | _ -> (level,names2) in
-  let (level,suffixNames) = loop 0 previousNames names in
-  let suffixPath =
-    Safelist.fold_left Path.child Path.empty suffixNames in
-  let spaces = String.make (level*3) ' ' in 
-  spaces ^ (Path.toString suffixPath)
-
-let roots2string () =
-  let replica1, replica2 = roots2niceStrings 12 (Globals.roots()) in
-  (Printf.sprintf "%s   %s       " replica1 replica2) 
-
-type action = AError | ASkip of bool | ALtoR of bool | ARtoL of bool | AMerge
-
-let direction2action partial dir =
-  match dir with
-    Conflict           -> ASkip partial
-  | Replica1ToReplica2 -> ALtoR partial
-  | Replica2ToReplica1 -> ARtoL partial
-  | Merge              -> AMerge
-
-let action2niceString action =
-  match action with
-    AError      -> "error"
-  | ASkip _     -> "<-?->"
-  | ALtoR false -> "---->"
-  | ALtoR true  -> "--?->"
-  | ARtoL false -> "<----"
-  | ARtoL true  -> "<-?--"
-  | AMerge      -> "<-M->"
-
-let reconItem2stringList oldPath theRI =
-  match theRI.replicas with
-    Problem s ->
-      ("        ", AError, "        ", displayPath oldPath theRI.path1)
-  | Different diff ->
-      let partial = diff.errors1 <> [] || diff.errors2 <> [] in
-      (replicaContent2shortString diff.rc1,
-       direction2action partial diff.direction,
-       replicaContent2shortString diff.rc2,
-       displayPath oldPath theRI.path1)
-
-let reconItem2string oldPath theRI status =
-  let (r1, action, r2, path) = reconItem2stringList oldPath theRI in
-  Format.sprintf "%s %s %s %s %s" r1 (action2niceString action) r2 status path
-
-let exn2string = function
-    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 *)
-let showDiffs ri printer errprinter id =
-  match ri.replicas with
-    Problem _ ->
-      errprinter
-        "Can't diff files: there was a problem during update detection"
-  | Different {rc1 = {typ = `FILE; ui = ui1}; rc2 = {typ = `FILE; ui = ui2}} ->
-      let (root1,root2) = Globals.roots() in
-      begin
-        try Files.diff root1 ri.path1 ui1 root2 ri.path2 ui2 printer id
-        with Util.Transient e -> errprinter e
-      end 
-  | Different _ ->
-      errprinter "Can't diff: path doesn't refer to a file in both replicas"
-
-
-exception Synch_props of Common.reconItem
-
-(**********************************************************************
-                  Common error messages
- **********************************************************************)
-
-let dangerousPathMsg dangerousPaths =
-  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.)\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.)\n"
-      (String.concat "\n  "
-         (Safelist.map (fun p -> "'" ^ (Path.toString p) ^ "'")
-            dangerousPaths))
-
-(**********************************************************************
-                  Useful patterns for ignoring paths
- **********************************************************************)
-
-let quote s =
-  let len = String.length s in
-  let buf = String.create (2 * len) in
-  let pos = ref 0 in
-  for i = 0 to len - 1 do
-    match s.[i] with
-      '*' | '?' | '[' | '{' | '}' | ',' | '\\' as c ->
-        buf.[!pos] <- '\\'; buf.[!pos + 1] <- c; pos := !pos + 2
-    | c ->
-        buf.[!pos] <- c; pos := !pos + 1
-  done;
-  "{" ^ String.sub buf 0 !pos ^ "}"
-
-let ignorePath path = "Path " ^ quote (Path.toString path)
-
-let ignoreName path =
-  match Path.finalName path with
-    Some name -> "Name " ^ quote (Name.toString name)
-  | None      -> assert false
-
-let ignoreExt path =
-  match Path.finalName path with
-    Some name ->
-      let str = Name.toString name in
-      begin try
-        let pos = String.rindex str '.' in
-        let ext = String.sub str pos (String.length str - pos) in
-        "Name {,.}*" ^ quote ext
-      with Not_found -> (* str does not contain '.' *)
-        "Name " ^ quote str
-      end
-  | None ->
-      assert false
-
-let addIgnorePattern theRegExp =
-  if theRegExp = "Path " then
-    raise (Util.Transient "Can't ignore the root path!");
-  Globals.addRegexpToIgnore theRegExp;
-  let r = Prefs.add "ignore" theRegExp in
-  Trace.status r;
-  (* Make sure the server has the same ignored paths (in case, for
-     example, we do a "rescan") *)
-  Lwt_unix.run (Globals.propagatePrefs ())
-
-(**********************************************************************
-                   Profile and command-line parsing
- **********************************************************************)
-
-let coreUsageMsg =
-   "Usage: " ^ Uutil.myName
- ^ " [options]\n"
- ^ "    or " ^ Uutil.myName
- ^ " root1 root2 [options]\n"
- ^ "    or " ^ Uutil.myName
- ^ " profilename [options]\n"
-
-let shortUsageMsg =
-     coreUsageMsg ^ "\n"
-   ^ "For a list of options, type \"" ^ Uutil.myName ^ " -help\".\n"
-   ^ "For a tutorial on basic usage, type \"" ^ Uutil.myName
-   ^ " -doc tutorial\".\n"
-   ^ "For other documentation, type \"" ^ Uutil.myName ^ " -doc topics\".\n"
-
-let usageMsg = coreUsageMsg 
-
-let debug = Trace.debug "startup"
-
-(* ---- *)
-
-(*FIX: remove when Unison version > 2.40 *)
-let _ =
-Remote.registerRootCmd "_unicodeCaseSensitive_" (fun _ -> Lwt.return ())
-let supportUnicodeCaseSensitive () =
-  if Uutil.myMajorVersion > "2.40" (* The test is correct until 2.99... *) then
-    Lwt.return true
-  else begin
-    Globals.allRootsMap
-      (fun r -> Remote.commandAvailable r "_unicodeCaseSensitive_")
-    >>= fun l ->
-    Lwt.return (List.for_all (fun x -> x) l)
-  end
-
-(* Determine the case sensitivity of a root (does filename FOO==foo?) *)
-let architecture =
-  Remote.registerRootCmd
-    "architecture"
-    (fun (_,()) -> return (Util.osType = `Win32, Osx.isMacOSX, Util.isCygwin))
-
-(* 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.  Also, detects HFS (needed for resource forks) and
-   Windows (needed for permissions) and does some sanity checking. *) 
-let validateAndFixupPrefs () =
-  Props.validatePrefs();
-  let supportUnicodeCaseSensitive = supportUnicodeCaseSensitive () in
-  Globals.allRootsMap (fun r -> architecture r ()) >>= (fun archs ->
-  supportUnicodeCaseSensitive >>= fun unicodeCS ->
-  let someHostIsRunningWindows =
-    Safelist.exists (fun (isWin, _, _) -> isWin) archs in
-  let allHostsAreRunningWindows =
-    Safelist.for_all (fun (isWin, _, _) -> isWin) archs in
-  let someHostIsRunningBareWindows =
-    Safelist.exists (fun (isWin, _, isCyg) -> isWin && not isCyg) archs in
-  let someHostRunningOsX =
-    Safelist.exists (fun (_, isOSX, _) -> isOSX) archs in
-  let someHostIsCaseInsensitive =
-    someHostIsRunningWindows || someHostRunningOsX in
-  if Prefs.read Globals.fatFilesystem then begin
-    Prefs.overrideDefault Props.permMask 0;
-    Prefs.overrideDefault Props.dontChmod true;
-    Prefs.overrideDefault Case.caseInsensitiveMode `True;
-    Prefs.overrideDefault Fileinfo.allowSymlinks `False;
-    Prefs.overrideDefault Fileinfo.ignoreInodeNumbers true
-  end;
-  Case.init someHostIsCaseInsensitive (someHostRunningOsX && unicodeCS);
-  Props.init someHostIsRunningWindows;
-  Osx.init someHostRunningOsX;
-  Fileinfo.init someHostIsRunningBareWindows;
-  Prefs.set Globals.someHostIsRunningWindows someHostIsRunningWindows;
-  Prefs.set Globals.allHostsAreRunningWindows allHostsAreRunningWindows;
-  return ())
-
-(* ---- *)
-
-let promptForRoots getFirstRoot getSecondRoot =
-  (* Ask the user for the roots *)
-  let r1 = match getFirstRoot() with None -> exit 0 | Some r -> r in
-  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 [r1; r2];
-  (* Save them in the current profile *)
-  ignore (Prefs.add "root" r1);
-  ignore (Prefs.add "root" r2)
-
-(* ---- *)
-
-(* The first time we load preferences, we also read the command line
-   arguments; if we re-load prefs (because the user selected a new profile)
-   we ignore the command line *)
-let firstTime = ref(true)
-
-(* Roots given on the command line *)
-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 !cmdLineRawRoots;
-
-  (* Tell the preferences module the name of the profile *)
-  Prefs.profileName := Some(profileName);
-  
-  (* Check whether the -selftest flag is present on the command line *)
-  let testFlagPresent =
-    Util.StringMap.mem runTestsPrefName (Prefs.scanCmdLine usageMsg) in
-  
-  (* If the -selftest flag is present, then we skip loading the preference file.
-     (This is prevents possible confusions where settings from a preference
-     file could cause unit tests to fail.) *)
-  if not testFlagPresent then begin
-    (* If the profile does not exist, create an empty one (this should only
-       happen if the profile is 'default', since otherwise we will already
-       have checked that the named one exists). *)
-    if not(System.file_exists (Prefs.profilePathname profileName)) then
-      Prefs.addComment "Unison preferences file";
-
-    (* Load the profile *)
-    (debug (fun() -> Util.msg "about to load prefs");
-     Prefs.loadTheFile());
-
-    (* Now check again that the -selftest flag has not been set, and barf otherwise *)
-    if Prefs.read runtests then raise (Util.Fatal
-      "The 'test' flag should only be given on the command line")
-  end;
-
-  (* Parse the command line.  This will override settings from the profile. *)
-  (* JV (6/09): always reparse the command line *)
-  if true (*!firstTime*) then begin
-    debug (fun() -> Util.msg "about to parse command line");
-    Prefs.parseCmdLine usageMsg;
-  end;
-
-  (* Install dummy roots and backup directory if we are running self-tests *)
-  if Prefs.read runtests then begin
-    if Globals.rawRoots() = [] then 
-      Prefs.loadStrings ["root = test-a.tmp"; "root = test-b.tmp"];
-    if (Prefs.read Stasher.backupdir) = "" then
-      Prefs.loadStrings ["backupdir = test-backup.tmp"];
-  end;
-
-  (* Print the preference settings *)
-  debug (fun() -> Prefs.dumpPrefsToStderr() );
-
-  (* If no roots are given either on the command line or in the profile,
-     ask the user *)
-  if Globals.rawRoots() = [] then begin
-    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
-    displayWaitMessage();
-
-  (* Canonize the names of the roots, sort them (with local roots first),
-     and install them in Globals. *)
-  Lwt_unix.run (Globals.installRoots termInteract);
-
-  (* If both roots are local, disable the xferhint table to save time *)
-  begin match Globals.roots() with
-    ((Local,_),(Local,_)) -> Prefs.set Xferhint.xferbycopying false
-  | _ -> ()
-  end;
-
-  (* FIX: This should be before Globals.installRoots *)
-  (* Check to be sure that there is at most one remote root *)
-  let numRemote =
-    Safelist.fold_left
-      (fun n (w,_) -> match w with Local -> n | Remote _ -> n+1)
-      0
-      (Globals.rootsList()) in
-      if numRemote > 1 then
-        raise(Util.Fatal "cannot synchronize more than one remote root");
-
-  (* If no paths were specified, then synchronize the whole replicas *)
-  if Prefs.read Globals.paths = [] then Prefs.set Globals.paths [Path.empty];
-
-  (* Expand any "wildcard" paths [with final component *] *)
-  Globals.expandWildcardPaths();
-
-  Update.storeRootsName ();
-
-  if
-    numRemote > 0 && not (Prefs.read contactquietly || Prefs.read Trace.terse)
-  then
-    Util.msg "Connected [%s]\n"
-      (Util.replacesubstring (Update.getRootsName()) ", " " -> ");
-
-  debug (fun() ->
-       Printf.eprintf "Roots: \n";
-       Safelist.iter (fun clr -> Printf.eprintf "        %s\n" clr)
-         (Globals.rawRoots ());
-       Printf.eprintf "  i.e. \n";
-       Safelist.iter (fun clr -> Printf.eprintf "        %s\n"
-                        (Clroot.clroot2string (Clroot.parseRoot clr)))
-         (Globals.rawRoots ());
-       Printf.eprintf "  i.e. (in canonical order)\n";
-       Safelist.iter (fun r -> 
-                        Printf.eprintf "       %s\n" (root2string r))
-         (Globals.rootsInCanonicalOrder());
-       Printf.eprintf "\n");
-  
-  Lwt_unix.run
-    (validateAndFixupPrefs () >>=
-     Globals.propagatePrefs);
-
-  (* Initializes some backups stuff according to the preferences just loaded from the profile.
-     Important to do it here, after prefs are propagated, because the function will also be
-     run on the server, if any. Also, this should be done each time a profile is reloaded
-     on this side, that's why it's here. *) 
-  Stasher.initBackups ();
-  
-  firstTime := false
-
-(**********************************************************************
-                       Common startup sequence
- **********************************************************************)
-
-let anonymousArgs =
-  Prefs.createStringList "rest"
-    "*roots or profile name" ""
-
-let testServer =
-  Prefs.createBool "testserver" false
-    "exit immediately after the connection to the server"
-    ("Setting this flag on the command line causes Unison to attempt to "
-     ^ "connect to the remote server and, if successful, print a message "
-     ^ "and immediately exit.  Useful for debugging installation problems. "
-     ^ "Should not be set in preference files.")
-
-(* For backward compatibility *)
-let _ = Prefs.alias testServer "testServer"
-
-(* ---- *)
-
-let uiInit
-    ~(reportError : string -> unit)
-    ~(tryAgainOrQuit : string -> bool)
-    ~(displayWaitMessage : unit -> unit)
-    ~(getProfile : unit -> string option)
-    ~(getFirstRoot : unit -> string option)
-    ~(getSecondRoot : unit -> string option)
-    ~(termInteract : (string -> string -> string) option) =
-
-  (* Make sure we have a directory for archives and profiles *)
-  Os.createUnisonDir();
- 
-  (* Extract any command line profile or roots *)
-  let clprofile = ref None in
-  begin
-    try
-      let args = Prefs.scanCmdLine usageMsg in
-      match Util.StringMap.find "rest" args with
-        [] -> ()
-      | [profile] -> clprofile := Some profile
-      | [root2;root1] -> cmdLineRawRoots := [root1;root2]
-      | [root2;root1;profile] ->
-          cmdLineRawRoots := [root1;root2];
-          clprofile := Some profile
-      | _ ->
-          (reportError(Printf.sprintf
-             "%s was invoked incorrectly (too many roots)" Uutil.myName);
-           exit 1)
-    with Not_found -> ()
-  end;
-
-  (* Print header for debugging output *)
-  debug (fun() ->
-    Printf.eprintf "%s, version %s\n\n" Uutil.myName Uutil.myVersion);
-  debug (fun() -> Util.msg "initializing UI");
-
-  debug (fun () ->
-    (match !clprofile with
-      None -> Util.msg "No profile given on command line"
-    | Some s -> Printf.eprintf "Profile '%s' given on command line" s);
-    (match !cmdLineRawRoots with
-      [] -> Util.msg "No roots given on command line"
-    | [root1;root2] ->
-        Printf.eprintf "Roots '%s' and '%s' given on command line"
-          root1 root2
-    | _ -> assert false));
-
-  let profileName =
-    begin match !clprofile with
-      None ->
-        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. *)
-            clprofile := getProfile();
-            match !clprofile with
-              None -> exit 0 (* None means the user wants to quit *)
-            | Some x -> x 
-          end else begin
-            (* Roots given on command line.
-               The profile should be the default. *)
-            clprofile := Some "default";
-            "default"
-          end in
-        n
-    | Some n ->
-        let f = Prefs.profilePathname n in
-        if not(System.file_exists f)
-        then (reportError (Printf.sprintf "Profile %s does not exist"
-                             (System.fspathToPrintString f));
-              exit 1);
-        n
-    end in
-
-  (* Load the profile and command-line arguments *)
-  initPrefs
-    profileName displayWaitMessage getFirstRoot getSecondRoot termInteract;
-  
-  (* Turn on GC messages, if the '-debug gc' flag was provided *)
-  if Trace.enabled "gc" then Gc.set {(Gc.get ()) with Gc.verbose = 0x3F};
-
-  if Prefs.read testServer then exit 0;
-
-  (* BCPFIX: Should/can this be done earlier?? *)
-  Files.processCommitLogs();
-
-  (* Run unit tests if requested *)
-  if Prefs.read runtests then begin
-    (!testFunction)();
-    exit 0
-  end
-
-(* Exit codes *)
-let perfectExit = 0   (* when everything's okay *)
-let skippyExit  = 1   (* when some items were skipped, but no failure occurred *)
-let failedExit  = 2   (* when there's some non-fatal failure *)
-let fatalExit   = 3   (* when fatal failure occurred *)
-let exitCode = function
-    (false, false) -> 0
-  | (true, false)  -> 1
-  | _              -> 2
-(* (anySkipped?, anyFailure?) -> exit code *)

Copied: branches/2.45/src/uicommon.ml (from rev 486, trunk/src/uicommon.ml)
===================================================================
--- branches/2.45/src/uicommon.ml	                        (rev 0)
+++ branches/2.45/src/uicommon.ml	2012-04-02 15:54:47 UTC (rev 487)
@@ -0,0 +1,759 @@
+(* Unison file synchronizer: src/uicommon.ml *)
+(* Copyright 1999-2012, 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 <http://www.gnu.org/licenses/>.
+*)
+
+
+open Common
+open Lwt
+
+(**********************************************************************
+                             UI selection
+ **********************************************************************)
+
+type interface =
+   Text
+ | Graphic
+
+module type UI =
+sig
+ val start : interface -> unit
+ val defaultUi : interface
+end
+
+
+(**********************************************************************
+                             Preferences
+ **********************************************************************)
+
+let auto =
+  Prefs.createBool "auto" false "automatically accept default (nonconflicting) actions"
+    ("When set to {\\tt true}, this flag causes the user "
+     ^ "interface to skip asking for confirmations on "
+     ^ "non-conflicting changes.  (More precisely, when the user interface "
+     ^ "is done setting the propagation direction for one entry and is about "
+     ^ "to move to the next, it will skip over all non-conflicting entries "
+     ^ "and go directly to the next conflict.)" )
+
+(* 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" 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.")
+
+let expert =
+  Prefs.createBool "expert" false
+    "*Enable some developers-only functionality in the UI" ""
+
+let profileLabel =
+  Prefs.createString "label" ""
+    "!provide a descriptive string label for this profile"
+    ("Used in a profile to provide a descriptive string documenting its "
+     ^ "settings.  (This is useful for users that switch between several "
+     ^ "profiles, especially using the `fast switch' feature of the "
+     ^ "graphical user interface.)")
+
+let profileKey =
+  Prefs.createString "key" ""
+    "!define a keyboard shortcut for this profile (in some UIs)"
+    ("Used in a profile to define a numeric key (0-9) that can be used in "
+     ^ "the graphical user interface to switch immediately to this profile.")
+(* This preference is not actually referred to in the code anywhere, since
+   the keyboard shortcuts are constructed by a separate scan of the preference
+   file in uigtk.ml, but it must be present to prevent the preferences module
+   from complaining about 'key = n' lines in profiles. *)
+
+let contactquietly =
+  Prefs.createBool "contactquietly" false
+    "!suppress the 'contacting server' message during startup"
+    ("If this flag is set, Unison will skip displaying the "
+     ^ "`Contacting server' message (which some users find annoying) "
+     ^ "during startup.")
+
+let contactingServerMsg () =
+  Printf.sprintf "Contacting server..." 
+
+let repeat =
+  Prefs.createString "repeat" ""
+    "!synchronize repeatedly (text interface only)"
+    ("Setting this preference causes the text-mode interface to synchronize "
+     ^ "repeatedly, rather than doing it just once and stopping.  If the "
+     ^ "argument is a number, Unison will pause for that many seconds before "
+     ^ "beginning again.")
+
+(*   ^ "If the argument is a path, Unison will wait for the "
+     ^ "file at this path---called a {\\em changelog}---to "
+     ^ "be modified (on either the client or the server "
+     ^ "machine), read the contents of the changelog (which should be a newline-"
+     ^ "separated list of paths) on both client and server, "
+     ^ "combine the results, "
+     ^ "and start again, using the list of paths read from the changelogs as the "
+     ^ " '-path' preference for the new run.  The idea is that an external "
+     ^ "process will watch the filesystem and, when it thinks something may have "
+     ^ "changed, write the changed pathname to its local changelog where Unison "
+     ^ "will find it the next time it looks.  If the changelogs have not been "
+     ^ "modified, Unison will wait, checking them again every few seconds."
+*)
+
+let retry =
+  Prefs.createInt "retry" 0
+    "!re-try failed synchronizations N times (text ui only)"
+    ("Setting this preference causes the text-mode interface to try again "
+     ^ "to synchronize "
+     ^ "updated paths where synchronization fails.  Each such path will be "
+     ^ "tried N times."
+    )
+
+let confirmmerge =
+  Prefs.createBool "confirmmerge" false
+    "!ask for confirmation before committing results of a merge"
+    ("Setting this preference causes both the text and graphical interfaces"
+     ^ " to ask the user if the results of a merge command may be committed "
+     ^ " to the replica or not. Since the merge command works on temporary files,"
+     ^ " the user can then cancel all the effects of applying the merge if it"
+     ^ " turns out that the result is not satisfactory.  In "
+     ^ " batch-mode, this preference has no effect.  Default is false.")
+    
+let runTestsPrefName = "selftest"
+let runtests =
+  Prefs.createBool runTestsPrefName false
+    "!run internal tests and exit"
+   ("Run internal tests and exit.  This option is mostly for developers and must be used "
+  ^ "carefully: in particular, "
+  ^ "it will delete the contents of both roots, so that it can install its own files "
+  ^ "for testing.  This flag only makes sense on the command line.  When it is "
+  ^ "provided, no preference file is read: all preferences must be specified on the"
+  ^ "command line.  Also, since the self-test procedure involves overwriting the roots "
+  ^ "and backup directory, the names of the roots and of the backupdir preference "
+  ^ "must include the string "
+  ^ "\"test\" or else the tests will be aborted.  (If these are not given "
+  ^ "on the command line, dummy "
+  ^ "subdirectories in the current directory will be created automatically.)")
+
+(* This ref is set to Test.test during initialization, avoiding a circular
+   dependency *)
+let testFunction = ref (fun () -> assert false)
+
+(**********************************************************************
+                         Formatting functions
+ **********************************************************************)
+
+(* When no archives were found, we omit 'new' in status descriptions, since
+   *all* files would be marked new and this won't make sense to the user. *)
+let choose s1 s2 = if !Update.foundArchives then s1 else s2
+
+let showprev =
+  Prefs.createBool "showprev" false
+    "*Show previous properties, if they differ from current"
+    ""
+
+(* The next function produces nothing unless the "showprev" 
+   preference is set.  This is because it tends to make the 
+   output trace too long and annoying. *)
+let prevProps newprops ui =
+  if not (Prefs.read showprev) then ""
+  else match ui with
+    NoUpdates | Error _
+      -> ""
+  | Updates (_, New) ->
+      " (new)"
+  | Updates (_, Previous(_,oldprops,_,_)) ->
+      (* || Props.similar newprops oldprops *)
+      " (was: "^(Props.toString oldprops)^")"
+
+let replicaContentDesc rc =
+  Props.toString (Props.setLength rc.desc (snd rc.size))
+
+let replicaContent2string rc sep =
+  let d s = s ^ sep ^ replicaContentDesc rc ^ prevProps rc.desc rc.ui in
+  match rc.typ, rc.status with
+    `ABSENT, `Unchanged ->
+      "absent"
+  | _, `Unchanged ->
+      "unchanged "
+     ^(Util.truncateString (Fileinfo.type2string rc.typ) 7)
+     ^ sep
+     ^ replicaContentDesc rc
+  | `ABSENT, `Deleted -> "deleted"
+  | `FILE, `Created ->
+     d (choose "new file         " "file             ")
+  | `FILE, `Modified ->
+     d "changed file     " 
+  | `FILE, `PropsChanged ->
+     d "changed props    " 
+  | `SYMLINK, `Created ->
+     d (choose "new symlink      " "symlink          ")
+  | `SYMLINK, `Modified ->
+     d "changed symlink  " 
+  | `DIRECTORY, `Created ->
+     d (choose "new dir          " "dir              ")
+  | `DIRECTORY, `Modified ->
+     d "changed dir      "
+  | `DIRECTORY, `PropsChanged ->
+     d "dir props changed" 
+
+  (* Some cases that can't happen... *)
+  | `ABSENT, (`Created | `Modified | `PropsChanged)
+  | `SYMLINK, `PropsChanged
+  | (`FILE|`SYMLINK|`DIRECTORY), `Deleted ->
+      assert false
+  
+let replicaContent2shortString rc =
+  match rc.typ, rc.status with
+    _, `Unchanged             -> "        "
+  | `ABSENT, `Deleted         -> "deleted "
+  | `FILE, `Created           -> choose "new file" "file    "
+  | `FILE, `Modified          -> "changed "
+  | `FILE, `PropsChanged      -> "props   "
+  | `SYMLINK, `Created        -> choose "new link" "link    "
+  | `SYMLINK, `Modified       -> "chgd lnk"
+  | `DIRECTORY, `Created      -> choose "new dir " "dir     "
+  | `DIRECTORY, `Modified     -> "chgd dir"
+  | `DIRECTORY, `PropsChanged -> "props   "
+  (* Cases that can't happen... *)
+  | `ABSENT, (`Created | `Modified | `PropsChanged)
+  | `SYMLINK, `PropsChanged
+  | (`FILE|`SYMLINK|`DIRECTORY), `Deleted
+                              -> assert false
+
+let roots2niceStrings length = function
+   (Local,fspath1), (Local,fspath2) ->
+    let name1, name2 = Fspath.differentSuffix fspath1 fspath2 in
+    (Util.truncateString name1 length, Util.truncateString name2 length)
+ | (Local,fspath1), (Remote host, fspath2) ->
+    (Util.truncateString "local" length, Util.truncateString host length)
+ | (Remote host, fspath1), (Local,fspath2) ->
+    (Util.truncateString host length, Util.truncateString "local" length)
+ | _ -> assert false  (* BOGUS? *)
+
+let details2string theRi sep =
+  match theRi.replicas with
+    Problem s ->
+      Printf.sprintf "Error: %s\n" s
+  | Different {rc1 = rc1; rc2 = rc2} ->
+      let root1str, root2str =
+        roots2niceStrings 12 (Globals.roots()) in
+      Printf.sprintf "%s : %s\n%s : %s"
+        root1str (replicaContent2string rc1 sep)
+        root2str (replicaContent2string rc2 sep)
+
+let displayPath previousPath path =
+  let previousNames = Path.toNames previousPath in
+  let names = Path.toNames path in
+  if names = [] then "/" else
+  (* Strip the greatest common prefix of previousNames and names
+     from names.  level is the number of names in the greatest
+     common prefix. *)
+  let rec loop level names1 names2 =
+    match (names1,names2) with
+      (hd1::tl1,hd2::tl2) ->
+        if Name.compare hd1 hd2 = 0
+        then loop (level+1) tl1 tl2
+        else (level,names2)
+    | _ -> (level,names2) in
+  let (level,suffixNames) = loop 0 previousNames names in
+  let suffixPath =
+    Safelist.fold_left Path.child Path.empty suffixNames in
+  let spaces = String.make (level*3) ' ' in 
+  spaces ^ (Path.toString suffixPath)
+
+let roots2string () =
+  let replica1, replica2 = roots2niceStrings 12 (Globals.roots()) in
+  (Printf.sprintf "%s   %s       " replica1 replica2) 
+
+type action = AError | ASkip of bool | ALtoR of bool | ARtoL of bool | AMerge
+
+let direction2action partial dir =
+  match dir with
+    Conflict           -> ASkip partial
+  | Replica1ToReplica2 -> ALtoR partial
+  | Replica2ToReplica1 -> ARtoL partial
+  | Merge              -> AMerge
+
+let action2niceString action =
+  match action with
+    AError      -> "error"
+  | ASkip _     -> "<-?->"
+  | ALtoR false -> "---->"
+  | ALtoR true  -> "--?->"
+  | ARtoL false -> "<----"
+  | ARtoL true  -> "<-?--"
+  | AMerge      -> "<-M->"
+
+let reconItem2stringList oldPath theRI =
+  match theRI.replicas with
+    Problem s ->
+      ("        ", AError, "        ", displayPath oldPath theRI.path1)
+  | Different diff ->
+      let partial = diff.errors1 <> [] || diff.errors2 <> [] in
+      (replicaContent2shortString diff.rc1,
+       direction2action partial diff.direction,
+       replicaContent2shortString diff.rc2,
+       displayPath oldPath theRI.path1)
+
+let reconItem2string oldPath theRI status =
+  let (r1, action, r2, path) = reconItem2stringList oldPath theRI in
+  Format.sprintf "%s %s %s %s %s" r1 (action2niceString action) r2 status path
+
+let exn2string = function
+    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 *)
+let showDiffs ri printer errprinter id =
+  match ri.replicas with
+    Problem _ ->
+      errprinter
+        "Can't diff files: there was a problem during update detection"
+  | Different {rc1 = {typ = `FILE; ui = ui1}; rc2 = {typ = `FILE; ui = ui2}} ->
+      let (root1,root2) = Globals.roots() in
+      begin
+        try Files.diff root1 ri.path1 ui1 root2 ri.path2 ui2 printer id
+        with Util.Transient e -> errprinter e
+      end 
+  | Different _ ->
+      errprinter "Can't diff: path doesn't refer to a file in both replicas"
+
+
+exception Synch_props of Common.reconItem
+
+(**********************************************************************
+                  Common error messages
+ **********************************************************************)
+
+let dangerousPathMsg dangerousPaths =
+  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.)\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.)\n"
+      (String.concat "\n  "
+         (Safelist.map (fun p -> "'" ^ (Path.toString p) ^ "'")
+            dangerousPaths))
+
+(**********************************************************************
+                  Useful patterns for ignoring paths
+ **********************************************************************)
+
+let quote s =
+  let len = String.length s in
+  let buf = String.create (2 * len) in
+  let pos = ref 0 in
+  for i = 0 to len - 1 do
+    match s.[i] with
+      '*' | '?' | '[' | '{' | '}' | ',' | '\\' as c ->
+        buf.[!pos] <- '\\'; buf.[!pos + 1] <- c; pos := !pos + 2
+    | c ->
+        buf.[!pos] <- c; pos := !pos + 1
+  done;
+  "{" ^ String.sub buf 0 !pos ^ "}"
+
+let ignorePath path = "Path " ^ quote (Path.toString path)
+
+let ignoreName path =
+  match Path.finalName path with
+    Some name -> "Name " ^ quote (Name.toString name)
+  | None      -> assert false
+
+let ignoreExt path =
+  match Path.finalName path with
+    Some name ->
+      let str = Name.toString name in
+      begin try
+        let pos = String.rindex str '.' in
+        let ext = String.sub str pos (String.length str - pos) in
+        "Name {,.}*" ^ quote ext
+      with Not_found -> (* str does not contain '.' *)
+        "Name " ^ quote str
+      end
+  | None ->
+      assert false
+
+let addIgnorePattern theRegExp =
+  if theRegExp = "Path " then
+    raise (Util.Transient "Can't ignore the root path!");
+  Globals.addRegexpToIgnore theRegExp;
+  let r = Prefs.add "ignore" theRegExp in
+  Trace.status r;
+  (* Make sure the server has the same ignored paths (in case, for
+     example, we do a "rescan") *)
+  Lwt_unix.run (Globals.propagatePrefs ())
+
+(**********************************************************************
+                   Profile and command-line parsing
+ **********************************************************************)
+
+let coreUsageMsg =
+   "Usage: " ^ Uutil.myName
+ ^ " [options]\n"
+ ^ "    or " ^ Uutil.myName
+ ^ " root1 root2 [options]\n"
+ ^ "    or " ^ Uutil.myName
+ ^ " profilename [options]\n"
+
+let shortUsageMsg =
+     coreUsageMsg ^ "\n"
+   ^ "For a list of options, type \"" ^ Uutil.myName ^ " -help\".\n"
+   ^ "For a tutorial on basic usage, type \"" ^ Uutil.myName
+   ^ " -doc tutorial\".\n"
+   ^ "For other documentation, type \"" ^ Uutil.myName ^ " -doc topics\".\n"
+
+let usageMsg = coreUsageMsg 
+
+let debug = Trace.debug "startup"
+
+(* ---- *)
+
+(*FIX: remove when Unison version > 2.40 *)
+let _ =
+Remote.registerRootCmd "_unicodeCaseSensitive_" (fun _ -> Lwt.return ())
+let supportUnicodeCaseSensitive () =
+  if Uutil.myMajorVersion > "2.40" (* The test is correct until 2.99... *) then
+    Lwt.return true
+  else begin
+    Globals.allRootsMap
+      (fun r -> Remote.commandAvailable r "_unicodeCaseSensitive_")
+    >>= fun l ->
+    Lwt.return (List.for_all (fun x -> x) l)
+  end
+
+(* Determine the case sensitivity of a root (does filename FOO==foo?) *)
+let architecture =
+  Remote.registerRootCmd
+    "architecture"
+    (fun (_,()) -> return (Util.osType = `Win32, Osx.isMacOSX, Util.isCygwin))
+
+(* 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.  Also, detects HFS (needed for resource forks) and
+   Windows (needed for permissions) and does some sanity checking. *) 
+let validateAndFixupPrefs () =
+  Props.validatePrefs();
+  let supportUnicodeCaseSensitive = supportUnicodeCaseSensitive () in
+  Globals.allRootsMap (fun r -> architecture r ()) >>= (fun archs ->
+  supportUnicodeCaseSensitive >>= fun unicodeCS ->
+  let someHostIsRunningWindows =
+    Safelist.exists (fun (isWin, _, _) -> isWin) archs in
+  let allHostsAreRunningWindows =
+    Safelist.for_all (fun (isWin, _, _) -> isWin) archs in
+  let someHostIsRunningBareWindows =
+    Safelist.exists (fun (isWin, _, isCyg) -> isWin && not isCyg) archs in
+  let someHostRunningOsX =
+    Safelist.exists (fun (_, isOSX, _) -> isOSX) archs in
+  let someHostIsCaseInsensitive =
+    someHostIsRunningWindows || someHostRunningOsX in
+  if Prefs.read Globals.fatFilesystem then begin
+    Prefs.overrideDefault Props.permMask 0;
+    Prefs.overrideDefault Props.dontChmod true;
+    Prefs.overrideDefault Case.caseInsensitiveMode `True;
+    Prefs.overrideDefault Fileinfo.allowSymlinks `False;
+    Prefs.overrideDefault Fileinfo.ignoreInodeNumbers true
+  end;
+  Case.init someHostIsCaseInsensitive (someHostRunningOsX && unicodeCS);
+  Props.init someHostIsRunningWindows;
+  Osx.init someHostRunningOsX;
+  Fileinfo.init someHostIsRunningBareWindows;
+  Prefs.set Globals.someHostIsRunningWindows someHostIsRunningWindows;
+  Prefs.set Globals.allHostsAreRunningWindows allHostsAreRunningWindows;
+  return ())
+
+(* ---- *)
+
+let promptForRoots getFirstRoot getSecondRoot =
+  (* Ask the user for the roots *)
+  let r1 = match getFirstRoot() with None -> exit 0 | Some r -> r in
+  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 [r1; r2];
+  (* Save them in the current profile *)
+  ignore (Prefs.add "root" r1);
+  ignore (Prefs.add "root" r2)
+
+(* ---- *)
+
+(* The first time we load preferences, we also read the command line
+   arguments; if we re-load prefs (because the user selected a new profile)
+   we ignore the command line *)
+let firstTime = ref(true)
+
+(* Roots given on the command line *)
+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 !cmdLineRawRoots;
+
+  (* Tell the preferences module the name of the profile *)
+  Prefs.profileName := Some(profileName);
+  
+  (* Check whether the -selftest flag is present on the command line *)
+  let testFlagPresent =
+    Util.StringMap.mem runTestsPrefName (Prefs.scanCmdLine usageMsg) in
+  
+  (* If the -selftest flag is present, then we skip loading the preference file.
+     (This is prevents possible confusions where settings from a preference
+     file could cause unit tests to fail.) *)
+  if not testFlagPresent then begin
+    (* If the profile does not exist, create an empty one (this should only
+       happen if the profile is 'default', since otherwise we will already
+       have checked that the named one exists). *)
+    if not(System.file_exists (Prefs.profilePathname profileName)) then
+      Prefs.addComment "Unison preferences file";
+
+    (* Load the profile *)
+    (debug (fun() -> Util.msg "about to load prefs");
+     Prefs.loadTheFile());
+
+    (* Now check again that the -selftest flag has not been set, and barf otherwise *)
+    if Prefs.read runtests then raise (Util.Fatal
+      "The 'test' flag should only be given on the command line")
+  end;
+
+  (* Parse the command line.  This will override settings from the profile. *)
+  (* JV (6/09): always reparse the command line *)
+  if true (*!firstTime*) then begin
+    debug (fun() -> Util.msg "about to parse command line");
+    Prefs.parseCmdLine usageMsg;
+  end;
+
+  (* Install dummy roots and backup directory if we are running self-tests *)
+  if Prefs.read runtests then begin
+    if Globals.rawRoots() = [] then 
+      Prefs.loadStrings ["root = test-a.tmp"; "root = test-b.tmp"];
+    if (Prefs.read Stasher.backupdir) = "" then
+      Prefs.loadStrings ["backupdir = test-backup.tmp"];
+  end;
+
+  (* Print the preference settings *)
+  debug (fun() -> Prefs.dumpPrefsToStderr() );
+
+  (* If no roots are given either on the command line or in the profile,
+     ask the user *)
+  if Globals.rawRoots() = [] then begin
+    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
+    displayWaitMessage();
+
+  (* Canonize the names of the roots, sort them (with local roots first),
+     and install them in Globals. *)
+  Lwt_unix.run (Globals.installRoots termInteract);
+
+  (* If both roots are local, disable the xferhint table to save time *)
+  begin match Globals.roots() with
+    ((Local,_),(Local,_)) -> Prefs.set Xferhint.xferbycopying false
+  | _ -> ()
+  end;
+
+  (* FIX: This should be before Globals.installRoots *)
+  (* Check to be sure that there is at most one remote root *)
+  let numRemote =
+    Safelist.fold_left
+      (fun n (w,_) -> match w with Local -> n | Remote _ -> n+1)
+      0
+      (Globals.rootsList()) in
+      if numRemote > 1 then
+        raise(Util.Fatal "cannot synchronize more than one remote root");
+
+  (* If no paths were specified, then synchronize the whole replicas *)
+  if Prefs.read Globals.paths = [] then Prefs.set Globals.paths [Path.empty];
+
+  (* Expand any "wildcard" paths [with final component *] *)
+  Globals.expandWildcardPaths();
+
+  Update.storeRootsName ();
+
+  if
+    numRemote > 0 && not (Prefs.read contactquietly || Prefs.read Trace.terse)
+  then
+    Util.msg "Connected [%s]\n"
+      (Util.replacesubstring (Update.getRootsName()) ", " " -> ");
+
+  debug (fun() ->
+       Printf.eprintf "Roots: \n";
+       Safelist.iter (fun clr -> Printf.eprintf "        %s\n" clr)
+         (Globals.rawRoots ());
+       Printf.eprintf "  i.e. \n";
+       Safelist.iter (fun clr -> Printf.eprintf "        %s\n"
+                        (Clroot.clroot2string (Clroot.parseRoot clr)))
+         (Globals.rawRoots ());
+       Printf.eprintf "  i.e. (in canonical order)\n";
+       Safelist.iter (fun r -> 
+                        Printf.eprintf "       %s\n" (root2string r))
+         (Globals.rootsInCanonicalOrder());
+       Printf.eprintf "\n");
+  
+  Lwt_unix.run
+    (validateAndFixupPrefs () >>=
+     Globals.propagatePrefs);
+
+  (* Initializes some backups stuff according to the preferences just loaded from the profile.
+     Important to do it here, after prefs are propagated, because the function will also be
+     run on the server, if any. Also, this should be done each time a profile is reloaded
+     on this side, that's why it's here. *) 
+  Stasher.initBackups ();
+  
+  firstTime := false
+
+(**********************************************************************
+                       Common startup sequence
+ **********************************************************************)
+
+let anonymousArgs =
+  Prefs.createStringList "rest"
+    "*roots or profile name" ""
+
+let testServer =
+  Prefs.createBool "testserver" false
+    "exit immediately after the connection to the server"
+    ("Setting this flag on the command line causes Unison to attempt to "
+     ^ "connect to the remote server and, if successful, print a message "
+     ^ "and immediately exit.  Useful for debugging installation problems. "
+     ^ "Should not be set in preference files.")
+
+(* For backward compatibility *)
+let _ = Prefs.alias testServer "testServer"
+
+(* ---- *)
+
+let uiInit
+    ~(reportError : string -> unit)
+    ~(tryAgainOrQuit : string -> bool)
+    ~(displayWaitMessage : unit -> unit)
+    ~(getProfile : unit -> string option)
+    ~(getFirstRoot : unit -> string option)
+    ~(getSecondRoot : unit -> string option)
+    ~(termInteract : (string -> string -> string) option) =
+
+  (* Make sure we have a directory for archives and profiles *)
+  Os.createUnisonDir();
+ 
+  (* Extract any command line profile or roots *)
+  let clprofile = ref None in
+  begin
+    try
+      let args = Prefs.scanCmdLine usageMsg in
+      match Util.StringMap.find "rest" args with
+        [] -> ()
+      | [profile] -> clprofile := Some profile
+      | [root2;root1] -> cmdLineRawRoots := [root1;root2]
+      | [root2;root1;profile] ->
+          cmdLineRawRoots := [root1;root2];
+          clprofile := Some profile
+      | _ ->
+          (reportError(Printf.sprintf
+             "%s was invoked incorrectly (too many roots)" Uutil.myName);
+           exit 1)
+    with Not_found -> ()
+  end;
+
+  (* Print header for debugging output *)
+  debug (fun() ->
+    Printf.eprintf "%s, version %s\n\n" Uutil.myName Uutil.myVersion);
+  debug (fun() -> Util.msg "initializing UI");
+
+  debug (fun () ->
+    (match !clprofile with
+      None -> Util.msg "No profile given on command line"
+    | Some s -> Printf.eprintf "Profile '%s' given on command line" s);
+    (match !cmdLineRawRoots with
+      [] -> Util.msg "No roots given on command line"
+    | [root1;root2] ->
+        Printf.eprintf "Roots '%s' and '%s' given on command line"
+          root1 root2
+    | _ -> assert false));
+
+  let profileName =
+    begin match !clprofile with
+      None ->
+        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. *)
+            clprofile := getProfile();
+            match !clprofile with
+              None -> exit 0 (* None means the user wants to quit *)
+            | Some x -> x 
+          end else begin
+            (* Roots given on command line.
+               The profile should be the default. *)
+            clprofile := Some "default";
+            "default"
+          end in
+        n
+    | Some n ->
+        let f = Prefs.profilePathname n in
+        if not(System.file_exists f)
+        then (reportError (Printf.sprintf "Profile %s does not exist"
+                             (System.fspathToPrintString f));
+              exit 1);
+        n
+    end in
+
+  (* Load the profile and command-line arguments *)
+  initPrefs
+    profileName displayWaitMessage getFirstRoot getSecondRoot termInteract;
+  
+  (* Turn on GC messages, if the '-debug gc' flag was provided *)
+  if Trace.enabled "gc" then Gc.set {(Gc.get ()) with Gc.verbose = 0x3F};
+
+  if Prefs.read testServer then exit 0;
+
+  (* BCPFIX: Should/can this be done earlier?? *)
+  Files.processCommitLogs();
+
+  (* Run unit tests if requested *)
+  if Prefs.read runtests then begin
+    (!testFunction)();
+    exit 0
+  end
+
+(* Exit codes *)
+let perfectExit = 0   (* when everything's okay *)
+let skippyExit  = 1   (* when some items were skipped, but no failure occurred *)
+let failedExit  = 2   (* when there's some non-fatal failure *)
+let fatalExit   = 3   (* when fatal failure occurred *)
+let exitCode = function
+    (false, false) -> 0
+  | (true, false)  -> 1
+  | _              -> 2
+(* (anySkipped?, anyFailure?) -> exit code *)

Deleted: branches/2.45/src/uicommon.mli
===================================================================
--- trunk/src/uicommon.mli	2012-03-10 16:12:51 UTC (rev 485)
+++ branches/2.45/src/uicommon.mli	2012-04-02 15:54:47 UTC (rev 487)
@@ -1,117 +0,0 @@
-(* Unison file synchronizer: src/uicommon.mli *)
-(* Copyright 1999-2012, Benjamin C. Pierce (see COPYING for details) *)
-
-(* Kinds of UI *)
-type interface =
-   Text
- | Graphic
-
-(* The interface of a concrete UI implementation *)
-module type UI =
-sig
-  val start : interface -> unit
-  val defaultUi : interface
-end
-
-(* User preference: when true, ask fewer questions *)
-val auto : bool Prefs.t
-
-(* User preference: How tall to make the main window in the GTK ui *)
-val mainWindowHeight : int Prefs.t
-
-(* User preference: Expert mode *)
-val expert : bool Prefs.t
-
-(* User preference: Whether to display 'contacting server' message *)
-val contactquietly : bool Prefs.t
-
-(* User preference: The 'contacting server' message itself *)
-val contactingServerMsg : unit -> string
-
-(* User preference: Descriptive label for this profile *)
-val profileLabel : string Prefs.t
-
-(* User preference: Synchronize repeatedly *)
-val repeat : string Prefs.t
-
-(* User preference: Try failing paths N times *)
-val retry : int Prefs.t
-
-(* User preference: confirmation before commiting merge results *)
-val confirmmerge : bool Prefs.t
-
-(* Format the information about current contents of a path in one replica (the second argument
-   is used as a separator) *)
-val details2string : Common.reconItem -> string -> string
-
-(* Format a path, eliding initial components that are the same as the
-   previous path *)
-val displayPath : Path.t -> Path.t -> string
-
-(* Format the names of the roots for display at the head of the
-   corresponding columns in the UI *)
-val roots2string : unit -> string
-
-(* Format a reconItem (and its status string) for display, eliding
-   initial components that are the same as the previous path *)
-val reconItem2string : Path.t -> Common.reconItem -> string -> string
-
-type action = AError | ASkip of bool | ALtoR of bool | ARtoL of bool | AMerge
-
-(* Same as previous function, but returns a tuple of strings *)
-val reconItem2stringList :
-  Path.t -> Common.reconItem -> string * action * string * string
-
-(* Format an exception for display *)
-val exn2string : exn -> string
-
-(* Calculate and display differences for a file *)
-val showDiffs :
-     Common.reconItem           (* what path *)
-  -> (string->string->unit)     (* how to display the (title and) result *)
-  -> (string->unit)             (* how to display errors *)
-  -> Uutil.File.t               (* id for transfer progress reports *)
-  -> unit
-
-val dangerousPathMsg : Path.t list -> string
-
-(* Utilities for adding ignore patterns *)
-val ignorePath : Path.t -> string
-val ignoreName : Path.t -> string
-val ignoreExt  : Path.t -> string
-val addIgnorePattern : string -> unit
-
-val usageMsg : string
-
-val shortUsageMsg : string
-
-val uiInit :
-    reportError:(string -> unit) ->
-    tryAgainOrQuit:(string -> bool) ->
-    displayWaitMessage:(unit -> unit) ->
-    getProfile:(unit -> string option) ->
-    getFirstRoot:(unit -> string option) ->
-    getSecondRoot:(unit -> string option) ->
-    termInteract:(string -> string -> string) option ->
-    unit
-
-val initPrefs :
-  profileName:string ->
-  displayWaitMessage:(unit->unit) ->
-  getFirstRoot:(unit->string option) ->
-  getSecondRoot:(unit->string option) ->
-  termInteract:(string -> string -> string) option ->
-  unit
-
-val validateAndFixupPrefs : unit -> unit Lwt.t
-
-(* Exit codes *)
-val perfectExit: int   (* when everything's okay *)
-val skippyExit: int    (* when some items were skipped, but no failure occurred *)
-val failedExit: int    (* when there's some non-fatal failure *)
-val fatalExit: int     (* when fatal failure occurred *)
-val exitCode: bool * bool -> int
-(* (anySkipped?, anyFailure?) -> exit code *)
-
-(* Initialization *)
-val testFunction : (unit->unit) ref

Copied: branches/2.45/src/uicommon.mli (from rev 486, trunk/src/uicommon.mli)
===================================================================
--- branches/2.45/src/uicommon.mli	                        (rev 0)
+++ branches/2.45/src/uicommon.mli	2012-04-02 15:54:47 UTC (rev 487)
@@ -0,0 +1,117 @@
+(* Unison file synchronizer: src/uicommon.mli *)
+(* Copyright 1999-2012, Benjamin C. Pierce (see COPYING for details) *)
+
+(* Kinds of UI *)
+type interface =
+   Text
+ | Graphic
+
+(* The interface of a concrete UI implementation *)
+module type UI =
+sig
+  val start : interface -> unit
+  val defaultUi : interface
+end
+
+(* User preference: when true, ask fewer questions *)
+val auto : bool Prefs.t
+
+(* User preference: How tall to make the main window in the GTK ui *)
+val mainWindowHeight : int Prefs.t
+
+(* User preference: Expert mode *)
+val expert : bool Prefs.t
+
+(* User preference: Whether to display 'contacting server' message *)
+val contactquietly : bool Prefs.t
+
+(* User preference: The 'contacting server' message itself *)
+val contactingServerMsg : unit -> string
+
+(* User preference: Descriptive label for this profile *)
+val profileLabel : string Prefs.t
+
+(* User preference: Synchronize repeatedly *)
+val repeat : string Prefs.t
+
+(* User preference: Try failing paths N times *)
+val retry : int Prefs.t
+
+(* User preference: confirmation before committing merge results *)
+val confirmmerge : bool Prefs.t
+
+(* Format the information about current contents of a path in one replica (the second argument
+   is used as a separator) *)
+val details2string : Common.reconItem -> string -> string
+
+(* Format a path, eliding initial components that are the same as the
+   previous path *)
+val displayPath : Path.t -> Path.t -> string
+
+(* Format the names of the roots for display at the head of the
+   corresponding columns in the UI *)
+val roots2string : unit -> string
+
+(* Format a reconItem (and its status string) for display, eliding
+   initial components that are the same as the previous path *)
+val reconItem2string : Path.t -> Common.reconItem -> string -> string
+
+type action = AError | ASkip of bool | ALtoR of bool | ARtoL of bool | AMerge
+
+(* Same as previous function, but returns a tuple of strings *)
+val reconItem2stringList :
+  Path.t -> Common.reconItem -> string * action * string * string
+
+(* Format an exception for display *)
+val exn2string : exn -> string
+
+(* Calculate and display differences for a file *)
+val showDiffs :
+     Common.reconItem           (* what path *)
+  -> (string->string->unit)     (* how to display the (title and) result *)
+  -> (string->unit)             (* how to display errors *)
+  -> Uutil.File.t               (* id for transfer progress reports *)
+  -> unit
+
+val dangerousPathMsg : Path.t list -> string
+
+(* Utilities for adding ignore patterns *)
+val ignorePath : Path.t -> string
+val ignoreName : Path.t -> string
+val ignoreExt  : Path.t -> string
+val addIgnorePattern : string -> unit
+
+val usageMsg : string
+
+val shortUsageMsg : string
+
+val uiInit :
+    reportError:(string -> unit) ->
+    tryAgainOrQuit:(string -> bool) ->
+    displayWaitMessage:(unit -> unit) ->
+    getProfile:(unit -> string option) ->
+    getFirstRoot:(unit -> string option) ->
+    getSecondRoot:(unit -> string option) ->
+    termInteract:(string -> string -> string) option ->
+    unit
+
+val initPrefs :
+  profileName:string ->
+  displayWaitMessage:(unit->unit) ->
+  getFirstRoot:(unit->string option) ->
+  getSecondRoot:(unit->string option) ->
+  termInteract:(string -> string -> string) option ->
+  unit
+
+val validateAndFixupPrefs : unit -> unit Lwt.t
+
+(* Exit codes *)
+val perfectExit: int   (* when everything's okay *)
+val skippyExit: int    (* when some items were skipped, but no failure occurred *)
+val failedExit: int    (* when there's some non-fatal failure *)
+val fatalExit: int     (* when fatal failure occurred *)
+val exitCode: bool * bool -> int
+(* (anySkipped?, anyFailure?) -> exit code *)
+
+(* Initialization *)
+val testFunction : (unit->unit) ref

Deleted: branches/2.45/src/uimacnew09/MyController.m
===================================================================
--- trunk/src/uimacnew09/MyController.m	2012-03-10 16:12:51 UTC (rev 485)
+++ branches/2.45/src/uimacnew09/MyController.m	2012-04-02 15:54:47 UTC (rev 487)
@@ -1,1213 +0,0 @@
-/* Copyright (c) 2003, see file COPYING for details. */
-
-#import "MyController.h"
-
-/* The following two define are a workaround for an incompatibility between
-Ocaml 3.11.2 (and older) and the Mac OS X header files */
-#define uint64 uint64_caml
-#define int64 int64_caml
-
-#define CAML_NAME_SPACE
-#include <caml/callback.h>
-#include <caml/alloc.h>
-#include <caml/mlvalues.h>
-#include <caml/memory.h>
-
- at interface NSString (_UnisonUtil)
-- (NSString *)trim;
- at end
-
- at implementation MyController
-
-static MyController *me; // needed by reloadTable and displayStatus, below
-
-// 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;
-}
-
-- (id)init
-{
-  if (([super init])) {
-    
-    /* Initialize locals */
-    me = self;
-    doneFirstDiff = NO;
-    
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-    NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
-                                 /* By default, invite user to install cltool */
-                                 @"YES",  @"CheckCltool",
-                                 @"NO", @"openProfileAtStartup",
-                                 @"",   @"profileToOpen",
-                                 @"NO", @"deleteLogOnExit",
-                                 @"",   @"detailsFont",
-                                 @"",   @"diffFont",
-                                 nil];
-    
-    [defaults registerDefaults:appDefaults];
-    fontChangeTarget = nil;
-  }
-  
-  return self;
-}
-
-- (void) applicationWillTerminate:(NSNotification *)aNotification {
-  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-  [defaults setObject:[NSArchiver archivedDataWithRootObject:[detailsTextView font]] forKey:@"detailsFont"];
-  [defaults setObject:[NSArchiver archivedDataWithRootObject:[diffView font]] forKey:@"diffFont"];
-  [defaults synchronize];
-}
-
-- (void)awakeFromNib
-{
-  [splitView setAutosaveName:@"splitView"];
-  
-  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];  
-  NSFont *defaultFont = [NSFont fontWithName:@"Monaco" size:11];
-  NSData *detailsFontData = [defaults dataForKey:@"detailsFont"];
-  if (detailsFontData) {
-    NSFont *tmpFont = (NSFont*) [NSUnarchiver unarchiveObjectWithData:detailsFontData];
-    if (tmpFont)
-      [detailsTextView setFont:tmpFont];
-    else
-      [detailsTextView setFont:defaultFont];
-  } else
-    [detailsTextView setFont:defaultFont];
-  
-  NSData *diffFontData = [defaults dataForKey:@"diffFont"];
-  if (diffFontData) {
-    NSFont *tmpFont = (NSFont*) [NSUnarchiver unarchiveObjectWithData:diffFontData];
-    if (tmpFont)
-      [diffView setFont:tmpFont];
-    else
-      [diffView setFont:defaultFont];
-  } else
-    [diffView setFont:defaultFont];
-  
-  blankView = [[NSView alloc] init];
-	
-  /* Double clicking in the profile list will open the profile */
-  [[profileController tableView] setTarget:self];
-  [[profileController tableView] setDoubleAction:@selector(openButton:)];
-  
-	[tableView setAutoresizesOutlineColumn:NO];
-  
-	// use combo-cell for path
-  [[tableView tableColumnWithIdentifier:@"path"] setDataCell:[[[ImageAndTextCell alloc] init] autorelease]];
-  
-	// Custom progress cell
-	ProgressCell *progressCell = [[[ProgressCell alloc] init] autorelease];
-	[[tableView tableColumnWithIdentifier:@"percentTransferred"] setDataCell:progressCell];
-	
-  /* Set up the version string in the about box.  We use a custom
-   about box just because PRCS doesn't seem capable of getting the
-   version into the InfoPlist.strings file; otherwise we'd use the
-   standard about box. */
-  [versionText setStringValue:ocamlCall("S", "unisonGetVersion")];
-  
-  /* Command-line processing */
-	OCamlValue *clprofile = (id)ocamlCall("@", "unisonInit0");
-
-	BOOL areRootsSet = (long)ocamlCall("i", "areRootsSet") ? YES : NO;
-	if (areRootsSet) {
-		NSLog(@"Roots are on the command line");
-	}
-	else {
-		NSLog(@"Roots are not set on the command line");
-	}
-	  
-  /* Add toolbar */
-  toolbar = [[[UnisonToolbar alloc] 
-              initWithIdentifier: @"unisonToolbar" :self :tableView] autorelease];
-  [mainWindow setToolbar: toolbar];
-	[toolbar takeTableModeView:tableModeSelector];
-	[self initTableMode];
-	
-  
-  /* Set up the first window the user will see */
-  if (clprofile) {
-    /* A profile name was given on the command line */
-		NSString *profileName = [clprofile getField:0 withType:'S'];
-    [self profileSelected:profileName];
-    
-    /* If invoked from terminal we need to bring the app to the front */
-    [NSApp activateIgnoringOtherApps:YES];
-    
-    /* Start the connection */
-    [self connect:profileName];
-  }
-  else if (areRootsSet) {
-	/* If invoked from terminal we need to bring the app to the front */
-	[NSApp activateIgnoringOtherApps:YES];
-	/* Start the connection with the empty profile name, indicating roots only */
-	[self connect:@""];
-  }
-  else {
-    /* If invoked from terminal we need to bring the app to the front */
-    [NSApp activateIgnoringOtherApps:YES];
-    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"openProfileAtStartup"]) {
-      NSString *profileToOpen = [[NSUserDefaults standardUserDefaults] 
-                                 stringForKey:@"profileToOpen"];
-      if ([[profileToOpen trim] compare:@""] != NSOrderedSame &&
-          [[profileController getProfiles] indexOfObject:profileToOpen] != NSNotFound) {
-        [self profileSelected:profileToOpen];
-        [self connect:profileToOpen];
-      } else {
-        /* Bring up the dialog to choose a profile */
-        [self chooseProfiles];
-      }
-    } else {
-      /* Bring up the dialog to choose a profile */
-      [self chooseProfiles];
-    }
-  }
-  
-  [mainWindow display];
-  [mainWindow makeKeyAndOrderFront:nil];
-
-  /* unless user has clicked Don't ask me again, ask about cltool */
-  if ( ([[NSUserDefaults standardUserDefaults] boolForKey:@"CheckCltool"]) && 
-	  (![[NSFileManager defaultManager]
-		 fileExistsAtPath:@"/usr/bin/unison"]) )
-	  [self raiseCltoolWindow:nil];
-}
-
-- (IBAction) checkOpenProfileChanged:(id)sender {
-  [profileBox setEnabled:[checkOpenProfile state]];
-  if ([profileBox isEnabled] && [profileBox indexOfSelectedItem] < 0) {
-    [profileBox selectItemAtIndex:0];
-    [[NSUserDefaults standardUserDefaults] setObject:[profileBox itemObjectValueAtIndex:0] forKey:@"profileToOpen"];
-  }
-}
-
-- (IBAction) chooseFont:(id)sender {
-  [[NSFontPanel sharedFontPanel] makeKeyAndOrderFront:self];
-  [[NSFontManager sharedFontManager] setDelegate:self];
-  fontChangeTarget = sender;
-}
-
-- (void) changeFont:(id)sender {
-  NSFont *newFont = [sender convertFont:[detailsTextView font]];
-  if (fontChangeTarget == chooseDetailsFont)
-    [detailsTextView setFont:newFont];
-  else if (fontChangeTarget == chooseDiffFont)
-    [diffView setFont:newFont];
-  [self updateFontDisplay];
-}
-
-- (void) updateFontDisplay {
-  NSFont *detailsFont = [detailsTextView font];
-  NSFont *diffFont = [diffView font];
-  [detailsFontLabel setStringValue:[NSString stringWithFormat:@"%@ : %d", [detailsFont displayName], (NSInteger) [detailsFont pointSize]]];
-  [diffFontLabel setStringValue:[NSString stringWithFormat:@"%@ : %d", [diffFont displayName], (NSInteger) [diffFont pointSize]]];
-}
-
-- (void)chooseProfiles
-{
-    [mainWindow setContentView:blankView];
-    [self resizeWindowToSize:[chooseProfileView frame].size];
-    [mainWindow setContentMinSize:
-        NSMakeSize(NSWidth([[mainWindow contentView] frame]),150)];
-    [mainWindow setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
-    [mainWindow setContentView:chooseProfileView];
-    [toolbar setView:@"chooseProfileView"];
-    [mainWindow setTitle:@"Unison"];
-	
-    // profiles get keyboard input
-    [mainWindow makeFirstResponder:[profileController tableView]];
-    [chooseProfileView display];
-}
-
-- (IBAction)createButton:(id)sender
-{
-    [preferencesController reset];
-    [mainWindow setContentView:blankView];
-    [self resizeWindowToSize:[preferencesView frame].size];
-    [mainWindow setContentMinSize:
-        NSMakeSize(400,NSHeight([[mainWindow contentView] frame]))];
-    [mainWindow setContentMaxSize:
-        NSMakeSize(FLT_MAX,NSHeight([[mainWindow contentView] frame]))];
-    [mainWindow setContentView:preferencesView];
-    [toolbar setView:@"preferencesView"];
-}
-
-- (IBAction)saveProfileButton:(id)sender
-{
-    if ([preferencesController validatePrefs]) {
-        // so the list contains the new profile
-        [profileController initProfiles];
-        [self chooseProfiles];
-    }
-}
-
-- (IBAction)cancelProfileButton:(id)sender
-{
-    [self chooseProfiles];
-}
-
-/* Only valid once a profile has been selected */
-- (NSString *)profile {
-    return myProfile;
-}
-
-- (void)profileSelected:(NSString *)aProfile
-{
-    [aProfile retain];
-    [myProfile release];
-    myProfile = aProfile;
-    [mainWindow setTitle: [NSString stringWithFormat:@"Unison: %@", myProfile]];
-}
-
-- (IBAction)showPreferences:(id)sender {
-  [profileBox removeAllItems];
-  [profileBox addItemsWithObjectValues:[profileController getProfiles]];
-  NSUInteger index = [[profileController getProfiles] indexOfObject:
-                      [[NSUserDefaults standardUserDefaults] 
-                       stringForKey:@"profileToOpen"]];
-  if (index == NSNotFound) {
-    [checkOpenProfile setState:NSOffState];
-    [profileBox setStringValue:@""];
-  } else
-    [profileBox selectItemAtIndex:index];
-
-  [profileBox setEnabled:[checkOpenProfile state]];
-  if ([profileBox isEnabled] && [profileBox indexOfSelectedItem] < 0)
-    [profileBox selectItemAtIndex:0];
-
-  [self updateFontDisplay];
-
-  [self raiseWindow:preferencesWindow];
-}
-
-- (IBAction)restartButton:(id)sender
-{
-    [tableView setEditable:NO];
-    [self chooseProfiles];
-}
-
-- (IBAction)rescan:(id)sender
-{
-    /* There is a delay between turning off the button and it
-       actually being disabled. Make sure we don't respond. */
-    if ([self validateItem:@selector(rescan:)]) {
-        waitingForPassword = NO;
-        [self afterOpen];
-    }
-}
-
-- (IBAction)openButton:(id)sender
-{
-    NSString *profile = [profileController selected];
-    [self profileSelected:profile];
-    [self connect:profile];
-    return;
-}
-
-- (void)updateToolbar
-{
-  [toolbar validateVisibleItems];	
-	[tableModeSelector setEnabled:((syncable && !duringSync) || afterSync)];
-
-	// Why?
-    [updatesView setNeedsDisplay:YES];
-}
-
-- (void)updateTableViewWithReset:(BOOL)shouldResetSelection
-{
-	[tableView reloadData]; 
-	if (shouldResetSelection) {
-		[tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
-		shouldResetSelection = NO;
-	}
-	[updatesView setNeedsDisplay:YES];	    
-}
-
-- (void)updateProgressBar:(NSNumber *)newProgress
-{
-	// NSLog(@"Updating progress bar: %i - %i", (int)[newProgress doubleValue], (int)[progressBar doubleValue]);
-	[progressBar incrementBy:([newProgress doubleValue] - [progressBar doubleValue])];
-}
-
-- (void)updateTableViewSelection
-{
-    int n = [tableView numberOfSelectedRows];
-    if (n == 1) [self displayDetails:[tableView itemAtRow:[tableView selectedRow]]];
-    else [self clearDetails];
-}
-
-- (void)outlineViewSelectionDidChange:(NSNotification *)note
-{
-	[self updateTableViewSelection];
-}
-
-- (void)connect:(NSString *)profileName
-{
-  // contact server, propagate prefs
-  NSLog(@"Connecting to %@...", profileName);
-  
-  // Switch to ConnectingView
-  [mainWindow setContentView:blankView];
-  [self resizeWindowToSize:[updatesView frame].size];
-  [mainWindow setContentMinSize:NSMakeSize(150,150)];
-  [mainWindow setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
-  [mainWindow setContentView:ConnectingView];
-  [toolbar setView:@"connectingView"];
-  
-  // Update (almost) immediately
-  [ConnectingView display];
-  [connectingAnimation startAnimation:self];
-  
-  syncable = NO;
-  afterSync = NO;    
-    
-	[self updateToolbar];
-    
-	// will spawn thread on OCaml side and callback when complete
-  (void)ocamlCall("xS", "unisonInit1", profileName);
-}
-
-CAMLprim value unisonInit1Complete(value v)
-{
-  id pool = [[NSAutoreleasePool alloc] init];
-  if (v == Val_unit) {
-    NSLog(@"Connected.");
-    [me->connectingAnimation stopAnimation:me];
-		[me->preconn release];
-		me->preconn = NULL;
-    [me performSelectorOnMainThread:@selector(afterOpen:) withObject:nil waitUntilDone:FALSE]; 
-  } else {
-    // prompting required
-		me->preconn = [[OCamlValue alloc] initWithValue:Field(v,0)]; // value of Some
-		[me performSelectorOnMainThread:@selector(unisonInit1Complete:) withObject:nil waitUntilDone:FALSE]; 
-	}
-  [pool release];
-  return Val_unit;
-}
-
-- (void)unisonInit1Complete:(id)ignore
-{
-	@try {
-		OCamlValue *prompt = ocamlCall("@@", "openConnectionPrompt", preconn);
-		if (!prompt) {
-			// turns out, no prompt needed, but must finish opening connection
-			ocamlCall("x@", "openConnectionEnd", preconn);
-			NSLog(@"Connected.");
-			waitingForPassword = NO;
-			[self afterOpen];
-			return;
-		}
-		waitingForPassword = YES;
-
-		[self raisePasswordWindow:[prompt getField:0 withType:'S']]; 
-	} @catch (NSException *ex) {
-	    NSRunAlertPanel(@"Connection Error", [ex description], @"OK", nil, nil);
-		[self chooseProfiles];
-		return;
-	}
-
-    NSLog(@"Connected.");
-}
-
-- (void)raisePasswordWindow:(NSString *)prompt
-{
-    // FIX: some prompts don't ask for password, need to look at it
-    NSLog(@"Got the prompt: '%@'",prompt);
-    if ((long)ocamlCall("iS", "unisonPasswordMsg", prompt)) {
-        [passwordPrompt setStringValue:@"Please enter your password"];
-        [NSApp beginSheet:passwordWindow
-            modalForWindow:mainWindow
-            modalDelegate:nil
-            didEndSelector:nil
-            contextInfo:nil];
-        return;
-    }
-    if ((long)ocamlCall("iS", "unisonPassphraseMsg", prompt)) {
-        [passwordPrompt setStringValue:@"Please enter your passphrase"];
-        [NSApp beginSheet:passwordWindow
-            modalForWindow:mainWindow
-            modalDelegate:nil
-            didEndSelector:nil
-            contextInfo:nil];
-        return;
-    }
-    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");
-			prompt = ocamlCall("S@", "openConnectionPrompt", preconn);
-            if (!prompt) {
-                // all done with prompts, finish opening connection
-				ocamlCall("x@", "openConnectionEnd", preconn);
-                waitingForPassword = NO;
-                [self afterOpen];
-                return;
-            }
-            else {
-				[self raisePasswordWindow:[NSString 
-                    stringWithUTF8String:String_val(Field(prompt,0))]];
-                return;
-            }
-        }
-        if (i == NSAlertAlternateReturn) {
-			ocamlCall("x@", "openConnectionCancel", preconn);
-            return;
-        }
-        else {
-            NSLog(@"Unrecognized response '%d' from NSRunAlertPanel",i);
-			ocamlCall("x@", "openConnectionCancel", preconn);
-            return;
-        }
-    }
-    NSLog(@"Unrecognized message from ssh: %@",prompt);
-	ocamlCall("x@", "openConnectionCancel", preconn);
-}
-
-// The password window will invoke this when Enter occurs, b/c we
-// are the delegate.
-- (void)controlTextDidEndEditing:(NSNotification *)notification
-{
-    NSNumber *reason = [[notification userInfo] objectForKey:@"NSTextMovement"];
-    int code = [reason intValue];
-    if (code == NSReturnTextMovement)
-        [self endPasswordWindow:self];
-}
-// Or, the Continue button will invoke this when clicked
-- (IBAction)endPasswordWindow:(id)sender
-{
-    [passwordWindow orderOut:self];
-    [NSApp endSheet:passwordWindow];
-    if ([sender isEqualTo:passwordCancelButton]) {
-		ocamlCall("x@", "openConnectionCancel", preconn);
-        [self chooseProfiles];
-        return;
-    }
-    NSString *password = [passwordText stringValue];
-	ocamlCall("x at S", "openConnectionReply", preconn, password);
-
-    OCamlValue *prompt = ocamlCall("@@", "openConnectionPrompt", preconn);
-    if (!prompt) {
-        // all done with prompts, finish opening connection
-		ocamlCall("x@", "openConnectionEnd", preconn);
-        waitingForPassword = NO;
-        [self afterOpen];
-    }
-    else {
-		[self raisePasswordWindow:[prompt getField:0 withType:'S']]; 
-    }
-}
-
-- (void)afterOpen:(id)ignore
-{
-	[self afterOpen];
-}
-
-- (void)afterOpen
-{
-    if (waitingForPassword) return;
-    // move to updates window after clearing it
-	[self updateReconItems:nil];
-	[progressBar setDoubleValue:0.0];
-	[progressBar stopAnimation:self];
-    // [self clearDetails];
-    [mainWindow setContentView:blankView];
-    [self resizeWindowToSize:[updatesView frame].size];
-    [mainWindow setContentMinSize:
-        NSMakeSize(NSWidth([[mainWindow contentView] frame]),200)];
-    [mainWindow setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
-    [mainWindow setContentView:updatesView];
-    [toolbar setView:@"updatesView"];
-
-    syncable = NO;
-    afterSync = NO;
-
-    [tableView deselectAll:self];
-	[self updateToolbar];
-	[self updateProgressBar:[NSNumber numberWithDouble:0.0]];
-    
-    // this should depend on the number of reconitems, and is now done
-    // in updateReconItems:
-    // reconItems table gets keyboard input
-    //[mainWindow makeFirstResponder:tableView];
-    [tableView scrollRowToVisible:0];
-
-	[preconn release];
-    preconn = nil; // so old preconn can be garbage collected
-	// This will run in another thread spawned in OCaml and will return immediately
-	// We'll get a call back to unisonInit2Complete() when it is complete
-	ocamlCall("x", "unisonInit2");
-}
-
-- (void)doSync
-{
-    [tableView setEditable:NO];
-    syncable = NO;
-    duringSync = YES;
-	
-	[self updateToolbar];
-    
-	// This will run in another thread spawned in OCaml and will return immediately
-	// We'll get a call back to syncComplete() when it is complete
-	ocamlCall("x", "unisonSynchronize");	
-}
-
-- (IBAction)syncButton:(id)sender
-{
-	[self doSync];
-}
-
-
-- (void)afterUpdate:(id)retainedReconItems
-{
-	// NSLog(@"In afterUpdate:...");
-    [self updateReconItems:retainedReconItems];
-	[retainedReconItems release];
-
-    [notificationController updateFinishedFor:[self profile]];
-
-    // label the left and right columns with the roots
-	NSString *leftHost = [(NSString *)ocamlCall("S", "unisonFirstRootString") trim];
-	NSString *rightHost = [(NSString *)ocamlCall("S", "unisonSecondRootString") trim];
-	/*
-    [[[tableView tableColumnWithIdentifier:@"left"] headerCell] setObjectValue:lefthost];
-    [[[tableView tableColumnWithIdentifier:@"right"] headerCell] setObjectValue:rightHost];
-    */
-    [mainWindow setTitle: [NSString stringWithFormat:@"Unison: %@ (%@ <-> %@)", 
-			[self profile], leftHost, rightHost]];
-	
-	// initial sort
-	[tableView setSortDescriptors:[NSArray arrayWithObjects: 
-		[[tableView tableColumnWithIdentifier:@"fileSizeString"] sortDescriptorPrototype],
-		[[tableView tableColumnWithIdentifier:@"path"] sortDescriptorPrototype],
-		nil]];
-
-	[self updateTableViewWithReset:([reconItems count] > 0)];
-	[self updateToolbar];
-	isBatchSet = (long)ocamlCall("i", "isBatchSet") ? YES : NO;
-	if (isBatchSet) {
-		NSLog(@"batch set on the command line");
-	}
-	else {
-		NSLog(@"batch not set on the command line");
-	}
-	
-	if (isBatchSet) {
-		[self doSync];
-	}
-}
-
-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;
-}
-
-- (void)afterSync:(id)ignore
-{
-    [notificationController syncFinishedFor:[self profile]];
-    duringSync = NO;
-    afterSync = YES;
-    [self updateToolbar];
-    
-    int i;
-    for (i = 0; i < [reconItems count]; i++) {
-        [[reconItems objectAtIndex:i] resetProgress];
-    }
-
-	[self updateTableViewSelection];
-		
-	[self updateTableViewWithReset:FALSE];
-}
-
-- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
-{
-    [_timer invalidate];
-
-    switch (returnCode) {
-        case NSAlertAlternateReturn:
-            return;
-            break;
-            
-        default:
-            [[NSApplication sharedApplication] performSelector: @selector(terminate:) withObject: nil afterDelay: 0.0];
-            break;
-    }
-}
-
-- (void)updateCountdown
-{
-    if (_secondsRemaining == 0) {
-        [_timer invalidate];
-        [[_timeoutAlert window] orderOut: nil];
-        [self alertDidEnd: _timeoutAlert returnCode: NSAlertDefaultReturn contextInfo: nil];
-    } else {
-        [_timeoutAlert setMessageText: [NSString stringWithFormat: @"Unison will quit in %lu seconds", _secondsRemaining]];
-        _secondsRemaining--;
-    }
-}
-
-
-- (void)quitIfBatch:(id)ignore
-{
-	if (isBatchSet) {
-		NSLog(@"Automatically quitting because of -batch");
-		_timeoutAlert = [NSAlert alertWithMessageText: @"" defaultButton: @"Quit" alternateButton: @"Cancel" otherButton: nil informativeTextWithFormat: @""];
-	
-		_secondsRemaining = 10;
-	
-		_timer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateCountdown) userInfo: nil repeats: YES];
-		
-		[_timeoutAlert beginSheetModalForWindow: mainWindow modalDelegate: self didEndSelector: @selector(alertDidEnd:returnCode:contextInfo:) contextInfo: NULL];
-	}  
-}
-
-CAMLprim value syncComplete()
-{
-  id pool = [[NSAutoreleasePool alloc] init];
-  [me performSelectorOnMainThread:@selector(afterSync:) withObject:nil waitUntilDone:FALSE];
-  if ([[NSUserDefaults standardUserDefaults] boolForKey:@"deleteLogOnExit"])
-    [[NSFileManager defaultManager] removeItemAtPath:[@"~/unison.log" stringByExpandingTildeInPath] error:nil];
-  [pool release];
-
-  [me performSelectorOnMainThread:@selector(quitIfBatch:) withObject:nil waitUntilDone:FALSE];
-  
-  return Val_unit;
-}
-
-// A function called from ocaml
-- (void)reloadTable:(NSNumber *)i
-{
-	// NSLog(@"*** ReloadTable: %i", [i intValue]);
-
-    [[reconItems objectAtIndex:[i intValue]] resetProgress];
-	[self updateTableViewWithReset:FALSE];
-}
-
-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;
-}
-
-- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
-	if (item == nil) item = rootItem;
-	return [[item children] count];
-}
-
-- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
-    return [item isKindOfClass:[ParentReconItem class]];
-}
-
-- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
-	if (item == nil) item = rootItem;
-	return [[item children] objectAtIndex:index];
-}
-
-- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
-    NSString *identifier = [tableColumn identifier];
-	if (item == nil) item = rootItem;
-
-	if ([identifier isEqualToString:@"percentTransferred"] && (!duringSync && !afterSync)) return nil;
-
-	return [item valueForKey:identifier];
-}
-
-static NSDictionary *_SmallGreyAttributes = nil;
-
-- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {    
-	NSString *identifier = [tableColumn identifier];
-    if ([identifier isEqualToString:@"path"]) {
-		// The file icon
-		[(ImageAndTextCell*)cell setImage:[item fileIcon]];
-		
-		// For parents, format the file count into the text
-		long fileCount = [item fileCount];
-		if (fileCount > 1) {
-			NSString *countString = [NSString stringWithFormat:@"  (%ld files)", fileCount];
-			NSString *fullString = [(NSString *)[cell objectValue] stringByAppendingString:countString];
-			NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithString:fullString];
-
-			if (!_SmallGreyAttributes) {		
-				NSColor *txtColor = [NSColor grayColor];
-				NSFont *txtFont = [NSFont systemFontOfSize:9.0];
-				_SmallGreyAttributes = [[NSDictionary dictionaryWithObjectsAndKeys:txtFont,
-					NSFontAttributeName, txtColor, NSForegroundColorAttributeName,  nil] retain];
-			}
-			[as setAttributes:_SmallGreyAttributes range:NSMakeRange([fullString length] - [countString length], [countString length])];
-			[cell setAttributedStringValue:as];
-			[as release];
-		}
-    } else if ([identifier isEqualToString:@"percentTransferred"]) {
-		[(ProgressCell*)cell setIcon:[item direction]];		
-		[(ProgressCell*)cell setStatusString:[item progressString]];
-		[(ProgressCell*)cell setIsActive:[item isKindOfClass:[LeafReconItem class]]];		
-    }
-}
-
-- (void)outlineView:(NSOutlineView *)outlineView 
-      sortDescriptorsDidChange:(NSArray *)oldDescriptors {
-	NSArray *originalSelection = [outlineView selectedObjects];
-	
-	// do we want to catch case of object changes to allow resort in same direction for progress / direction?
-	// Could check if our objects change and if the first item at the head of new and old were the same
-	[rootItem sortUsingDescriptors:[outlineView sortDescriptors]];
-	[outlineView reloadData];
-	[outlineView setSelectedObjects:originalSelection];
-}
-
-// Delegate methods
-
-- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item {
-    return NO;
-}
-
-- (NSMutableArray *)reconItems // used in ReconTableView only
-{
-    return reconItems;
-}
-
-- (int)tableMode
-{
-	return [tableModeSelector selectedSegment];
-}
-
-- (IBAction)tableModeChanged:(id)sender
-{
-	[[NSUserDefaults standardUserDefaults] setInteger:[self tableMode]+1 forKey:@"TableLayout"];
-	[self updateForChangedItems];
-}
-
-- (void)initTableMode
-{
-	int mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"TableLayout"] - 1;
-	if (mode == -1) mode = 1;
-	[tableModeSelector setSelectedSegment:mode];
-}
-
-- (void)updateReconItems:(OCamlValue *)caml_reconItems
-{
-    [reconItems release];
-    reconItems = [[NSMutableArray alloc] init];
-	long i, n =[caml_reconItems count];
-    for (i=0; i<n; i++) {
-		LeafReconItem *item = [[LeafReconItem alloc] initWithRiAndIndex:(id)[caml_reconItems getField:i withType:'@'] index:i];
-        [reconItems addObject:item];
-		[item release];
-    }
-	[self updateForChangedItems];
-}
-
-- (void)expandConflictedParent:(ParentReconItem *)parent
-{
-	if ([parent hasConflictedChildren]) {
-		// NSLog(@"Expanding conflictedParent: %@", [parent fullPath]);
-		[tableView expandItem:parent expandChildren:NO];
-		NSArray *children = [parent children];
-		int i = 0, count = [children count];
-		for (;i < count; i++) {
-			id child = [children objectAtIndex:i];
-			if ([child isKindOfClass:[ParentReconItem class]]) [self expandConflictedParent:child];
-		}
-	}
-}
-
-- (void)updateForChangedItems
-{
-	int tableMode = [self tableMode];
-	
-	[rootItem release];
-	ParentReconItem *root = rootItem = [[ParentReconItem alloc] init];
-	
-	if (tableMode != 0 && [reconItems count]) {
-		// Special roll-up root item for outline displays
-		root = [[ParentReconItem alloc] init];
-		[rootItem addChild:root nested:NO];
-		[root setPath:@"All Changes..."];
-		[root setFullPath:@""];
-		[root release];
-	}
-	
-    int j = 0, n =[reconItems count];
-    for (; j<n; j++) {
-		[root addChild:[reconItems objectAtIndex:j] nested:(tableMode != 0)];
-    }
-	
-	if (tableMode == 1) [root collapseParentsWithSingleChildren:YES];
-
-	[tableView reloadData];
-	
-	if (NO) {
-		// Pre-expand entire tree
-		int i = [[rootItem children] count];
-		while (i--) {
-			[tableView expandItem:[[rootItem children] objectAtIndex:i] expandChildren:YES];
-		}
-	} else if (tableMode != 0) {
-		// Always open root node
-		[tableView expandItem:rootItem expandChildren:NO];
-
-		// then smart expand to reveal conflicts / changes in direction
-		[self expandConflictedParent:root];
-		
-		// then open more levels if we can do so without causing scrolling
-		[tableView expandChildrenIfSpace];
-	}
-	
-    // Make sure details get updated (or cleared)
-	[self updateTableViewSelection];
-
-    // Only enable sync if there are reconitems
-    if ([reconItems count]>0) {
-        [tableView setEditable:YES];
-
-        // reconItems table gets keyboard input
-        [mainWindow makeFirstResponder:tableView];
-
-        syncable = YES;
-    }
-    else {
-        [tableView setEditable:NO];
-        afterSync = YES; // rescan should be enabled
-	
-        // reconItems table no longer gets keyboard input
-        [mainWindow makeFirstResponder:nil];
-    }
-	[self updateToolbar];
-}
-
-- (id)updateForIgnore:(id)item
-{
-    long j = (long)ocamlCall("ii", "unisonUpdateForIgnore", [reconItems indexOfObjectIdenticalTo:item]);
-	NSLog(@"Updating for ignore...");
-    [self updateReconItems:(OCamlValue *)ocamlCall("@", "unisonState")];
-    return [reconItems objectAtIndex:j];
-}
-
-// 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;
-}
-
-- (void)statusTextSet:(NSString *)s {
-    /* filter out strings with # reconitems, and empty strings */
-    if (!NSEqualRanges([s rangeOfString:@"reconitems"], 
-         NSMakeRange(NSNotFound,0))) return;
-    [statusText setStringValue:s];
-}
-
-// 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:@" "];
-	[me->statusText performSelectorOnMainThread:@selector(setStringValue:) 
-				withObject:str waitUntilDone:FALSE];
-  [pool release];
-  return Val_unit;
-}
-
-- (void)diffViewTextSet:(NSArray *)args
-{
-	[self diffViewTextSet:[args objectAtIndex:0] bodyText:[args objectAtIndex:1]];
-}
-
-- (void)diffViewTextSet:(NSString *)title bodyText:(NSString *)body {
-   if ([body length]==0) return;
-   [diffWindow setTitle:title];
-   //[diffView setFont:diffFont];
-   [diffView setString:body];
-   if (!doneFirstDiff) {
-       /* On first open, position the diff window to the right of
-       the main window, but without going off the mainwindow's screen */
-       float screenOriginX = [[mainWindow screen] visibleFrame].origin.x;
-       float screenWidth = [[mainWindow screen] visibleFrame].size.width;
-       float mainOriginX = [mainWindow frame].origin.x;
-       float mainOriginY = [mainWindow frame].origin.y;
-       float mainWidth = [mainWindow frame].size.width;
-       float mainHeight = [mainWindow frame].size.height;       
-       float diffWidth = [diffWindow frame].size.width;
-
-       float diffX = mainOriginX+mainWidth;
-       float maxX = screenOriginX+screenWidth-diffWidth;
-       if (diffX > maxX) diffX = maxX;
-       float diffY = mainOriginY + mainHeight;
-       
-       NSPoint diffOrigin = NSMakePoint(diffX,diffY);
-       [diffWindow cascadeTopLeftFromPoint:diffOrigin];
-       
-       doneFirstDiff = YES;
-   }
-   [diffWindow orderFront:nil];
-}
-
-- (void)displayDetails:(ReconItem *)item
-{
-	//[detailsTextView setFont:diffFont];
-	NSString *text = [item details];
-	if (!text) text = @"";
-	[detailsTextView setStringValue:text];
-}
-
-- (void)clearDetails
-{
-    [detailsTextView setStringValue:@""];
-}
-
-- (IBAction)raiseCltoolWindow:(id)sender
-{
-  [cltoolPref setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"CheckCltool"] ? NSOffState : NSOnState];
-  [self raiseWindow: cltoolWindow];
-}
-
-- (IBAction)cltoolYesButton:(id)sender;
-{
-  [[NSUserDefaults standardUserDefaults] setBool:([cltoolPref state] != NSOnState) forKey:@"CheckCltool"];
-  [self installCommandLineTool:self];
-  [cltoolWindow close];
-}
-
-- (IBAction)cltoolNoButton:(id)sender;
-{
-  [[NSUserDefaults standardUserDefaults] setBool:([cltoolPref state] != NSOnState) forKey:@"CheckCltool"];
-  [cltoolWindow close];
-}
-
-- (IBAction)raiseAboutWindow:(id)sender
-{
-    [self raiseWindow: aboutWindow];
-}
-
-- (void)raiseWindow:(NSWindow *)theWindow
-{
-    NSRect screenFrame = [[mainWindow screen] visibleFrame];
-    NSRect mainWindowFrame = [mainWindow frame];
-    NSRect theWindowFrame = [theWindow frame];
-    
-    float winX = mainWindowFrame.origin.x + 
-        (mainWindowFrame.size.width - theWindowFrame.size.width)/2;
-    float winY = mainWindowFrame.origin.y + 
-        (mainWindowFrame.size.height + theWindowFrame.size.height)/2;
-
-    if (winX<screenFrame.origin.x) winX=screenFrame.origin.x;
-    float maxX = screenFrame.origin.x+screenFrame.size.width-
-        theWindowFrame.size.width;
-    if (winX>maxX) winX=maxX;
-    float minY = screenFrame.origin.y+theWindowFrame.size.height;
-    if (winY<minY) winY=minY;
-    float maxY = screenFrame.origin.y+screenFrame.size.height;
-    if (winY>maxY) winY=maxY;
-
-    [theWindow cascadeTopLeftFromPoint:
-        NSMakePoint(winX,winY)];
-    
-    [theWindow makeKeyAndOrderFront:nil];
-}
-
-- (IBAction)onlineHelp:(id)sender
-{
-    [[NSWorkspace sharedWorkspace]
-        openURL:[NSURL URLWithString:@"http://www.cis.upenn.edu/~bcpierce/unison/docs.html"]];
-}
-
-/* from http://developer.apple.com/documentation/Security/Conceptual/authorization_concepts/index.html */
-#include <Security/Authorization.h>
-#include <Security/AuthorizationTags.h>
-- (IBAction)installCommandLineTool:(id)sender
-{
-  /* Install the command-line tool in /usr/bin/unison.
-     Requires root privilege, so we ask for it and 
-     pass the task off to /bin/sh. */
-
-  OSStatus myStatus;
-
-  AuthorizationFlags myFlags = kAuthorizationFlagDefaults;
-  AuthorizationRef myAuthorizationRef;
-  myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
-				 myFlags, &myAuthorizationRef);
-  if (myStatus != errAuthorizationSuccess) return;
-
-  {
-    AuthorizationItem myItems = {kAuthorizationRightExecute, 0,
-				 NULL, 0};
-    AuthorizationRights myRights = {1, &myItems};
-    myFlags = kAuthorizationFlagDefaults |
-      kAuthorizationFlagInteractionAllowed |
-      kAuthorizationFlagPreAuthorize |
-      kAuthorizationFlagExtendRights;
-    myStatus =
-      AuthorizationCopyRights(myAuthorizationRef,&myRights,NULL,myFlags,NULL);
-  }
-  if (myStatus == errAuthorizationSuccess) {
-    NSBundle *bundle = [NSBundle mainBundle];
-    NSString *bundle_path = [bundle bundlePath];
-    NSString *exec_path =
-      [bundle_path stringByAppendingString:@"/Contents/MacOS/cltool"];
-    // Not sure why but this doesn't work:
-    // [bundle pathForResource:@"cltool" ofType:nil];
-
-    if (exec_path == nil) return;
-    char *args[] = { "-f", (char *)[exec_path UTF8String], 
-		     "/usr/bin/unison", NULL };
-
-    myFlags = kAuthorizationFlagDefaults;
-    myStatus = AuthorizationExecuteWithPrivileges
-      (myAuthorizationRef, "/bin/cp", myFlags, args,
-       NULL);
-  }
-  AuthorizationFree (myAuthorizationRef, kAuthorizationFlagDefaults);
-
-  /*
-  if (myStatus == errAuthorizationCanceled)
-    NSLog(@"The attempt was canceled\n");
-  else if (myStatus) 
-      NSLog(@"There was an authorization error: %ld\n", myStatus);
-  */
-}
-
-- (BOOL)validateItem:(IBAction *) action
-{
-    if (action == @selector(syncButton:)) return syncable;
-    // FIXME Restarting during sync is disabled because it causes UI corruption
-    else if (action == @selector(restartButton:)) return !duringSync;
-    else if (action == @selector(rescan:)) return ((syncable && !duringSync) || afterSync);
-    else return YES;
-}
-
-- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
-{
-    return [self validateItem:[menuItem action]];
-}
-
-- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem
-{
-    return [self validateItem:[toolbarItem action]];
-}
-
-- (void)resizeWindowToSize:(NSSize)newSize
-{
-    NSRect aFrame;
-
-    float newHeight = newSize.height+[self toolbarHeightForWindow:mainWindow];
-    float newWidth = newSize.width;
-
-    aFrame = [NSWindow contentRectForFrameRect:[mainWindow frame]
-                       styleMask:[mainWindow styleMask]];
-
-    aFrame.origin.y += aFrame.size.height;
-    aFrame.origin.y -= newHeight;
-    aFrame.size.height = newHeight;
-    aFrame.size.width = newWidth;
-
-    aFrame = [NSWindow frameRectForContentRect:aFrame
-                       styleMask:[mainWindow styleMask]];
-
-    [mainWindow setFrame:aFrame display:YES animate:YES];
-}
-
-- (float)toolbarHeightForWindow:(NSWindow *)window
-{
-    NSToolbar *aToolbar;
-    float toolbarHeight = 0.0;
-    NSRect windowFrame;
-
-    aToolbar = [window toolbar];
-    if(aToolbar && [aToolbar isVisible])
-    {
-        windowFrame = [NSWindow contentRectForFrameRect:[window frame]
-            styleMask:[window styleMask]];
-        toolbarHeight = NSHeight(windowFrame)
-            - NSHeight([[window contentView] frame]);
-    }
-    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);
-}
-
-/* Returns true if we need to exit, false if we proceed */
-
-CAMLprim value warnPanel(value s)
-{
-	NSString *str = [[NSString alloc] initWithUTF8String:String_val(s)];
-    
-  [me performSelectorOnMainThread:@selector(warnPanel:) withObject:str waitUntilDone:TRUE];
-	[str release];
-  if (me -> shouldExitAfterWarning) {
-    return Val_true;
-  } else {
-    return Val_false;
-  }
-}
-
-- (void)warnPanel:(NSString *)msg {
-  int warnVal = NSRunAlertPanel(@"Warning", msg, @"Proceed", @"Exit", nil);
-  NSLog(@"Warning Panel Returned %d",warnVal);
-  if (warnVal == NSAlertAlternateReturn) {
-    shouldExitAfterWarning = YES;
-  } else {
-    shouldExitAfterWarning = FALSE;
-  }
-}
-
- at end
-
- at implementation NSString (_UnisonUtil)
-- (NSString *)trim
-{
-	NSCharacterSet *ws = [NSCharacterSet whitespaceCharacterSet];
-	int len = [self length], i = len;
-	while (i && [ws characterIsMember:[self characterAtIndex:i-1]]) i--;
-	return (i == len) ? self : [self substringToIndex:i];
-}
- at end

Copied: branches/2.45/src/uimacnew09/MyController.m (from rev 486, trunk/src/uimacnew09/MyController.m)
===================================================================
--- branches/2.45/src/uimacnew09/MyController.m	                        (rev 0)
+++ branches/2.45/src/uimacnew09/MyController.m	2012-04-02 15:54:47 UTC (rev 487)
@@ -0,0 +1,1215 @@
+/* Copyright (c) 2003, see file COPYING for details. */
+
+#import "MyController.h"
+
+/* The following two define are a workaround for an incompatibility between
+Ocaml 3.11.2 (and older) and the Mac OS X header files */
+#define uint64 uint64_caml
+#define int64 int64_caml
+
+#define CAML_NAME_SPACE
+#include <caml/callback.h>
+#include <caml/alloc.h>
+#include <caml/mlvalues.h>
+#include <caml/memory.h>
+
+ at interface NSString (_UnisonUtil)
+- (NSString *)trim;
+ at end
+
+ at implementation MyController
+
+static MyController *me; // needed by reloadTable and displayStatus, below
+
+// 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;
+}
+
+- (id)init
+{
+  if (([super init])) {
+    
+    /* Initialize locals */
+    me = self;
+    doneFirstDiff = NO;
+    
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
+                                 /* By default, invite user to install cltool */
+                                 @"YES",  @"CheckCltool",
+                                 @"NO", @"openProfileAtStartup",
+                                 @"",   @"profileToOpen",
+                                 @"NO", @"deleteLogOnExit",
+                                 @"",   @"detailsFont",
+                                 @"",   @"diffFont",
+                                 nil];
+    
+    [defaults registerDefaults:appDefaults];
+    fontChangeTarget = nil;
+  }
+  
+  return self;
+}
+
+- (void) applicationWillTerminate:(NSNotification *)aNotification {
+  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+  [defaults setObject:[NSArchiver archivedDataWithRootObject:[detailsTextView font]] forKey:@"detailsFont"];
+  [defaults setObject:[NSArchiver archivedDataWithRootObject:[diffView font]] forKey:@"diffFont"];
+  [defaults synchronize];
+}
+
+- (void)awakeFromNib
+{
+  [splitView setAutosaveName:@"splitView"];
+  
+  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];  
+  NSFont *defaultFont = [NSFont fontWithName:@"Monaco" size:11];
+  NSData *detailsFontData = [defaults dataForKey:@"detailsFont"];
+  if (detailsFontData) {
+    NSFont *tmpFont = (NSFont*) [NSUnarchiver unarchiveObjectWithData:detailsFontData];
+    if (tmpFont)
+      [detailsTextView setFont:tmpFont];
+    else
+      [detailsTextView setFont:defaultFont];
+  } else
+    [detailsTextView setFont:defaultFont];
+  
+  NSData *diffFontData = [defaults dataForKey:@"diffFont"];
+  if (diffFontData) {
+    NSFont *tmpFont = (NSFont*) [NSUnarchiver unarchiveObjectWithData:diffFontData];
+    if (tmpFont)
+      [diffView setFont:tmpFont];
+    else
+      [diffView setFont:defaultFont];
+  } else
+    [diffView setFont:defaultFont];
+  
+  blankView = [[NSView alloc] init];
+	
+  /* Double clicking in the profile list will open the profile */
+  [[profileController tableView] setTarget:self];
+  [[profileController tableView] setDoubleAction:@selector(openButton:)];
+  
+	[tableView setAutoresizesOutlineColumn:NO];
+  
+	// use combo-cell for path
+  [[tableView tableColumnWithIdentifier:@"path"] setDataCell:[[[ImageAndTextCell alloc] init] autorelease]];
+  
+	// Custom progress cell
+	ProgressCell *progressCell = [[[ProgressCell alloc] init] autorelease];
+	[[tableView tableColumnWithIdentifier:@"percentTransferred"] setDataCell:progressCell];
+	
+  /* Set up the version string in the about box.  We use a custom
+   about box just because PRCS doesn't seem capable of getting the
+   version into the InfoPlist.strings file; otherwise we'd use the
+   standard about box. */
+  [versionText setStringValue:ocamlCall("S", "unisonGetVersion")];
+  
+  /* Command-line processing */
+	OCamlValue *clprofile = (id)ocamlCall("@", "unisonInit0");
+
+	BOOL areRootsSet = (long)ocamlCall("i", "areRootsSet") ? YES : NO;
+	if (areRootsSet) {
+		NSLog(@"Roots are on the command line");
+	}
+	else {
+		NSLog(@"Roots are not set on the command line");
+	}
+	  
+  /* Add toolbar */
+  toolbar = [[[UnisonToolbar alloc] 
+              initWithIdentifier: @"unisonToolbar" :self :tableView] autorelease];
+  [mainWindow setToolbar: toolbar];
+	[toolbar takeTableModeView:tableModeSelector];
+	[self initTableMode];
+	
+  
+  /* Set up the first window the user will see */
+  if (clprofile) {
+    /* A profile name was given on the command line */
+		NSString *profileName = [clprofile getField:0 withType:'S'];
+    [self profileSelected:profileName];
+    
+    /* If invoked from terminal we need to bring the app to the front */
+    [NSApp activateIgnoringOtherApps:YES];
+    
+    /* Start the connection */
+    [self connect:profileName];
+  }
+  else if (areRootsSet) {
+	/* If invoked from terminal we need to bring the app to the front */
+	[NSApp activateIgnoringOtherApps:YES];
+	/* Start the connection with the empty profile name, indicating roots only */
+	[self connect:@""];
+  }
+  else {
+    /* If invoked from terminal we need to bring the app to the front */
+    [NSApp activateIgnoringOtherApps:YES];
+    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"openProfileAtStartup"]) {
+      NSString *profileToOpen = [[NSUserDefaults standardUserDefaults] 
+                                 stringForKey:@"profileToOpen"];
+      if ([[profileToOpen trim] compare:@""] != NSOrderedSame &&
+          [[profileController getProfiles] indexOfObject:profileToOpen] != NSNotFound) {
+        [self profileSelected:profileToOpen];
+        [self connect:profileToOpen];
+      } else {
+        /* Bring up the dialog to choose a profile */
+        [self chooseProfiles];
+      }
+    } else {
+      /* Bring up the dialog to choose a profile */
+      [self chooseProfiles];
+    }
+  }
+  
+  [mainWindow display];
+  [mainWindow makeKeyAndOrderFront:nil];
+
+  /* unless user has clicked Don't ask me again, ask about cltool */
+  if ( ([[NSUserDefaults standardUserDefaults] boolForKey:@"CheckCltool"]) && 
+	  (![[NSFileManager defaultManager]
+		 fileExistsAtPath:@"/usr/bin/unison"]) )
+	  [self raiseCltoolWindow:nil];
+}
+
+- (IBAction) checkOpenProfileChanged:(id)sender {
+  [profileBox setEnabled:[checkOpenProfile state]];
+  if ([profileBox isEnabled] && [profileBox indexOfSelectedItem] < 0) {
+    [profileBox selectItemAtIndex:0];
+    [[NSUserDefaults standardUserDefaults] setObject:[profileBox itemObjectValueAtIndex:0] forKey:@"profileToOpen"];
+  }
+}
+
+- (IBAction) chooseFont:(id)sender {
+  [[NSFontPanel sharedFontPanel] makeKeyAndOrderFront:self];
+  [[NSFontManager sharedFontManager] setDelegate:self];
+  fontChangeTarget = sender;
+}
+
+- (void) changeFont:(id)sender {
+  NSFont *newFont = [sender convertFont:[detailsTextView font]];
+  if (fontChangeTarget == chooseDetailsFont)
+    [detailsTextView setFont:newFont];
+  else if (fontChangeTarget == chooseDiffFont)
+    [diffView setFont:newFont];
+  [self updateFontDisplay];
+}
+
+- (void) updateFontDisplay {
+  NSFont *detailsFont = [detailsTextView font];
+  NSFont *diffFont = [diffView font];
+  [detailsFontLabel setStringValue:[NSString stringWithFormat:@"%@ : %d", [detailsFont displayName], (NSInteger) [detailsFont pointSize]]];
+  [diffFontLabel setStringValue:[NSString stringWithFormat:@"%@ : %d", [diffFont displayName], (NSInteger) [diffFont pointSize]]];
+}
+
+- (void)chooseProfiles
+{
+    [mainWindow setContentView:blankView];
+    [self resizeWindowToSize:[chooseProfileView frame].size];
+    [mainWindow setContentMinSize:
+        NSMakeSize(NSWidth([[mainWindow contentView] frame]),150)];
+    [mainWindow setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+    [mainWindow setContentView:chooseProfileView];
+    [toolbar setView:@"chooseProfileView"];
+    [mainWindow setTitle:@"Unison"];
+	
+    // profiles get keyboard input
+    [mainWindow makeFirstResponder:[profileController tableView]];
+    [chooseProfileView display];
+}
+
+- (IBAction)createButton:(id)sender
+{
+    [preferencesController reset];
+    [mainWindow setContentView:blankView];
+    [self resizeWindowToSize:[preferencesView frame].size];
+    [mainWindow setContentMinSize:
+        NSMakeSize(400,NSHeight([[mainWindow contentView] frame]))];
+    [mainWindow setContentMaxSize:
+        NSMakeSize(FLT_MAX,NSHeight([[mainWindow contentView] frame]))];
+    [mainWindow setContentView:preferencesView];
+    [toolbar setView:@"preferencesView"];
+}
+
+- (IBAction)saveProfileButton:(id)sender
+{
+    if ([preferencesController validatePrefs]) {
+        // so the list contains the new profile
+        [profileController initProfiles];
+        [self chooseProfiles];
+    }
+}
+
+- (IBAction)cancelProfileButton:(id)sender
+{
+    [self chooseProfiles];
+}
+
+/* Only valid once a profile has been selected */
+- (NSString *)profile {
+    return myProfile;
+}
+
+- (void)profileSelected:(NSString *)aProfile
+{
+    [aProfile retain];
+    [myProfile release];
+    myProfile = aProfile;
+    [mainWindow setTitle: [NSString stringWithFormat:@"Unison: %@", myProfile]];
+}
+
+- (IBAction)showPreferences:(id)sender {
+  [profileBox removeAllItems];
+  [profileBox addItemsWithObjectValues:[profileController getProfiles]];
+  NSUInteger index = [[profileController getProfiles] indexOfObject:
+                      [[NSUserDefaults standardUserDefaults] 
+                       stringForKey:@"profileToOpen"]];
+  if (index == NSNotFound) {
+    [checkOpenProfile setState:NSOffState];
+    [profileBox setStringValue:@""];
+  } else
+    [profileBox selectItemAtIndex:index];
+
+  [profileBox setEnabled:[checkOpenProfile state]];
+  if ([profileBox isEnabled] && [profileBox indexOfSelectedItem] < 0)
+    [profileBox selectItemAtIndex:0];
+
+  [self updateFontDisplay];
+
+  [self raiseWindow:preferencesWindow];
+}
+
+- (IBAction)restartButton:(id)sender
+{
+    [tableView setEditable:NO];
+    [self chooseProfiles];
+}
+
+- (IBAction)rescan:(id)sender
+{
+    /* There is a delay between turning off the button and it
+       actually being disabled. Make sure we don't respond. */
+    if ([self validateItem:@selector(rescan:)]) {
+        waitingForPassword = NO;
+        [self afterOpen];
+    }
+}
+
+- (IBAction)openButton:(id)sender
+{
+    NSString *profile = [profileController selected];
+    [self profileSelected:profile];
+    [self connect:profile];
+    return;
+}
+
+- (void)updateToolbar
+{
+  [toolbar validateVisibleItems];	
+	[tableModeSelector setEnabled:((syncable && !duringSync) || afterSync)];
+
+	// Why?
+    [updatesView setNeedsDisplay:YES];
+}
+
+- (void)updateTableViewWithReset:(BOOL)shouldResetSelection
+{
+	[tableView reloadData]; 
+	if (shouldResetSelection) {
+		[tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
+		shouldResetSelection = NO;
+	}
+	[updatesView setNeedsDisplay:YES];	    
+}
+
+- (void)updateProgressBar:(NSNumber *)newProgress
+{
+	// NSLog(@"Updating progress bar: %i - %i", (int)[newProgress doubleValue], (int)[progressBar doubleValue]);
+	[progressBar incrementBy:([newProgress doubleValue] - [progressBar doubleValue])];
+}
+
+- (void)updateTableViewSelection
+{
+    int n = [tableView numberOfSelectedRows];
+    if (n == 1) [self displayDetails:[tableView itemAtRow:[tableView selectedRow]]];
+    else [self clearDetails];
+}
+
+- (void)outlineViewSelectionDidChange:(NSNotification *)note
+{
+	[self updateTableViewSelection];
+}
+
+- (void)connect:(NSString *)profileName
+{
+  // contact server, propagate prefs
+  NSLog(@"Connecting to %@...", profileName);
+  
+  // Switch to ConnectingView
+  [mainWindow setContentView:blankView];
+  [self resizeWindowToSize:[updatesView frame].size];
+  [mainWindow setContentMinSize:NSMakeSize(150,150)];
+  [mainWindow setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+  [mainWindow setContentView:ConnectingView];
+  [toolbar setView:@"connectingView"];
+  
+  // Update (almost) immediately
+  [ConnectingView display];
+  [connectingAnimation startAnimation:self];
+  
+  syncable = NO;
+  afterSync = NO;    
+    
+	[self updateToolbar];
+    
+	// will spawn thread on OCaml side and callback when complete
+  (void)ocamlCall("xS", "unisonInit1", profileName);
+}
+
+CAMLprim value unisonInit1Complete(value v)
+{
+  id pool = [[NSAutoreleasePool alloc] init];
+  if (v == Val_unit) {
+    NSLog(@"Connected.");
+    [me->connectingAnimation stopAnimation:me];
+		[me->preconn release];
+		me->preconn = NULL;
+    [me performSelectorOnMainThread:@selector(afterOpen:) withObject:nil waitUntilDone:FALSE]; 
+  } else {
+    // prompting required
+		me->preconn = [[OCamlValue alloc] initWithValue:Field(v,0)]; // value of Some
+		[me performSelectorOnMainThread:@selector(unisonInit1Complete:) withObject:nil waitUntilDone:FALSE]; 
+	}
+  [pool release];
+  return Val_unit;
+}
+
+- (void)unisonInit1Complete:(id)ignore
+{
+	@try {
+		OCamlValue *prompt = ocamlCall("@@", "openConnectionPrompt", preconn);
+		if (!prompt) {
+			// turns out, no prompt needed, but must finish opening connection
+			ocamlCall("x@", "openConnectionEnd", preconn);
+			NSLog(@"Connected.");
+			waitingForPassword = NO;
+			[self afterOpen];
+			return;
+		}
+		waitingForPassword = YES;
+
+		[self raisePasswordWindow:[prompt getField:0 withType:'S']]; 
+	} @catch (NSException *ex) {
+	    NSRunAlertPanel(@"Connection Error", [ex description], @"OK", nil, nil);
+		[self chooseProfiles];
+		return;
+	}
+
+    NSLog(@"Connected.");
+}
+
+- (void)raisePasswordWindow:(NSString *)prompt
+{
+    // FIX: some prompts don't ask for password, need to look at it
+    NSLog(@"Got the prompt: '%@'",prompt);
+    if ((long)ocamlCall("iS", "unisonPasswordMsg", prompt)) {
+        [passwordPrompt setStringValue:@"Please enter your password"];
+        [NSApp beginSheet:passwordWindow
+            modalForWindow:mainWindow
+            modalDelegate:nil
+            didEndSelector:nil
+            contextInfo:nil];
+        return;
+    }
+    if ((long)ocamlCall("iS", "unisonPassphraseMsg", prompt)) {
+        [passwordPrompt setStringValue:@"Please enter your passphrase"];
+        [NSApp beginSheet:passwordWindow
+            modalForWindow:mainWindow
+            modalDelegate:nil
+            didEndSelector:nil
+            contextInfo:nil];
+        return;
+    }
+    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");
+			prompt = ocamlCall("S@", "openConnectionPrompt", preconn);
+            if (!prompt) {
+                // all done with prompts, finish opening connection
+				ocamlCall("x@", "openConnectionEnd", preconn);
+                waitingForPassword = NO;
+                [self afterOpen];
+                return;
+            }
+            else {
+				[self raisePasswordWindow:[NSString 
+                    stringWithUTF8String:String_val(Field(prompt,0))]];
+                return;
+            }
+        }
+        if (i == NSAlertAlternateReturn) {
+			ocamlCall("x@", "openConnectionCancel", preconn);
+            return;
+        }
+        else {
+            NSLog(@"Unrecognized response '%d' from NSRunAlertPanel",i);
+			ocamlCall("x@", "openConnectionCancel", preconn);
+            return;
+        }
+    }
+    NSLog(@"Unrecognized message from ssh: %@",prompt);
+	ocamlCall("x@", "openConnectionCancel", preconn);
+}
+
+// The password window will invoke this when Enter occurs, b/c we
+// are the delegate.
+- (void)controlTextDidEndEditing:(NSNotification *)notification
+{
+    NSNumber *reason = [[notification userInfo] objectForKey:@"NSTextMovement"];
+    int code = [reason intValue];
+    if (code == NSReturnTextMovement)
+        [self endPasswordWindow:self];
+}
+// Or, the Continue button will invoke this when clicked
+- (IBAction)endPasswordWindow:(id)sender
+{
+    [passwordWindow orderOut:self];
+    [NSApp endSheet:passwordWindow];
+    if ([sender isEqualTo:passwordCancelButton]) {
+		ocamlCall("x@", "openConnectionCancel", preconn);
+        [self chooseProfiles];
+        return;
+    }
+    NSString *password = [passwordText stringValue];
+	ocamlCall("x at S", "openConnectionReply", preconn, password);
+
+    OCamlValue *prompt = ocamlCall("@@", "openConnectionPrompt", preconn);
+    if (!prompt) {
+        // all done with prompts, finish opening connection
+		ocamlCall("x@", "openConnectionEnd", preconn);
+        waitingForPassword = NO;
+        [self afterOpen];
+    }
+    else {
+		[self raisePasswordWindow:[prompt getField:0 withType:'S']]; 
+    }
+}
+
+- (void)afterOpen:(id)ignore
+{
+	[self afterOpen];
+}
+
+- (void)afterOpen
+{
+    if (waitingForPassword) return;
+    // move to updates window after clearing it
+	[self updateReconItems:nil];
+	[progressBar setDoubleValue:0.0];
+	[progressBar stopAnimation:self];
+    // [self clearDetails];
+    [mainWindow setContentView:blankView];
+    [self resizeWindowToSize:[updatesView frame].size];
+    [mainWindow setContentMinSize:
+        NSMakeSize(NSWidth([[mainWindow contentView] frame]),200)];
+    [mainWindow setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+    [mainWindow setContentView:updatesView];
+    [toolbar setView:@"updatesView"];
+
+    syncable = NO;
+    afterSync = NO;
+
+    [tableView deselectAll:self];
+	[self updateToolbar];
+	[self updateProgressBar:[NSNumber numberWithDouble:0.0]];
+    
+    // this should depend on the number of reconitems, and is now done
+    // in updateReconItems:
+    // reconItems table gets keyboard input
+    //[mainWindow makeFirstResponder:tableView];
+    [tableView scrollRowToVisible:0];
+
+	[preconn release];
+    preconn = nil; // so old preconn can be garbage collected
+	// This will run in another thread spawned in OCaml and will return immediately
+	// We'll get a call back to unisonInit2Complete() when it is complete
+	ocamlCall("x", "unisonInit2");
+}
+
+- (void)doSync
+{
+    [tableView setEditable:NO];
+    syncable = NO;
+    duringSync = YES;
+	
+	[self updateToolbar];
+    
+	// This will run in another thread spawned in OCaml and will return immediately
+	// We'll get a call back to syncComplete() when it is complete
+	ocamlCall("x", "unisonSynchronize");	
+}
+
+- (IBAction)syncButton:(id)sender
+{
+	[self doSync];
+}
+
+
+- (void)afterUpdate:(id)retainedReconItems
+{
+	// NSLog(@"In afterUpdate:...");
+    [self updateReconItems:retainedReconItems];
+	[retainedReconItems release];
+
+    [notificationController updateFinishedFor:[self profile]];
+
+    // label the left and right columns with the roots
+	NSString *leftHost = [(NSString *)ocamlCall("S", "unisonFirstRootString") trim];
+	NSString *rightHost = [(NSString *)ocamlCall("S", "unisonSecondRootString") trim];
+	/*
+    [[[tableView tableColumnWithIdentifier:@"left"] headerCell] setObjectValue:lefthost];
+    [[[tableView tableColumnWithIdentifier:@"right"] headerCell] setObjectValue:rightHost];
+    */
+    [mainWindow setTitle: [NSString stringWithFormat:@"Unison: %@ (%@ <-> %@)", 
+			[self profile], leftHost, rightHost]];
+	
+	// initial sort
+	[tableView setSortDescriptors:[NSArray arrayWithObjects: 
+		[[tableView tableColumnWithIdentifier:@"fileSizeString"] sortDescriptorPrototype],
+		[[tableView tableColumnWithIdentifier:@"path"] sortDescriptorPrototype],
+		nil]];
+
+	[self updateTableViewWithReset:([reconItems count] > 0)];
+	[self updateToolbar];
+	isBatchSet = (long)ocamlCall("i", "isBatchSet") ? YES : NO;
+	if (isBatchSet) {
+		NSLog(@"batch set on the command line");
+	}
+	else {
+		NSLog(@"batch not set on the command line");
+	}
+	
+	if (isBatchSet) {
+		[self doSync];
+	}
+}
+
+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;
+}
+
+- (void)afterSync:(id)ignore
+{
+    [notificationController syncFinishedFor:[self profile]];
+    duringSync = NO;
+    afterSync = YES;
+    [self updateToolbar];
+    
+    int i;
+    for (i = 0; i < [reconItems count]; i++) {
+        [[reconItems objectAtIndex:i] resetProgress];
+    }
+
+	[self updateTableViewSelection];
+		
+	[self updateTableViewWithReset:FALSE];
+}
+
+- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+{
+    [_timer invalidate];
+
+    switch (returnCode) {
+        case NSAlertAlternateReturn:
+            return;
+            break;
+            
+        default:
+            [[NSApplication sharedApplication] performSelector: @selector(terminate:) withObject: nil afterDelay: 0.0];
+            break;
+    }
+}
+
+- (void)updateCountdown
+{
+    if (_secondsRemaining == 0) {
+        [_timer invalidate];
+        [[_timeoutAlert window] orderOut: nil];
+        [self alertDidEnd: _timeoutAlert returnCode: NSAlertDefaultReturn contextInfo: nil];
+    } else {
+        [_timeoutAlert setMessageText: [NSString stringWithFormat: @"Unison will quit in %lu seconds", _secondsRemaining]];
+        _secondsRemaining--;
+    }
+}
+
+
+- (void)quitIfBatch:(id)ignore
+{
+	if (isBatchSet) {
+		NSLog(@"Automatically quitting because of -batch");
+		_timeoutAlert = [NSAlert alertWithMessageText: @"" defaultButton: @"Quit" alternateButton: @"Cancel" otherButton: nil informativeTextWithFormat: @""];
+	
+		_secondsRemaining = 10;
+	
+		_timer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateCountdown) userInfo: nil repeats: YES];
+		
+		[_timeoutAlert beginSheetModalForWindow: mainWindow modalDelegate: self didEndSelector: @selector(alertDidEnd:returnCode:contextInfo:) contextInfo: NULL];
+	}  
+}
+
+// TODO: (BCP, 3/2012) Note that the string literal "~/unison.log" here is wrong -- 
+// this is a user-settable preference (in ubase/trace.ml) and we should ask for its value.
+CAMLprim value syncComplete()
+{
+  id pool = [[NSAutoreleasePool alloc] init];
+  [me performSelectorOnMainThread:@selector(afterSync:) withObject:nil waitUntilDone:FALSE];
+  if ([[NSUserDefaults standardUserDefaults] boolForKey:@"deleteLogOnExit"])
+    [[NSFileManager defaultManager] removeItemAtPath:[@"~/unison.log" stringByExpandingTildeInPath] error:nil];
+  [pool release];
+
+  [me performSelectorOnMainThread:@selector(quitIfBatch:) withObject:nil waitUntilDone:FALSE];
+  
+  return Val_unit;
+}
+
+// A function called from ocaml
+- (void)reloadTable:(NSNumber *)i
+{
+	// NSLog(@"*** ReloadTable: %i", [i intValue]);
+
+    [[reconItems objectAtIndex:[i intValue]] resetProgress];
+	[self updateTableViewWithReset:FALSE];
+}
+
+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;
+}
+
+- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
+	if (item == nil) item = rootItem;
+	return [[item children] count];
+}
+
+- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
+    return [item isKindOfClass:[ParentReconItem class]];
+}
+
+- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
+	if (item == nil) item = rootItem;
+	return [[item children] objectAtIndex:index];
+}
+
+- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
+    NSString *identifier = [tableColumn identifier];
+	if (item == nil) item = rootItem;
+
+	if ([identifier isEqualToString:@"percentTransferred"] && (!duringSync && !afterSync)) return nil;
+
+	return [item valueForKey:identifier];
+}
+
+static NSDictionary *_SmallGreyAttributes = nil;
+
+- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {    
+	NSString *identifier = [tableColumn identifier];
+    if ([identifier isEqualToString:@"path"]) {
+		// The file icon
+		[(ImageAndTextCell*)cell setImage:[item fileIcon]];
+		
+		// For parents, format the file count into the text
+		long fileCount = [item fileCount];
+		if (fileCount > 1) {
+			NSString *countString = [NSString stringWithFormat:@"  (%ld files)", fileCount];
+			NSString *fullString = [(NSString *)[cell objectValue] stringByAppendingString:countString];
+			NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithString:fullString];
+
+			if (!_SmallGreyAttributes) {		
+				NSColor *txtColor = [NSColor grayColor];
+				NSFont *txtFont = [NSFont systemFontOfSize:9.0];
+				_SmallGreyAttributes = [[NSDictionary dictionaryWithObjectsAndKeys:txtFont,
+					NSFontAttributeName, txtColor, NSForegroundColorAttributeName,  nil] retain];
+			}
+			[as setAttributes:_SmallGreyAttributes range:NSMakeRange([fullString length] - [countString length], [countString length])];
+			[cell setAttributedStringValue:as];
+			[as release];
+		}
+    } else if ([identifier isEqualToString:@"percentTransferred"]) {
+		[(ProgressCell*)cell setIcon:[item direction]];		
+		[(ProgressCell*)cell setStatusString:[item progressString]];
+		[(ProgressCell*)cell setIsActive:[item isKindOfClass:[LeafReconItem class]]];		
+    }
+}
+
+- (void)outlineView:(NSOutlineView *)outlineView 
+      sortDescriptorsDidChange:(NSArray *)oldDescriptors {
+	NSArray *originalSelection = [outlineView selectedObjects];
+	
+	// do we want to catch case of object changes to allow resort in same direction for progress / direction?
+	// Could check if our objects change and if the first item at the head of new and old were the same
+	[rootItem sortUsingDescriptors:[outlineView sortDescriptors]];
+	[outlineView reloadData];
+	[outlineView setSelectedObjects:originalSelection];
+}
+
+// Delegate methods
+
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item {
+    return NO;
+}
+
+- (NSMutableArray *)reconItems // used in ReconTableView only
+{
+    return reconItems;
+}
+
+- (int)tableMode
+{
+	return [tableModeSelector selectedSegment];
+}
+
+- (IBAction)tableModeChanged:(id)sender
+{
+	[[NSUserDefaults standardUserDefaults] setInteger:[self tableMode]+1 forKey:@"TableLayout"];
+	[self updateForChangedItems];
+}
+
+- (void)initTableMode
+{
+	int mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"TableLayout"] - 1;
+	if (mode == -1) mode = 1;
+	[tableModeSelector setSelectedSegment:mode];
+}
+
+- (void)updateReconItems:(OCamlValue *)caml_reconItems
+{
+    [reconItems release];
+    reconItems = [[NSMutableArray alloc] init];
+	long i, n =[caml_reconItems count];
+    for (i=0; i<n; i++) {
+		LeafReconItem *item = [[LeafReconItem alloc] initWithRiAndIndex:(id)[caml_reconItems getField:i withType:'@'] index:i];
+        [reconItems addObject:item];
+		[item release];
+    }
+	[self updateForChangedItems];
+}
+
+- (void)expandConflictedParent:(ParentReconItem *)parent
+{
+	if ([parent hasConflictedChildren]) {
+		// NSLog(@"Expanding conflictedParent: %@", [parent fullPath]);
+		[tableView expandItem:parent expandChildren:NO];
+		NSArray *children = [parent children];
+		int i = 0, count = [children count];
+		for (;i < count; i++) {
+			id child = [children objectAtIndex:i];
+			if ([child isKindOfClass:[ParentReconItem class]]) [self expandConflictedParent:child];
+		}
+	}
+}
+
+- (void)updateForChangedItems
+{
+	int tableMode = [self tableMode];
+	
+	[rootItem release];
+	ParentReconItem *root = rootItem = [[ParentReconItem alloc] init];
+	
+	if (tableMode != 0 && [reconItems count]) {
+		// Special roll-up root item for outline displays
+		root = [[ParentReconItem alloc] init];
+		[rootItem addChild:root nested:NO];
+		[root setPath:@"All Changes..."];
+		[root setFullPath:@""];
+		[root release];
+	}
+	
+    int j = 0, n =[reconItems count];
+    for (; j<n; j++) {
+		[root addChild:[reconItems objectAtIndex:j] nested:(tableMode != 0)];
+    }
+	
+	if (tableMode == 1) [root collapseParentsWithSingleChildren:YES];
+
+	[tableView reloadData];
+	
+	if (NO) {
+		// Pre-expand entire tree
+		int i = [[rootItem children] count];
+		while (i--) {
+			[tableView expandItem:[[rootItem children] objectAtIndex:i] expandChildren:YES];
+		}
+	} else if (tableMode != 0) {
+		// Always open root node
+		[tableView expandItem:rootItem expandChildren:NO];
+
+		// then smart expand to reveal conflicts / changes in direction
+		[self expandConflictedParent:root];
+		
+		// then open more levels if we can do so without causing scrolling
+		[tableView expandChildrenIfSpace];
+	}
+	
+    // Make sure details get updated (or cleared)
+	[self updateTableViewSelection];
+
+    // Only enable sync if there are reconitems
+    if ([reconItems count]>0) {
+        [tableView setEditable:YES];
+
+        // reconItems table gets keyboard input
+        [mainWindow makeFirstResponder:tableView];
+
+        syncable = YES;
+    }
+    else {
+        [tableView setEditable:NO];
+        afterSync = YES; // rescan should be enabled
+	
+        // reconItems table no longer gets keyboard input
+        [mainWindow makeFirstResponder:nil];
+    }
+	[self updateToolbar];
+}
+
+- (id)updateForIgnore:(id)item
+{
+    long j = (long)ocamlCall("ii", "unisonUpdateForIgnore", [reconItems indexOfObjectIdenticalTo:item]);
+	NSLog(@"Updating for ignore...");
+    [self updateReconItems:(OCamlValue *)ocamlCall("@", "unisonState")];
+    return [reconItems objectAtIndex:j];
+}
+
+// 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;
+}
+
+- (void)statusTextSet:(NSString *)s {
+    /* filter out strings with # reconitems, and empty strings */
+    if (!NSEqualRanges([s rangeOfString:@"reconitems"], 
+         NSMakeRange(NSNotFound,0))) return;
+    [statusText setStringValue:s];
+}
+
+// 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:@" "];
+	[me->statusText performSelectorOnMainThread:@selector(setStringValue:) 
+				withObject:str waitUntilDone:FALSE];
+  [pool release];
+  return Val_unit;
+}
+
+- (void)diffViewTextSet:(NSArray *)args
+{
+	[self diffViewTextSet:[args objectAtIndex:0] bodyText:[args objectAtIndex:1]];
+}
+
+- (void)diffViewTextSet:(NSString *)title bodyText:(NSString *)body {
+   if ([body length]==0) return;
+   [diffWindow setTitle:title];
+   //[diffView setFont:diffFont];
+   [diffView setString:body];
+   if (!doneFirstDiff) {
+       /* On first open, position the diff window to the right of
+       the main window, but without going off the mainwindow's screen */
+       float screenOriginX = [[mainWindow screen] visibleFrame].origin.x;
+       float screenWidth = [[mainWindow screen] visibleFrame].size.width;
+       float mainOriginX = [mainWindow frame].origin.x;
+       float mainOriginY = [mainWindow frame].origin.y;
+       float mainWidth = [mainWindow frame].size.width;
+       float mainHeight = [mainWindow frame].size.height;       
+       float diffWidth = [diffWindow frame].size.width;
+
+       float diffX = mainOriginX+mainWidth;
+       float maxX = screenOriginX+screenWidth-diffWidth;
+       if (diffX > maxX) diffX = maxX;
+       float diffY = mainOriginY + mainHeight;
+       
+       NSPoint diffOrigin = NSMakePoint(diffX,diffY);
+       [diffWindow cascadeTopLeftFromPoint:diffOrigin];
+       
+       doneFirstDiff = YES;
+   }
+   [diffWindow orderFront:nil];
+}
+
+- (void)displayDetails:(ReconItem *)item
+{
+	//[detailsTextView setFont:diffFont];
+	NSString *text = [item details];
+	if (!text) text = @"";
+	[detailsTextView setStringValue:text];
+}
+
+- (void)clearDetails
+{
+    [detailsTextView setStringValue:@""];
+}
+
+- (IBAction)raiseCltoolWindow:(id)sender
+{
+  [cltoolPref setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"CheckCltool"] ? NSOffState : NSOnState];
+  [self raiseWindow: cltoolWindow];
+}
+
+- (IBAction)cltoolYesButton:(id)sender;
+{
+  [[NSUserDefaults standardUserDefaults] setBool:([cltoolPref state] != NSOnState) forKey:@"CheckCltool"];
+  [self installCommandLineTool:self];
+  [cltoolWindow close];
+}
+
+- (IBAction)cltoolNoButton:(id)sender;
+{
+  [[NSUserDefaults standardUserDefaults] setBool:([cltoolPref state] != NSOnState) forKey:@"CheckCltool"];
+  [cltoolWindow close];
+}
+
+- (IBAction)raiseAboutWindow:(id)sender
+{
+    [self raiseWindow: aboutWindow];
+}
+
+- (void)raiseWindow:(NSWindow *)theWindow
+{
+    NSRect screenFrame = [[mainWindow screen] visibleFrame];
+    NSRect mainWindowFrame = [mainWindow frame];
+    NSRect theWindowFrame = [theWindow frame];
+    
+    float winX = mainWindowFrame.origin.x + 
+        (mainWindowFrame.size.width - theWindowFrame.size.width)/2;
+    float winY = mainWindowFrame.origin.y + 
+        (mainWindowFrame.size.height + theWindowFrame.size.height)/2;
+
+    if (winX<screenFrame.origin.x) winX=screenFrame.origin.x;
+    float maxX = screenFrame.origin.x+screenFrame.size.width-
+        theWindowFrame.size.width;
+    if (winX>maxX) winX=maxX;
+    float minY = screenFrame.origin.y+theWindowFrame.size.height;
+    if (winY<minY) winY=minY;
+    float maxY = screenFrame.origin.y+screenFrame.size.height;
+    if (winY>maxY) winY=maxY;
+
+    [theWindow cascadeTopLeftFromPoint:
+        NSMakePoint(winX,winY)];
+    
+    [theWindow makeKeyAndOrderFront:nil];
+}
+
+- (IBAction)onlineHelp:(id)sender
+{
+    [[NSWorkspace sharedWorkspace]
+        openURL:[NSURL URLWithString:@"http://www.cis.upenn.edu/~bcpierce/unison/docs.html"]];
+}
+
+/* from http://developer.apple.com/documentation/Security/Conceptual/authorization_concepts/index.html */
+#include <Security/Authorization.h>
+#include <Security/AuthorizationTags.h>
+- (IBAction)installCommandLineTool:(id)sender
+{
+  /* Install the command-line tool in /usr/bin/unison.
+     Requires root privilege, so we ask for it and 
+     pass the task off to /bin/sh. */
+
+  OSStatus myStatus;
+
+  AuthorizationFlags myFlags = kAuthorizationFlagDefaults;
+  AuthorizationRef myAuthorizationRef;
+  myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
+				 myFlags, &myAuthorizationRef);
+  if (myStatus != errAuthorizationSuccess) return;
+
+  {
+    AuthorizationItem myItems = {kAuthorizationRightExecute, 0,
+				 NULL, 0};
+    AuthorizationRights myRights = {1, &myItems};
+    myFlags = kAuthorizationFlagDefaults |
+      kAuthorizationFlagInteractionAllowed |
+      kAuthorizationFlagPreAuthorize |
+      kAuthorizationFlagExtendRights;
+    myStatus =
+      AuthorizationCopyRights(myAuthorizationRef,&myRights,NULL,myFlags,NULL);
+  }
+  if (myStatus == errAuthorizationSuccess) {
+    NSBundle *bundle = [NSBundle mainBundle];
+    NSString *bundle_path = [bundle bundlePath];
+    NSString *exec_path =
+      [bundle_path stringByAppendingString:@"/Contents/MacOS/cltool"];
+    // Not sure why but this doesn't work:
+    // [bundle pathForResource:@"cltool" ofType:nil];
+
+    if (exec_path == nil) return;
+    char *args[] = { "-f", (char *)[exec_path UTF8String], 
+		     "/usr/bin/unison", NULL };
+
+    myFlags = kAuthorizationFlagDefaults;
+    myStatus = AuthorizationExecuteWithPrivileges
+      (myAuthorizationRef, "/bin/cp", myFlags, args,
+       NULL);
+  }
+  AuthorizationFree (myAuthorizationRef, kAuthorizationFlagDefaults);
+
+  /*
+  if (myStatus == errAuthorizationCanceled)
+    NSLog(@"The attempt was canceled\n");
+  else if (myStatus) 
+      NSLog(@"There was an authorization error: %ld\n", myStatus);
+  */
+}
+
+- (BOOL)validateItem:(IBAction *) action
+{
+    if (action == @selector(syncButton:)) return syncable;
+    // FIXME Restarting during sync is disabled because it causes UI corruption
+    else if (action == @selector(restartButton:)) return !duringSync;
+    else if (action == @selector(rescan:)) return ((syncable && !duringSync) || afterSync);
+    else return YES;
+}
+
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+    return [self validateItem:[menuItem action]];
+}
+
+- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem
+{
+    return [self validateItem:[toolbarItem action]];
+}
+
+- (void)resizeWindowToSize:(NSSize)newSize
+{
+    NSRect aFrame;
+
+    float newHeight = newSize.height+[self toolbarHeightForWindow:mainWindow];
+    float newWidth = newSize.width;
+
+    aFrame = [NSWindow contentRectForFrameRect:[mainWindow frame]
+                       styleMask:[mainWindow styleMask]];
+
+    aFrame.origin.y += aFrame.size.height;
+    aFrame.origin.y -= newHeight;
+    aFrame.size.height = newHeight;
+    aFrame.size.width = newWidth;
+
+    aFrame = [NSWindow frameRectForContentRect:aFrame
+                       styleMask:[mainWindow styleMask]];
+
+    [mainWindow setFrame:aFrame display:YES animate:YES];
+}
+
+- (float)toolbarHeightForWindow:(NSWindow *)window
+{
+    NSToolbar *aToolbar;
+    float toolbarHeight = 0.0;
+    NSRect windowFrame;
+
+    aToolbar = [window toolbar];
+    if(aToolbar && [aToolbar isVisible])
+    {
+        windowFrame = [NSWindow contentRectForFrameRect:[window frame]
+            styleMask:[window styleMask]];
+        toolbarHeight = NSHeight(windowFrame)
+            - NSHeight([[window contentView] frame]);
+    }
+    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);
+}
+
+/* Returns true if we need to exit, false if we proceed */
+
+CAMLprim value warnPanel(value s)
+{
+	NSString *str = [[NSString alloc] initWithUTF8String:String_val(s)];
+    
+  [me performSelectorOnMainThread:@selector(warnPanel:) withObject:str waitUntilDone:TRUE];
+	[str release];
+  if (me -> shouldExitAfterWarning) {
+    return Val_true;
+  } else {
+    return Val_false;
+  }
+}
+
+- (void)warnPanel:(NSString *)msg {
+  int warnVal = NSRunAlertPanel(@"Warning", msg, @"Proceed", @"Exit", nil);
+  NSLog(@"Warning Panel Returned %d",warnVal);
+  if (warnVal == NSAlertAlternateReturn) {
+    shouldExitAfterWarning = YES;
+  } else {
+    shouldExitAfterWarning = FALSE;
+  }
+}
+
+ at end
+
+ at implementation NSString (_UnisonUtil)
+- (NSString *)trim
+{
+	NSCharacterSet *ws = [NSCharacterSet whitespaceCharacterSet];
+	int len = [self length], i = len;
+	while (i && [ws characterIsMember:[self characterAtIndex:i-1]]) i--;
+	return (i == len) ? self : [self substringToIndex:i];
+}
+ at end



More information about the Unison-hackers mailing list