TweetFollow Us on Twitter

Debugging Terminal
Volume Number:12
Issue Number:10
Column Tag:Communications Toolbox Connections


A Poor Man’s Debugging Terminal

By Mark Chally, Covina, California

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

The purpose of this article is to implement a useful C-language debugging tool and to discuss the basics of Apple’s Macintosh Communications Toolbox, including methods for using the Connection Manager. In the process, we will also create a base for a more powerful application that may be presented in a later issue or one that you may develop on your own.

We will begin by discussing the Communications Toolbox (CTB), what it’s good for, why you need to know about it, and why you want to read about it here. Next, we will summarize the use and structure of the CTB.

With a general understanding of the CTB under our belts, we will be ready to discuss the purpose and use of DebugTerm. Once we know what DebugTerm is expected to do, we can walk through the code and see how it takes advantage of the CTB. In the penultimate section, we’ll take DebugTerm for a test spin and see the results of function calls we make.

Finally, we will discuss problems that may occur when using DebugTerm, and possible enhancements to it. We will summarize what we have accomplished and ponder future directions.


The Advent of the Communications Toolbox

Before Apple created the CTB, there were already many ways to connect the Macintosh to the outside world. Being so easily connected was a fabulous feature. Even the first Macs had built-in networking, and counting connections to LaserWriters, the Mac was the world’s most networked personal computer.

However, as the variety of ways to connect the Macintosh increased, so did the difficulty encountered when implementing programs that connect in various different ways. Even before the Mac // arrived, it would have been useful to have a single library which would allow a program to communicate through whatever connection the user desired, using the same high-level code for each type of connection. The application programmer would not have needed to be concerned with the intricacies of any specific connection type. Ideally, it would have been better to avoid having to deal with the complexities of different brands of modems, too. Making matters worse, the Mac // came out, with an open architecture through which a user could add internal modems and multiple serial connections. Apple had a real mess on their hands!

The Macintosh Communications Toolbox Arrives

As Apple mapped the way to System 7, they apparently became aware of the morass that had grown-and found a gem which was already under development. As the forward to Inside the Macintosh® Communications Toolbox (Inside CTB) indicates, “Initially conceived as a better way to engineer MacTerminal 2.0-it enabled MacTerminal to support new protocols without having to be revised-the Communications Toolbox has evolved into an integral component of our system software.”

With the CTB, programmers can now write programs which connect to other computers using tools for connection, terminal emulation and file transfer protocols whose intricacies the programmer is not required to know. In theory, with a well-designed program and tools, a networking protocol that the programmer was unaware of could be supported, in addition to a slew of modems and file transfer protocols.

Given such a powerful API (Application Program Interface), communications features can be uniformly and reliably added to just about any type of application, with minimal effort. When I learned this, I was eager to begin taking advantage of the CTB.

Obstacles for the Communications Toolbox, or Why read this article?

My enthusiasm for the CTB was tempered by some harsh realities. I found an early SDK (Software Developer’s Kit) woefully inadequate. It came with only a smattering of example code and unfinished documentation. Perhaps the current SDK is more useful. Until now, MacTech had never printed an article on the subject, and I saw none elsewhere. Nor did I find available on-line source code examples satisfactory. The code that I used to enhance my understanding was inspired by a simple Pascal program called Surfer, from Apple’s ETO (Essentials•Tools•Objects) mailing. Once Inside CTB was completed, I used it as documentation, which I highly recommend.

Another topic of confusion was whether or not Apple and vendors would continue to support the CTB. Since programmers did not receive it warmly, and other parts of System 7 never really “caught on,” some questioned whether it would become like PowerTalk and other orphaned technologies. It was pleasing to see that, though there have been a few problems implementing Open Transport, it seems to support the CTB seamlessly, which still works fine with the serial port, as well as over networks too. Modem manufacturers’ responses have been mixed. Some companies such as Global Village have done a good job, but some others have not. One company’s support technician seemed peeved to “have to write Apple’s code for them.” Despite a rough start, the future looks good for the CTB.

If you think you can recognize every program using the CTB, you may be surprised. There is a very recognizable user interface, but there is also an API which lets programmers implement the user interface themselves. The CTB is not difficult to learn to program. Although I am far from a CTB expert, I was able to put together the code I needed fairly quickly.

What is the Communications Toolbox?

Included as an extension to System 6 and built into System 7, the CTB consists of four managers and a set of utilities. Together, they provide a means to control three separate aspects of communications: connections, file transfers, and terminal emulation. Inside CTB has some pretty pictures-most of which are probably not necessary here. In the interest of clarity, however, here is a representative one:

Figure 1. The Realm of Macintosh
Communications Toobox Influence

Normally, the CTB and the client application do not interact much with the user or handle the details the managers oversee. That responsibility is handled by communications tools. Standard tools come with the CTB; users can obtain various others from third parties, and drop them into their extensions folder to use them with programs that employ the CTB. There are public domain, shareware, direct mail, and various other commercially available tools. Others are distributed with their client applications. The managers provide the API and the utilities handle housekeeping chores.

