CvsGui : MacCVS and WinCvs

Alexandre Parenteau, alexp@strata3d.com

 v1.0 30 October 1997 - A contribution of Strata Inc. 1997

This document describes both the MacCvs and WinCvs client. More informations can be found on the MacCVS home page and at the Cyclic Software home page.

• 1 About CVS

• 1.1 Where to find CVS ?

• 1.2 What is CvsGui ?

• 1.3 Why Cvs and CvsGui ?

• 1.4 Which are the features inside cvs that are not avalaible in CvsGui ?

• 1.5 Which are the features inside CvsGui that are not avalaible in cvs ?

• 1.6 What is the relationship with the "official" maintainers of cvs ?

• 1.7 What are the differences with jcvs, MacCvsClient, MacCvsPro ?

• 1.8 What about Rhapsody ?

• 2 The MacCVS Client

• 2.1 Introduction

• 2.2 What is inside

• 2.3 Binary and Text files

• 2.4 The "import" problem

• 2.5 Getting started

• 2.6 Hacking MacCvs

• 3 The WinCVS Client and Local

• 3.1 Introduction

• 3.2 What is inside

• 3.3 Binary and Text files

• 3.4 The "import" problem

• 3.5 Getting started

• 3.6 Hacking WinCvs

• 4 Summary of CvsGui commands

• 4.1 Cvs Main menu

• 4.2 Cvs Files menu

• 4.3 Cvs Folder menu

• 4.4 Preferences dialog

• 5 Note on dirty files

• 5.1 A little bit of history first

• 5.2 Advantage of "Open Code Software"

• 6 Goodies with MacCvs/WinCvs

• 6.1 CWCVS : a cvs plug-in for Metrowerks (Mac only)

• 6.2 MPW tools (Mac only)

• 7 Annex

• 7.1 Environment variables used by MacCVS/WinCVS

• 7.2 A discussion about MacCVS architecture and the CR/LF problem


About_CVS
1 About CVS

1.1 Where to find CVS ?

The official web site for CVS is www.cyclic.com.

Cyclic Software provides source code, informations and support for CVS.

Before reading this document, you should be familiar with CVS.

1.2 What is CvsGui ?

CvsGui is a set of little applications with a user interface for several platforms (right now only Mac and Windows 32). It is developped and used mostly in Strata Inc. by several developers.

The user interface is not designed to replace the command lines of cvs, but is provided in order to help people to get started with cvs and to take advantages of platforms capabilities (move to trash, multi-files picker, improved interface...).

While developping GuiCvs, we concentrate on speed and reliability.

1.3 Why Cvs and CvsGui ?

The main reason is because we had a very bad time by using a commercial product named ...$ :-.

The second reason is due to cvs itself : cvs is incredibly powerfull, fast, well documented and has a great support with Cyclic Software.

MacCvs was the first version of CvsGui : we finished the port of cvs on Macintosh by fixing bugs and adding features.

Then we did the WinCvs port so the Windows developper can have the same features than the Mac developpers.

Finnally why not in the future a kdecvs, qtcvs, wtcvs, gtkcvs ? You will find a hacking section explaining the common architecture of CvsGui and why it's so easy to make new ports by using the CvsGui architecture.

1.4 Which are the features inside cvs that are not avalaible in CvsGui ?

None. That's because CvsGui uses the cvs code and inherits of all the features of cvs.

1.5 Which are the features inside CvsGui that are not avalaible in cvs ?

CvsGui is designed to add on the top of cvs some cool stuff that people wants. You'll find an ISO8559 encoding support, MacBinary support, extras features and more. But we still try to keep the cvs feeling.

1.6 What is the relationship with the "official" maintainers of cvs ?

We try to keep in touch with them and look for the next version they'll provide. We think we'll be able before the announcement of the next version of cvs to provide some patches useful for all the platforms.

1.7 What are the differences with jcvs, MacCvsClient, MacCvsPro ?

jcvs is a cvs client implemented in Java which has his own format. jcvs is not compatible with MacCVS.

MacCvsClient and MacCvsPro are two cvs client implemented in C++ using PowerPlant with a very nice user interface. Like jcvs, MacCvsClient and MacCvsPro have their own format and are not compatibles with MacCvs.

