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.

Introduction

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

Purpose

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:

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.

DebugTerm.h

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:

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);

TestDebugTerm.h

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

DebugTerm.c

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sasyncCount 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.

TestDebugTerm.r

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:

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 OpenDebugTerm 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:

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.

Conclusion

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.

References

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

PCalc is a full-featured, scriptable scientific calculator with support for hexadecimal, octal, and binary calculations, as well as an RPN mode, programmable functions, and an extensive set of unit... Read more

FileZilla 3.10.2 - Fast and reliable FTP...

FileZilla (ported from Windows) is a fast and reliable FTP client and server with lots of useful features and an intuitive interface.
Version 3.10.2:
Note: Now requires a 64-bit Intel processor.... Read more

The Hit List 1.1.11 - Advanced reminder...

The Hit List manages the daily chaos of your modern life. It's easy to learn - it's as easy as making lists. And it's powerful enough to let you plan, then forget, then act when the time is right.... Read more

Bartender 1.2.32 - Organize your menu ba...

Bartender lets you organize your menu bar apps.
Features:
Lets you tidy your menu bar apps how you want.
See your menu bar apps when you want.
Hide the apps you need to run, but do not need to... Read more

ClamXav 2.7.5 - Free virus checker, base...

ClamXav is a free virus checker for OS X. It uses the tried, tested, and very popular ClamAV open source antivirus engine as a back end.
I hope you like and use ClamXav a lot and that it helps keep... Read more

xScope 4.1.2 - Onscreen graphic measurem...

xScope is powerful set of tools that are ideal for measuring, inspecting, and testing on-screen graphics and layouts. Its tools float above your desktop windows and can be accessed via a toolbar,... Read more

MacFamilyTree 7.3.3 - Create and explore...

MacFamilyTree gives genealogy a facelift: it's modern, interactive, incredibly fast, and easy to use. We're convinced that generations of chroniclers would have loved to trade in their genealogy... Read more

Skype 7.5.0.738 - Voice-over-internet ph...

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

PushPal 3.0 - Mirror Android notificatio...

PushPal is a client for Pushbullet, which automatically shows you all of your phone's notifications right on your computer. This means you can see who's calling or read text messages even if your... Read more

At this point it’s pretty safe to say that no MOBA is going to dethrone Dota 2 and League of Legends anytime soon. After all, if Batman can’t do it, nobody can. However, with a genre as popular and profitable as this one, there’s still room for... | Read more »

Final February Fun at 148Apps
How do you know what apps are worth your time and money? Just look to the review team at 148Apps. We sort through the chaos and find the apps you’re looking for. The ones we love become Editor’s Choice, standing out... | Read more »

GDC 2015 – Does Not Commute is Definitel...

GDC 2015 – Does Not Commute is Definitely a Game You Should Keep an Eye on
Posted by Rob Rich on March 2nd, 2015 [ permalink ]
We were teased about Mediocre Games’ (Smash Hit,
| Read more »

F84 Games & POW! Announce Stan Lee V...

F84 Games has announced that it is working with legendary comic creator Stan Lee and POW! Entertainment to produce Stan Lee’s Hero Command. The game will be a action adventure of heroic proportions.
| Read more »

Setlyst Keeps Your Set Straight So You C...

Setlyst Keeps Your Set Straight So You Can Focus On Rocking Out.
Posted by Jessica Fisher on March 2nd, 2015 [ permalink ]
Universal App - Designed for iPhone and iPad
| Read more »

Space is Vast, So Space Agency Has a Vas...

Space is Vast, So Space Agency Has a Vast New Update!
Posted by Jessica Fisher on March 2nd, 2015 [ permalink ]
Universal App - Designed for iPhone and iPad
| Read more »

Size DOES Matter Review

Size DOES Matter Review
By Campbell Bird on March 2nd, 2015
Our Rating: :: HARD TO BEATUniversal App - Designed for iPhone and iPad
This rhythm game has a unique control scheme and performance system that make it feel like a true... | Read more »

The first ever action 3D card battler Al...

On the other hand, you probably haven’t played an action 3D card battler – until now. Step forward, All Star Legion.
All Star Legion is a 3D QTE-based action RPG card battler, but fear not – the game itself isn’t as convoluted as its description.... | Read more »

Travel Back to the 1980s With the Making...

Headup Games has released a hilarious making of video for its upcoming title, Pixel Heroes: Byte & Magic. The game is a RPG/Roguelike where you control three heroes set to save the township of Pixton from an evil cult called The Sons of Dawn.... | Read more »

Price Scanner via MacPrices.net

Sale! 15-inch 2.2GHz Retina MacBook Pro for $...

Best Buy has the 15″ 2.2GHz Retina MacBook Pro on sale for $1774.99 $1799.99, or $225 off MSRP. Choose free home shipping or free local store pickup (if available). Price valid for online orders... Read more

27-inch 3.5GHz 5K iMac in stock today and on...

B&H Photo has the 27″ 3.5GHz 5K iMac in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP, and it’s the lowest price available for... Read more

Apple Launches Free Web-Based Pages and Other...

Apple’s new Web-only access to iWork productivity apps is a free level of iCloud service available to anyone, including people who don’t own or use Apple devices. The service includes access to Apple... Read more

Survey Reveals Solid State Disk (SSD) Technol...

In a recent SSD technology use survey, Kroll Ontrack, a firm specializing in data recovery, found that while nearly 90 percent of respondents leverage the performance and reliability benefits of SSD... Read more

Save up to $600 with Apple refurbished Mac Pr...

The Apple Store is offering Apple Certified Refurbished Mac Pros for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The... Read more

Apple CEO Tim Cook will deliver the George Washington University’s Commencement address to GWU grads on May 17, at which time he will also be awarded an honorary doctorate of public service from the... Read more

Apple restocks refurbished Mac minis for up t...

The Apple Store has restocked Apple Certified Refurbished 2014 Mac minis, with models available starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free:
- 1.4GHz... Read more

Walmart has the 16GB iPad Air 2 WiFi on sale for $446.99 on their online store for a limited time. Choose free shipping or free local store pickup (if available). Sale price for online orders only,... Read more

Jobs Board

*Apple* Solutions Consultant - Retail Sales...

**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail
Read more

*Apple* Pay Automation Engineer - iOS System...

**Job Summary** At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring passion and dedication to your job
Read more

Sr. Technical Services Consultant, *Apple*...

**Job Summary** Apple Professional Services (APS) has an opening for a senior technical position that contributes to Apple 's efforts for strategic and transactional
Read more

Event Director, *Apple* Retail Marketing -...

…This senior level position is responsible for leading and imagining the Apple Retail Team's global engagement strategy and team. Delivering an overarching brand
Read more

*Apple* Pay - Site Reliability Engineer - Ap...

**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring
Read more

MacTech is a registered trademark of Xplain Corporation. Xplain, "The journal of Apple technology", Apple Expo, Explain It, MacDev, MacDev-1, THINK Reference, NetProfessional, Apple Expo, MacTech Central, MacTech Domains, MacNews, MacForge, and the MacTutorMan are trademarks or service marks of Xplain Corporation. Sprocket is a registered trademark of eSprocket Corporation. Other trademarks and copyrights appearing in this printing or software remain the property of their respective holders. Not responsible for typographical errors.

All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.