The Connection Manager provides an API through which the client application (your code) can use a connection tool. Each, such as the Serial Tool, Apple Modem Tool and AppleTalk ADSP Tool, allows the client application to connect through the specific medium in which the tool specializes. The Serial Tool, for example, is responsible for transferring data only through the serial ports. The Apple Modem Tool, on the other hand, has to know about the complexities of modems-which it does with the help of plug-in modules that manufacturers provide to meet Apple’s specifications. The AppleTalk ADSP Tool, provides an AppleTalk network connection using the ADSP protocol. Other connection tools exist, such as an ISDN tool, according to Inside CTB.

The Terminal Manager provides an API through which the client application can use terminal emulation tools. The TTY Tool behaves as a dumb terminal, as do most terminal programs in their simplest modes. An additional standard terminal emulation is the VT102 Tool, which is somewhat more complex. Tools to emulate other standard terminals, such as IBM 3270, are also available.

The File Transfer Manager provides an API through which the client application can use file transfer tools. Standard tools include the Text Tool, which sends and receives text straight through the connection, as archaic text file captures did, and the XMODEM Tool. There are tools that support other file transfer protocols, such as YMODEM, ZMODEM, and Kermit.

Users are typically unaware of the structure through which they are communicating, as Apple apparently intended. In most cases, they only know that they are using a familiar user interface for connections, file transfers, and terminal emulation. Programmers should seriously consider allowing the user to have access to the standard interface, as opposed to the scripted interface described in Inside CTB as Custom Configuration. Using the scripted interface allows the application to tell the CTB that it knows what the user needs to see. Aside from being beyond the scope of this article, it deprives the user of the look and feel he expects and may limit the availability and features of other tools if the application is implemented too narrowly. We will not cover the scripted interface.

The Communications Resource Manager provides a specialized interface to Macintosh resources for CTB purposes. It has some useful calls related to structures that are unique to the CTB. Rounding out the CTB package, the Communications Toolbox Utilities provide standardized routines that a client application may need for using the CTB.

A Poor Man’s Debugging Terminal


The purpose of DebugTerm is to use the CTB to pipe information from an application under development to some other destination, such as a computer, terminal, or other device. Because the CTB is so handy at connecting to things, it makes a good method for piping information from your running program to...whatever! In fact, the CTB is so flexible, DebugTerm can be used to pipe information to a terminal program running on the same machine! But why do these things?

As I began learning Macintosh programming, I recall hearing of devices called hardware debuggers or debugging terminals. Not having seen one, I imagined them to use software stubs to send the status of a computer environment to the outside world, where information could be inspected using a terminal or device. Perhaps Macsbug grew out of one of those stubs. Using such an item, one might evaluate the status of an environment with little or no effect on it. At first, the Mac’s simplest debugging tool was a function, DebugStr(Str255 theString), which allowed the program to break into the debugger to display theString. The program then patiently waited for the operator to work with the debugger.

Of course, that was years ago, and development systems are more advanced today. I use source code debuggers, but sometimes I distrust them. They may provide dubious information, or change the program environment, or perhaps my program crashes before I can examine the suspect variables. Of course, I still use source code debuggers. I just do not always want to depend on them. I am not interested in using low level debuggers to examine disassembled code either.

The first inspiration for DebugTerm may have come from a MacTutor article written several years ago by Bob Gordon. Bob came up with a nifty idea for outputting debugging information to a Mac window, other than the STDIO window, so that the ANSI environment did not need to linked in. Much of debugf(), a major function within DebugTerm, was taken from The C Programming Language, 2nd Ed., (K&R) page 156. Kernighan and Ritchie used an example function to show how logic similar to printf() can be simulated using the ANSI C header stdarg.h.

DebugTerm, the C Source File

DebugTerm is a C source code file that can be compiled into an application under development. Before output can be sent, a connection must be opened using OpenDebugTerm(). While the connection is open, the simple DebugTerm() call can be used to send a Pascal string through the connection. The more flexible debugf() can be used to send formatted output through the connection in a way that mimics the C STDIO library’s printf() function. Although modest in comparison to printf(), debugf() is quite useful and easily extensible for various programming needs. The connection should be closed using CloseDebugTerm() afterward. If any of the functions encounters an error, it returns the error it tripped on-either by passing through a CTB or OS error, or one listed in DebugTerm.h.

OpenDebugTerm() is used to initialize DebugTerm and prepare it to send information to the chosen recipient. It requires three Boolean parameters: async, initCTB, and prompt, and returns an OSErr.

async indicates whether the connection will take place using asynchronous calls. If the object is to connect to a terminal program on the same machine, DebugTerm must use asynchronous calls. Otherwise, synchronous calls are probably best. Asynchronous calls allow the CTB to carry out requests in the background instead of while you wait, but the programmer assumes additional responsibilities (more on this later).

initCTB indicates whether to initialize the CTB for use. The value true is usually necessary, since the CTB managers must be initialized before being used. It is possible, however, that you may wish to use DebugTerm to debug a program which already uses and initializes the CTB. In this case, initCTB would be false. If a true value is provided for this parameter, and for some reason DebugTerm has already initialized the CTB, it will remember that it has, and refuse to reinitialize it.

prompt indicates whether to pose the Connection Settings dialog on opening, or use the parameters saved from the last session. If DebugTerm cannot find a preferences file in the folder enclosing the application being developed, or the tool changes, it will pose Connection Settings-even if prompt is false.