MacCvs uses directly the cvs code (C code) : so it inherits all the capabilities of cvs. jcvs, MacCvsClient and MacCvsPro use a private code so you can't expect some features to be implemented in thses clients, unless the author implements it (the Mac resources support, the patch support, the compression support, the kerberos support and so on). But you may find in MacCVS some ideas stolen from all of these excellents programs.

1.8 What about Rhapsody ?

Hey, good news : Apple works on a version of cvs for Rhapsody. Should be nice to have a MacCVS for it, isn't it ;-> ?

Note: We are using currently MacCVS on Rhapsody without any kind of problem using the Blue Box.


The_MacCVS_Client
2 The MacCVS Client

2.1 Introduction

The first port on Macintosh was made by Mike Ladwig using CodeWarrior 9, GUSI 1.7.2 and cvs 1.84. It allowed to use MacCVS as a console program or as a MacPerl command.

A second port was made by Matthias Neeracher. He implemented cvs as a MPW tool and used the cvs 1.9 distribution, dmake and CodeWarrior 9. This version works nicely but was never included into the official release of cvs.

Both use the excellent Matthias Neeracher's GUSI Library (for Grand Unified Sockets Library) which allows to port easily Unix code on Macintosh.

Despite of this ports, the cvs implementation for Macintosh is still experimental and only the client side is avalaible.

This current port uses CodeWarrior Pro 3, the MSL library, cvs 1.10, GUSI 1.8.4, Waste 1.3, TCL 8.1 and optionnaly the KClient library (Kerberos authentication, a contribution of Miro Jurisic)

2.2 What is inside

This is my second tentative to port CVS for Macintosh.

The first time I used the port of Mike Ladwig and added a little UI to remember the CVSROOT and provide menu access.

Now using the Matthias Neeracher port, I've added again a little UI and also enhanced or added some capabilities to the MacCVS client to :

• deal with Macintosh files with a resource fork (using a HQX encoding or a MacBinary encoding).

• be scriptable by Apple Events.

• allow to import initial modules directly from the macintosh (see the problems relative to the import command).

• control when adding files : that's because many user make mistakes when adding files and forget to add "as binary" when the file is binary.

• use Internet Config to restore Mac signatures.

• provide a work around for files which auto-modify each time you open it (such as CW Pro 2 projects) : this behaviour is not compatible with CVS which checks for the modification date on your disk. We refer to this kind of files as dirty files

• rewrite the architecture of MacCVS so it uses cvs as a shared library (this has several advantages I discuss in Hacking CVS).

For more informations, look at the Hacking CVS section.

2.3 Binary and Text files

For the macintosh cvs client :

• a plain binary file is a file with a signature different of "TEXT" (for example a Word document .doc) and no resource fork.

• a mac binary file is a file with a signature different of "TEXT" and a resource fork (for example a ResEdit file and most of the mac files).

• a text file is a file with a signature of "TEXT".

MacCVS now supply some controls when adding files : that's because many user make mistakes when adding files and forget to add "as binary" when the file is binary. It will warn you if you are about to make a mistake.

Important : You can force a binary file to be handled as plain binary when you add or import files : that's because sometimes you can accidentaly have a resource fork (like a CKID resource) onto a plain binary file (for example on a CW project, a GIF file...). If you want to add files types to be handled as plain binary, go to the preferences and add a new type.

Note : The resource fork on a text file is ignored. If you want to keep the resource fork of a text file, force to add the file as binary : but you won't be able to use this file on others platforms.

2.4 The "import" problem

The import command in cvs should be seen as inconsistent since it imports initially all the files as text files (so it makes a line feed conversion between the client and the server).

When the initial import is done and all the binary files have the binary attribute, the server and the client agree to not convert this files and everything is working finely.

So the problem appears ONLY during the first import, and cvs user know that the initial import is THE main difficulty with cvs.

Happy-fully, there are some turn-around :

• Make the initial import directly from the cvs server machine (for now on Unix or Windows platforms). But be careful with the line feeds automatic translation when you copy files from and to !

• Use the cvs wrappers file to mark extensions as binary. But you'll still not be able to import from the client since all the files are initially transfered as text to the server.