When called, OpenDebugTerm() begins by initializing the CTB, if the initCTB parameter is true, and it has not yet done so during the current execution of the program. It defaults to the information in the preferences file it finds in the application folder. If it finds no preferences or they are invalid, it chooses default information for the first tool it finds in the Extensions folder. If the prompt parameter is true, or the preferences are invalid or not found, the Connection Settings dialog (Figure 2) is posed, showing the default settings.

Figure 2. Connection Settings Dialog,
with the Modem Tool’s options

If the Cancel button is pressed to dismiss the Connection Settings dialog, no preferences are written and no connection is established. If the OK button is pressed, the results of any change in connection tool choice are written to the preferences file, which is created if it does not yet exist. Finally, a connection is established according to the Connection Settings dialog, or according to the preferences, if the dialog was not posed.

CloseDebugTerm() closes the connection that OpenDebugTerm() established, and deallocates resources used. It is smart enough to avoid trying to close itself a second time if it has already been closed, but it has no way to close itself if the program quits without calling CloseDebugTerm(). Therefore, it is harmless to put a call to CloseDebugTerm() in the Exit() function of the main program and it is probably a good idea, since abandoning a connection confuses the CTB. CloseDebugTerm() requires no parameters, and returns an OSErr.

DebugTerm() is a simple procedure that requires a single Str255 parameter, and returns an OSErr. Actually, it can receive a Str255 or any “Pascal string,” meaning it understands a call like:

DebugTerm(“\pHello, World!\r\n”)

DebugTerm() responds by sending the supplied string through the current connection.

debugf() is a more complex procedure which could be described, behaviorally speaking, as the progeny of DebugTerm() and printf(). Some powerful and really stupid things can be done with debugf(), just as with printf(). Most C users will recognize printf() as the STDIO function used liberally by most non-Mac programmers and rarely by Mac programmers. Nevertheless, it is one of the “way cool” features of C. It is C’s steroid-laced equivalent to Pascal’s Write statement. Of course, the previous sentence was a heresy for which I will pay stiff penance to the C/UNIX gods.

debugf() takes a variable-length parameter list. The only required parameter, a format string, is a C string. Each of the parameters that follow the format string corresponds to a ‘%’ embedded in the format string. For each ‘%’, which is followed by a specifier, there must be an additional parameter of the expected type. If not, you too must pay stiff penance to the C/UNIX gods, or the Bomb Box altar, or Macsbug gods, or the gods of whatever debugger you use. If the first string contains no ‘%’, no additional parameters are expected or used (and of course you could just as well be using DebugTerm()). For each ‘%’ embedded in the format string, the character following it is a specifier, which indicates the type of data contained in the corresponding parameter. This specifier is not case sensitive. Horrible things may happen if you specify the wrong type, so be deliberate. Following is a list of valid types for the parameters:

P Pascal string, whose first character is a length byte n, and is followed by n additional characters.

C C string, containing an arbitrary number of characters, terminated by the null character.

I int-whatever that means to your development system, short or long-it better be an int (signed) to your compiler.

S short signed integer data type.

L long signed integer data type.

If the specifier is not one of those listed above, the specifier itself is sent, having the effect of ignoring the ‘%’ and leaving the parameter to be processed with the next ‘%’. The output from debugf() is such that the string in the first parameter is sent through the connection, with the text version of each following parameter replacing its corresponding ‘%’ in the first parameter. Therefore, if:

pstr = “\pRocko”
cstr = “had an accident. He fell on his stiletto”
aShort = 47

the following call:

debugf(“%% %p %c %s times.”, pstr, cstr, aShort)

would produce the result:

% Rocko had an accident. He fell on his stiletto 47 times.

Synchronous vs. Asynchronous Calls

Some CTB calls can be executed either synchronously or asynchronously. Synchronous calls are performed while you wait. Asynchronous calls are performed in the background by the CTB tool. In a nutshell, if you choose to perform an operation synchronously, you must wait for the operation to complete or fail before your process can continue. If you do this in a cooperatively multitasking environment such as System 7, almost the entire system will wait for an answer from the tool, which will probably wait for an answer from a device-or another program on the same machine.

Synchronous calls are probably acceptable for DebugTerm in most situations. You can usually wait for DebugTerm to finish sending information before your program resumes. However, if DebugTerm is attempting to synchronously connect to or send information to a terminal program on the same Mac, you will find that there is no apparent response. The reason for this is that no cooperatively multitasking process is allotted any time by the CTB tool unless you call the tool asynchronously. If no other process is allotted any time, a terminal program running on the same machine has no chance to “notice” it is being addressed until the process you have started “gives up.” Therefore, unless you are a masochist, you will want to use DebugTerm asynchronously if you intend to send information to a terminal program on the same machine.

Asynchronous calls have a drawback. When you call them, they tell you everything is fine-but they do not know yet, because they answer before attempting to do what you asked! For this reason, routines that operate asynchronously expect to call a completion routine, which can find out how everything went, once the tool has completed or failed the process. The completion routine supplied with DebugTerm is an empty function. Much like elected government officials, it is only there to fulfill the requirement of being there. Therefore, you will want to use DebugTerm synchronously if you do not intend to send information to a terminal program on the same machine. Completion routines should always have a mechanism for trapping and/or reporting errors-that is what they are for. Of course you can use this one as is, because you get to suffer if you experience an error and you do not know why.

Source Code Walkthrough