• Use the -W option of cvs import : the problem is that you have to figure yourself all the extensions to map to a binary format (the -W option works this way : you can tell cvs that an extension, .gif for example, represents a set of binary file). My experience shows you can make a lot of errors this way.

That's why the MacCVS port was implemented for :

• Using the import command of the MacCVS client, you'll be able to have a dialog which tells you the current binary mapping and let you modify it. It detects also a lot of common errors and sometimes warn you about something you may care about.

• When you hit continue, MacCVS will prepare automatically the line command to cvs with the binary wrappers you want to use.

• Finally you can import safely. Both the client and the server will know exactly which files are binary and which one are text.

Very important note : You still have to set the binary preferences in order to force MacCVS to send the files as plain-binary to the server.

Very important note 2 : When you use plain binary files, be sure to have Internet Config well-configured for these files : if not, you may loose your plain binary representation and won't be able to open these files on others platforms (since they would be mac-encoded).

2.5 Getting started

• Be sure to be familiar with the import problem.

• While you use CVS, make sure your Internet Config is always up-to-date when using plain-binary files or if you set the Internet Config option for text.

• Set in the preferences the CVSROOT and the kind of authentication.

• If you create a new module using the import command, use first carefully the report and fix all the errors or conflicts.

• Use the online-help avalaible on most of the menu entries or dialogs by activating "Show ballons" in the help menu (unavalaible on MacCVS 3.1).

2.6 Hacking MacCvs

The MacCvs port consists in :

• A shared library with a MixedMode interface which contains the cvs code and mimic the cvs command line. This architecture allows to re-initialize the globals of cvs as well to separate really the implementation of the client and cvs.

• An application which implements a UI, Apple Events, a console and call the shared library to make the stuff.

What I've done after is to change a little bit client.c to oblige the client to convert the file whenever it sends it to the server. This allows to encode/decode the files in HQX format and also the disable/enable the line feed conversion when the file is flat (no resource fork) and is not a text file.


The_WinCVS_Client_and_Local
3 The WinCVS Client and Local

3.1 Introduction

The first port on cvs on Windows was done by Cyclic Software and is excellent. Why ? Because they ported also the server and local capability and made a great work to deal with the non-case sensitive Windows file system.

Except some hacks inside the cvs code to take advantages of the cvs features, WinCvs does not change the cvs source code : all it does it is to compile cvs as a dll (Dynamic Linked Library).

3.2 What is inside

You will find inside WinCvs a set of commands (all have a on-line help entry) that you can use to manage source repositories.

WinCvs looks like that :

• An ouput console : you see all the messages of WinCvs, cvs client and cvs server so you can control that everything is going fine.

• A preferences dialog : the CVSROOT, authentication...

• A set of common menu commands (Copy, Save, Paste...).

• A set of cvs menu commands.

• A line commands dialog provided when the menu command is not avalaible.

3.3 Binary and Text files

You have to be very carefull about the usage of binary files : read the cvs documentation carefully.

WinCvs provides two ways to add files to a repository :

• Add files...

• Add binary files...

WinCVS now supply some controls when adding files : that's because many user make mistakes when adding files and forget to add "as binary" when the file is binary. It will warn you if you are about to make a mistake.

3.4 The "import" problem

The import command in cvs should be seen as inconsistent since it imports initially all the files as text files (so it makes a line feed conversion between the client and the server).

When the initial import is done and all the binary files have the binary attribute, the server and the client agree to not convert this files and everything is working finely.

So the problem appears ONLY during the first import, and cvs user know that the initial import is THE main difficulty with cvs.

Happy-fully, there are some turn-around :

• Make the initial import directly from the cvs server machine (for now on Unix or Windows platforms). But be careful with the line feeds automatic translation when you copy files from and to !

• Use the cvs wrappers file to mark extensions as binary. But you'll still not be able to import from the client since all the files are initially transfered as text to the server.

• Use the -W option of cvs import : the problem is that you have to figure yourself all the extensions to map to a binary format (the -W option works this way : you can tell cvs that an extension, .gif for example, represents a set of binary files). My experience shows you can make a lot of errors this way.

That's why the WinCVS port was implemented for :