DebugTerm and TestDebugTerm were implemented using Metrowerks C with CodeWarrior Integrated Development Environment (IDE) 1.4, so a CodeWarrior IDE project file has been provided. It has only the standard MacOS.lib linked in. If you are using another development system, your default project preferences should work. The code was intended to be compatible with Macintosh development systems, but your mileage may vary. You may wish to have a printed copy of the source code, or to view it on your monitor alongside the article as we walk through it.


This file contains declarations necessary for both DebugTerm and the client application. Literals have been defined to make Boolean parameters more readable. Some error codes have been defined for DebugTerm to return if an unreasonable request is made but does not cause a system error. If the error returned by DebugTerm is in this range, it is probably the situation noted here, not a system error. An effort was made to choose a range not found in the system error list. The file is completed by a list of prototypes for the functions externally available to the client application:

OSErr OpenDebugTerm(Boolean async, Boolean initCTB,
 Boolean prompt);
OSErr CloseDebugTerm(void);
OSErr DebugTerm(Str255 outStr);
OSErr debugf(char *fmt, ...);

If you are concerned about the readability of your code, consider using the literals supplied, instead of true or false, for OpenDebugTerm() parameters. The line of code:

OpenDebugTerm(kAsync, kDoInitCTB, kDoPrompt);

says much more than:

OpenDebugTerm(true, true, true);


This file contains mostly resource IDs and offsets used to implement TestDebugTerm, the application we will use to demonstrate DebugTerm.


This file contains the brains of DebugTerm. It holds the source code for the externally declared functions, as well as the ones declared and used internally. Some of the functions, many of those declared as static, are simply utilities to make the business of writing the program easier, while others relate to the use of the CTB. Here, we will discuss the latter ones, in the order you will find them in the source code.

CMCompletion() is a function whose address must be furnished when making asynchronous calls to the CTB. It is included in DebugTerm.c to make asynchronous calls possible. Asynchronous calls, as opposed to synchronous calls, cause work to be performed in the background by the CTB tool, while asynchronous calls are performed while you wait. The difference and their use, and a warning about leaving this and any other completion routine “empty” are discussed in detail earlier in this article. Please do not distribute a product with a useless completion routine just because you saw it here!

CMSendProc() gets called any time DebugTerm sends information through the connection. Originally written as a wrapper for the CTB function CMWrite(), it became more sophisticated to support asynchronous calls. The first thing it does is validate that gConn points to an actual connection record.

Used synchronously, it simply calls CMWrite(), and if all the requested characters are not written, it calls it again, until they all are. If at any time in the process it finds an error, it aborts, reporting the error code.

Used asynchronously, CMSendProc() has extra duties. Before each call to CMWrite(), it checks, by calling CMStatus(), to be certain that there are no write or open operations pending. Because asynchronous calls are in use, the tool may not have completed its last assignment, or even begun. If it finds that a write or open operation is still pending, it calls WaitNextEvent() to kill about one sixtieth of a second before resuming. Because it calls WaitNextEvent(), other processes in the environment get housekeeping time-including your terminal program, if you are using one on the same machine to listen to DebugTerm. Notice that it uses a nullEvent mask to avoid getting any real events because it does not actually deal with them.

Another extra duty involved when sending asynchronously is finding out how many bytes were actually sent. When CMWrite() is called asynchronously, it returns a bogus value for the number of bytes sent, because it does not actually know yet. Therefore, CMSendProc() continuously monitors the cmDataOut element of gConn’s asyncCount array for the correct answer.

InitCTBStuff() is called by OpenDebugTerm() before it does any other business, and calls the CTB procedures responsible for initializing the various managers used. However, before taking the caller’s word, it checks to see if it has initialized the CTB since the program was launched. If it has, it ignores the call. Otherwise, it calls InitCTBUtilities(), InitCRM() and InitCM(), which must always be called before another connection manager routine is called.

FindTool() is called by ReadPrefs() to get the ID and name of the tool last used by DebugTerm. It does this by looking in the preferences file’s resource fork for the one with the ID and type whose literals are in the header file. When it finds the resource, it copies the string, containing the name of the tool last used. If there is no resource file open (probably because it does not exist yet), or the tool is no longer there (CMGetProcID() fails) then it calls CRMGetIndToolName() specifying the connection tool class kClassCM, and an index of 1, to get the name of the first tool the Connection Manager finds. If it still cannot find a tool, it returns the error kToolNotAvailErr.

ReadPrefs() is called by OpenDebugTerm() to find the preferences written the last time DebugTerm was executed. It first calls OpenPrefsFile(), instructing it to avoid creating the file if it does not find it (WritePrefs() will create the file later if necessary). Even if the preferences file cannot be opened, FindTool() will provide the name of the first tool the Connection Manager finds. Once it obtains the name of the tool last used, it opens the resource in the preferences file that has been previously saved with the tool’s name. Because the configuration string is saved with the tool’s name, a preference can be stored and retrieved separately for each tool used.

WritePrefs() is fairly long, mostly because it includes so much error checking and resource housekeeping. It is not complicated. The first thing it does is return the error kNoConnectionErr if there is no current connection. If there is a connection, after getting the name of the currently used tool from CMGetToolName() (which it supplies the ID it got from the connection record) it gets the connection tool’s configuration string using CMGetConfig(). Because the tool in use must have a name, there is no error checking on CMGetToolName(), but it may be possible for CMGetConfig() to fail, perhaps because there was not enough memory available to allocate the configuration string. If so, it returns kGetConfigErr. Notice that disposal of the connection string is the application’s responsibility (DebugTerm’s), not the CTB’s.