• Using the import command of the WinCVS client, you'll be able to have a dialog which tells you the current binary mapping and let you modify it. It detects also a lot of common errors and sometimes warn you about something you may care about.

• When you hit continue, WinCVS will prepare automatically the line command to cvs with the binary wrappers you want to use.

• Finally you can import safely. Both the client and the server will know exactly which files are binary and which one are text.

3.5 Getting started

• Be sure to be familiar with the import problem.

• Set in the preferences the CVSROOT and the kind of authentication.

• Use the online-help avalaible on most of the menu entries or dialogs by clicking on the arrow located on the right of the toolbar which enables a sensitive help.

3.6 Hacking WinCvs

The WinCvs port consists in :

• A dll which contains the cvs code and mimic the cvs command line. This architecture allows to re-initialize the globals of cvs as well to separate really the implementation of the client and cvs.

• A MFC application which implements a UI, a console and call the dll to make the stuff.


Summary_of_CvsGui_commands
4 Summary of CvsGui commands

4.1 Cvs Main menu

Main cvs commands : Most of the time you will use only the update and commit commands.

Update folder...

Merge recursively on your disk your changes with the cvs repository. Use "Commit folder..." to commit your changes INTO the cvs repository.

Commit folder...

Commit your changes INTO the cvs repository. You may use "Update folder..." first to merge locally on your disk your changes with the cvs repository.

The no recursive option let you commit a folder without the sub-folders.

Preferences...

Use this option to change settings like the CVSROOT, the read-only option, the TCP/IP compression and many other things...

Command line...

Allows to enter a cvs command line (like "cvs rtags -b -r arev arevbranch") when ever the cvs command is not avalaible thru the menus.

Note that you don't have necessary to specify a local directory. That's because some commands (like rtags) execute only on the server. For the most of the others commands, you need to specify where the command is executed.

Checkout module...

Use this option to checkout from scratch a new module on your disk. You need to give the complete path of the module on the server : for example : "myprog/mysubfolder1/mysubfolder2"

The no recursive option let you commit a folder without the sub-folders.

Import module...

Use this option to create a new module into the cvs repository located on the cvs server machine. After importing, you should use the "Checkout module..." command to start using this new module.

You need to give the complete path of the module on the server : for example : "myprog/mysubfolder1/finalPlaceForTheModule"

Refer to the explanation of the import problem (Mac import or Windows import).

The vendor and release tags are usefull whenever you'll want to get back to the initial version or create a branch. Refer to the cvs documentation for more details.

Login...

Use this option when you want to login for the first time to a cvs server machine. You should need to login ONLY if the kind of authentication in the preferences is "pserver" (password authenticate).

4.2 Cvs Files menu

Menu with cvs commands which apply on files.

Update files...

Merge locally on your disk your changes and the changes of the cvs repository so you can use the "Commit files..." command to commit your change on the cvs server repository.

Commit files...

Commit your changes into the cvs server repository. You may need first to "Update files..." in order to merge your changes with the cvs server before you can commit them.

Diff files...

Show the difference between your files and the files located on the cvs server repository : this way you can check the changes with the remote server.

Log files...

Ask the server to tell the history of the files (comments, revision number, tags and so on...)

Add files...

Mark the files to be added to the master repository located on the server. These files will be really added AFTER you did commit them. In order to add files enclosed into a new folder, use the "Add folder..." option first on the folder.

(Windows only)

Add binary files...

Same as the previous but add binary files.

Remove files...

Mark the files to be removed from the master repository located on the server. These files will be really removed AFTER you did commit the folder where these files were located.

Status files...

Ask the cvs server to tell the status of the files (changed locally, unchanged, current status, locks, and so on...).

Cancel changes...

It is not a real cvs command but it's provided for convenience. This option will send to trash the files in the trash (you can on Macintosh use the "Put Away" option of the Finder to recover them) then will ask the cvs server to update these files.

4.3 Cvs Folder menu

The cvs commands which apply on a folder.

Add a folder...

Whenever you add files to the repository, you need first to add the folders.

Diff a folder...

Print the differences recursively with the cvs remote repository for all the files enclosed in the folder.

4.4 Preferences dialog

The cvs commands which apply on a folder.

The CVSROOT field

It should be something like alexp@cvs.strata3d.com:/cvsroot. Never enter the ":pserver:" or ":kserver:" directly, use the radio buttons instead.

Checkout read-only

Turn-on if you want the files created by MacCVS to appear locked in the finder.

Prune empty directories

If On, this option will automatically remove empty folders when you update a module.

TCP/IP compression

When this option is on, the server and MacCVS compress informations so they use less bandwidth : use only if you have a slow (modem) connection with the server because it causes the server to work much more.

Quiet mode

If on, cause the command to be somewhat quiet; informational messages, such as reports of recursion through subdirectories, are suppressed.

Control when adding files

Most of the errors using cvs are related to binary files. Since the client and the server usually convert the line feeds, it's important when you add a file to specify if you add a binary file or a text file. This option will help you to not make errors by parsing the file you add and check if it's a binary file, if the file has the good line feed for the machine and so on...

".rhosts" authentication

If On, the cvs server will authenticate MacCVS regarding the IP number of the machine running MacCVS : you should have a proper ".rhosts" file on the server account with an access 0600 which contains something like "192.10.10.37 alexp" or "alexpmac.strata3d.com alexp".

":pserver:" authentication

If On, the cvs server will authenticate MacCVS regarding a password located in the cvs administration file "passwd" on the server : you should have a proper entry for you in this file, something like  "alexp:X5gFtdhaDke93j".

":kserver:" authentication

If On, the cvs server will authenticate MacCVS regarding a Kerberos authentication session. Refer to your adminstrator to know how to use Kerberos.

Modify the server ports & name

Regarding the authentication you use (pserver, kserver...) you can change the default TCP ports and name cvs uses to reach the server. Refer to the cvs documentation for more details.

ISO8559 translation on text files (Mac only)

If on, MacCvs will translate (only on text files) the mac characters encoding into a standard ISO8559_xxx encoding when it sends the file to the server. It will obviously translate back ISO8559_xxx characters encoding into the Mac encoding when getting a file from the server. This option is usefull if you use accentuations in text files because the accents will remain readable on others systems (Windows, Unix). Only ISO8559_1 Latin 1 (Western Europe) is implemented right now. Thanks to Jim Matthews to provide the ISO8559_1 table in the Fetch program.

Use Internet Config for text files (Mac only)

MacCvs usually asks Internet Config, whenever it creates a binary file, the default type and creator signature for the file. So by using Internet Config, you can tell MacCvs that, for example, all the files ending by the extension .mcp have the file type/creator set to MMPr/CWIE. By using this option, you can extend this behaviour to the text files so you can for example map a type/creator for the .c, .h, .html...


Note_on_dirty_files
5 Note on dirty files

5.1 A little bit of history first

We started using cvs in our company when we turned to Code Warrior Pro 2. We realized Code Warrior had a very annoying bug and we were unable to find a work around with Source Safe. When you lock the projects, and if you have a very deep and complex set of projects, the projects behave badly and recompile each time, plus, they take a long-long time to open...

So we thought : "OK, let's unlock them". Then the horror : the project were auto-modifying themselves when unlocked. The problem appeared when everybody tried to check-in the first time : all the projects were modified each time, resulting in corrupting and increasing fastly the size of the repository.

5.2 Advantage of "Open Code Software"

Then I suggested we tried cvs so we could hack cvs by making it behave the way we expect.

We came up with this solution :

• 1. The file with the extension .mcp will be a dirty file.

• 2. The file will be ignored by cvs, like .cvsignore does, unless the user asks to explicitely to something with this file : the method to tell cvs to commit a dirty file is to ask to commit this files and only this file (a recursive commit will ignore the dirty file).

• 3. The only way to add, remove, commit, merge a dirty file is to explicitly invoke cvs with this operation and with only this file.

No need to tell you we'll be happy when finally Metrowerks will have fixed this bug :<...


Goodies_with_MacCvs_WinCvs
6 Goodies with MacCvs/WinCvs

6.1 CWCVS : a cvs plug-in for Metrowerks (Mac only)

We use it all the time and it's nice !