Once the tool name and configuration string are available, they can be written to the preferences file. This is done by updating the resources accessed by ReadPrefs(). WritePrefs(), however, has to do the business of creating the resources if necessary, which includes a lot of busy work and error checking. Notice the care taken to name the resources it creates by adding tName as the resource name, and to find a unique resource ID above a specified value for the resource containing the tool’s configuration string by calling UniqueID(). Memory that was allocated during this process is also deallocated.

ChooseConnection() is called by OpenDebugTerm() to choose a tool and configure it. It calls CMChoose(), providing the connection handle and a Point indicating the upper-left-hand corner of the dialog. It sends a nil for the last parameter, because there is no need for an “idle” procedure. It is a good idea to place the dialog near the top-left corner of the screen, because there is no way to know how much space various tools will use. If CMChoose() returns chooseDisaster, a fatal error has occurred, and the connection record has been destroyed. If it returns chooseFailed, the user pressed Cancel. In both cases, ChooseConnection() returns its own error, kNothingChosenErr. If CMChoose() returns chooseOKMinor, the user changed the current tool and pressed OK. If it returns chooseOKMajor, the user has kept the current tool and pressed OK. In both of these situations, ChooseConnection() returns noErr. In the case of an unpredicted error, ChooseConnection() returns that error.

OpenDebugTerm() is called by the client application sometime before DebugTerm() or debugf(). It does all the things necessary for DebugTerm to live in the application’s world. The first thing it does is check to see whether the global handle gConn points to an existing connection record. If it does, DebugTerm is already open, and OpenDebugTerm() returns kAlreadyOpenErr. Otherwise, it calls InitCTB(), if the client application has requested, and returns any error encountered. After that, it calls ReadPrefs() to get the ID of the saved or default tool, and its saved or default configuration information. If ReadPrefs() returned kToolNotAvailErr because it was unable to select the tool indicated in preferences (or there were no preferences) then a flag is set so that the Connection Settings dialog can be posed later. If the tool ID is -1, then no connection tool was found, and kNoConnToolErr is returned.

Now CMNew() can be called to create a connection record. Because only a basic connection is required, the minimal information is passed along in addition to the tool ID. The sizes array gets filled with zeros, indicating that the connection tool is expected to choose a size for its own buffers and maintain them itself. The cmData constant is passed, indicating that only a data channel is necessary-some tools support multiple channels. No refCon or userData value is passed. If the call to CMNew() fails, gConn is nil, and kCMNewErr is returned. Otherwise, CMSetConfig() is called to set the configuration preferences to the values retrieved, and the handle to the connection record is moved as high in memory as the Memory Manager can get it before it is locked. In either case, the configuration record’s handle is deallocated afterward.

Originally, I wondered why Surfer.p locked the connection record. I have assumed that it was being locked so it could be accessed during interrupt time, when unlocked handles are invalid. It is probably a good idea to lock that handle, which is what DebugTerm does.

Now that a new connection record has been successfully created and configured, it is a good time to allow the user to choose a different tool or reconfigure the current one. If the prompt parameter was set to true or a new tool was defaulted to because the tool in preferences was not available, or the preferences did not exist, the Connection Settings dialog is posed. Following that, the preferences and tool choice that have been agreed on are written to the preferences file, which is created if it does not yet exist.

Only now is a connection actually opened, if no error has occurred so far. The connection handle is provided as a parameter, along with the caller’s choice of whether the attempt should be asynchronous, the address to the completion routine, and -1 to indicate that the tool is allowed to take as long as it needs to establish the connection.

If there is an error in CMOpen(), ChooseConnection() or WritePrefs(), it is finally checked at this point. If a connection record was established, it is deallocated and the handle is marked nil before the error is returned. Otherwise, the global gAsynchronousCalls is set so other functions can reference it, and another global variable, gFlags, is set to false. Other Connection Manager calls will pass gFlags through to the tool as necessary to indicate that we do not wish to send end-of-message indicators.

CloseDebugTerm() is called when the client application is done using DebugTerm. It closes the connection, if any, and deallocates the connection record. Of course, it first verifies that there is a connection record before it attempts to deallocate it, and if so it verifies that it is open or opening before attempting to close it.

DebugTerm() makes the simplest possible call to illustrate the debugging terminal concept. It looks at the length byte of the parameter, and uses it to tell CMSendProc() how long the output will be. It also passes along a pointer which points to the first character to be sent, one position past the length byte.

debugf() is the function around which the project is centered. Its purpose is to send formatted output through the current connection. The mechanism by which this works is similar to printf() and minprintf(), found in K&R (second edition), page 156.

debugf() begins life knowing only the address of the first parameter, fmt, it has received. After verifying that a connection exists, it calls va_start() to fill the list ap with pointers to arguments, starting at fmt. Having built the list, it begins iterating through the characters in fmt. Until it reaches a ‘%’, it simply responds by sending fmt’s current character.

When a ‘%’ is encountered, the next job is to act on the specifier that precedes it. The switch statement channels the logic so that Pascal strings and C strings are correctly passed to CMSendProc(). Integer values, whether short, long or int, are converted to strings and passed through. If the specifier is not in the switch statement, the character itself is passed. This has the effect of passing “%” through the connection when “%%” is found in the string, and otherwise ignoring bad specifiers. Bad specifiers are a really bad thing anyway, because a parameter fails to get handled when expected, but it is okay because if you screw up your debugging code, you get to be the victim- since you are the user. va_end() is called last, and if there is an error, you get to hear about that too.