Since Richard Wesley, the author, is American, you'd better go directly to  http://www.electricfish.com/hawkfish/CWCVS/CWCVS.html for a good explanation in plain English...

Only the Mac side is provided right now and it is a shareware.

6.2 MPW tools (Mac only)

Description

Right now, two MPW tools are provided with MacCVS with complete source code :

• cvs : the same exact code which uses MacCvs.

• cvslog : invokes "cvs log" but filters the ouput for a more convenient use. Basically it allows to retrieve a set of files by date/user/branch/revision/tags.

Getting started with MPW cvs tools

Place these tools into the tools folder of MPW.

To get the usage of these tools, simply type the command + enter.

All you need after is to read how to set-up environment variables for cvs, then you can use it straight. Most of the time you don't even need to set any variable because cvs keeps track on your disk about the informations it needs.

Important note : these tools require to find the shared library provided with MacCVS. So you can place the shared library in the system folder or create two aliases of thse libraries at the same directory level than the mpw tools.


Annex
7 Annex

7.1 Environment variables used by MacCVS/WinCVS

Most the communication of MacCVS & WinCVS with cvs is done thru environment variables.

The cvs documentation will tell you about these variables. We'll refer here only to the most used ones or to the one only used by WinCVS/MacCVS.

The reason you may want to be aware of this variables is because you want to use the AppleEvent capability of MacCVS or the MPW tools.

Mac Note : Some of the variables are noted as "globals" : it means that if you do not define it, the MPW tool or MacCVS by AppleEvent will get these values directly from the MacCVS preferences. So you don't need to define them, unless you want to overide these values.

• CVSROOT : your CVSROOT, something like alexp@cvs.strata3d.com:path/to/repository/.

• CVSREAD : if set to "yes" or "1" or any non-null value, means you want to check-out read-only.

• USER (Mac only) : the user account you use on the remote server.

• LOGNAME : overides the result of getlogin(). Set it on Windows when your cvs account is different from your login account (for example "administrator").

• CVS_GETPASS (MacCVS/WinCVS only) : used by CvsGui to prompt a password for the pserver authentication. You have only to define it the first time you login to the cvs server (see cvs login). After don't define it, that's not safe to define a clear password. cvs stores the password in a scrambled file named ".cvspass". You might prefer to login in MacCVS/WinCVS before so you don't have to use it.

• HOME (global) : used by cvs to get globals settings files like the .cvspass, .cvsrc... You need it most of the time so it's usefull to define it to your preference folder on Mac and somewhere else for Windows. Mac only : the '@@@' trick have been removed and you should give now a regular Mac path instead (e.g. System:System Folder:Preferences:). The default is automatically set to your preference folder.

• IC_ON_TXT (global) (Mac only) : Use Internet Config services also on text files when set to "1".

• ISO8859 (global) : use ISO-Latin-1 encoding on text files when set to "1".

• CVS_PSERVER_PORT (global) : default to null. Overides the default port for pserver authentication (usually 2401).

• CVS_RCMD_PORT (global) : default to null. Overides the default port for rcmd authentication (usually 514).

• CVS_SERVER (global) : default to null. Overides the default cvs server's name (usually "cvs").

• MAC_DEFAULT_RESOURCE_ENCODING (global) : the value can be either "HQX" or "AppleSingle".

• MAC_BINARY_TYPES_PLAIN (global) : the value is a semi-colon separated set of Mac signatures that you want to be turned into plain binary  on the server (ex: "MMPr;JPEG"). This option will force to ignore the resource fork.

• MAC_BINARY_TYPES_HQX (global) : the value is a semi-colon separated set of Mac signatures that you want to be turned into hqx format on the server.

• MAC_BINARY_TYPES_SINGLE (global) : the value is a semi-colon separated set of Mac signatures that you want to be turned into AppleSingle format on the server.

7.2 A discussion about MacCVS architecture and the CR/LF problem

email of Miro Jurisic to Alexandre Parenteau

I think it's very important that you know what was going on, so I'll write a hopefully clear description here... if you have any questions please ask :)

• 1. How MSL writes to a file: When MSL writes to a disk file, every will be replaced with , and every will be replaced with , if the file was _not_ opened in the binary mode. If the file was opened in binary mode, this replacement does not occur. The exact place where this replacement occurs is in __flush_buffer (buffer_io.c). This routine is called from fclose to write the data out before a file is closed.