Boilerplate definitions for the resources used in TestDebugTerm can be found here, including the various items for the Test menu. By the way, if you hate having to calculate the enableFlags value for rez (you know, that “0x7FFFF819” thing) then check out the HexFlags desk accessory. It may save you some grief.

Test Spin

It is time to see how DebugTerm responds to your requests. There are various ways to run TestDebugTerm, but you will probably want to think about a few things before you blaze ahead.

Means of Connection

DebugTerm can be used in many ways. If you choose the Apple Modem Tool, you can connect via the modem-probably not very practical for debugging, but if you want to, have fun. The same thing goes for any other type of connection not listed here. I am interested in hearing about your experiences.

Using the Serial Tool you can connect to another Mac, PC, Newton, or device. You can even wire a connection between the modem port and serial port on the same Macintosh. To connect by serial port requires a null modem cable. This is the cable the Macintosh uses to print to most printers and to communicate with the Newton by serial port. It is up to you to get the connection wired right, but a hint here: if it works with a terminal program, it should work with DebugTerm. I have tested the Serial Tool from Mac-to-Mac, from one Mac’s modem port to its own printer port, and from a Mac-to-Newton. None of these options has been problematic.

If you choose the AppleTalk ADSP Tool, you can connect between two Macs on a network, or from one Mac to itself. To connect a Mac to itself with the AppleTalk ADSP tool, no physical connection is necessary, but make sure AppleTalk is turned on in the Chooser, or you will get nowhere fast.

Connecting to Yourself

If you choose to connect the Macintosh to itself, either by using AppleTalk ADSP for a virtual hookup, or Serial Tool from modem to printer port, you will need to keep one thing in mind. To connect a Macintosh to itself, you must use asynchronous calls. Many readers, who do not have two machines or other devices handy, will probably take advantage of the feature.

“Come here Watson, I need you”

The following assumes you are using ClarisWorks to monitor the connection, but you can use anything you have available to you, such as FreeTerm. You can use a Newton or Macintosh with a terminal program, and whatever cable necessary. ClarisWorks will not care whether DebugTerm is on the same machine or another. If you do not have the Communications Toolbox installed or do not have the connection tool you need, install and/or get them. We do not have a license to distribute them, but they come with System 7 and various products such as ClarisWorks. They may also be on your copy of ETO or the CTB SDK.

If you are using the ClarisWorks terminal emulator for the first time, it will choose default tools and preferences for you. Select the Connection... item from the Settings menu. You should see a Connection Settings dialog similar to figure 2. Next, use the Method: popup menu to choose the AppleTalk ADSP Tool or Serial Tool, according to the method you have chosen. Next you will see one of the following dialogs:

Figure 3. Connection Settings Dialog, with the AppleTalk ADSP Tool’s options

Figure 4. Connection Settings Dialog,
with the Serial Tool’s options

If you chose the serial tool, you will probably want to use the options shown above, selecting the appropriate serial port. You will not want to change anything for the AppleTalk ADSP Tool. Having chosen the tool you expect to use and set sensible communications parameters if using the Serial Tool, dismiss the dialog by pressing OK.

If you are using the Serial Tool, choose Open Connection from the Settings menu. ClarisWorks will indicate that the connection is established. As far as it is concerned, a connection is established-though you are only connected to your serial cable.

If you are using the AppleTalk ADSP tool, select Wait for Connection from the Session menu. ClarisWorks patiently waits for a connection to be opened from remote. Now that ClarisWorks is listening, it is time to fire up TestDebugTerm.

If you were curious and you have already run TestDebugTerm, it would be a good idea to throw away the file called “DebugTerm Prefs” right now. You can find it in the same folder as TestDebugTerm. Launch TestDebugTerm. If you are connecting to a terminal program on the same machine, make sure Asynchronous Calls on the Test menu is checked. Select Open DebugTerm from the Test menu. Because DebugTerm has not been run, and you left Prompt for Tool on the Test menu checked, it will pose the Connection Settings dialog. Choose the same tool you chose from ClarisWorks. If you are using the Serial Tool, make sure you select the correct port and that the parameters match those you selected in ClarisWorks, then press OK.

If you are using the AppleTalk ADSP Tool, you should see the name of at least one remote process in the scrollable list on the right-hand side. If you did not change anything, the name will correspond to the machine you are trying to connect to. Select that list item, and press OK. ClarisWorks now notices that you have established a connection.

Now that the two programs are connected, various tests can be conducted. Select Simple DebugTerm from the Test menu. ClarisWorks responds with “Hello World!” followed by a line feed and a carriage return. The menu item initiated the following call:

DebugTerm(“\pHello, World!\n\r”);

Select Sluggish Method. The output in ClarisWorks may or may not come through sluggishly:

Elapsed Time: 5 minutes and 3 seconds.

This resulted from the call:

debugf(“Elapsed time: %s minutes and %i seconds.\n\r”, 5, 3);

Go ahead and try the Faster Method, Imaginative Use and Absurd Use items. They produce the results you should expect:

Figure 5. Sample ClarisWorks output from TestDebugTerm

If you try the Stress Flow Control item, you may find it does not pass all 500 lines through. This is probably because you did not select handshake in the serial tool. Go back and choose either XON/XOFF or DTR & CTS on each end, and try again. If you still have problems with DTR & CTS, perhaps you do not have a proper hardware handshaking cable (XON/XOFF does not require a handshaking cable). Choose Close DebugTerm from the Test menu and watch ClarisWorks notice that the connection has been terminated.

Reopen the connection if you like, and play with the parameters to see how it handles. Build the program from your development system with some of your favorite printf()-style statements.

Some Notes

“Waiter, there’s a fly in my soup.”

Bugs? What bugs? I do not know of any existing bugs, but....

There are many opportunities to cause spectacular anomalies by passing bad arguments to debugf() or calling functions out of order. One problem could be the “empty” completion routine. Filling in that void is left as an exercise for the reader. There could also be a messy result if the number of parameters to debugf() is not the same as the number of ‘%’ signs. Passing an invalid type specifier to debugf() may generate an opportunity for a coffee break while you restart your Mac.

Possible Enhancements

Perhaps you may wish to add better error handling, but that goes without saying for such source code examples as this one. Obviously, it would be nice to add derived types that debugf() would understand. One interesting diversion might be to support the toolbox Rect structure, for example, by recursively calling debugf() with the various elements. The fragment, placed in the debugf() switch statement, might look like this:

case ‘R’:
case ‘r’:
 r = va_arg(ap, Rect)
 theErr = debugf(“{%s, %s, %s, %s}”,, r.left, r.bottom, r.right);

Certainly this brings other possibilities to mind, but it also makes obvious the limited utility of single-character specifiers; perhaps another project would provide a better parsing algorithm. A keyword, terminated by some token, may be a useful alternative. Another possibility would be to provide a mechanism to query DebugTerm from the terminal program. This would clearly be a serious project unto itself.

Other CTB Topics

Of course, this article has merely scratched the surface. This example does not handle any incoming information, or terminal emulation, or file transfer tools. Later articles may cover some of those topics.


In this article, we have discussed the basics of Apple’s Macintosh Communications Toolbox, including methods for using the Connection Manager. We used a variety of Connection Manager function calls to configure and operate connection tools, in addition to using some general CTB routines.

We implemented DebugTerm, a useful debugging tool, which was a good initial project for the CTB because it provided a simple example of how to program a CTB connection. Because DebugTerm is so simple, it may be desirable in the future to discuss more complex examples that would build on this code base to create a more complete example.

Do not hesitate to send your comments, questions, and criticism regarding this and future articles on the subject.


Following is a list of the most salient publications used to produce DebugTerm.

• Gordon - Gordon, Bob. “A Print Window for Debugging”. MacTutor Magazine 2:10 (October, 1986)

• Inside CTB - Apple Computer, Inc. Inside Macintosh® Communications Toolbox. Addison Wesley, 1991.

• Inside Mac Files - Apple Computer, Inc. Inside Macintosh® Files. Addison Wesley, 1992.

• K&R - Kernighan, Brian W. and Dennis M. Ritchie. The C Programming Language, 2nd Ed. Prentice Hall, 1988

• Surfer - Alex Kazim. “Surfer” (Sample application program with Pascal source code) Essentials•Tools•Objects Vol 13. Apple Computer, 1989.


Community Search:
MacTech Search:

Software Updates via MacUpdate

Skype - Voice-over-internet p...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
Bookends 13.2.6 - Reference management a...
Bookends is a full-featured bibliography/reference and information-management system for students and professionals. Bookends uses the cloud to sync reference libraries on all the Macs you use.... Read more
BusyContacts 1.4.0 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
Chromium 77.0.3865.75 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. Version 77.0.3865.75: A list of changes is available... Read more
DiskCatalogMaker 7.5.5 - Catalog your di...
DiskCatalogMaker is a simple disk management tool which catalogs disks. Simple, light-weight, and fast Finder-like intuitive look and feel Super-fast search algorithm Can compress catalog data for... Read more
Alfred 4.0.4 - Quick launcher for apps a...
Alfred is an award-winning productivity application for OS X. Alfred saves you time when you search for files online or on your Mac. Be more productive with hotkeys, keywords, and file actions at... Read more
A Better Finder Rename 10.45 - File, pho...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more
iFinance 4.5.11 - Comprehensively manage...
iFinance allows you to keep track of your income and spending -- from your lunchbreak coffee to your new car -- in the most convenient and fastest way. Clearly arranged transaction lists of all your... Read more
OmniGraffle Pro 7.11.3 - Create diagrams...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more
BBEdit 12.6.7 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more

Latest Forum Discussions

See All