• 2. How MSL writes to console: When MSL writes to a console, it treats it as an ASCII file. This means that when __flush_buffer is called for the console, conversion occurs, and then WriteCharsToConsole is written.

• 3. What 'Map newlines to CR' does: If this is turned on in language settings, compilers will switch and when compiling your code

Now that we got those clearly written, let's see what the cvsLib glue does:

cvsLib has its own WriteCharsToConsole which calls into MacCVS. This is setup when the library is loaded (in function loadCVS). Specifically, cvsLib's WriteCharsToConsole calls MacCVS' consoleout. consoleout does one of two things: if MacCVS is being run via AppleEvents, it writes output to the reply event. Otherwise, if calls fwrite on stdout to write the data to the console.

Okay, so the way that the 3.0b3 code works is like this:

• Case 1: MacCVS writing to the console : MacCVS calls printf (or fprintf/write on stdout/stderr). Since MacCVS is compiled with 'Map newlines to CR' off, there is exactl one conversion going on, and that is MSL converting when flushing the console buffer. That works fine.

• Case 2: cvsLib writing to an ASCII file (the broken case) : cvsLib calls fwrite/fprintf. Since cvsLib is compiled with 'Map newlines to CR', conversion occurs _twice_: first, at compile time, becaue of the settings; second, at the time __flush_buffer is called, because we are writing to an ASCII file. This causes files written by cvsLib to have the wrong linebreaks.

• Case 3: cvsLib writing to the console (the tricky case) : cvsLib calls fwrite/fprintf/printf on stdout/stderr. First conversion occured at compile time. When it is the time to flush the console buffer, __flush_bufer is called. It performs a conversion and then calls WriteCharsToConsole. This is the glue WriteCharsToConsole, which calls consoleout. consoleout calls fwrite, which eventually calls __flush_buffer again. __flush_buffer performs another conversion, and then calls the real WriteCharsToConsole. Since there were 3 conversions happening, the overall effect is that is converted to and vice versa.

Hopefully, now you see where the problem is (this is where I was late Monday afternoon).

I decided that it was not necessary to go through the process of loading and unloading cvsLib all the time (I was not aware of the problems that would cause), so I rearranged the libraries between fragments. I fixed the problem, but I introduced a new problem: cvs was crashing reproducibly the _second_ time a cvs command was invoked. This is where I went to bed on Monday.

On Tuesday, I woke up and started stepping through the code to determine why the garbage collector is blowing up. I traced the problem to some globals being freed twice. What was going on? Since I changed the linkage so that cvsLib was only loaded once, its globals were initialized when MacCVS was started up. The first time cvsLib was called, it allocated space for some strings and set some globals to point to them. Before returning it freed those globals - but it didn't set them to NULL after freeing them. The second time cvsLib was invoked, some of those globals were freed again (because they weren't NULL), which was wrong.

At that point I realized that the library must be loaded and unloaded repeatedly; I also got your email in which you said that GUSI should be loaded and unloaded repeatedly because it contains those static destructors that clean up MacTCP state.

After reading that I stepped away from my computer, went to the drawing board, and concluded that it is impossible to do anything but to have a closer look at the cvsLib glue, and see whether the hidden conversion can be eliminated. This turned out to be far simpler that I could hope for: instead of calling fwrite from consoleout, I called directly WriteCharsToConsole, thereby avoiding the extra conversing that was the problem. Of course, I also had to turn 'Map newline to CR' in the settings, to even everything out. The result is that:

• Case 1: MacCVS writing to the console : Nothing changed

• Case 2: cvsLib writing to an ASCII file (the fixed case) : Since there is only one conversion going on (in __flush_buffer), the files are written correctly.

• Case 3: cvsLIb writing to console (the less tricky case) : Two conversion were removed: the compile-time conversion, and the hidden conversion in the cvsLib glue. Therefore, nothing changed - the console still works perfectly.

Whew!

Please let me know if you are unclear on this... now I have a good view of the problem and how I fixed it, and some really great insight on how pieces of MacCVS fit together.

...

cheers,

meeroh