Five Nights at Freddy's AR: Special...
Five Nights at Freddy's AR: Special Delivery is a terrifying new nightmare from developer Illumix. Last week, FNAF fans were sent into a frenzy by a short teaser for what we now know to be Special Delivery. Those in the comments were quick to... | Read more »
Rush Rally 3's new live events are...
Last week, Rush Rally 3 got updated with live events, and it’s one of the best things to happen to racing games on mobile. Prior to this update, the game already had multiplayer, but live events are more convenient in the sense that it’s somewhat... | Read more »
Why your free-to-play racer sucks
It’s been this way for a while now, but playing Hot Wheels Infinite Loop really highlights a big issue with free-to-play mobile racing games: They suck. It doesn’t matter if you’re trying going for realism, cart racing, or arcade nonsense, they’re... | Read more »
Steam Link Spotlight - The Banner Saga 3
Steam Link Spotlight is a new feature where we take a look at PC games that play exceptionally well using the Steam Link app. Our last entry talked about Terry Cavanaugh’s incredible Dicey Dungeons. Read about how it’s a great mobile experience... | Read more »
PSA: GRIS has some issues
You may or may not have seen that Devolver Digital just released GRIS on the App Store, but we wanted to do a quick public service announcement to say that you might not want to hop on buying it just yet. The puzzle platformer has come to small... | Read more »
Explore the world around you in new matc...
Got a hankering for a fresh-feeling Match-3 puzzle game that offers a unique twist? You might find exactly what you’re looking for with What a Wonderful World, a new spin on the classic mobile genre which merges entertaining puzzles with global... | Read more »
Combo Quest (Games)
Combo Quest 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Combo Quest is an epic, time tap role-playing adventure. In this unique masterpiece, you are a knight on a heroic quest to retrieve... | Read more »
Hero Emblems (Games)
Hero Emblems 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ** 25% OFF for a limited time to celebrate the release ** ** Note for iPhone 6 user: If it doesn't run fullscreen on your device... | Read more »
Puzzle Blitz (Games)
Puzzle Blitz 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Puzzle Blitz is a frantic puzzle solving race against the clock! Solve as many puzzles as you can, before time runs out! You have... | Read more »
Sky Patrol (Games)
Sky Patrol 1.0.1 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.1 (iTunes) Description: 'Strategic Twist On The Classic Shooter Genre' - Indie Game Mag... | Read more »

Price Scanner via

Save $150-$250 on 10.2″ WiFi + Cellular iPads...
Verizon is offering $150-$250 discounts on Apple’s new 10.2″ WiFi + Cellular iPad with service. Buy the iPad itself and save $150. Save $250 on the purchase of an iPad along with an iPhone. The fine... Read more
Apple continues to offer 13″ 2.3GHz Dual-Core...
Apple has Certified Refurbished 2017 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros available starting at $1019. An standard Apple one-year warranty is included with each model, outer cases are new... Read more
Apple restocks 2018 MacBook Airs, Certified R...
Apple has restocked Certified Refurbished 2018 13″ MacBook Airs starting at only $849. Each MacBook features a new outer case, comes with a standard Apple one-year warranty, and is shipped free. The... Read more
Sunday Sale! 2019 27″ 5K 6-Core iMacs for $20...
B&H Photo has the new 2019 27″ 5K 6-Core iMacs on stock today and on sale for up to $250 off Apple’s MSRP. Overnight shipping is free to many locations in the US. These are the same iMacs sold by... Read more
Weekend Sale! 2019 13″ MacBook Airs for $200...
Amazon has new 2019 13″ MacBook Airs on sale for $200 off Apple’s MSRP, with prices starting at $899, each including free shipping. Be sure to select Amazon as the seller during checkout, rather than... Read more
2019 15″ MacBook Pros now on sale for $350-$4...
B&H Photo has Apple’s 2019 15″ 6-Core and 8-Core MacBook Pros on sale today for $350-$400 off MSRP, starting at $2049, with free overnight shipping available to many addresses in the US: – 2019... Read more
Buy one Apple Watch Series 5 at Verizon, get...
Buy one Apple Watch Series 5 at Verizon, and get a second Watch for 50% off. Plus save $10 on your first month of service. The fine print: “Buy Apple Watch, get another up to 50% off on us. Plus $10... Read more
Sprint offers 64GB iPhone 11 for free to new...
Sprint will include the 64GB iPhone 11 for free for new customers with an eligible trade-in in of the iPhone 7 or newer through September 19, 2019. The fine print: “iPhone 11 64GB $0/mo. iPhone 11... Read more
Verizon offers new iPhone 11 models for up to...
Verizon is offering Apple’s new iPhone 11 models for $500 off MSRP to new customers with an eligible trade-in (see list below). Discount is applied via monthly bill credits over 24 months. Verizon is... Read more
AT&T offers free $300 reward card + free...
AT&T Wireless will include a second free 64GB iPhone 11 with the purchase of one eligible iPhone at full price. They will also include a free $300 rewards card. The fine print: “Buy an elig.... Read more

Jobs Board

Student Employment (Blue *Apple* Cafe) Spri...
Student Employment (Blue Apple Cafe) Spring 2019 Penn State University Campus/Location: Penn State Brandywine Campus City: Media, PA Date Announced: 12/20/2018 Date Read more
Best Buy *Apple* Computing Master - Best Bu...
**732359BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 000171-Winchester Road-Store **Job Description:** Read more
*Apple* Mobile Master - Best Buy (United Sta...
**732324BR** **Job Title:** Apple Mobile Master **Job Category:** Store Associates **Location Number:** 000013-Fargo-Store **Job Description:** **What does a Best Read more
Best Buy *Apple* Computing Master - Best Bu...
**732455BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000449-Auburn Hills-Store **Job Description:** **What does a Read more
*Apple* Mobility Pro - Best Buy (United Stat...
**732490BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Location Number:** 000449-Auburn Hills-Store **Job Description:** At Best Buy, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.